Skip to content
Snippets Groups Projects
Commit fd82245b authored by Rémy Coutable's avatar Rémy Coutable
Browse files

Merge remote-tracking branch 'ce/8-6-stable' into 8-6-stable-ee

parents c5745c40 834dae6f
No related branches found
No related tags found
No related merge requests found
Pipeline #
Showing
with 230 additions and 21 deletions
Please view this file on the master branch, on stable branches it's out of date.
 
v 8.6.0 (unreleased)
- Add ability to move issue to another project
- Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu)
- Add confidential issues
- Bump gitlab_git to 9.0.3 (Stan Hu)
- Fix diff image view modes (2-up, swipe, onion skin) not working (Stan Hu)
- Support Golang subpackage fetching (Stan Hu)
- Bump Capybara gem to 2.6.2 (Stan Hu)
- New branch button appears on issues where applicable
Loading
Loading
0.6.5
0.7.1
Loading
Loading
@@ -7,6 +7,7 @@
#= require jquery
#= require jquery-ui/autocomplete
#= require jquery-ui/datepicker
#= require jquery-ui/draggable
#= require jquery-ui/effect-highlight
#= require jquery-ui/sortable
#= require jquery_ujs
Loading
Loading
class @IssuableForm
issueMoveConfirmMsg: 'Are you sure you want to move this issue to another project?'
wipRegex: /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i
constructor: (@form) ->
GitLab.GfmAutoComplete.setup()
new UsersSelect()
Loading
Loading
@@ -7,12 +9,13 @@ class @IssuableForm
 
@titleField = @form.find("input[name*='[title]']")
@descriptionField = @form.find("textarea[name*='[description]']")
@issueMoveField = @form.find("#move_to_project_id")
 
return unless @titleField.length && @descriptionField.length
 
@initAutosave()
 
@form.on "submit", @resetAutosave
@form.on "submit", @handleSubmit
@form.on "click", ".btn-cancel", @resetAutosave
 
@initWip()
Loading
Loading
@@ -30,6 +33,12 @@ class @IssuableForm
"description"
]
 
handleSubmit: =>
if (parseInt(@issueMoveField?.val()) ? 0) > 0
return false unless confirm(@issueMoveConfirmMsg)
@resetAutosave()
resetAutosave: =>
@titleField.data("autosave").reset()
@descriptionField.data("autosave").reset()
Loading
Loading
Loading
Loading
@@ -96,6 +96,12 @@ def create
def update
@issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue)
 
if params[:move_to_project_id].to_i > 0
new_project = Project.find(params[:move_to_project_id])
move_service = Issues::MoveService.new(project, current_user)
@issue = move_service.execute(@issue, new_project)
end
respond_to do |format|
format.js
format.html do
Loading
Loading
Loading
Loading
@@ -57,6 +57,19 @@ def milestone_options(object)
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
end
 
def project_options(issuable, current_user, ability: :read_project)
projects = current_user.authorized_projects
projects = projects.select do |project|
current_user.can?(ability, project)
end
no_project = OpenStruct.new(id: 0, name_with_namespace: 'No project')
projects.unshift(no_project)
projects.delete(issuable.project)
options_from_collection_for_select(projects, :id, :name_with_namespace)
end
def status_box_class(item)
if item.respond_to?(:expired?) && item.expired?
'status-box-expired'
Loading
Loading
Loading
Loading
@@ -36,6 +36,14 @@ def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_i
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end
 
def issue_moved_email(recipient, issue, new_issue, updated_by_user)
setup_issue_mail(issue.id, recipient.id)
@new_issue = new_issue
@new_project = new_issue.project
mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id))
end
private
 
def setup_issue_mail(issue_id, recipient_id)
Loading
Loading
Loading
Loading
@@ -209,4 +209,13 @@ def updated_tasks
Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
new_content: description)
end
##
# Method that checks if issuable can be moved to another project.
#
# Should be overridden if issuable can be moved.
#
def can_move?(*)
false
end
end
Loading
Loading
@@ -16,6 +16,7 @@
# state :string(255)
# iid :integer
# updated_by_id :integer
# moved_to_id :integer
#
 
require 'carrierwave/orm/activerecord'
Loading
Loading
@@ -37,6 +38,8 @@ class Issue < ActiveRecord::Base
ActsAsTaggableOn.strict_case_match = true
 
belongs_to :project
belongs_to :moved_to, class_name: 'Issue'
validates :project, presence: true
 
scope :of_group,
Loading
Loading
@@ -111,9 +114,9 @@ def referenced_merge_requests(current_user = nil)
end
 
def related_branches
return [] if self.project.empty_repo?
self.project.repository.branch_names.select { |branch| branch.end_with?("-#{iid}") }
project.repository.branch_names.select do |branch|
branch.end_with?("-#{iid}")
end
end
 
# Reset issue events cache
Loading
Loading
@@ -143,6 +146,18 @@ def closed_by_merge_requests(current_user = nil)
end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
end
 
def moved?
!moved_to.nil?
end
def can_move?(user, to_project = nil)
if to_project
return false unless user.can?(:admin_issue, to_project)
end
!moved? && user.can?(:admin_issue, self.project)
end
def to_branch_name
"#{title.parameterize}-#{iid}"
end
Loading
Loading
Loading
Loading
@@ -989,6 +989,7 @@ def create_repository
# Forked import is handled asynchronously
unless forked?
if gitlab_shell.add_repository(path_with_namespace)
repository.after_create
true
else
errors.add(:base, 'Failed to create repository via gitlab-shell')
Loading
Loading
Loading
Loading
@@ -135,23 +135,27 @@ def search_files(query)
end
 
def repository
Repository.new(path_with_namespace, @project)
@repository ||= Repository.new(path_with_namespace, @project)
end
 
def default_branch
wiki.class.default_ref
end
 
private
def create_repo!
if init_repo(path_with_namespace)
Gollum::Wiki.new(path_to_repo)
wiki = Gollum::Wiki.new(path_to_repo)
else
raise CouldNotCreateWikiError
end
repository.after_create
wiki
end
 
private
def init_repo(path_with_namespace)
gitlab_shell.add_repository(path_with_namespace)
end
Loading
Loading
Loading
Loading
@@ -47,12 +47,15 @@ def path_to_repo
end
 
def exists?
return false unless raw_repository
return @exists unless @exists.nil?
 
raw_repository.rugged
true
rescue Gitlab::Git::Repository::NoRepository
false
@exists = cache.fetch(:exists?) do
begin
raw_repository && raw_repository.rugged ? true : false
rescue Gitlab::Git::Repository::NoRepository
false
end
end
end
 
def empty?
Loading
Loading
@@ -358,12 +361,23 @@ def expire_avatar_cache(branch_name = nil, revision = nil)
@avatar = nil
end
 
def expire_exists_cache
cache.expire(:exists?)
@exists = nil
end
# Runs code after a repository has been created.
def after_create
expire_exists_cache
end
# Runs code just before a repository is deleted.
def before_delete
expire_cache if exists?
 
expire_root_ref_cache
expire_emptiness_caches
expire_exists_cache
end
 
# Runs code just before the HEAD of a repository is changed.
Loading
Loading
@@ -389,6 +403,7 @@ def before_remove_tag
# Runs code after a repository has been forked/imported.
def after_import
expire_emptiness_caches
expire_exists_cache
end
 
# Runs code after a new commit has been pushed.
Loading
Loading
Loading
Loading
@@ -464,7 +464,7 @@ def authorized_groups
Group.where("namespaces.id IN (#{union.to_sql})")
end
 
# Returns the groups a user is authorized to access.
# Returns projects user is authorized to access.
def authorized_projects
Project.where("projects.id IN (#{projects_union.to_sql})")
end
Loading
Loading
Loading
Loading
@@ -134,7 +134,7 @@ def process_commit_messages
closed_issues = commit.closes_issues(current_user)
closed_issues.each do |issue|
if can?(current_user, :update_issue, issue)
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit: commit)
end
end
end
Loading
Loading
module Issues
class CloseService < Issues::BaseService
def execute(issue, commit = nil)
def execute(issue, commit: nil, notifications: true, system_note: true)
if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue)
todo_service.close_issue(issue, current_user)
Loading
Loading
@@ -9,8 +9,8 @@ def execute(issue, commit = nil)
 
if project.default_issues_tracker? && issue.close
event_service.close_issue(issue, current_user)
create_note(issue, commit)
notification_service.close_issue(issue, current_user)
create_note(issue, commit) if system_note
notification_service.close_issue(issue, current_user) if notifications
todo_service.close_issue(issue, current_user)
execute_hooks(issue, 'close')
end
Loading
Loading
Loading
Loading
@@ -4,7 +4,7 @@ def execute
filter_params
label_params = params[:label_ids]
issue = project.issues.new(params.except(:label_ids))
issue.author = current_user
issue.author = params[:author] || current_user
 
if issue.save
issue.update_attributes(label_ids: label_params)
Loading
Loading
module Issues
class MoveService < Issues::BaseService
class MoveError < StandardError; end
def execute(issue, new_project)
@old_issue = issue
@old_project = @project
@new_project = new_project
unless issue.can_move?(current_user, new_project)
raise MoveError, 'Cannot move issue due to insufficient permissions!'
end
if @project == new_project
raise MoveError, 'Cannot move issue to project it originates from!'
end
# Using transaction because of a high resources footprint
# on rewriting notes (unfolding references)
#
ActiveRecord::Base.transaction do
# New issue tasks
#
@new_issue = create_new_issue
rewrite_notes
add_note_moved_from
# Old issue tasks
#
add_note_moved_to
close_issue
mark_as_moved
end
notify_participants
@new_issue
end
private
def create_new_issue
new_params = { id: nil, iid: nil, label_ids: [], milestone: nil,
project: @new_project, author: @old_issue.author,
description: unfold_references(@old_issue.description) }
new_params = @old_issue.serializable_hash.merge(new_params)
CreateService.new(@new_project, @current_user, new_params).execute
end
def rewrite_notes
@old_issue.notes.find_each do |note|
new_note = note.dup
new_params = { project: @new_project, noteable: @new_issue,
note: unfold_references(new_note.note),
created_at: note.created_at }
new_note.update(new_params)
end
end
def close_issue
close_service = CloseService.new(@old_project, @current_user)
close_service.execute(@old_issue, notifications: false, system_note: false)
end
def add_note_moved_from
SystemNoteService.noteable_moved(@new_issue, @new_project,
@old_issue, @current_user,
direction: :from)
end
def add_note_moved_to
SystemNoteService.noteable_moved(@old_issue, @old_project,
@new_issue, @current_user,
direction: :to)
end
def unfold_references(content)
rewriter = Gitlab::Gfm::ReferenceRewriter.new(content, @old_project,
@current_user)
rewriter.rewrite(@new_project)
end
def notify_participants
notification_service.issue_moved(@old_issue, @new_issue, @current_user)
end
def mark_as_moved
@old_issue.update(moved_to: @new_issue)
end
end
end
Loading
Loading
@@ -22,7 +22,7 @@ def close_issues(merge_request)
closed_issues = merge_request.closes_issues(current_user)
closed_issues.each do |issue|
if can?(current_user, :update_issue, issue)
Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request)
Issues::CloseService.new(project, current_user, {}).execute(issue, commit: merge_request)
end
end
end
Loading
Loading
Loading
Loading
@@ -236,6 +236,16 @@ def project_was_moved(project, old_path_with_namespace)
end
end
 
def issue_moved(issue, new_issue, current_user)
recipients = build_recipients(issue, issue.project, current_user)
recipients.map do |recipient|
email = mailer.issue_moved_email(recipient, issue, new_issue, current_user)
email.deliver_later
email
end
end
protected
 
# Get project users with WATCH notification level
Loading
Loading
Loading
Loading
@@ -427,4 +427,26 @@ def self.change_task_status(noteable, project, author, new_task)
body = "Marked the task **#{new_task.source}** as #{status_label}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when noteable has been moved to another project
#
# direction - symbol, :to or :from
# noteable - Noteable object
# noteable_ref - Referenced noteable
# author - User performing the move
#
# Example Note text:
#
# "Moved to some_namespace/project_new#11"
#
# Returns the created Note object
def self.noteable_moved(noteable, project, noteable_ref, author, direction:)
unless [:to, :from].include?(direction)
raise ArgumentError, "Invalid direction `#{direction}`"
end
cross_reference = noteable_ref.to_reference(project)
body = "Moved #{direction} #{cross_reference}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment