summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ability.rb142
-rw-r--r--app/models/application_setting.rb13
-rw-r--r--app/models/ci/application_setting.rb12
-rw-r--r--app/models/ci/build.rb33
-rw-r--r--app/models/ci/commit.rb33
-rw-r--r--app/models/ci/event.rb2
-rw-r--r--app/models/ci/project.rb28
-rw-r--r--app/models/ci/runner.rb2
-rw-r--r--app/models/ci/runner_project.rb2
-rw-r--r--app/models/ci/service.rb2
-rw-r--r--app/models/ci/trigger.rb2
-rw-r--r--app/models/ci/trigger_request.rb2
-rw-r--r--app/models/ci/variable.rb2
-rw-r--r--app/models/ci/web_hook.rb2
-rw-r--r--app/models/commit_status.rb37
-rw-r--r--app/models/concerns/issuable.rb53
-rw-r--r--app/models/concerns/sortable.rb3
-rw-r--r--app/models/concerns/taskable.rb33
-rw-r--r--app/models/event.rb12
-rw-r--r--app/models/generic_commit_status.rb33
-rw-r--r--app/models/global_label.rb17
-rw-r--r--app/models/global_milestone.rb (renamed from app/models/group_milestone.rb)22
-rw-r--r--app/models/group.rb16
-rw-r--r--app/models/group_label.rb9
-rw-r--r--app/models/hooks/project_hook.rb25
-rw-r--r--app/models/hooks/service_hook.rb25
-rw-r--r--app/models/hooks/system_hook.rb25
-rw-r--r--app/models/hooks/web_hook.rb25
-rw-r--r--app/models/label.rb1
-rw-r--r--app/models/lfs_object.rb8
-rw-r--r--app/models/lfs_objects_project.rb8
-rw-r--r--app/models/member.rb38
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/models/merge_request_diff.rb16
-rw-r--r--app/models/namespace.rb1
-rw-r--r--app/models/note.rb58
-rw-r--r--app/models/project.rb59
-rw-r--r--app/models/project_services/ci/hip_chat_service.rb2
-rw-r--r--app/models/project_services/ci/mail_service.rb2
-rw-r--r--app/models/project_services/ci/slack_service.rb2
-rw-r--r--app/models/project_services/drone_ci_service.rb2
-rw-r--r--app/models/project_services/gitlab_ci_service.rb1
-rw-r--r--app/models/project_services/slack_service/note_message.rb31
-rw-r--r--app/models/project_wiki.rb10
-rw-r--r--app/models/release.rb12
-rw-r--r--app/models/repository.rb70
-rw-r--r--app/models/user.rb75
47 files changed, 666 insertions, 350 deletions
diff --git a/app/models/ability.rb b/app/models/ability.rb
index b72178fa126..07f3a56ec7a 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -1,8 +1,8 @@
class Ability
class << self
def allowed(user, subject)
- return not_auth_abilities(user, subject) if user.nil?
- return [] unless user.kind_of?(User)
+ return anonymous_abilities(user, subject) if user.nil?
+ return [] unless user.is_a?(User)
return [] if user.blocked?
case subject.class.name
@@ -15,19 +15,30 @@ class Ability
when "Group" then group_abilities(user, subject)
when "Namespace" then namespace_abilities(user, subject)
when "GroupMember" then group_member_abilities(user, subject)
+ when "ProjectMember" then project_member_abilities(user, subject)
else []
end.concat(global_abilities(user))
end
- # List of possible abilities
- # for non-authenticated user
- def not_auth_abilities(user, subject)
- project = if subject.kind_of?(Project)
+ # List of possible abilities for anonymous user
+ def anonymous_abilities(user, subject)
+ case true
+ when subject.is_a?(PersonalSnippet)
+ anonymous_personal_snippet_abilities(subject)
+ when subject.is_a?(Project) || subject.respond_to?(:project)
+ anonymous_project_abilities(subject)
+ when subject.is_a?(Group) || subject.respond_to?(:group)
+ anonymous_group_abilities(subject)
+ else
+ []
+ end
+ end
+
+ def anonymous_project_abilities(subject)
+ project = if subject.is_a?(Project)
subject
- elsif subject.respond_to?(:project)
- subject.project
else
- nil
+ subject.project
end
if project && project.public?
@@ -47,19 +58,29 @@ class Ability
rules - project_disabled_features_rules(project)
else
- group = if subject.kind_of?(Group)
- subject
- elsif subject.respond_to?(:group)
- subject.group
- else
- nil
- end
+ []
+ end
+ end
- if group && group.public_profile?
- [:read_group]
- else
- []
- end
+ def anonymous_group_abilities(subject)
+ group = if subject.is_a?(Group)
+ subject
+ else
+ subject.group
+ end
+
+ if group && group.public_profile?
+ [:read_group]
+ else
+ []
+ end
+ end
+
+ def anonymous_personal_snippet_abilities(snippet)
+ if snippet.public?
+ [:read_personal_snippet]
+ else
+ []
end
end
@@ -154,6 +175,7 @@ class Ability
:create_merge_request,
:create_wiki,
:manage_builds,
+ :download_build_artifacts,
:push_code
]
end
@@ -230,18 +252,19 @@ class Ability
# Only group masters and group owners can create new projects in group
if group.has_master?(user) || group.has_owner?(user) || user.admin?
- rules.push(*[
+ rules += [
:create_projects,
- ])
+ :admin_milestones
+ ]
end
# Only group owner and administrators can admin group
if group.has_owner?(user) || user.admin?
- rules.push(*[
+ rules += [
:admin_group,
:admin_namespace,
:admin_group_member
- ])
+ ]
end
rules.flatten
@@ -252,16 +275,15 @@ class Ability
# Only namespace owner and administrators can admin it
if namespace.owner == user || user.admin?
- rules.push(*[
+ rules += [
:create_projects,
:admin_namespace
- ])
+ ]
end
rules.flatten
end
-
[:issue, :merge_request].each do |name|
define_method "#{name}_abilities" do |user, subject|
rules = []
@@ -278,7 +300,7 @@ class Ability
end
end
- [:note, :project_snippet, :personal_snippet].each do |name|
+ [:note, :project_snippet].each do |name|
define_method "#{name}_abilities" do |user, subject|
rules = []
@@ -298,19 +320,61 @@ class Ability
end
end
+ def personal_snippet_abilities(user, snippet)
+ rules = []
+
+ if snippet.author == user
+ rules += [
+ :read_personal_snippet,
+ :update_personal_snippet,
+ :admin_personal_snippet
+ ]
+ end
+
+ if snippet.public? || snippet.internal?
+ rules << :read_personal_snippet
+ end
+
+ rules
+ end
+
def group_member_abilities(user, subject)
rules = []
target_user = subject.user
group = subject.group
- can_manage = group_abilities(user, group).include?(:admin_group_member)
- if can_manage && (user != target_user)
- rules << :update_group_member
- rules << :destroy_group_member
+ unless group.last_owner?(target_user)
+ can_manage = group_abilities(user, group).include?(:admin_group_member)
+
+ if can_manage && user != target_user
+ rules << :update_group_member
+ rules << :destroy_group_member
+ end
+
+ if user == target_user
+ rules << :destroy_group_member
+ end
end
- if !group.last_owner?(user) && (can_manage || (user == target_user))
- rules << :destroy_group_member
+ rules
+ end
+
+ def project_member_abilities(user, subject)
+ rules = []
+ target_user = subject.user
+ project = subject.project
+
+ unless target_user == project.owner
+ can_manage = project_abilities(user, project).include?(:admin_project_member)
+
+ if can_manage && user != target_user
+ rules << :update_project_member
+ rules << :destroy_project_member
+ end
+
+ if user == target_user
+ rules << :destroy_project_member
+ end
end
rules
@@ -318,10 +382,10 @@ class Ability
def abilities
@abilities ||= begin
- abilities = Six.new
- abilities << self
- abilities
- end
+ abilities = Six.new
+ abilities << self
+ abilities
+ end
end
private
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 266045f7afa..b2d5fe1558f 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -23,6 +23,10 @@
# after_sign_out_path :string(255)
# session_expire_delay :integer default(10080), not null
# import_sources :text
+# help_page_text :text
+# admin_notification_email :string(255)
+# shared_runners_enabled :boolean default(TRUE), not null
+# max_artifacts_size :integer default(100), not null
#
class ApplicationSetting < ActiveRecord::Base
@@ -68,8 +72,14 @@ class ApplicationSetting < ActiveRecord::Base
end
end
+ after_commit do
+ Rails.cache.write('application_setting.last', self)
+ end
+
def self.current
- ApplicationSetting.last
+ Rails.cache.fetch('application_setting.last') do
+ ApplicationSetting.last
+ end
end
def self.create_from_defaults
@@ -89,6 +99,7 @@ class ApplicationSetting < ActiveRecord::Base
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
+ max_artifacts_size: Settings.artifacts['max_size'],
)
end
diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb
index 0cf496f7d81..1307fa0b472 100644
--- a/app/models/ci/application_setting.rb
+++ b/app/models/ci/application_setting.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: application_settings
+# Table name: ci_application_settings
#
# id :integer not null, primary key
# all_broken_builds :boolean
@@ -12,9 +12,15 @@
module Ci
class ApplicationSetting < ActiveRecord::Base
extend Ci::Model
-
+
+ after_commit do
+ Rails.cache.write('ci_application_setting.last', self)
+ end
+
def self.current
- Ci::ApplicationSetting.last
+ Rails.cache.fetch('ci_application_setting.last') do
+ Ci::ApplicationSetting.last
+ end
end
def self.create_from_defaults
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 7f185ae7cc3..52ce1b920fa 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: builds
+# Table name: ci_builds
#
# id :integer not null, primary key
# project_id :integer
@@ -11,16 +11,24 @@
# updated_at :datetime
# started_at :datetime
# runner_id :integer
-# commit_id :integer
# coverage :float
+# commit_id :integer
# commands :text
# job_id :integer
# name :string(255)
+# deploy :boolean default(FALSE)
# options :text
# allow_failure :boolean default(FALSE), not null
# stage :string(255)
-# deploy :boolean default(FALSE)
# trigger_request_id :integer
+# stage_idx :integer
+# tag :boolean
+# ref :string(255)
+# user_id :integer
+# type :string(255)
+# target_url :string(255)
+# description :string(255)
+# artifacts_file :text
#
module Ci
@@ -39,6 +47,8 @@ module Ci
scope :ignore_failures, ->() { where(allow_failure: false) }
scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
+ mount_uploader :artifacts_file, ArtifactUploader
+
acts_as_taggable
# To prevent db load megabytes of data from trace
@@ -87,6 +97,8 @@ module Ci
state_machine :status, initial: :pending do
after_transition any => [:success, :failed, :canceled] do |build, transition|
+ return unless build.gl_project
+
project = build.project
if project.web_hooks?
@@ -217,6 +229,14 @@ module Ci
"#{dir_to_trace}/#{id}.log"
end
+ def token
+ project.token
+ end
+
+ def valid_token? token
+ project.valid_token? token
+ end
+
def target_url
Gitlab::Application.routes.url_helpers.
namespace_project_build_url(gl_project.namespace, gl_project, self)
@@ -248,6 +268,13 @@ module Ci
pending? && !any_runners_online?
end
+ def download_url
+ if artifacts_file.exists?
+ Gitlab::Application.routes.url_helpers.
+ download_namespace_project_build_path(gl_project.namespace, gl_project, self)
+ end
+ end
+
private
def yaml_variables
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index e58420d82d4..971e899de84 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -1,18 +1,19 @@
# == Schema Information
#
-# Table name: commits
+# Table name: ci_commits
#
-# id :integer not null, primary key
-# project_id :integer
-# ref :string(255)
-# sha :string(255)
-# before_sha :string(255)
-# push_data :text
-# created_at :datetime
-# updated_at :datetime
-# tag :boolean default(FALSE)
-# yaml_errors :text
-# committed_at :datetime
+# id :integer not null, primary key
+# project_id :integer
+# ref :string(255)
+# sha :string(255)
+# before_sha :string(255)
+# push_data :text
+# created_at :datetime
+# updated_at :datetime
+# tag :boolean default(FALSE)
+# yaml_errors :text
+# committed_at :datetime
+# gl_project_id :integer
#
module Ci
@@ -187,13 +188,13 @@ module Ci
end
def config_processor
+ return nil unless ci_yaml_file
@config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace)
- rescue Ci::GitlabCiYamlProcessor::ValidationError => e
+ rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
save_yaml_error(e.message)
nil
- rescue Exception => e
- logger.error e.message + "\n" + e.backtrace.join("\n")
- save_yaml_error("Undefined yaml error")
+ rescue
+ save_yaml_error("Undefined error")
nil
end
diff --git a/app/models/ci/event.rb b/app/models/ci/event.rb
index cac3a7a49c1..8c39be42677 100644
--- a/app/models/ci/event.rb
+++ b/app/models/ci/event.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: events
+# Table name: ci_events
#
# id :integer not null, primary key
# project_id :integer
diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb
index 4e806ca1a68..669ee1cc0d2 100644
--- a/app/models/ci/project.rb
+++ b/app/models/ci/project.rb
@@ -1,9 +1,9 @@
# == Schema Information
#
-# Table name: projects
+# Table name: ci_projects
#
# id :integer not null, primary key
-# name :string(255) not null
+# name :string(255)
# timeout :integer default(3600), not null
# created_at :datetime
# updated_at :datetime
@@ -66,30 +66,6 @@ module Ci
class << self
include Ci::CurrentSettings
- def base_build_script
- <<-eos
- git submodule update --init
- ls -la
- eos
- end
-
- def parse(project)
- params = {
- gitlab_id: project.id,
- default_ref: project.default_branch || 'master',
- email_add_pusher: current_application_settings.add_pusher,
- email_only_broken_builds: current_application_settings.all_broken_builds,
- }
-
- project = Ci::Project.new(params)
- project.build_missing_services
- project
- end
-
- def already_added?(project)
- where(gitlab_id: project.id).any?
- end
-
def unassigned(runner)
joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \
"AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}").
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index b719ad3c87e..89710485811 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: runners
+# Table name: ci_runners
#
# id :integer not null, primary key
# token :string(255)
diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb
index 44453ee4b41..3f4fc43873e 100644
--- a/app/models/ci/runner_project.rb
+++ b/app/models/ci/runner_project.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: runner_projects
+# Table name: ci_runner_projects
#
# id :integer not null, primary key
# runner_id :integer not null
diff --git a/app/models/ci/service.rb b/app/models/ci/service.rb
index ed5e3f940b6..8063c51e82b 100644
--- a/app/models/ci/service.rb
+++ b/app/models/ci/service.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: services
+# Table name: ci_services
#
# id :integer not null, primary key
# type :string(255)
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index fe224b7dc70..b73c35d5ae5 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: triggers
+# Table name: ci_triggers
#
# id :integer not null, primary key
# token :string(255)
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index 29cd9553394..9973d2e5ade 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: trigger_requests
+# Table name: ci_trigger_requests
#
# id :integer not null, primary key
# trigger_id :integer not null
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 7a542802fa6..b3d2b809e03 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: variables
+# Table name: ci_variables
#
# id :integer not null, primary key
# project_id :integer not null
diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb
index 8f03b0625da..7ca16a1bde8 100644
--- a/app/models/ci/web_hook.rb
+++ b/app/models/ci/web_hook.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: web_hooks
+# Table name: ci_web_hooks
#
# id :integer not null, primary key
# url :string(255) not null
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 7d54d83974a..e70f4d37184 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+# id :integer not null, primary key
+# project_id :integer
+# status :string(255)
+# finished_at :datetime
+# trace :text
+# created_at :datetime
+# updated_at :datetime
+# started_at :datetime
+# runner_id :integer
+# coverage :float
+# commit_id :integer
+# commands :text
+# job_id :integer
+# name :string(255)
+# deploy :boolean default(FALSE)
+# options :text
+# allow_failure :boolean default(FALSE), not null
+# stage :string(255)
+# trigger_request_id :integer
+# stage_idx :integer
+# tag :boolean
+# ref :string(255)
+# user_id :integer
+# type :string(255)
+# target_url :string(255)
+# description :string(255)
+# artifacts_file :text
+#
+
class CommitStatus < ActiveRecord::Base
self.table_name = 'ci_builds'
@@ -92,4 +125,8 @@ class CommitStatus < ActiveRecord::Base
def show_warning?
false
end
+
+ def download_url
+ nil
+ end
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 5e964f04ef5..68138688aab 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -24,7 +24,7 @@ module Issuable
scope :authored, ->(user) { where(author_id: user) }
scope :assigned_to, ->(u) { where(assignee_id: u.id)}
- scope :recent, -> { order("created_at DESC") }
+ scope :recent, -> { reorder(id: :desc) }
scope :assigned, -> { where("assignee_id IS NOT NULL") }
scope :unassigned, -> { where("assignee_id IS NULL") }
scope :of_projects, ->(ids) { where(project_id: ids) }
@@ -35,6 +35,9 @@ module Issuable
scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') }
scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') }
+ scope :join_project, -> { joins(:project) }
+ scope :references_project, -> { references(:project) }
+
delegate :name,
:email,
to: :author,
@@ -89,39 +92,14 @@ module Issuable
opened? || reopened?
end
- #
- # Votes
- #
-
- # Return the number of -1 comments (downvotes)
+ # Deprecated. Still exists to preserve API compatibility.
def downvotes
- filter_superceded_votes(notes.select(&:downvote?), notes).size
+ 0
end
- def downvotes_in_percent
- if votes_count.zero?
- 0
- else
- 100.0 - upvotes_in_percent
- end
- end
-
- # Return the number of +1 comments (upvotes)
+ # Deprecated. Still exists to preserve API compatibility.
def upvotes
- filter_superceded_votes(notes.select(&:upvote?), notes).size
- end
-
- def upvotes_in_percent
- if votes_count.zero?
- 0
- else
- 100.0 / votes_count * upvotes
- end
- end
-
- # Return the total number of votes
- def votes_count
- upvotes + downvotes
+ 0
end
def subscribed?(user)
@@ -184,17 +162,8 @@ module Issuable
notes.includes(:author, :project)
end
- private
-
- def filter_superceded_votes(votes, notes)
- filteredvotes = [] + votes
-
- votes.each do |vote|
- if vote.superceded?(notes)
- filteredvotes.delete(vote)
- end
- end
-
- filteredvotes
+ def updated_tasks
+ Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
+ new_content: description)
end
end
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index 913c747a1c3..7391a77383c 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -8,8 +8,9 @@ module Sortable
included do
# By default all models should be ordered
# by created_at field starting from newest
- default_scope { order(id: :desc) }
+ default_scope { order_id_desc }
+ scope :order_id_desc, -> { reorder(id: :desc) }
scope :order_created_desc, -> { reorder(created_at: :desc) }
scope :order_created_asc, -> { reorder(created_at: :asc) }
scope :order_updated_desc, -> { reorder(updated_at: :desc) }
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index 660e58b876d..df2a9e3e84b 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -7,14 +7,39 @@ require 'task_list/filter'
#
# Used by MergeRequest and Issue
module Taskable
+ COMPLETED = 'completed'.freeze
+ INCOMPLETE = 'incomplete'.freeze
+ ITEM_PATTERN = /
+ ^
+ (?:\s*[-+*]|(?:\d+\.))? # optional list prefix
+ \s* # optional whitespace prefix
+ (\[\s\]|\[[xX]\]) # checkbox
+ (\s.+) # followed by whitespace and some text.
+ /x
+
+ def self.get_tasks(content)
+ content.to_s.scan(ITEM_PATTERN).map do |checkbox, label|
+ # ITEM_PATTERN strips out the hyphen, but Item requires it. Rabble rabble.
+ TaskList::Item.new("- #{checkbox}", label.strip)
+ end
+ end
+
+ def self.get_updated_tasks(old_content:, new_content:)
+ old_tasks, new_tasks = get_tasks(old_content), get_tasks(new_content)
+
+ new_tasks.select.with_index do |new_task, i|
+ old_task = old_tasks[i]
+ next unless old_task
+
+ new_task.source == old_task.source && new_task.complete? != old_task.complete?
+ end
+ end
+
# Called by `TaskList::Summary`
def task_list_items
return [] if description.blank?
- @task_list_items ||= description.scan(TaskList::Filter::ItemPattern).collect do |item|
- # ItemPattern strips out the hyphen, but Item requires it. Rabble rabble.
- TaskList::Item.new("- #{item}")
- end
+ @task_list_items ||= Taskable.get_tasks(description)
end
def tasks
diff --git a/app/models/event.rb b/app/models/event.rb
index 47600c57e35..9afd223bce5 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -45,7 +45,7 @@ class Event < ActiveRecord::Base
after_create :reset_project_activity
# Scopes
- scope :recent, -> { order(created_at: :desc) }
+ scope :recent, -> { reorder(id: :desc) }
scope :code_push, -> { where(action: PUSHED) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
scope :with_associations, -> { includes(project: :namespace) }
@@ -63,6 +63,16 @@ class Event < ActiveRecord::Base
Event::PUSHED, ["MergeRequest", "Issue"],
[Event::CREATED, Event::CLOSED, Event::MERGED])
end
+
+ def latest_update_time
+ row = select(:updated_at, :project_id).reorder(id: :desc).take
+
+ row ? row.updated_at : nil
+ end
+
+ def limit_recent(limit = 20, offset = nil)
+ recent.limit(limit).offset(offset)
+ end
end
def proper?
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
index fa54e3540d0..12c934e2494 100644
--- a/app/models/generic_commit_status.rb
+++ b/app/models/generic_commit_status.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+# id :integer not null, primary key
+# project_id :integer
+# status :string(255)
+# finished_at :datetime
+# trace :text
+# created_at :datetime
+# updated_at :datetime
+# started_at :datetime
+# runner_id :integer
+# coverage :float
+# commit_id :integer
+# commands :text
+# job_id :integer
+# name :string(255)
+# deploy :boolean default(FALSE)
+# options :text
+# allow_failure :boolean default(FALSE), not null
+# stage :string(255)
+# trigger_request_id :integer
+# stage_idx :integer
+# tag :boolean
+# ref :string(255)
+# user_id :integer
+# type :string(255)
+# target_url :string(255)
+# description :string(255)
+# artifacts_file :text
+#
+
class GenericCommitStatus < CommitStatus
before_validation :set_default_values
diff --git a/app/models/global_label.rb b/app/models/global_label.rb
new file mode 100644
index 00000000000..0171f7d54b7
--- /dev/null
+++ b/app/models/global_label.rb
@@ -0,0 +1,17 @@
+class GlobalLabel
+ attr_accessor :title, :labels
+ alias_attribute :name, :title
+
+ def self.build_collection(labels)
+ labels = labels.group_by(&:title)
+
+ labels.map do |title, label|
+ new(title, label)
+ end
+ end
+
+ def initialize(title, labels)
+ @title = title
+ @labels = labels
+ end
+end
diff --git a/app/models/group_milestone.rb b/app/models/global_milestone.rb
index 91844da62e2..1321ccd963f 100644
--- a/app/models/group_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -1,7 +1,15 @@
-class GroupMilestone
+class GlobalMilestone
attr_accessor :title, :milestones
alias_attribute :name, :title
+ def self.build_collection(milestones)
+ milestones = milestones.group_by(&:title)
+
+ milestones.map do |title, milestones|
+ new(title, milestones)
+ end
+ end
+
def initialize(title, milestones)
@title = title
@milestones = milestones
@@ -10,7 +18,7 @@ class GroupMilestone
def safe_title
@title.parameterize
end
-
+
def projects
milestones.map { |milestone| milestone.project }
end
@@ -60,15 +68,15 @@ class GroupMilestone
end
def issues
- @group_issues ||= milestones.map(&:issues).flatten.group_by(&:state)
+ @issues ||= milestones.map(&:issues).flatten.group_by(&:state)
end
def merge_requests
- @group_merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state)
+ @merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state)
end
def participants
- @group_participants ||= milestones.map(&:participants).flatten.compact.uniq
+ @participants ||= milestones.map(&:participants).flatten.compact.uniq
end
def opened_issues
@@ -86,4 +94,8 @@ class GroupMilestone
def closed_merge_requests
merge_requests.values_at("closed", "merged", "locked").compact.flatten
end
+
+ def complete?
+ total_items_count == closed_items_count
+ end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 34904af3b5b..1b5b875a19e 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -11,6 +11,7 @@
# type :string(255)
# description :string(255) default(""), not null
# avatar :string(255)
+# public :boolean default(FALSE)
#
require 'carrierwave/orm/activerecord'
@@ -19,8 +20,9 @@ require 'file_size_validator'
class Group < Namespace
include Gitlab::ConfigHelper
include Referable
-
+
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
+ alias_method :members, :group_members
has_many :users, through: :group_members
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
@@ -47,6 +49,14 @@ class Group < Namespace
def reference_pattern
User.reference_pattern
end
+
+ def public_and_given_groups(ids)
+ where('public IS TRUE OR namespaces.id IN (?)', ids)
+ end
+
+ def visible_to_user(user)
+ where(id: user.authorized_groups.select(:id).reorder(nil))
+ end
end
def to_reference(_from_project = nil)
@@ -109,10 +119,6 @@ class Group < Namespace
has_owner?(user) && owners.size == 1
end
- def members
- group_members
- end
-
def avatar_type
unless self.avatar.image?
self.errors.add :avatar, "only images allowed"
diff --git a/app/models/group_label.rb b/app/models/group_label.rb
deleted file mode 100644
index 0fc39cb8771..00000000000
--- a/app/models/group_label.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class GroupLabel
- attr_accessor :title, :labels
- alias_attribute :name, :title
-
- def initialize(title, labels)
- @title = title
- @labels = labels
- end
-end
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index ca7066b959a..337b3097126 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -2,18 +2,19 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# type :string(255) default("ProjectHook")
-# service_id :integer
-# push_events :boolean default(TRUE), not null
-# issues_events :boolean default(FALSE), not null
-# merge_requests_events :boolean default(FALSE), not null
-# tag_push_events :boolean default(FALSE)
-# note_events :boolean default(FALSE), not null
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
+# tag_push_events :boolean default(FALSE)
+# note_events :boolean default(FALSE), not null
+# enable_ssl_verification :boolean default(TRUE)
#
class ProjectHook < WebHook
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index b55e217975f..09bb3ee52a2 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -2,18 +2,19 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# type :string(255) default("ProjectHook")
-# service_id :integer
-# push_events :boolean default(TRUE), not null
-# issues_events :boolean default(FALSE), not null
-# merge_requests_events :boolean default(FALSE), not null
-# tag_push_events :boolean default(FALSE)
-# note_events :boolean default(FALSE), not null
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
+# tag_push_events :boolean default(FALSE)
+# note_events :boolean default(FALSE), not null
+# enable_ssl_verification :boolean default(TRUE)
#
class ServiceHook < WebHook
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index 6fb2d421026..2f63c59b07e 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -2,18 +2,19 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# type :string(255) default("ProjectHook")
-# service_id :integer
-# push_events :boolean default(TRUE), not null
-# issues_events :boolean default(FALSE), not null
-# merge_requests_events :boolean default(FALSE), not null
-# tag_push_events :boolean default(FALSE)
-# note_events :boolean default(FALSE), not null
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
+# tag_push_events :boolean default(FALSE)
+# note_events :boolean default(FALSE), not null
+# enable_ssl_verification :boolean default(TRUE)
#
class SystemHook < WebHook
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index a078accbdbd..d6c6f415c4a 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -2,18 +2,19 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# type :string(255) default("ProjectHook")
-# service_id :integer
-# push_events :boolean default(TRUE), not null
-# issues_events :boolean default(FALSE), not null
-# merge_requests_events :boolean default(FALSE), not null
-# tag_push_events :boolean default(FALSE)
-# note_events :boolean default(FALSE), not null
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
+# tag_push_events :boolean default(FALSE)
+# note_events :boolean default(FALSE), not null
+# enable_ssl_verification :boolean default(TRUE)
#
class WebHook < ActiveRecord::Base
diff --git a/app/models/label.rb b/app/models/label.rb
index 1bb4b5f55cf..b306aecbac1 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -8,6 +8,7 @@
# project_id :integer
# created_at :datetime
# updated_at :datetime
+# template :boolean default(FALSE)
#
class Label < ActiveRecord::Base
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
new file mode 100644
index 00000000000..3c1426f59d0
--- /dev/null
+++ b/app/models/lfs_object.rb
@@ -0,0 +1,8 @@
+class LfsObject < ActiveRecord::Base
+ has_many :lfs_objects_projects, dependent: :destroy
+ has_many :projects, through: :lfs_objects_projects
+
+ validates :oid, presence: true, uniqueness: true
+
+ mount_uploader :file, LfsObjectUploader
+end
diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb
new file mode 100644
index 00000000000..0fd5f089db9
--- /dev/null
+++ b/app/models/lfs_objects_project.rb
@@ -0,0 +1,8 @@
+class LfsObjectsProject < ActiveRecord::Base
+ belongs_to :project
+ belongs_to :lfs_object
+
+ validates :lfs_object_id, presence: true
+ validates :lfs_object_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
+ validates :project_id, presence: true
+end
diff --git a/app/models/member.rb b/app/models/member.rb
index cae8caa23fb..28aee2e3799 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -30,13 +30,22 @@ class Member < ActiveRecord::Base
validates :user, presence: true, unless: :invite?
validates :source, presence: true
- validates :user_id, uniqueness: { scope: [:source_type, :source_id],
+ validates :user_id, uniqueness: { scope: [:source_type, :source_id],
message: "already exists in source",
allow_nil: true }
validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
- validates :invite_email, presence: { if: :invite? },
- email: { strict_mode: true, allow_nil: true },
- uniqueness: { scope: [:source_type, :source_id], allow_nil: true }
+ validates :invite_email,
+ presence: {
+ if: :invite?
+ },
+ email: {
+ strict_mode: true,
+ allow_nil: true
+ },
+ uniqueness: {
+ scope: [:source_type, :source_id],
+ allow_nil: true
+ }
scope :invite, -> { where(user_id: nil) }
scope :non_invite, -> { where("user_id IS NOT NULL") }
@@ -73,7 +82,7 @@ class Member < ActiveRecord::Base
def add_user(members, user_id, access_level, current_user = nil)
user = user_for_id(user_id)
-
+
# `user` can be either a User object or an email to be invited
if user.is_a?(User)
member = members.find_or_initialize_by(user_id: user.id)
@@ -82,10 +91,21 @@ class Member < ActiveRecord::Base
member.invite_email = user
end
- member.created_by ||= current_user
- member.access_level = access_level
+ if can_update_member?(current_user, member)
+ member.created_by ||= current_user
+ member.access_level = access_level
+
+ member.save
+ end
+ end
+
+ private
- member.save
+ def can_update_member?(current_user, member)
+ # There is no current user for bulk actions, in which case anything is allowed
+ !current_user ||
+ current_user.can?(:update_group_member, member) ||
+ current_user.can?(:update_project_member, member)
end
end
@@ -95,7 +115,7 @@ class Member < ActiveRecord::Base
def accept_invite!(new_user)
return false unless invite?
-
+
self.invite_token = nil
self.invite_accepted_at = Time.now.utc
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 85f37e49e62..1b3d6079d2c 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -20,6 +20,7 @@
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
+# merge_error :string(255)
#
require Rails.root.join("app/models/commit")
@@ -40,7 +41,7 @@ class MergeRequest < ActiveRecord::Base
after_create :create_merge_request_diff
after_update :update_merge_request_diff
- delegate :commits, :diffs, to: :merge_request_diff, prefix: nil
+ delegate :commits, :diffs, :diffs_no_whitespace, to: :merge_request_diff, prefix: nil
# When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests
@@ -133,6 +134,9 @@ class MergeRequest < ActiveRecord::Base
scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
+ scope :join_project, -> { joins(:target_project) }
+ scope :references_project, -> { references(:target_project) }
+
def self.reference_prefix
'!'
end
@@ -472,7 +476,7 @@ class MergeRequest < ActiveRecord::Base
end
def ci_commit
- if last_commit
+ if last_commit and source_project
source_project.ci_commit(last_commit.id)
end
end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 6575d0bc81f..c499a4b5b4c 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -19,7 +19,7 @@ class MergeRequestDiff < ActiveRecord::Base
# Prevent store of diff if commits amount more then 500
COMMITS_SAFE_SIZE = 500
- attr_reader :commits, :diffs
+ attr_reader :commits, :diffs, :diffs_no_whitespace
belongs_to :merge_request
@@ -47,6 +47,20 @@ class MergeRequestDiff < ActiveRecord::Base
@diffs ||= (load_diffs(st_diffs) || [])
end
+ def diffs_no_whitespace
+ # Get latest sha of branch from source project
+ source_sha = merge_request.source_project.commit(source_branch).sha
+
+ compare_result = Gitlab::CompareResult.new(
+ Gitlab::Git::Compare.new(
+ merge_request.target_project.repository.raw_repository,
+ merge_request.target_branch,
+ source_sha,
+ ), { ignore_whitespace_change: true }
+ )
+ @diffs_no_whitespace ||= load_diffs(dump_commits(compare_result.diffs))
+ end
+
def commits
@commits ||= load_commits(st_commits || [])
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 5782e649f8b..20b92e68d61 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -11,6 +11,7 @@
# type :string(255)
# description :string(255) default(""), not null
# avatar :string(255)
+# public :boolean default(FALSE)
#
class Namespace < ActiveRecord::Base
diff --git a/app/models/note.rb b/app/models/note.rb
index 0b3aa30abd7..1c6345e735c 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -40,16 +40,20 @@ class Note < ActiveRecord::Base
delegate :name, :email, to: :author, prefix: true
validates :note, :project, presence: true
+ validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award }
validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true
# Attachments are deprecated and are handled by Markdown uploader
validates :attachment, file_size: { maximum: :max_attachment_size }
validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' }
validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' }
+ validates :author, presence: true
mount_uploader :attachment, AttachmentUploader
# Scopes
+ scope :awards, ->{ where(is_award: true) }
+ scope :nonawards, ->{ where(is_award: false) }
scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) }
scope :inline, ->{ where("line_code IS NOT NULL") }
scope :not_inline, ->{ where(line_code: [nil, '']) }
@@ -97,6 +101,12 @@ class Note < ActiveRecord::Base
def search(query)
where("LOWER(note) like :query", query: "%#{query.downcase}%")
end
+
+ def grouped_awards
+ awards.select(:note).distinct.map do |note|
+ [ note.note, where(note: note.note) ]
+ end
+ end
end
def cross_reference?
@@ -288,44 +298,6 @@ class Note < ActiveRecord::Base
nil
end
- DOWNVOTES = %w(-1 :-1: :thumbsdown: :thumbs_down_sign:)
-
- # Check if the note is a downvote
- def downvote?
- votable? && note.start_with?(*DOWNVOTES)
- end
-
- UPVOTES = %w(+1 :+1: :thumbsup: :thumbs_up_sign:)
-
- # Check if the note is an upvote
- def upvote?
- votable? && note.start_with?(*UPVOTES)
- end
-
- def superceded?(notes)
- return false unless vote?
-
- notes.each do |note|
- next if note == self
-
- if note.vote? &&
- self[:author_id] == note[:author_id] &&
- self[:created_at] <= note[:created_at]
- return true
- end
- end
-
- false
- end
-
- def vote?
- upvote? || downvote?
- end
-
- def votable?
- for_issue? || (for_merge_request? && !for_diff_line?)
- end
-
# Mentionable override.
def gfm_reference(from_project = nil)
noteable.gfm_reference(from_project)
@@ -363,6 +335,16 @@ class Note < ActiveRecord::Base
read_attribute(:system)
end
+ # Deprecated. Still exists to preserve API compatibility.
+ def downvote?
+ false
+ end
+
+ # Deprecated. Still exists to preserve API compatibility.
+ def upvote?
+ false
+ end
+
def editable?
!system?
end
diff --git a/app/models/project.rb b/app/models/project.rb
index bdb22e49bb5..f0a4b6aae7b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -42,7 +42,7 @@ class Project < ActiveRecord::Base
include Sortable
include AfterCommitQueue
include CaseSensitivity
-
+
extend Gitlab::ConfigHelper
extend Enumerize
@@ -52,6 +52,7 @@ class Project < ActiveRecord::Base
default_value_for :visibility_level, gitlab_config_features.visibility_level
default_value_for :issues_enabled, gitlab_config_features.issues
default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
+ default_value_for :builds_enabled, gitlab_config_features.builds
default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :wall_enabled, false
default_value_for :snippets_enabled, gitlab_config_features.snippets
@@ -123,6 +124,8 @@ class Project < ActiveRecord::Base
has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build'
has_many :releases, dependent: :destroy
+ has_many :lfs_objects_projects, dependent: :destroy
+ has_many :lfs_objects, through: :lfs_objects_projects
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id
@@ -283,6 +286,10 @@ class Project < ActiveRecord::Base
joins(join_body).reorder('join_note_counts.amount DESC')
end
+
+ def visible_to_user(user)
+ where(id: user.authorized_projects.select(:id).reorder(nil))
+ end
end
def team
@@ -307,15 +314,17 @@ class Project < ActiveRecord::Base
def add_import_job
if forked?
- unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path)
- import_fail
- end
+ RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path)
else
- RepositoryImportWorker.perform_async(id)
+ RepositoryImportWorker.perform_async(self.id)
end
end
def clear_import_data
+ update(import_error: nil)
+
+ ProjectCacheWorker.perform_async(self.id)
+
self.import_data.destroy if self.import_data
end
@@ -343,6 +352,14 @@ class Project < ActiveRecord::Base
import_status == 'finished'
end
+ def safe_import_url
+ result = URI.parse(self.import_url)
+ result.password = '*****' unless result.password.nil?
+ result.to_s
+ rescue
+ original_url
+ end
+
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")
@@ -457,10 +474,6 @@ class Project < ActiveRecord::Base
list.find { |service| service.to_param == name }
end
- def gitlab_ci?
- gitlab_ci_service && gitlab_ci_service.active && gitlab_ci_project.present?
- end
-
def ci_services
services.select { |service| service.category == :ci }
end
@@ -782,9 +795,33 @@ class Project < ActiveRecord::Base
)
end
- def enable_ci
+ # TODO: this should be migrated to Project table,
+ # the same as issues_enabled
+ def builds_enabled
+ gitlab_ci_service && gitlab_ci_service.active
+ end
+
+ def builds_enabled?
+ builds_enabled
+ end
+
+ def builds_enabled=(value)
service = gitlab_ci_service || create_gitlab_ci_service
- service.active = true
+ service.active = value
service.save
end
+
+ def enable_ci
+ self.builds_enabled = true
+ end
+
+ def unlink_fork
+ if forked?
+ forked_from_project.lfs_objects.find_each do |lfs_object|
+ lfs_object.projects << self
+ end
+
+ forked_project_link.destroy
+ end
+ end
end
diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb
index f17993d9f3b..0df03890efb 100644
--- a/app/models/project_services/ci/hip_chat_service.rb
+++ b/app/models/project_services/ci/hip_chat_service.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: services
+# Table name: ci_services
#
# id :integer not null, primary key
# type :string(255)
diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb
index fd193301001..d31dd6899c1 100644
--- a/app/models/project_services/ci/mail_service.rb
+++ b/app/models/project_services/ci/mail_service.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: services
+# Table name: ci_services
#
# id :integer not null, primary key
# type :string(255)
diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb
index ee8e4988826..7064bfe78db 100644
--- a/app/models/project_services/ci/slack_service.rb
+++ b/app/models/project_services/ci/slack_service.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: services
+# Table name: ci_services
#
# id :integer not null, primary key
# type :string(255)
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index c240213200d..127684bd274 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -32,6 +32,8 @@ class DroneCiService < CiService
def compose_service_hook
hook = service_hook || build_service_hook
+ # If using a service template, project may not be available
+ hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project
hook.enable_ssl_verification = enable_ssl_verification
hook.save
end
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 095d04e0df4..c5657b5070e 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -30,6 +30,7 @@ class GitlabCiService < CiService
end
def ensure_gitlab_ci_project
+ return unless project
project.ensure_gitlab_ci_project
end
diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/slack_service/note_message.rb
index 074478b292d..b15d9a14677 100644
--- a/app/models/project_services/slack_service/note_message.rb
+++ b/app/models/project_services/slack_service/note_message.rb
@@ -45,30 +45,27 @@ class SlackService
def create_commit_note(commit)
commit_sha = commit[:id]
commit_sha = Commit.truncate_sha(commit_sha)
- commit_link = "[commit #{commit_sha}](#{@note_url})"
- title = format_title(commit[:message])
- @message = "#{@user_name} commented on #{commit_link} in #{project_link}: *#{title}*"
+ commented_on_message(
+ "[commit #{commit_sha}](#{@note_url})",
+ format_title(commit[:message]))
end
def create_issue_note(issue)
- issue_iid = issue[:iid]
- note_link = "[issue ##{issue_iid}](#{@note_url})"
- title = format_title(issue[:title])
- @message = "#{@user_name} commented on #{note_link} in #{project_link}: *#{title}*"
+ commented_on_message(
+ "[issue ##{issue[:iid]}](#{@note_url})",
+ format_title(issue[:title]))
end
def create_merge_note(merge_request)
- merge_request_id = merge_request[:iid]
- merge_request_link = "[merge request ##{merge_request_id}](#{@note_url})"
- title = format_title(merge_request[:title])
- @message = "#{@user_name} commented on #{merge_request_link} in #{project_link}: *#{title}*"
+ commented_on_message(
+ "[merge request ##{merge_request[:iid]}](#{@note_url})",
+ format_title(merge_request[:title]))
end
def create_snippet_note(snippet)
- snippet_id = snippet[:id]
- snippet_link = "[snippet ##{snippet_id}](#{@note_url})"
- title = format_title(snippet[:title])
- @message = "#{@user_name} commented on #{snippet_link} in #{project_link}: *#{title}*"
+ commented_on_message(
+ "[snippet ##{snippet[:id]}](#{@note_url})",
+ format_title(snippet[:title]))
end
def description_message
@@ -78,5 +75,9 @@ class SlackService
def project_link
"[#{@project_name}](#{@project_url})"
end
+
+ def commented_on_message(target_link, title)
+ @message = "#{@user_name} commented on #{target_link} in #{project_link}: *#{title}*"
+ end
end
end
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 231973fa543..b5fec38378b 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -86,6 +86,8 @@ class ProjectWiki
commit = commit_details(:created, message, title)
wiki.write_page(title, format, content, commit)
+
+ update_project_activity
rescue Gollum::DuplicatePageError => e
@error_message = "Duplicate page: #{e.message}"
return false
@@ -95,10 +97,14 @@ class ProjectWiki
commit = commit_details(:updated, message, page.title)
wiki.update_page(page, page.name, format, content, commit)
+
+ update_project_activity
end
def delete_page(page, message = nil)
wiki.delete_page(page, commit_details(:deleted, message, page.title))
+
+ update_project_activity
end
def page_title_and_dir(title)
@@ -146,4 +152,8 @@ class ProjectWiki
def path_to_repo
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
end
+
+ def update_project_activity
+ @project.touch(:last_activity_at)
+ end
end
diff --git a/app/models/release.rb b/app/models/release.rb
index e196b84eb18..89f70278af5 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: releases
+#
+# id :integer not null, primary key
+# tag :string(255)
+# description :text
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+#
+
class Release < ActiveRecord::Base
belongs_to :project
diff --git a/app/models/repository.rb b/app/models/repository.rb
index f8c4cb1387b..c1836103463 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -6,7 +6,7 @@ class Repository
include Gitlab::ShellAdapter
- attr_accessor :raw_repository, :path_with_namespace, :project
+ attr_accessor :path_with_namespace, :project
def self.clean_old_archives
repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path
@@ -19,14 +19,18 @@ class Repository
def initialize(path_with_namespace, default_branch = nil, project = nil)
@path_with_namespace = path_with_namespace
@project = project
+ end
- if path_with_namespace
- @raw_repository = Gitlab::Git::Repository.new(path_to_repo)
- @raw_repository.autocrlf = :input
- end
+ def raw_repository
+ return nil unless path_with_namespace
- rescue Gitlab::Git::Repository::NoRepository
- nil
+ @raw_repository ||= begin
+ repo = Gitlab::Git::Repository.new(path_to_repo)
+ repo.autocrlf = :input
+ repo
+ rescue Gitlab::Git::Repository::NoRepository
+ nil
+ end
end
# Return absolute path to repository
@@ -105,29 +109,25 @@ class Repository
end
def add_branch(branch_name, ref)
- cache.expire(:branch_names)
- @branches = nil
+ expire_branches_cache
gitlab_shell.add_branch(path_with_namespace, branch_name, ref)
end
def add_tag(tag_name, ref, message = nil)
- cache.expire(:tag_names)
- @tags = nil
+ expire_tags_cache
gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message)
end
def rm_branch(branch_name)
- cache.expire(:branch_names)
- @branches = nil
+ expire_branches_cache
gitlab_shell.rm_branch(path_with_namespace, branch_name)
end
def rm_tag(tag_name)
- cache.expire(:tag_names)
- @tags = nil
+ expire_tags_cache
gitlab_shell.rm_tag(path_with_namespace, tag_name)
end
@@ -169,6 +169,16 @@ class Repository
end
end
+ def expire_tags_cache
+ cache.expire(:tag_names)
+ @tags = nil
+ end
+
+ def expire_branches_cache
+ cache.expire(:branch_names)
+ @branches = nil
+ end
+
def expire_cache
cache_keys.each do |key|
cache.expire(key)
@@ -346,8 +356,8 @@ class Repository
end
end
- def branch_names_contains(sha)
- args = %W(#{Gitlab.config.git.bin_path} branch --contains #{sha})
+ def refs_contains_sha(ref_type, sha)
+ args = %W(#{Gitlab.config.git.bin_path} #{ref_type} --contains #{sha})
names = Gitlab::Popen.popen(args, path_to_repo).first
if names.respond_to?(:split)
@@ -363,21 +373,12 @@ class Repository
end
end
- def tag_names_contains(sha)
- args = %W(#{Gitlab.config.git.bin_path} tag --contains #{sha})
- names = Gitlab::Popen.popen(args, path_to_repo).first
-
- if names.respond_to?(:split)
- names = names.split("\n").map(&:strip)
-
- names.each do |name|
- name.slice! '* '
- end
+ def branch_names_contains(sha)
+ refs_contains_sha('branch', sha)
+ end
- names
- else
- []
- end
+ def tag_names_contains(sha)
+ refs_contains_sha('tag', sha)
end
def branches
@@ -493,7 +494,7 @@ class Repository
root_ref_commit = commit(root_ref)
if branch_commit
- rugged.merge_base(root_ref_commit.id, branch_commit.id) == branch_commit.id
+ is_ancestor?(branch_commit.id, root_ref_commit.id)
else
nil
end
@@ -503,6 +504,11 @@ class Repository
rugged.merge_base(first_commit_id, second_commit_id)
end
+ def is_ancestor?(ancestor_id, descendant_id)
+ merge_base(ancestor_id, descendant_id) == ancestor_id
+ end
+
+
def search_files(query, ref)
offset = 2
args = %W(#{Gitlab.config.git.bin_path} grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref})
diff --git a/app/models/user.rb b/app/models/user.rb
index 67fef1c1e6a..9374f01f99f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -54,6 +54,7 @@
# public_email :string(255) default(""), not null
# dashboard :integer default(0)
# project_view :integer default(0)
+# consumed_timestep :integer
# layout :integer default(0)
#
@@ -388,33 +389,23 @@ class User < ActiveRecord::Base
end
end
- # Groups user has access to
+ # Returns the groups a user has access to
def authorized_groups
- @authorized_groups ||= begin
- group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
- Group.where(id: group_ids)
- end
- end
+ union = Gitlab::SQL::Union.
+ new([groups.select(:id), authorized_projects.select(:namespace_id)])
- def authorized_projects_id
- @authorized_projects_id ||= begin
- project_ids = personal_projects.pluck(:id)
- project_ids.push(*groups_projects.pluck(:id))
- project_ids.push(*projects.pluck(:id).uniq)
- end
+ Group.where("namespaces.id IN (#{union.to_sql})")
end
- # Projects user has access to
+ # Returns the groups a user is authorized to access.
def authorized_projects
- @authorized_projects ||= Project.where(id: authorized_projects_id)
+ Project.where("projects.id IN (#{projects_union.to_sql})")
end
def owned_projects
@owned_projects ||=
- begin
- namespace_ids = owned_groups.pluck(:id).push(namespace.id)
- Project.in_namespace(namespace_ids).joins(:namespace)
- end
+ Project.where('namespace_id IN (?) OR namespace_id = ?',
+ owned_groups.select(:id), namespace.id).joins(:namespace)
end
# Team membership in authorized projects
@@ -729,12 +720,25 @@ class User < ActiveRecord::Base
Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
end
- def contributed_projects_ids
- Event.contributions.where(author_id: self).
+ # Returns the projects a user contributed to in the last year.
+ #
+ # This method relies on a subquery as this performs significantly better
+ # compared to a JOIN when coupled with, for example,
+ # `Project.visible_to_user`. That is, consider the following code:
+ #
+ # some_user.contributed_projects.visible_to_user(other_user)
+ #
+ # If this method were to use a JOIN the resulting query would take roughly 200
+ # ms on a database with a similar size to GitLab.com's database. On the other
+ # hand, using a subquery means we can get the exact same data in about 40 ms.
+ def contributed_projects
+ events = Event.select(:project_id).
+ contributions.where(author_id: self).
where("created_at > ?", Time.now - 1.year).
- reorder(project_id: :desc).
- select(:project_id).
- uniq.map(&:project_id)
+ uniq.
+ reorder(nil)
+
+ Project.where(id: events)
end
def restricted_signup_domains
@@ -764,15 +768,30 @@ class User < ActiveRecord::Base
!solo_owned_groups.present?
end
- def ci_authorized_projects
- @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects_id)
- end
-
def ci_authorized_runners
@ci_authorized_runners ||= begin
runner_ids = Ci::RunnerProject.joins(:project).
- where(ci_projects: { gitlab_id: authorized_projects_id }).select(:runner_id)
+ where("ci_projects.gitlab_id IN (#{ci_projects_union.to_sql})").
+ select(:runner_id)
+
Ci::Runner.specific.where(id: runner_ids)
end
end
+
+ private
+
+ def projects_union
+ Gitlab::SQL::Union.new([personal_projects.select(:id),
+ groups_projects.select(:id),
+ projects.select(:id)])
+ end
+
+ def ci_projects_union
+ scope = { access_level: [Gitlab::Access::MASTER, Gitlab::Access::OWNER] }
+ groups = groups_projects.where(members: scope)
+ other = projects.where(members: scope)
+
+ Gitlab::SQL::Union.new([personal_projects.select(:id), groups.select(:id),
+ other.select(:id)])
+ end
end