summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ability.rb4
-rw-r--r--app/models/abuse_report.rb11
-rw-r--r--app/models/application_setting.rb43
-rw-r--r--app/models/broadcast_message.rb18
-rw-r--r--app/models/ci/build.rb108
-rw-r--r--app/models/ci/runner_project.rb11
-rw-r--r--app/models/ci/trigger.rb17
-rw-r--r--app/models/ci/variable.rb9
-rw-r--r--app/models/commit_status.rb63
-rw-r--r--app/models/concerns/issuable.rb12
-rw-r--r--app/models/concerns/mentionable.rb7
-rw-r--r--app/models/concerns/sortable.rb3
-rw-r--r--app/models/generic_commit_status.rb1
-rw-r--r--app/models/global_milestone.rb4
-rw-r--r--app/models/group.rb9
-rw-r--r--app/models/hooks/project_hook.rb1
-rw-r--r--app/models/hooks/service_hook.rb1
-rw-r--r--app/models/hooks/system_hook.rb1
-rw-r--r--app/models/hooks/web_hook.rb7
-rw-r--r--app/models/identity.rb4
-rw-r--r--app/models/issue.rb8
-rw-r--r--app/models/merge_request.rb62
-rw-r--r--app/models/milestone.rb26
-rw-r--r--app/models/namespace.rb1
-rw-r--r--app/models/note.rb10
-rw-r--r--app/models/project.rb32
-rw-r--r--app/models/project_services/asana_service.rb84
-rw-r--r--app/models/project_services/assembla_service.rb1
-rw-r--r--app/models/project_services/bamboo_service.rb1
-rw-r--r--app/models/project_services/buildkite_service.rb1
-rw-r--r--app/models/project_services/builds_email_service.rb7
-rw-r--r--app/models/project_services/campfire_service.rb1
-rw-r--r--app/models/project_services/ci_service.rb1
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb1
-rw-r--r--app/models/project_services/drone_ci_service.rb1
-rw-r--r--app/models/project_services/emails_on_push_service.rb1
-rw-r--r--app/models/project_services/external_wiki_service.rb1
-rw-r--r--app/models/project_services/flowdock_service.rb1
-rw-r--r--app/models/project_services/gemnasium_service.rb1
-rw-r--r--app/models/project_services/gitlab_ci_service.rb1
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb1
-rw-r--r--app/models/project_services/hipchat_service.rb9
-rw-r--r--app/models/project_services/irker_service.rb8
-rw-r--r--app/models/project_services/issue_tracker_service.rb1
-rw-r--r--app/models/project_services/jira_service.rb11
-rw-r--r--app/models/project_services/pivotaltracker_service.rb1
-rw-r--r--app/models/project_services/pushover_service.rb1
-rw-r--r--app/models/project_services/redmine_service.rb1
-rw-r--r--app/models/project_services/slack_service.rb1
-rw-r--r--app/models/project_services/teamcity_service.rb1
-rw-r--r--app/models/project_wiki.rb4
-rw-r--r--app/models/repository.rb49
-rw-r--r--app/models/sent_notification.rb12
-rw-r--r--app/models/service.rb1
-rw-r--r--app/models/tree.rb14
-rw-r--r--app/models/user.rb132
-rw-r--r--app/models/wiki_page.rb2
57 files changed, 557 insertions, 268 deletions
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 1b3ee757040..5375148a654 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -69,7 +69,7 @@ class Ability
subject.group
end
- if group && group.public_profile?
+ if group && group.projects.public_only.any?
[:read_group]
else
[]
@@ -175,7 +175,7 @@ class Ability
:create_merge_request,
:create_wiki,
:manage_builds,
- :download_build_artifacts,
+ :read_build_artifacts,
:push_code
]
end
diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb
index 89b3116b9f2..2bc15c60d57 100644
--- a/app/models/abuse_report.rb
+++ b/app/models/abuse_report.rb
@@ -18,4 +18,15 @@ class AbuseReport < ActiveRecord::Base
validates :user, presence: true
validates :message, presence: true
validates :user_id, uniqueness: true
+
+ def remove_user
+ user.block
+ user.destroy
+ end
+
+ def notify
+ return unless self.persisted?
+
+ AbuseReportMailer.notify(self.id).deliver_later
+ end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 7c107da116c..6c6c2468374 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -27,9 +27,20 @@
# admin_notification_email :string(255)
# shared_runners_enabled :boolean default(TRUE), not null
# max_artifacts_size :integer default(100), not null
-# runners_registration_token :string(255)
-# require_two_factor_authentication :boolean default(TRUE)
+# runners_registration_token :string
+# require_two_factor_authentication :boolean default(FALSE)
# two_factor_grace_period :integer default(48)
+# metrics_enabled :boolean default(FALSE)
+# metrics_host :string default("localhost")
+# metrics_username :string
+# metrics_password :string
+# metrics_pool_size :integer default(16)
+# metrics_timeout :integer default(10)
+# metrics_method_call_threshold :integer default(10)
+# recaptcha_enabled :boolean default(FALSE)
+# recaptcha_site_key :string
+# recaptcha_private_key :string
+# metrics_port :integer default(8089)
#
class ApplicationSetting < ActiveRecord::Base
@@ -44,24 +55,32 @@ class ApplicationSetting < ActiveRecord::Base
attr_accessor :restricted_signup_domains_raw
validates :session_expire_delay,
- presence: true,
- numericality: { only_integer: true, greater_than_or_equal_to: 0 }
+ presence: true,
+ numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :home_page_url,
- allow_blank: true,
- url: true,
- if: :home_page_url_column_exist
+ allow_blank: true,
+ url: true,
+ if: :home_page_url_column_exist
validates :after_sign_out_path,
- allow_blank: true,
- url: true
+ allow_blank: true,
+ url: true
validates :admin_notification_email,
- allow_blank: true,
- email: true
+ allow_blank: true,
+ email: true
validates :two_factor_grace_period,
- numericality: { greater_than_or_equal_to: 0 }
+ numericality: { greater_than_or_equal_to: 0 }
+
+ validates :recaptcha_site_key,
+ presence: true,
+ if: :recaptcha_enabled
+
+ validates :recaptcha_private_key,
+ presence: true,
+ if: :recaptcha_enabled
validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil?
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index ad514706160..61119633717 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -6,7 +6,6 @@
# message :text not null
# starts_at :datetime
# ends_at :datetime
-# alert_type :integer
# created_at :datetime
# updated_at :datetime
# color :string(255)
@@ -23,7 +22,22 @@ class BroadcastMessage < ActiveRecord::Base
validates :color, allow_blank: true, color: true
validates :font, allow_blank: true, color: true
+ default_value_for :color, '#E75E40'
+ default_value_for :font, '#FFFFFF'
+
def self.current
- where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last
+ where("ends_at > :now AND starts_at <= :now", now: Time.zone.now).last
+ end
+
+ def active?
+ started? && !ended?
+ end
+
+ def started?
+ Time.zone.now >= starts_at
+ end
+
+ def ended?
+ ends_at < Time.zone.now
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 7b89fe069ea..6cc26abce66 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -29,10 +29,13 @@
# target_url :string(255)
# description :string(255)
# artifacts_file :text
+# gl_project_id :integer
+# artifacts_metadata :text
#
module Ci
class Build < CommitStatus
+ include Gitlab::Application.routes.url_helpers
LAZY_ATTRIBUTES = ['trace']
belongs_to :runner, class_name: 'Ci::Runner'
@@ -48,12 +51,15 @@ module Ci
scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
mount_uploader :artifacts_file, ArtifactUploader
+ mount_uploader :artifacts_metadata, ArtifactUploader
acts_as_taggable
# To prevent db load megabytes of data from trace
default_scope -> { select(Ci::Build.columns_without_lazy) }
+ before_destroy { project }
+
class << self
def columns_without_lazy
(column_names - LAZY_ATTRIBUTES).map do |column_name|
@@ -145,10 +151,6 @@ module Ci
end
end
- def project
- commit.project
- end
-
def project_id
commit.project.id
end
@@ -194,8 +196,11 @@ module Ci
end
def raw_trace
- if File.exist?(path_to_trace)
+ if File.file?(path_to_trace)
File.read(path_to_trace)
+ elsif project.ci_id && File.file?(old_path_to_trace)
+ # Temporary fix for build trace data integrity
+ File.read(old_path_to_trace)
else
# backward compatibility
read_attribute :trace
@@ -204,7 +209,7 @@ module Ci
def trace
trace = raw_trace
- if project && trace.present?
+ if project && trace.present? && project.runners_token.present?
trace.gsub(project.runners_token, 'xxxxxx')
else
trace
@@ -212,8 +217,8 @@ module Ci
end
def trace=(trace)
- unless Dir.exists? dir_to_trace
- FileUtils.mkdir_p dir_to_trace
+ unless Dir.exists?(dir_to_trace)
+ FileUtils.mkdir_p(dir_to_trace)
end
File.write(path_to_trace, trace)
@@ -231,6 +236,55 @@ module Ci
"#{dir_to_trace}/#{id}.log"
end
+ ##
+ # Deprecated
+ #
+ # This is a hotfix for CI build data integrity, see #4246
+ # Should be removed in 8.4, after CI files migration has been done.
+ #
+ def old_dir_to_trace
+ File.join(
+ Settings.gitlab_ci.builds_path,
+ created_at.utc.strftime("%Y_%m"),
+ project.ci_id.to_s
+ )
+ end
+
+ ##
+ # Deprecated
+ #
+ # This is a hotfix for CI build data integrity, see #4246
+ # Should be removed in 8.4, after CI files migration has been done.
+ #
+ def old_path_to_trace
+ "#{old_dir_to_trace}/#{id}.log"
+ end
+
+ ##
+ # Deprecated
+ #
+ # This contains a hotfix for CI build data integrity, see #4246
+ #
+ # This method is used by `ArtifactUploader` to create a store_dir.
+ # Warning: Uploader uses it after AND before file has been stored.
+ #
+ # This method returns old path to artifacts only if it already exists.
+ #
+ def artifacts_path
+ old = File.join(created_at.utc.strftime('%Y_%m'),
+ project.ci_id.to_s,
+ id.to_s)
+
+ old_store = File.join(ArtifactUploader.artifacts_path, old)
+ return old if project.ci_id && File.directory?(old_store)
+
+ File.join(
+ created_at.utc.strftime('%Y_%m'),
+ project.id.to_s,
+ id.to_s
+ )
+ end
+
def token
project.runners_token
end
@@ -240,21 +294,18 @@ module Ci
end
def target_url
- Gitlab::Application.routes.url_helpers.
- namespace_project_build_url(project.namespace, project, self)
+ namespace_project_build_url(project.namespace, project, self)
end
def cancel_url
if active?
- Gitlab::Application.routes.url_helpers.
- cancel_namespace_project_build_path(project.namespace, project, self)
+ cancel_namespace_project_build_path(project.namespace, project, self)
end
end
def retry_url
if retryable?
- Gitlab::Application.routes.url_helpers.
- retry_namespace_project_build_path(project.namespace, project, self)
+ retry_namespace_project_build_path(project.namespace, project, self)
end
end
@@ -270,20 +321,35 @@ module Ci
pending? && !any_runners_online?
end
- def download_url
- if artifacts_file.exists?
- Gitlab::Application.routes.url_helpers.
- download_namespace_project_build_path(project.namespace, project, self)
- end
- end
-
def execute_hooks
build_data = Gitlab::BuildDataBuilder.build(self)
project.execute_hooks(build_data.dup, :build_hooks)
project.execute_services(build_data.dup, :build_hooks)
end
+ def artifacts?
+ artifacts_file.exists?
+ end
+
+ def artifacts_download_url
+ if artifacts?
+ download_namespace_project_build_artifacts_path(project.namespace, project, self)
+ end
+ end
+ def artifacts_browse_url
+ if artifacts_browser_supported?
+ browse_namespace_project_build_artifacts_path(project.namespace, project, self)
+ end
+ end
+
+ def artifacts_browser_supported?
+ artifacts? && artifacts_metadata.exists?
+ end
+
+ def artifacts_metadata_entry(path)
+ Gitlab::Ci::Build::Artifacts::Metadata.new(artifacts_metadata.path, path).to_entry
+ end
private
diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb
index 93d9be144e8..7b16f207a26 100644
--- a/app/models/ci/runner_project.rb
+++ b/app/models/ci/runner_project.rb
@@ -2,11 +2,12 @@
#
# Table name: ci_runner_projects
#
-# id :integer not null, primary key
-# runner_id :integer not null
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
+# id :integer not null, primary key
+# runner_id :integer not null
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# gl_project_id :integer
#
module Ci
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index 23516709a41..2b9a457c8ab 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -2,12 +2,13 @@
#
# Table name: ci_triggers
#
-# id :integer not null, primary key
-# token :string(255)
-# project_id :integer not null
-# deleted_at :datetime
-# created_at :datetime
-# updated_at :datetime
+# id :integer not null, primary key
+# token :string(255)
+# project_id :integer
+# deleted_at :datetime
+# created_at :datetime
+# updated_at :datetime
+# gl_project_id :integer
#
module Ci
@@ -32,6 +33,10 @@ module Ci
trigger_requests.last
end
+ def last_used
+ last_trigger_request.try(:created_at)
+ end
+
def short_token
token[0...10]
end
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 56759d3e50f..e786bd7dd93 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -3,12 +3,13 @@
# Table name: ci_variables
#
# id :integer not null, primary key
-# project_id :integer not null
+# project_id :integer
# key :string(255)
# value :text
# encrypted_value :text
# encrypted_value_salt :string(255)
# encrypted_value_iv :string(255)
+# gl_project_id :integer
#
module Ci
@@ -17,8 +18,12 @@ module Ci
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
- validates_presence_of :key
validates_uniqueness_of :key, scope: :gl_project_id
+ validates :key,
+ presence: true,
+ length: { within: 0..255 },
+ format: { with: /\A[a-zA-Z0-9_]+\z/,
+ message: "can contain only letters, digits and '_'." }
attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base
end
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 21c5c87bc3d..66e0502fc0c 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,30 +1,35 @@
# == Schema Information
#
-# project_id integer
-# status string
-# 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
-# deploy boolean default: false
-# options text
-# allow_failure boolean default: false, null: false
-# stage string
-# trigger_request_id integer
-# stage_idx integer
-# tag boolean
-# ref string
-# user_id integer
-# type string
-# target_url string
-# description string
+# 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
+# gl_project_id :integer
#
class CommitStatus < ActiveRecord::Base
@@ -51,6 +56,8 @@ class CommitStatus < ActiveRecord::Base
scope :ordered, -> { order(:ref, :stage_idx, :name) }
scope :for_ref, ->(ref) { where(ref: ref) }
+ AVAILABLE_STATUSES = ['pending', 'running', 'success', 'failed', 'canceled']
+
state_machine :status, initial: :pending do
event :run do
transition pending: :running
@@ -126,7 +133,11 @@ class CommitStatus < ActiveRecord::Base
false
end
- def download_url
+ def artifacts_download_url
+ nil
+ end
+
+ def artifacts_browse_url
nil
end
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 919833f6df5..04650a9e67a 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -95,14 +95,12 @@ module Issuable
opened? || reopened?
end
- # Deprecated. Still exists to preserve API compatibility.
def downvotes
- 0
+ notes.awards.where(note: "thumbsdown").count
end
- # Deprecated. Still exists to preserve API compatibility.
def upvotes
- 0
+ notes.awards.where(note: "thumbsup").count
end
def subscribed?(user)
@@ -121,6 +119,12 @@ module Issuable
update(subscribed: !subscribed?(user))
end
+ def unsubscribe(user)
+ subscriptions.
+ find_or_initialize_by(user_id: user.id).
+ update(subscribed: false)
+ end
+
def to_hook_data(user)
{
object_kind: self.class.name.underscore,
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 6316ee208b5..98f71ae8cb0 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -51,8 +51,11 @@ module Mentionable
else
self.class.mentionable_attrs.each do |attr, options|
text = send(attr)
- options[:cache_key] = [self, attr] if options.delete(:cache) && self.persisted?
- ext.analyze(text, options)
+
+ context = options.dup
+ context[:cache_key] = [self, attr] if context.delete(:cache) && self.persisted?
+
+ ext.analyze(text, context)
end
end
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index 7391a77383c..8b47b9e0abd 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -11,6 +11,7 @@ module Sortable
default_scope { order_id_desc }
scope :order_id_desc, -> { reorder(id: :desc) }
+ scope :order_id_asc, -> { reorder(id: :asc) }
scope :order_created_desc, -> { reorder(created_at: :desc) }
scope :order_created_asc, -> { reorder(created_at: :asc) }
scope :order_updated_desc, -> { reorder(updated_at: :desc) }
@@ -28,6 +29,8 @@ module Sortable
when 'updated_desc' then order_updated_desc
when 'created_asc' then order_created_asc
when 'created_desc' then order_created_desc
+ when 'id_desc' then order_id_desc
+ when 'id_asc' then order_id_asc
else
all
end
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
index 12c934e2494..97f4f03a9a5 100644
--- a/app/models/generic_commit_status.rb
+++ b/app/models/generic_commit_status.rb
@@ -29,6 +29,7 @@
# target_url :string(255)
# description :string(255)
# artifacts_file :text
+# gl_project_id :integer
#
class GenericCommitStatus < CommitStatus
diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb
index af1d7562ebe..7ee276255a0 100644
--- a/app/models/global_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -121,9 +121,9 @@ class GlobalMilestone
def expires_at
if due_date
if due_date.past?
- "expired at #{due_date.stamp("Aug 21, 2011")}"
+ "expired on #{due_date.to_s(:medium)}"
else
- "expires at #{due_date.stamp("Aug 21, 2011")}"
+ "expires on #{due_date.to_s(:medium)}"
end
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 1b5b875a19e..5a31b46920c 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -11,7 +11,6 @@
# type :string(255)
# description :string(255) default(""), not null
# avatar :string(255)
-# public :boolean default(FALSE)
#
require 'carrierwave/orm/activerecord'
@@ -50,10 +49,6 @@ class Group < Namespace
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
@@ -125,10 +120,6 @@ class Group < Namespace
end
end
- def public_profile?
- self.public || projects.public_only.any?
- end
-
def post_create_hook
Gitlab::AppLogger.info("Group \"#{name}\" was created")
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index 22638057773..fa18ba5dbbe 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
+# build_events :boolean default(FALSE), not null
#
class ProjectHook < WebHook
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index 09bb3ee52a2..b333a337347 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
+# build_events :boolean default(FALSE), not null
#
class ServiceHook < WebHook
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index 2f63c59b07e..d81512fae5d 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
+# build_events :boolean default(FALSE), not null
#
class SystemHook < WebHook
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 40eb0e20b4b..3bb50c63cac 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
+# build_events :boolean default(FALSE), not null
#
class WebHook < ActiveRecord::Base
@@ -47,8 +48,8 @@ class WebHook < ActiveRecord::Base
else
post_url = url.gsub("#{parsed_url.userinfo}@", "")
auth = {
- username: URI.decode(parsed_url.user),
- password: URI.decode(parsed_url.password),
+ username: CGI.unescape(parsed_url.user),
+ password: CGI.unescape(parsed_url.password),
}
response = WebHook.post(post_url,
body: data.to_json,
@@ -60,7 +61,7 @@ class WebHook < ActiveRecord::Base
basic_auth: auth)
end
- [response.code == 200, ActionView::Base.full_sanitizer.sanitize(response.to_s)]
+ [(response.code >= 200 && response.code < 300), ActionView::Base.full_sanitizer.sanitize(response.to_s)]
rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
logger.error("WebHook Error => #{e}")
[false, e.to_s]
diff --git a/app/models/identity.rb b/app/models/identity.rb
index 8bcdc194953..e1915b079d4 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -18,4 +18,8 @@ class Identity < ActiveRecord::Base
validates :provider, presence: true
validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider }
validates :user_id, uniqueness: { scope: :provider }
+
+ def ldap?
+ provider.starts_with?('ldap')
+ end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 80ecd15077f..7beba984608 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -33,7 +33,9 @@ class Issue < ActiveRecord::Base
belongs_to :project
validates :project, presence: true
- scope :of_group, ->(group) { where(project_id: group.project_ids) }
+ scope :of_group,
+ ->(group) { where(project_id: group.projects.select(:id).reorder(nil)) }
+
scope :cared, ->(user) { where(assignee_id: user) }
scope :open_for, ->(user) { opened.assigned_to(user) }
@@ -83,10 +85,10 @@ class Issue < ActiveRecord::Base
reference
end
- def referenced_merge_requests
+ def referenced_merge_requests(current_user = nil)
Gitlab::ReferenceExtractor.lazily do
[self, *notes].flat_map do |note|
- note.all_references.merge_requests
+ note.all_references(current_user).merge_requests
end
end.sort_by(&:iid)
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index fe87b820e98..a9fc6bc167a 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -2,28 +2,28 @@
#
# Table name: merge_requests
#
-# id :integer not null, primary key
-# target_branch :string(255) not null
-# source_branch :string(255) not null
-# source_project_id :integer not null
-# author_id :integer
-# assignee_id :integer
-# title :string(255)
-# created_at :datetime
-# updated_at :datetime
-# milestone_id :integer
-# state :string(255)
-# merge_status :string(255)
-# target_project_id :integer not null
-# iid :integer
-# description :text
-# position :integer default(0)
-# locked_at :datetime
-# updated_by_id :integer
-# merge_error :string(255)
-# merge_params :text (serialized to hash)
-# merge_when_build_succeeds :boolean default(false), not null
-# merge_user_id :integer
+# id :integer not null, primary key
+# target_branch :string(255) not null
+# source_branch :string(255) not null
+# source_project_id :integer not null
+# author_id :integer
+# assignee_id :integer
+# title :string(255)
+# created_at :datetime
+# updated_at :datetime
+# milestone_id :integer
+# state :string(255)
+# merge_status :string(255)
+# target_project_id :integer not null
+# iid :integer
+# description :text
+# position :integer default(0)
+# locked_at :datetime
+# updated_by_id :integer
+# merge_error :string(255)
+# merge_params :text
+# merge_when_build_succeeds :boolean default(FALSE), not null
+# merge_user_id :integer
#
require Rails.root.join("app/models/commit")
@@ -131,7 +131,7 @@ 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.project_ids) }
+ 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) }
@@ -229,6 +229,8 @@ class MergeRequest < ActiveRecord::Base
end
def check_if_can_be_merged
+ return unless unchecked?
+
can_be_merged =
project.repository.can_be_merged?(source_sha, target_branch)
@@ -252,7 +254,11 @@ class MergeRequest < ActiveRecord::Base
end
def mergeable?
- open? && !work_in_progress? && can_be_merged?
+ return false unless open? && !work_in_progress?
+
+ check_if_can_be_merged
+
+ can_be_merged?
end
def gitlab_merge_status
@@ -452,6 +458,10 @@ class MergeRequest < ActiveRecord::Base
!source_branch_exists? || !target_branch_exists?
end
+ def broken?
+ self.commits.blank? || branch_missing? || cannot_be_merged?
+ end
+
def can_be_merged_by?(user)
::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch)
end
@@ -508,10 +518,6 @@ class MergeRequest < ActiveRecord::Base
@ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project
end
- def broken?
- self.commits.blank? || branch_missing? || cannot_be_merged?
- end
-
def diff_range
[last_commit.parent, first_commit]
end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index d8c7536cd31..c9a0ad8b9b6 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -22,6 +22,7 @@ class Milestone < ActiveRecord::Base
include InternalId
include Sortable
+ include Referable
include StripAttribute
belongs_to :project
@@ -61,6 +62,27 @@ class Milestone < ActiveRecord::Base
end
end
+ def self.reference_pattern
+ nil
+ end
+
+ def self.link_reference_pattern
+ super("milestones", /(?<milestone>\d+)/)
+ end
+
+ def to_reference(from_project = nil)
+ escaped_title = self.title.gsub("]", "\\]")
+
+ h = Gitlab::Application.routes.url_helpers
+ url = h.namespace_project_milestone_url(self.project.namespace, self.project, self)
+
+ "[#{escaped_title}](#{url})"
+ end
+
+ def reference_link_text(from_project = nil)
+ self.title
+ end
+
def expired?
if due_date
due_date.past?
@@ -90,9 +112,9 @@ class Milestone < ActiveRecord::Base
def expires_at
if due_date
if due_date.past?
- "expired at #{due_date.stamp("Aug 21, 2011")}"
+ "expired on #{due_date.to_s(:medium)}"
else
- "expires at #{due_date.stamp("Aug 21, 2011")}"
+ "expires on #{due_date.to_s(:medium)}"
end
end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index adafabbec07..bdb33f37495 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -11,7 +11,6 @@
# 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 1222d99cf1f..3e1375e5ad6 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -346,20 +346,22 @@ class Note < ActiveRecord::Base
read_attribute(:system)
end
- # Deprecated. Still exists to preserve API compatibility.
def downvote?
- false
+ is_award && note == "thumbsdown"
end
- # Deprecated. Still exists to preserve API compatibility.
def upvote?
- false
+ is_award && note == "thumbsup"
end
def editable?
!system? && !is_award
end
+ def cross_reference_not_visible_for?(user)
+ cross_reference? && referenced_mentionables(user).empty?
+ end
+
# Checks if note is an award added as a comment
#
# If note is an award, this method sets is_award to true
diff --git a/app/models/project.rb b/app/models/project.rb
index 75f85310d5f..7e131151513 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -29,6 +29,13 @@
# import_source :string(255)
# commit_count :integer default(0)
# import_error :text
+# ci_id :integer
+# builds_enabled :boolean default(TRUE), not null
+# shared_runners_enabled :boolean default(TRUE), not null
+# runners_token :string
+# build_coverage_regex :string
+# build_allow_git_fetch :boolean default(TRUE), not null
+# build_timeout :integer default(3600), not null
#
require 'carrierwave/orm/activerecord'
@@ -43,6 +50,7 @@ class Project < ActiveRecord::Base
include Sortable
include AfterCommitQueue
include CaseSensitivity
+ include TokenAuthenticatable
extend Gitlab::ConfigHelper
@@ -81,6 +89,7 @@ class Project < ActiveRecord::Base
acts_as_taggable_on :tags
attr_accessor :new_default_branch
+ attr_accessor :old_path_with_namespace
# Relations
belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
@@ -185,10 +194,8 @@ class Project < ActiveRecord::Base
if: ->(project) { project.avatar.present? && project.avatar_changed? }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
- before_validation :set_runners_token_token
- def set_runners_token_token
- self.runners_token = SecureRandom.hex(15) if self.runners_token.blank?
- end
+ add_authentication_token_field :runners_token
+ before_save :ensure_runners_token
mount_uploader :avatar, AvatarUploader
@@ -390,7 +397,7 @@ class Project < ActiveRecord::Base
result.password = '*****' unless result.password.nil?
result.to_s
rescue
- original_url
+ self.import_url
end
def check_limit
@@ -555,7 +562,9 @@ class Project < ActiveRecord::Base
end
def send_move_instructions(old_path_with_namespace)
- NotificationService.new.project_was_moved(self, old_path_with_namespace)
+ # New project path needs to be committed to the DB or notification will
+ # retrieve stale information
+ run_after_commit { NotificationService.new.project_was_moved(self, old_path_with_namespace) }
end
def owner
@@ -699,6 +708,11 @@ class Project < ActiveRecord::Base
gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
send_move_instructions(old_path_with_namespace)
reset_events_cache
+
+ @old_path_with_namespace = old_path_with_namespace
+
+ SystemHooksService.new.execute_hooks_for(self, :rename)
+
@repository = nil
rescue
# Returning false does not rollback after_* transaction but gives
@@ -767,6 +781,8 @@ class Project < ActiveRecord::Base
end
def change_head(branch)
+ # Cached divergent commit counts are based on repository head
+ repository.expire_branch_cache
gitlab_shell.update_repository_head(self.path_with_namespace, branch)
reload_default_branch
end
@@ -883,4 +899,8 @@ class Project < ActiveRecord::Base
return true unless forked?
Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i)
end
+
+ def runners_token
+ ensure_runners_token!
+ end
end
diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb
index e6e16058d41..792ad804575 100644
--- a/app/models/project_services/asana_service.rb
+++ b/app/models/project_services/asana_service.rb
@@ -16,7 +16,9 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
+
require 'asana'
class AsanaService < Service
@@ -40,8 +42,8 @@ get the commit comment added to it.
You can also close a task with a message containing: `fix #123456`.
-You can find your Api Keys here:
-http://developer.asana.com/documentation/#api_keys'
+You can create a Personal Access Token here:
+http://app.asana.com/-/account_api'
end
def to_param
@@ -53,14 +55,12 @@ http://developer.asana.com/documentation/#api_keys'
{
type: 'text',
name: 'api_key',
- placeholder: 'User API token. User must have access to task,
-all comments will be attributed to this user.'
+ placeholder: 'User Personal Access Token. User must have access to task, all comments will be attributed to this user.'
},
{
type: 'text',
name: 'restrict_to_branch',
- placeholder: 'Comma-separated list of branches which will be
-automatically inspected. Leave blank to include all branches.'
+ placeholder: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches.'
}
]
end
@@ -69,58 +69,58 @@ automatically inspected. Leave blank to include all branches.'
%w(push)
end
+ def client
+ @_client ||= begin
+ Asana::Client.new do |c|
+ c.authentication :access_token, api_key
+ end
+ end
+ end
+
def execute(data)
return unless supported_events.include?(data[:object_kind])
- Asana.configure do |client|
- client.api_key = api_key
- end
-
- user = data[:user_name]
+ # check the branch restriction is poplulated and branch is not included
branch = Gitlab::Git.ref_name(data[:ref])
-
branch_restriction = restrict_to_branch.to_s
-
- # check the branch restriction is poplulated and branch is not included
if branch_restriction.length > 0 && branch_restriction.index(branch).nil?
return
end
+ user = data[:user_name]
project_name = project.name_with_namespace
- push_msg = user + ' pushed to branch ' + branch + ' of ' + project_name
data[:commits].each do |commit|
- check_commit(' ( ' + commit[:url] + ' ): ' + commit[:message], push_msg)
+ push_msg = "#{user} pushed to branch #{branch} of #{project_name} ( #{commit[:url]} ):"
+ check_commit(commit[:message], push_msg)
end
end
def check_commit(message, push_msg)
- task_list = []
- close_list = []
-
- message.split("\n").each do |line|
- # look for a task ID or a full Asana url
- task_list.concat(line.scan(/#(\d+)/))
- task_list.concat(line.scan(/https:\/\/app\.asana\.com\/\d+\/\d+\/(\d+)/))
- # look for a word starting with 'fix' followed by a task ID
- close_list.concat(line.scan(/(fix\w*)\W*#(\d+)/i))
- end
-
- # post commit to every taskid found
- task_list.each do |taskid|
- task = Asana::Task.find(taskid[0])
-
- if task
- task.create_story(text: push_msg + ' ' + message)
- end
- end
-
- # close all tasks that had 'fix(ed/es/ing) #:id' in them
- close_list.each do |taskid|
- task = Asana::Task.find(taskid.last)
-
- if task
- task.modify(completed: true)
+ # matches either:
+ # - #1234
+ # - https://app.asana.com/0/0/1234
+ # optionally preceded with:
+ # - fix/ed/es/ing
+ # - close/s/d
+ # - closing
+ issue_finder = /(fix\w*|clos[ei]\w*+)?\W*(?:https:\/\/app\.asana\.com\/\d+\/\d+\/(\d+)|#(\d+))/i
+
+ message.scan(issue_finder).each do |tuple|
+ # tuple will be
+ # [ 'fix', 'id_from_url', 'id_from_pound' ]
+ taskid = tuple[2] || tuple[1]
+
+ begin
+ task = Asana::Task.find_by_id(client, taskid)
+ task.add_comment(text: "#{push_msg} #{message}")
+
+ if tuple[0]
+ task.update(completed: true)
+ end
+ rescue => e
+ Rails.logger.error(e.message)
+ next
end
end
end
diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb
index fb7e0c0fb0d..29d841faed8 100644
--- a/app/models/project_services/assembla_service.rb
+++ b/app/models/project_services/assembla_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class AssemblaService < Service
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index aa8746beb80..9e7f642180e 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class BambooService < CiService
diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb
index 199ee3a9d0d..3efbfd2eec3 100644
--- a/app/models/project_services/buildkite_service.rb
+++ b/app/models/project_services/buildkite_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
require "addressable/uri"
diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb
index 8247c79fc33..f6313255cbb 100644
--- a/app/models/project_services/builds_email_service.rb
+++ b/app/models/project_services/builds_email_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class BuildsEmailService < Service
@@ -72,12 +73,16 @@ class BuildsEmailService < Service
when 'success'
!notify_only_broken_builds?
when 'failed'
- true
+ !allow_failure?(data)
else
false
end
end
+ def allow_failure?(data)
+ data[:build_allow_failure] == true
+ end
+
def all_recipients(data)
all_recipients = recipients.split(',')
diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb
index e591afdda64..6e8f0842524 100644
--- a/app/models/project_services/campfire_service.rb
+++ b/app/models/project_services/campfire_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class CampfireService < Service
diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb
index 88186113c68..c3f70d1f972 100644
--- a/app/models/project_services/ci_service.rb
+++ b/app/models/project_services/ci_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
# Base class for CI services
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
index 7c2027c18e6..88a3e9218cb 100644
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class CustomIssueTrackerService < IssueTrackerService
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index 08e5ccb3855..b4724bb647e 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class DroneCiService < CiService
diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb
index 8f5d8b086eb..b831577cd97 100644
--- a/app/models/project_services/emails_on_push_service.rb
+++ b/app/models/project_services/emails_on_push_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class EmailsOnPushService < Service
diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb
index 74c57949b4d..b402b68665a 100644
--- a/app/models/project_services/external_wiki_service.rb
+++ b/app/models/project_services/external_wiki_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class ExternalWikiService < Service
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index 15c7c907f7e..8605ce66e48 100644
--- a/app/models/project_services/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
require "flowdock-git-hook"
diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
index 202fee042e3..61babe9cfe5 100644
--- a/app/models/project_services/gemnasium_service.rb
+++ b/app/models/project_services/gemnasium_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
require "gemnasium/gitlab_service"
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index b64d97ce75d..33f0d7ea01a 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
# TODO(ayufan): The GitLabCiService is deprecated and the type should be removed when the database entries are removed
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 9558292fea3..7aa04309f54 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class GitlabIssueTrackerService < IssueTrackerService
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 1e1686a11c6..0e3fa4a40fe 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class HipchatService < Service
@@ -119,13 +120,13 @@ class HipchatService < Service
message << "#{push[:user_name]} "
if Gitlab::Git.blank_ref?(before)
message << "pushed new #{ref_type} <a href=\""\
- "#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\
+ "#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"\
" to #{project_link}\n"
elsif Gitlab::Git.blank_ref?(after)
message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n"
else
message << "pushed to #{ref_type} <a href=\""\
- "#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a> "
+ "#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> "
message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> "
message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)"
@@ -254,8 +255,8 @@ class HipchatService < Service
status = data[:commit][:status]
duration = data[:commit][:duration]
- branch_link = "<a href=\"#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"
- commit_link = "<a href=\"#{project_url}/commit/#{URI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>"
+ branch_link = "<a href=\"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"
+ commit_link = "<a href=\"#{project_url}/commit/#{CGI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>"
"#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
end
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index d24aa317cf3..04c714bfaad 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
require 'uri'
@@ -72,9 +73,10 @@ class IrkerService < Service
'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\
'you want the channel to be a nickname instead, append ",isnick" to ' \
'the channel name; if the channel is protected by a secret password, ' \
- ' append "?key=secretpassword" to the URI. Note that if you specify a ' \
- ' default IRC URI to prepend before each recipient, you can just give ' \
- ' a channel name.' },
+ ' append "?key=secretpassword" to the URI (Note that due to a bug, if you ' \
+ ' want to use a password, you have to omit the "#" on the channel). If you ' \
+ ' specify a default IRC URI to prepend before each recipient, you can just ' \
+ ' give a channel name.' },
{ type: 'checkbox', name: 'colorize_messages' },
]
end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index 936e574cccd..ed201979d39 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class IssueTrackerService < Service
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index e216f406e1c..f6571fc063e 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class JiraService < IssueTrackerService
@@ -39,15 +40,10 @@ class JiraService < IssueTrackerService
end
def help
- line1 = 'Setting `project_url`, `issues_url` and `new_issue_url` will '\
+ 'Setting `project_url`, `issues_url` and `new_issue_url` will '\
'allow a user to easily navigate to the Jira issue tracker. See the '\
'[integration doc](http://doc.gitlab.com/ce/integration/external-issue-tracker.html) '\
'for details.'
-
- line2 = 'Support for referencing commits and automatic closing of Jira issues directly '\
- 'from GitLab is [available in GitLab EE.](http://doc.gitlab.com/ee/integration/jira.html)'
-
- [line1, line2].join("\n\n")
end
def title
@@ -120,6 +116,7 @@ class JiraService < IssueTrackerService
end
def test_settings
+ return unless api_url.present?
result = JiraService.get(
jira_api_test_url,
headers: {
@@ -217,6 +214,7 @@ class JiraService < IssueTrackerService
end
def send_message(url, message)
+ return unless api_url.present?
result = JiraService.post(
url,
body: message,
@@ -242,6 +240,7 @@ class JiraService < IssueTrackerService
end
def existing_comment?(issue_name, new_comment)
+ return unless api_url.present?
result = JiraService.get(
comment_url(issue_name),
headers: {
diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb
index ade9ee97873..c9a890c7e3f 100644
--- a/app/models/project_services/pivotaltracker_service.rb
+++ b/app/models/project_services/pivotaltracker_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class PivotaltrackerService < Service
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
index 53edf522e9a..3d7e8bbee61 100644
--- a/app/models/project_services/pushover_service.rb
+++ b/app/models/project_services/pushover_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class PushoverService < Service
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
index dd9ba97ee1f..de974354c77 100644
--- a/app/models/project_services/redmine_service.rb
+++ b/app/models/project_services/redmine_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class RedmineService < IssueTrackerService
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index 375b4534d07..d89cf6d17b2 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class SlackService < Service
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index a63700693d7..b8e9416131a 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
class TeamcityService < CiService
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index b5fec38378b..8ce47495971 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -38,6 +38,10 @@ class ProjectWiki
[Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
end
+ def wiki_base_path
+ ["/", @project.path_with_namespace, "/wikis"].join('')
+ end
+
# Returns the Gollum::Wiki object.
def wiki
@wiki ||= begin
diff --git a/app/models/repository.rb b/app/models/repository.rb
index a9bf4eb4033..d9ff71c01ed 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -92,9 +92,12 @@ class Repository
commits
end
- def find_commits_by_message(query)
+ def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0)
+ ref ||= root_ref
+
# Limited to 1000 commits for now, could be parameterized?
- args = %W(#{Gitlab.config.git.bin_path} log --pretty=%H --max-count 1000 --grep=#{query})
+ args = %W(#{Gitlab.config.git.bin_path} log #{ref} --pretty=%H --skip #{offset} --max-count #{limit} --grep=#{query})
+ args = args.concat(%W(-- #{path})) if path.present?
git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp)
commits = git_log_results.map { |c| commit(c) }
@@ -176,17 +179,41 @@ class Repository
cache.fetch(:size) { raw_repository.size }
end
+ def diverging_commit_counts(branch)
+ root_ref_hash = raw_repository.rev_parse_target(root_ref).oid
+ cache.fetch(:"diverging_commit_counts_#{branch.name}") do
+ # Rugged seems to throw a `ReferenceError` when given branch_names rather
+ # than SHA-1 hashes
+ number_commits_behind = commits_between(branch.target, root_ref_hash).size
+ number_commits_ahead = commits_between(root_ref_hash, branch.target).size
+
+ { behind: number_commits_behind, ahead: number_commits_ahead }
+ end
+ end
+
def cache_keys
%i(size branch_names tag_names commit_count
readme version contribution_guide changelog license)
end
+ def branch_cache_keys
+ branches.map do |branch|
+ :"diverging_commit_counts_#{branch.name}"
+ end
+ end
+
def build_cache
cache_keys.each do |key|
unless cache.exist?(key)
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
@@ -203,6 +230,14 @@ class Repository
cache_keys.each do |key|
cache.expire(key)
end
+
+ expire_branch_cache
+ end
+
+ def expire_branch_cache
+ branches.each do |branch|
+ cache.expire(:"diverging_commit_counts_#{branch.name}")
+ end
end
def rebuild_cache
@@ -210,6 +245,11 @@ class Repository
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
@@ -644,6 +684,11 @@ class Repository
end
end
+ def ls_files(ref)
+ actual_ref = ref || root_ref
+ raw_repository.ls_files(actual_ref)
+ end
+
private
def cache
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index f36eda1531b..77115597d71 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -25,8 +25,6 @@ class SentNotification < ActiveRecord::Base
class << self
def reply_key
- return nil unless Gitlab::IncomingEmail.enabled?
-
SecureRandom.hex(16)
end
@@ -59,11 +57,15 @@ class SentNotification < ActiveRecord::Base
def record_note(note, recipient_id, reply_key, params = {})
params[:line_code] = note.line_code
-
+
record(note.noteable, recipient_id, reply_key, params)
end
end
+ def unsubscribable?
+ !for_commit?
+ end
+
def for_commit?
noteable_type == "Commit"
end
@@ -75,4 +77,8 @@ class SentNotification < ActiveRecord::Base
super
end
end
+
+ def to_param
+ self.reply_key
+ end
end
diff --git a/app/models/service.rb b/app/models/service.rb
index d3bf7f0ebd1..24f4bf7646e 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
#
# To add new service you should build a class inherited from Service
diff --git a/app/models/tree.rb b/app/models/tree.rb
index 93b3246a668..e0e04d8859f 100644
--- a/app/models/tree.rb
+++ b/app/models/tree.rb
@@ -17,18 +17,16 @@ class Tree
def readme
return @readme if defined?(@readme)
- available_readmes = blobs.select(&:readme?)
+ # Take the first previewable readme, or return nil if none is available or
+ # we can't preview any of them
+ readme_tree = blobs.find do |blob|
+ blob.readme? && (previewable?(blob.name) || plain?(blob.name))
+ end
- if available_readmes.count == 0
+ if readme_tree.nil?
return @readme = nil
end
- # Take the first previewable readme, or the first available readme, if we
- # can't preview any of them
- readme_tree = available_readmes.find do |readme|
- previewable?(readme.name)
- end || available_readmes.first
-
readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name)
git_repo = repository.raw_repository
diff --git a/app/models/user.rb b/app/models/user.rb
index df87f3b79bd..592468933ed 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -2,62 +2,63 @@
#
# Table name: users
#
-# id :integer not null, primary key
-# email :string(255) default(""), not null
-# encrypted_password :string(255) default(""), not null
-# reset_password_token :string(255)
-# reset_password_sent_at :datetime
-# remember_created_at :datetime
-# sign_in_count :integer default(0)
-# current_sign_in_at :datetime
-# last_sign_in_at :datetime
-# current_sign_in_ip :string(255)
-# last_sign_in_ip :string(255)
-# created_at :datetime
-# updated_at :datetime
-# name :string(255)
-# admin :boolean default(FALSE), not null
-# projects_limit :integer default(10)
-# skype :string(255) default(""), not null
-# linkedin :string(255) default(""), not null
-# twitter :string(255) default(""), not null
-# authentication_token :string(255)
-# theme_id :integer default(1), not null
-# bio :string(255)
-# failed_attempts :integer default(0)
-# locked_at :datetime
-# unlock_token :string(255)
-# username :string(255)
-# can_create_group :boolean default(TRUE), not null
-# can_create_team :boolean default(TRUE), not null
-# state :string(255)
-# color_scheme_id :integer default(1), not null
-# notification_level :integer default(1), not null
-# password_expires_at :datetime
-# created_by_id :integer
-# last_credential_check_at :datetime
-# avatar :string(255)
-# confirmation_token :string(255)
-# confirmed_at :datetime
-# confirmation_sent_at :datetime
-# unconfirmed_email :string(255)
-# hide_no_ssh_key :boolean default(FALSE)
-# website_url :string(255) default(""), not null
-# notification_email :string(255)
-# hide_no_password :boolean default(FALSE)
-# password_automatically_set :boolean default(FALSE)
-# location :string(255)
-# encrypted_otp_secret :string(255)
-# encrypted_otp_secret_iv :string(255)
-# encrypted_otp_secret_salt :string(255)
-# otp_required_for_login :boolean default(FALSE), not null
-# otp_backup_codes :text
-# public_email :string(255) default(""), not null
-# dashboard :integer default(0)
-# project_view :integer default(0)
-# consumed_timestep :integer
-# layout :integer default(0)
-# hide_project_limit :boolean default(FALSE)
+# id :integer not null, primary key
+# email :string(255) default(""), not null
+# encrypted_password :string(255) default(""), not null
+# reset_password_token :string(255)
+# reset_password_sent_at :datetime
+# remember_created_at :datetime
+# sign_in_count :integer default(0)
+# current_sign_in_at :datetime
+# last_sign_in_at :datetime
+# current_sign_in_ip :string(255)
+# last_sign_in_ip :string(255)
+# created_at :datetime
+# updated_at :datetime
+# name :string(255)
+# admin :boolean default(FALSE), not null
+# projects_limit :integer default(10)
+# skype :string(255) default(""), not null
+# linkedin :string(255) default(""), not null
+# twitter :string(255) default(""), not null
+# authentication_token :string(255)
+# theme_id :integer default(1), not null
+# bio :string(255)
+# failed_attempts :integer default(0)
+# locked_at :datetime
+# username :string(255)
+# can_create_group :boolean default(TRUE), not null
+# can_create_team :boolean default(TRUE), not null
+# state :string(255)
+# color_scheme_id :integer default(1), not null
+# notification_level :integer default(1), not null
+# password_expires_at :datetime
+# created_by_id :integer
+# last_credential_check_at :datetime
+# avatar :string(255)
+# confirmation_token :string(255)
+# confirmed_at :datetime
+# confirmation_sent_at :datetime
+# unconfirmed_email :string(255)
+# hide_no_ssh_key :boolean default(FALSE)
+# website_url :string(255) default(""), not null
+# notification_email :string(255)
+# hide_no_password :boolean default(FALSE)
+# password_automatically_set :boolean default(FALSE)
+# location :string(255)
+# encrypted_otp_secret :string(255)
+# encrypted_otp_secret_iv :string(255)
+# encrypted_otp_secret_salt :string(255)
+# otp_required_for_login :boolean default(FALSE), not null
+# otp_backup_codes :text
+# public_email :string(255) default(""), not null
+# dashboard :integer default(0)
+# project_view :integer default(0)
+# consumed_timestep :integer
+# layout :integer default(0)
+# hide_project_limit :boolean default(FALSE)
+# unlock_token :string
+# otp_grace_period_started_at :datetime
#
require 'carrierwave/orm/activerecord'
@@ -195,10 +196,22 @@ class User < ActiveRecord::Base
state_machine :state, initial: :active do
event :block do
transition active: :blocked
+ transition ldap_blocked: :blocked
+ end
+
+ event :ldap_block do
+ transition active: :ldap_blocked
end
event :activate do
transition blocked: :active
+ transition ldap_blocked: :active
+ end
+
+ state :blocked, :ldap_blocked do
+ def blocked?
+ true
+ end
end
end
@@ -206,7 +219,7 @@ class User < ActiveRecord::Base
# Scopes
scope :admins, -> { where(admin: true) }
- scope :blocked, -> { with_state(:blocked) }
+ scope :blocked, -> { with_states(:blocked, :ldap_blocked) }
scope :active, -> { with_state(:active) }
scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
@@ -352,10 +365,13 @@ class User < ActiveRecord::Base
end
def namespace_uniq
+ # Return early if username already failed the first uniqueness validation
+ return if self.errors[:username].include?('has already been taken')
+
namespace_name = self.username
existing_namespace = Namespace.by_path(namespace_name)
if existing_namespace && existing_namespace != self.namespace
- self.errors.add :username, "already exists"
+ self.errors.add(:username, 'has already been taken')
end
end
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index e9413c34bae..2a65f0431c4 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -169,7 +169,7 @@ class WikiPage
private
def set_attributes
- attributes[:slug] = @page.escaped_url_path
+ attributes[:slug] = @page.url_path
attributes[:title] = @page.title
attributes[:format] = @page.format
end