diff options
Diffstat (limited to 'app/models')
25 files changed, 249 insertions, 198 deletions
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 826539d5c4d..3fee6c18770 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -30,23 +30,23 @@ module Ci end event :run do - transition any => :running + transition any - [:running] => :running end event :skip do - transition any => :skipped + transition any - [:skipped] => :skipped end event :drop do - transition any => :failed + transition any - [:failed] => :failed end event :succeed do - transition any => :success + transition any - [:success] => :success end event :cancel do - transition any => :canceled + transition any - [:canceled] => :canceled end # IMPORTANT @@ -271,7 +271,7 @@ module Ci end def update_status - with_lock do + Gitlab::OptimisticLocking.retry_lock(self) do case latest_builds_status when 'pending' then enqueue when 'running' then run diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 4cb3a69416e..d159fc6c5c7 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -73,16 +73,16 @@ class CommitStatus < ActiveRecord::Base transition [:created, :pending, :running] => :canceled end - after_transition created: [:pending, :running] do |commit_status| - commit_status.update_attributes queued_at: Time.now + before_transition created: [:pending, :running] do |commit_status| + commit_status.queued_at = Time.now end - after_transition [:created, :pending] => :running do |commit_status| - commit_status.update_attributes started_at: Time.now + before_transition [:created, :pending] => :running do |commit_status| + commit_status.started_at = Time.now end - after_transition any => [:success, :failed, :canceled] do |commit_status| - commit_status.update_attributes finished_at: Time.now + before_transition any => [:success, :failed, :canceled] do |commit_status| + commit_status.finished_at = Time.now end after_transition do |commit_status, transition| diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 17c3b526c97..613444e0d70 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -12,6 +12,7 @@ module Issuable include Subscribable include StripAttribute include Awardable + include Taskable included do cache_markdown_field :title, pipeline: :single_line diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb index 9216122923e..6d88951c713 100644 --- a/app/models/concerns/project_features_compatibility.rb +++ b/app/models/concerns/project_features_compatibility.rb @@ -31,7 +31,7 @@ module ProjectFeaturesCompatibility def write_feature_attribute(field, value) build_project_feature unless project_feature - access_level = value == "true" ? ProjectFeature::ENABLED : ProjectFeature::DISABLED + access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED project_feature.update_attribute(field, access_level) end end diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 12b23f00769..7edb0acd56c 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -38,16 +38,21 @@ module Sortable private - def highest_label_priority(target_type:, target_column:, project_column:, excluded_labels: []) + def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: []) query = Label.select(LabelPriority.arel_table[:priority].minimum). left_join_priorities. joins(:label_links). where("label_priorities.project_id = #{project_column}"). - where(label_links: { target_type: target_type }). where("label_links.target_id = #{target_column}"). reorder(nil) - query.where.not(title: excluded_labels) if excluded_labels.present? + if target_type_column + query = query.where("label_links.target_type = #{target_type_column}") + else + query = query.where(label_links: { target_type: target_type }) + end + + query = query.where.not(title: excluded_labels) if excluded_labels.present? query end diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index a3ac577cf3e..ebc75100a54 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -53,10 +53,22 @@ module Taskable # Return a string that describes the current state of this Taskable's task # list items, e.g. "12 of 20 tasks completed" - def task_status + def task_status(short: false) return '' if description.blank? + prep, completed = if short + ['/', ''] + else + [' of ', ' completed'] + end + sum = tasks.summary - "#{sum.complete_count} of #{sum.item_count} #{'task'.pluralize(sum.item_count)} completed" + "#{sum.complete_count}#{prep}#{sum.item_count} #{'task'.pluralize(sum.item_count)}#{completed}" + end + + # Return a short string that describes the current state of this Taskable's + # task list items -- for small screens + def task_status_short + task_status(short: true) end end diff --git a/app/models/event.rb b/app/models/event.rb index 3993b35f96d..43e67069b70 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,6 +1,6 @@ class Event < ActiveRecord::Base include Sortable - default_scope { where.not(author_id: nil) } + default_scope { reorder(nil).where.not(author_id: nil) } CREATED = 1 UPDATED = 2 diff --git a/app/models/group.rb b/app/models/group.rb index 552e1154df6..d9e90cd256a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -68,7 +68,7 @@ class Group < Namespace end def web_url - Gitlab::Routing.url_helpers.group_url(self) + Gitlab::Routing.url_helpers.group_canonical_url(self) end def human_name diff --git a/app/models/group_label.rb b/app/models/group_label.rb index a698b532d19..68841ace2e6 100644 --- a/app/models/group_label.rb +++ b/app/models/group_label.rb @@ -5,6 +5,10 @@ class GroupLabel < Label alias_attribute :subject, :group + def subject_foreign_key + 'group_id' + end + def to_reference(source_project = nil, target_project = nil, format: :id) super(source_project, target_project, format: format) end diff --git a/app/models/issue.rb b/app/models/issue.rb index 89158a50353..4f02b02c488 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -5,7 +5,6 @@ class Issue < ActiveRecord::Base include Issuable include Referable include Sortable - include Taskable include Spammable include FasterCacheKeys @@ -287,10 +286,12 @@ class Issue < ActiveRecord::Base def as_json(options = {}) super(options).tap do |json| + json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user) + if options.has_key?(:labels) json[:labels] = labels.as_json( project: project, - only: [:id, :title, :description, :color], + only: [:id, :title, :description, :color, :priority], methods: [:text_color] ) end diff --git a/app/models/label.rb b/app/models/label.rb index 149fd98ecb3..d9287f2dc29 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -92,16 +92,23 @@ class Label < ActiveRecord::Base nil end - def open_issues_count(user = nil, project = nil) - issues_count(user, project_id: project.try(:id) || project_id, state: 'opened') + def open_issues_count(user = nil) + issues_count(user, state: 'opened') end - def closed_issues_count(user = nil, project = nil) - issues_count(user, project_id: project.try(:id) || project_id, state: 'closed') + def closed_issues_count(user = nil) + issues_count(user, state: 'closed') end - def open_merge_requests_count(user = nil, project = nil) - merge_requests_count(user, project_id: project.try(:id) || project_id, state: 'opened') + def open_merge_requests_count(user = nil) + params = { + subject_foreign_key => subject.id, + label_name: title, + scope: 'all', + state: 'opened' + } + + MergeRequestsFinder.new(user, params.with_indifferent_access).execute.count end def prioritize!(project, value) @@ -167,15 +174,8 @@ class Label < ActiveRecord::Base end def issues_count(user, params = {}) - IssuesFinder.new(user, params.reverse_merge(label_name: title, scope: 'all')) - .execute - .count - end - - def merge_requests_count(user, params = {}) - MergeRequestsFinder.new(user, params.reverse_merge(label_name: title, scope: 'all')) - .execute - .count + params.merge!(subject_foreign_key => subject.id, label_name: title, scope: 'all') + IssuesFinder.new(user, params.with_indifferent_access).execute.count end def label_format_reference(format = :id) diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 18657c3e1c8..7712d5783e0 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -17,4 +17,10 @@ class LfsObject < ActiveRecord::Base def project_allowed_access?(project) projects.exists?(storage_project(project).id) end + + def self.destroy_unreferenced + joins("LEFT JOIN lfs_objects_projects ON lfs_objects_projects.lfs_object_id = #{table_name}.id") + .where(lfs_objects_projects: { id: nil }) + .destroy_all + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 4872f8b8649..0397c57f935 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -3,7 +3,6 @@ class MergeRequest < ActiveRecord::Base include Issuable include Referable include Sortable - include Taskable include Importable belongs_to :target_project, class_name: "Project" diff --git a/app/models/project.rb b/app/models/project.rb index fbf7012972e..d5512dfaf9c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -30,6 +30,11 @@ class Project < ActiveRecord::Base default_value_for :container_registry_enabled, gitlab_config_features.container_registry default_value_for(:repository_storage) { current_application_settings.repository_storage } default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } + 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 :snippets_enabled, gitlab_config_features.snippets after_create :ensure_dir_exist after_create :create_project_feature, unless: :project_feature @@ -390,7 +395,7 @@ class Project < ActiveRecord::Base end def group_ids - joins(:namespace).where(namespaces: { type: 'Group' }).pluck(:namespace_id) + joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id) end end @@ -738,7 +743,7 @@ class Project < ActiveRecord::Base def create_labels Label.templates.each do |label| params = label.attributes.except('id', 'template', 'created_at', 'updated_at') - Labels::FindOrCreateService.new(owner, self, params).execute + Labels::FindOrCreateService.new(nil, self, params).execute(skip_authorization: true) end end diff --git a/app/models/project_label.rb b/app/models/project_label.rb index 33c2b617715..82f47f0e8fd 100644 --- a/app/models/project_label.rb +++ b/app/models/project_label.rb @@ -12,6 +12,10 @@ class ProjectLabel < Label alias_attribute :subject, :project + def subject_foreign_key + 'project_id' + end + def to_reference(target_project = nil, format: :id) super(project, target_project, format: format) end diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb index 81af55aa29a..338e685339a 100644 --- a/app/models/project_services/bugzilla_service.rb +++ b/app/models/project_services/bugzilla_service.rb @@ -1,4 +1,6 @@ class BugzillaService < IssueTrackerService + validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated? + prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url def title diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index d9fba3d4a41..b2f426dc2ac 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -1,4 +1,6 @@ class CustomIssueTrackerService < IssueTrackerService + validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated? + prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url def title diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index 5d17c358330..6bd8d4ec568 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -1,6 +1,8 @@ class GitlabIssueTrackerService < IssueTrackerService include Gitlab::Routing.url_helpers + validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated? + prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url default_value_for :default, true diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index b26ddd518d7..207bb816ad1 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -1,6 +1,4 @@ class IssueTrackerService < Service - validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated? - default_value_for :category, 'issue_tracker' # Pattern used to extract links from comments @@ -38,18 +36,24 @@ class IssueTrackerService < Service ] end - def initialize_properties - if properties.nil? - if enabled_in_gitlab_config + # Initialize with default properties values + # or receive a block with custom properties + def initialize_properties(&block) + return unless properties.nil? + + if enabled_in_gitlab_config + if block_given? + yield + else self.properties = { title: issues_tracker['title'], project_url: issues_tracker['project_url'], issues_url: issues_tracker['issues_url'], new_issue_url: issues_tracker['new_issue_url'] } - else - self.properties = {} end + else + self.properties = {} end end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index f81b66fd219..5bcf199d468 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -1,15 +1,32 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# 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 - include HTTParty include Gitlab::Routing.url_helpers - DEFAULT_API_VERSION = 2 - - prop_accessor :username, :password, :api_url, :jira_issue_transition_id, - :title, :description, :project_url, :issues_url, :new_issue_url + validates :url, url: true, presence: true, if: :activated? + validates :project_key, presence: true, if: :activated? - validates :api_url, presence: true, url: true, if: :activated? - - before_validation :set_api_url, :set_jira_issue_transition_id + prop_accessor :username, :password, :url, :project_key, + :jira_issue_transition_id, :title, :description before_update :reset_password @@ -18,16 +35,46 @@ class JiraService < IssueTrackerService @reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)} end + def initialize_properties + super do + self.properties = { + title: issues_tracker['title'], + url: issues_tracker['url'] + } + end + end + def reset_password # don't reset the password if a new one is provided - if api_url_changed? && !password_touched? + if url_changed? && !password_touched? self.password = nil end end + def options + url = URI.parse(self.url) + + { + username: self.username, + password: self.password, + site: URI.join(url, '/').to_s, + context_path: url.path, + auth_type: :basic, + read_timeout: 120, + use_ssl: url.scheme == 'https' + } + end + + def client + @client ||= JIRA::Client.new(options) + end + + def jira_project + @jira_project ||= client.Project.find(project_key) + end + def help - 'Setting `project_url`, `issues_url` and `new_issue_url` will '\ - 'allow a user to easily navigate to the Jira issue tracker. See the '\ + 'See the ' \ '[integration doc](http://doc.gitlab.com/ce/integration/external-issue-tracker.html) '\ 'for details.' end @@ -53,12 +100,26 @@ class JiraService < IssueTrackerService end def fields - super.push( - { type: 'text', name: 'api_url', placeholder: 'https://jira.example.com/rest/api/2' }, + [ + { type: 'text', name: 'url', title: 'URL', placeholder: 'https://jira.example.com' }, + { type: 'text', name: 'project_key', placeholder: 'Project Key' }, { type: 'text', name: 'username', placeholder: '' }, { type: 'password', name: 'password', placeholder: '' }, { type: 'text', name: 'jira_issue_transition_id', placeholder: '2' } - ) + ] + end + + # URLs to redirect from Gitlab issues pages to jira issue tracker + def project_url + "#{url}/issues/?jql=project=#{project_key}" + end + + def issues_url + "#{url}/browse/:id" + end + + def new_issue_url + "#{url}/secure/CreateIssue.jspa" end def execute(push, issue = nil) @@ -72,7 +133,7 @@ class JiraService < IssueTrackerService end def create_cross_reference_note(mentioned, noteable, author) - issue_name = mentioned.id + issue_key = mentioned.id project = self.project noteable_name = noteable.class.name.underscore.downcase noteable_id = if noteable.is_a?(Commit) @@ -99,58 +160,28 @@ class JiraService < IssueTrackerService } } - add_comment(data, issue_name) + add_comment(data, issue_key) end def test_settings - return unless api_url.present? - result = JiraService.get( - jira_api_test_url, - headers: { - 'Content-Type' => 'application/json', - 'Authorization' => "Basic #{auth}" - } - ) + return unless url.present? + # Test settings by getting the project + jira_project - case result.code - when 201, 200 - Rails.logger.info("#{self.class.name} SUCCESS #{result.code}: Successfully connected to #{api_url}.") - true - else - Rails.logger.info("#{self.class.name} ERROR #{result.code}: #{result.parsed_response}") - false - end - rescue Errno::ECONNREFUSED => e - Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{api_url}." + rescue Errno::ECONNREFUSED, JIRA::HTTPError => e + Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{url}." false end private - def build_api_url_from_project_url - server = URI(project_url) - default_ports = [["http", 80], ["https", 443]].include?([server.scheme, server.port]) - server_url = "#{server.scheme}://#{server.host}" - server_url.concat(":#{server.port}") unless default_ports - "#{server_url}/rest/api/#{DEFAULT_API_VERSION}" - rescue - "" # looks like project URL was not valid - end - - def set_api_url - self.api_url = build_api_url_from_project_url if self.api_url.blank? - end - - def set_jira_issue_transition_id - self.jira_issue_transition_id ||= "2" - end - def close_issue(entity, issue) commit_id = if entity.is_a?(Commit) entity.id elsif entity.is_a?(MergeRequest) entity.diff_head_sha end + commit_url = build_entity_url(:commit, commit_id) # Depending on the JIRA project's workflow, a comment during transition @@ -161,24 +192,16 @@ class JiraService < IssueTrackerService end def transition_issue(issue) - message = { - transition: { - id: jira_issue_transition_id - } - } - send_message(close_issue_url(issue.iid), message.to_json) + issue = client.Issue.find(issue.iid) + issue.transitions.build.save(transition: { id: jira_issue_transition_id }) end def add_issue_solved_comment(issue, commit_id, commit_url) - comment = { - body: "Issue solved with [#{commit_id}|#{commit_url}]." - } - - send_message(comment_url(issue.iid), comment.to_json) + comment = "Issue solved with [#{commit_id}|#{commit_url}]." + send_message(issue.iid, comment) end - def add_comment(data, issue_name) - url = comment_url(issue_name) + def add_comment(data, issue_key) user_name = data[:user][:name] user_url = data[:user][:url] entity_name = data[:entity][:name] @@ -186,68 +209,31 @@ class JiraService < IssueTrackerService entity_title = data[:entity][:title] project_name = data[:project][:name] - message = { - body: %Q{[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'} - } + message = "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'" - unless existing_comment?(issue_name, message[:body]) - send_message(url, message.to_json) + unless comment_exists?(issue_key, message) + send_message(issue_key, message) end end - def auth - require 'base64' - Base64.urlsafe_encode64("#{self.username}:#{self.password}") - end - - def send_message(url, message) - return unless api_url.present? - result = JiraService.post( - url, - body: message, - headers: { - 'Content-Type' => 'application/json', - 'Authorization' => "Basic #{auth}" - } - ) - - message = case result.code - when 201, 200, 204 - "#{self.class.name} SUCCESS #{result.code}: Successfully posted to #{url}." - when 401 - "#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again." - else - "#{self.class.name} ERROR #{result.code}: #{result.parsed_response}" - end - - Rails.logger.info(message) - message - rescue URI::InvalidURIError, Errno::ECONNREFUSED => e - Rails.logger.info "#{self.class.name} ERROR: #{e.message}. Hostname: #{url}." + def comment_exists?(issue_key, message) + comments = client.Issue.find(issue_key).comments + comments.map { |comment| comment.body.include?(message) }.any? end - def existing_comment?(issue_name, new_comment) - return unless api_url.present? - result = JiraService.get( - comment_url(issue_name), - headers: { - 'Content-Type' => 'application/json', - 'Authorization' => "Basic #{auth}" - } - ) + def send_message(issue_key, message) + return unless url.present? - case result.code - when 201, 200 - existing_comments = JSON.parse(result.body)['comments'] + issue = client.Issue.find(issue_key) - if existing_comments.present? - return existing_comments.map { |comment| comment['body'].include?(new_comment) }.any? - end + if issue.comments.build.save!(body: message) + result_message = "#{self.class.name} SUCCESS: Successfully posted to #{url}." end - false - rescue JSON::ParserError - false + Rails.logger.info(result_message) + result_message + rescue URI::InvalidURIError, Errno::ECONNREFUSED, JIRA::HTTPError => e + Rails.logger.info "#{self.class.name} Send message ERROR: #{url} - #{e.message}" end def resource_url(resource) @@ -267,16 +253,4 @@ class JiraService < IssueTrackerService ) ) end - - def close_issue_url(issue_name) - "#{self.api_url}/issue/#{issue_name}/transitions" - end - - def comment_url(issue_name) - "#{self.api_url}/issue/#{issue_name}/comment" - end - - def jira_api_test_url - "#{self.api_url}/myself" - end end diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb index f634e0772c0..f9da273cf08 100644 --- a/app/models/project_services/redmine_service.rb +++ b/app/models/project_services/redmine_service.rb @@ -1,4 +1,6 @@ class RedmineService < IssueTrackerService + validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated? + prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url def title diff --git a/app/models/project_team.rb b/app/models/project_team.rb index 79d041d2775..a6e911df9bd 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -125,14 +125,8 @@ class ProjectTeam max_member_access(user.id) == Gitlab::Access::MASTER end - def member?(user, min_member_access = nil) - member = !!find_member(user.id) - - if min_member_access - member && max_member_access(user.id) >= min_member_access - else - member - end + def member?(user, min_member_access = Gitlab::Access::GUEST) + max_member_access(user.id) >= min_member_access end def human_max_access(user_id) diff --git a/app/models/repository.rb b/app/models/repository.rb index 4ae9c20726f..30be7262438 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -11,6 +11,20 @@ class Repository attr_accessor :path_with_namespace, :project + def self.storages + Gitlab.config.repositories.storages + end + + def self.remove_storage_from_path(repo_path) + storages.find do |_, storage_path| + if repo_path.start_with?(storage_path) + return repo_path.sub(storage_path, '') + end + end + + repo_path + end + def initialize(path_with_namespace, project) @path_with_namespace = path_with_namespace @project = project @@ -181,7 +195,7 @@ class Repository before_remove_branch branch = find_branch(branch_name) - oldrev = branch.try(:target).try(:id) + oldrev = branch.try(:dereferenced_target).try(:id) newrev = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name @@ -297,10 +311,10 @@ class Repository # Rugged seems to throw a `ReferenceError` when given branch_names rather # than SHA-1 hashes number_commits_behind = raw_repository. - count_commits_between(branch.target.sha, root_ref_hash) + count_commits_between(branch.dereferenced_target.sha, root_ref_hash) number_commits_ahead = raw_repository. - count_commits_between(root_ref_hash, branch.target.sha) + count_commits_between(root_ref_hash, branch.dereferenced_target.sha) { behind: number_commits_behind, ahead: number_commits_ahead } end @@ -682,11 +696,11 @@ class Repository branches.sort_by(&:name) when 'updated_desc' branches.sort do |a, b| - commit(b.target).committed_date <=> commit(a.target).committed_date + commit(b.dereferenced_target).committed_date <=> commit(a.dereferenced_target).committed_date end when 'updated_asc' branches.sort do |a, b| - commit(a.target).committed_date <=> commit(b.target).committed_date + commit(a.dereferenced_target).committed_date <=> commit(b.dereferenced_target).committed_date end else branches @@ -861,7 +875,7 @@ class Repository branch = find_branch(ref) if branch - last_commit = branch.target + last_commit = branch.dereferenced_target index.read_tree(last_commit.raw_commit.tree) parents = [last_commit.sha] end @@ -948,7 +962,7 @@ class Repository end def revert(user, commit, base_branch, revert_tree_id = nil) - source_sha = find_branch(base_branch).target.sha + source_sha = find_branch(base_branch).dereferenced_target.sha revert_tree_id ||= check_revert_content(commit, base_branch) return false unless revert_tree_id @@ -965,7 +979,7 @@ class Repository end def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil) - source_sha = find_branch(base_branch).target.sha + source_sha = find_branch(base_branch).dereferenced_target.sha cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch) return false unless cherry_pick_tree_id @@ -994,7 +1008,7 @@ class Repository end def check_revert_content(commit, base_branch) - source_sha = find_branch(base_branch).target.sha + source_sha = find_branch(base_branch).dereferenced_target.sha args = [commit.id, source_sha] args << { mainline: 1 } if commit.merge_commit? @@ -1008,7 +1022,7 @@ class Repository end def check_cherry_pick_content(commit, base_branch) - source_sha = find_branch(base_branch).target.sha + source_sha = find_branch(base_branch).dereferenced_target.sha args = [commit.id, source_sha] args << 1 if commit.merge_commit? @@ -1081,7 +1095,7 @@ class Repository if rugged.lookup(newrev).parent_ids.empty? || target_branch.nil? oldrev = Gitlab::Git::BLANK_SHA else - oldrev = rugged.merge_base(newrev, target_branch.target.sha) + oldrev = rugged.merge_base(newrev, target_branch.dereferenced_target.sha) end GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do @@ -1141,7 +1155,7 @@ class Repository end def tags_sorted_by_committed_date - tags.sort_by { |tag| tag.target.committed_date } + tags.sort_by { |tag| tag.dereferenced_target.committed_date } end def keep_around_ref_name(sha) diff --git a/app/models/todo.rb b/app/models/todo.rb index 11c072dd000..f5ade1cc293 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -53,7 +53,7 @@ class Todo < ActiveRecord::Base # Need to order by created_at last because of differences on Mysql and Postgres when joining by type "Merge_request/Issue" def order_by_labels_priority params = { - target_type: ['Issue', 'MergeRequest'], + target_type_column: "todos.target_type", target_column: "todos.target_id", project_column: "todos.project_id" } diff --git a/app/models/user.rb b/app/models/user.rb index 9e76df63d31..af3c0b7dc02 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -93,8 +93,10 @@ class User < ActiveRecord::Base # # Validations # + # Note: devise :validatable above adds validations for :email and :password validates :name, presence: true - validates :notification_email, presence: true, email: true + validates :notification_email, presence: true + validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email } validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true validates :bio, length: { maximum: 255 }, allow_blank: true validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 } @@ -256,6 +258,24 @@ class User < ActiveRecord::Base ) end + # searches user by given pattern + # it compares name, email, username fields and user's secondary emails with given pattern + # This method uses ILIKE on PostgreSQL and LIKE on MySQL. + + def search_with_secondary_emails(query) + table = arel_table + email_table = Email.arel_table + pattern = "%#{query}%" + matched_by_emails_user_ids = email_table.project(email_table[:user_id]).where(email_table[:email].matches(pattern)) + + where( + table[:name].matches(pattern). + or(table[:email].matches(pattern)). + or(table[:username].matches(pattern)). + or(table[:id].in(matched_by_emails_user_ids)) + ) + end + def by_login(login) return nil unless login |