399 lines
15 KiB
Ruby
399 lines
15 KiB
Ruby
|
# Code Review plugin for Redmine
|
||
|
# Copyright (C) 2009-2013 Haruyuki Iida
|
||
|
#
|
||
|
# This program is free software; you can redistribute it and/or
|
||
|
# modify it under the terms of the GNU General Public License
|
||
|
# as published by the Free Software Foundation; either version 2
|
||
|
# of the License, or (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program; if not, write to the Free Software
|
||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
|
||
|
class CodeReviewController < ApplicationController
|
||
|
unloadable
|
||
|
before_filter :find_project, :authorize, :find_user, :find_setting, :find_repository
|
||
|
|
||
|
helper :sort
|
||
|
include SortHelper
|
||
|
helper :journals
|
||
|
helper :projects
|
||
|
include ProjectsHelper
|
||
|
helper :issues
|
||
|
include IssuesHelper
|
||
|
helper :code_review
|
||
|
include CodeReviewHelper
|
||
|
helper :custom_fields
|
||
|
include CustomFieldsHelper
|
||
|
|
||
|
def index
|
||
|
sort_init "#{Issue.table_name}.id", 'desc'
|
||
|
sort_update ["#{Issue.table_name}.id", "#{Issue.table_name}.status_id", "#{Issue.table_name}.subject", "path", "updated_at", "user_id", "#{Changeset.table_name}.committer", "#{Changeset.table_name}.revision"]
|
||
|
|
||
|
limit = per_page_option
|
||
|
@review_count = CodeReview.count(:conditions => ['project_id = ? and issue_id is NOT NULL', @project.id])
|
||
|
@all_review_count = CodeReview.count(:conditions => ['project_id = ?', @project.id])
|
||
|
@review_pages = Paginator.new self, @review_count, limit, params['page']
|
||
|
@show_closed = (params['show_closed'] == 'true')
|
||
|
show_closed_option = " and #{IssueStatus.table_name}.is_closed = ? "
|
||
|
if (@show_closed)
|
||
|
show_closed_option = ''
|
||
|
end
|
||
|
conditions = ["#{CodeReview.table_name}.project_id = ? and issue_id is NOT NULL" + show_closed_option, @project.id]
|
||
|
unless (@show_closed)
|
||
|
conditions << false
|
||
|
end
|
||
|
|
||
|
@reviews = CodeReview.order(sort_clause).limit(limit).where(conditions).joins(
|
||
|
"left join #{Change.table_name} on change_id = #{Change.table_name}.id left join #{Changeset.table_name} on #{Change.table_name}.changeset_id = #{Changeset.table_name}.id " +
|
||
|
"left join #{Issue.table_name} on issue_id = #{Issue.table_name}.id " +
|
||
|
"left join #{IssueStatus.table_name} on #{Issue.table_name}.status_id = #{IssueStatus.table_name}.id").offset(@review_pages.current.offset)
|
||
|
@i_am_member = @user.member_of?(@project)
|
||
|
render :template => 'code_review/index.html.erb', :layout => !request.xhr?
|
||
|
end
|
||
|
|
||
|
def new
|
||
|
begin
|
||
|
CodeReview.transaction {
|
||
|
@review = CodeReview.new
|
||
|
@review.issue = Issue.new
|
||
|
|
||
|
if params[:issue] and params[:issue][:tracker_id]
|
||
|
@review.issue.tracker_id = params[:issue][:tracker_id].to_i
|
||
|
else
|
||
|
@review.issue.tracker_id = @setting.tracker_id
|
||
|
end
|
||
|
@review.safe_attributes = params[:review]
|
||
|
@review.project_id = @project.id
|
||
|
@review.issue.project_id = @project.id
|
||
|
|
||
|
@review.user_id = @user.id
|
||
|
@review.updated_by_id = @user.id
|
||
|
@review.issue.start_date = Date.today
|
||
|
@review.action_type = params[:action_type]
|
||
|
@review.rev = params[:rev] unless params[:rev].blank?
|
||
|
@review.rev_to = params[:rev_to] unless params[:rev_to].blank?
|
||
|
@review.file_path = params[:path] unless params[:path].blank?
|
||
|
@review.file_count = params[:file_count].to_i unless params[:file_count].blank?
|
||
|
@review.attachment_id = params[:attachment_id].to_i unless params[:attachment_id].blank?
|
||
|
@issue = @review.issue
|
||
|
@review.issue.safe_attributes = params[:issue] unless params[:issue].blank?
|
||
|
@review.diff_all = (params[:diff_all] == 'true')
|
||
|
|
||
|
@parent_candidate = get_parent_candidate(@review.rev) if @review.rev
|
||
|
|
||
|
if request.post?
|
||
|
@review.issue.save!
|
||
|
if @review.changeset
|
||
|
@review.changeset.issues.each {|issue|
|
||
|
create_relation @review, issue, @setting.issue_relation_type
|
||
|
} if @setting.auto_relation?
|
||
|
elsif @review.attachment and @review.attachment.container_type == 'Issue'
|
||
|
issue = Issue.find_by_id(@review.attachment.container_id)
|
||
|
create_relation @review, issue, @setting.issue_relation_type if @setting.auto_relation?
|
||
|
end
|
||
|
@review.open_assignment_issues(@user.id).each {|issue|
|
||
|
create_relation @review, issue, IssueRelation::TYPE_RELATES
|
||
|
watcher = Watcher.new
|
||
|
watcher.watchable_id = @review.issue.id
|
||
|
watcher.watchable_type = 'Issue'
|
||
|
watcher.user = issue.author
|
||
|
watcher.save!
|
||
|
}
|
||
|
@review.save!
|
||
|
|
||
|
render :partial => 'add_success', :status => 200
|
||
|
return
|
||
|
else
|
||
|
change_id = params[:change_id].to_i unless params[:change_id].blank?
|
||
|
@review.change = Change.find(change_id) if change_id
|
||
|
@review.line = params[:line].to_i unless params[:line].blank?
|
||
|
if (@review.changeset and @review.changeset.user_id)
|
||
|
@review.issue.assigned_to_id = @review.changeset.user_id
|
||
|
end
|
||
|
@default_version_id = @review.issue.fixed_version.id if @review.issue.fixed_version
|
||
|
if @review.changeset and @default_version_id.blank?
|
||
|
@review.changeset.issues.each {|issue|
|
||
|
if issue.fixed_version
|
||
|
@default_version_id = issue.fixed_version.id
|
||
|
break;
|
||
|
end
|
||
|
}
|
||
|
end
|
||
|
@review.open_assignment_issues(@user.id).each {|issue|
|
||
|
if issue.fixed_version
|
||
|
@default_version_id = issue.fixed_version.id
|
||
|
break;
|
||
|
end
|
||
|
} unless @default_version_id
|
||
|
|
||
|
|
||
|
end
|
||
|
render :partial => 'new_form', :status => 200
|
||
|
}
|
||
|
rescue ActiveRecord::RecordInvalid
|
||
|
render :partial => 'new_form', :status => 200
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def assign
|
||
|
code = {}
|
||
|
code[:action_type] = params[:action_type] unless params[:action_type].blank?
|
||
|
code[:rev] = params[:rev] unless params[:rev].blank?
|
||
|
code[:rev_to] = params[:rev_to] unless params[:rev_to].blank?
|
||
|
code[:path] = params[:path] unless params[:path].blank?
|
||
|
code[:change_id] = params[:change_id].to_i unless params[:change_id].blank?
|
||
|
code[:changeset_id] = params[:changeset_id].to_i unless params[:changeset_id].blank?
|
||
|
code[:attachment_id] = params[:attachment_id].to_i unless params[:attachment_id].blank?
|
||
|
code[:repository_id] = @repository_id if @repository_id
|
||
|
|
||
|
changeset = Changeset.find(code[:changeset_id]) if code[:changeset_id]
|
||
|
if (changeset == nil and code[:change_id] != nil)
|
||
|
change = Change.find(code[:change_id])
|
||
|
changeset = change.changeset if change
|
||
|
end
|
||
|
attachment = Attachment.find(code[:attachment_id]) if code[:attachment_id]
|
||
|
|
||
|
issue = {}
|
||
|
issue[:subject] = l(:code_review_requrest)
|
||
|
issue[:subject] << " [#{changeset.text_tag}: #{changeset.short_comments}]" if changeset
|
||
|
unless changeset
|
||
|
issue[:subject] << " [#{attachment.filename}]" if attachment
|
||
|
end
|
||
|
issue[:tracker_id] = @setting.assignment_tracker_id if @setting.assignment_tracker_id
|
||
|
|
||
|
redirect_to :controller => 'issues', :action => "new" , :project_id => @project,
|
||
|
:issue => issue, :code => code
|
||
|
end
|
||
|
|
||
|
def update_diff_view
|
||
|
@show_review_id = params[:review_id].to_i unless params[:review_id].blank?
|
||
|
@show_review = CodeReview.find(@show_review_id) if @show_review_id
|
||
|
@review = CodeReview.new
|
||
|
@rev = params[:rev] unless params[:rev].blank?
|
||
|
@rev_to = params[:rev_to] unless params[:rev_to].blank?
|
||
|
@path = params[:path] unless params[:path].blank?
|
||
|
@paths = []
|
||
|
@paths << @path unless @path.blank?
|
||
|
|
||
|
@action_type = params[:action_type]
|
||
|
changeset = @repository.find_changeset_by_name(@rev)
|
||
|
if @paths.empty?
|
||
|
changeset.filechanges.each{|chg|
|
||
|
}
|
||
|
end
|
||
|
|
||
|
url = @repository.url
|
||
|
root_url = @repository.root_url
|
||
|
if (url == nil || root_url == nil)
|
||
|
fullpath = @path
|
||
|
else
|
||
|
rootpath = url[root_url.length, url.length - root_url.length]
|
||
|
if rootpath.blank?
|
||
|
fullpath = @path
|
||
|
else
|
||
|
fullpath = (rootpath + '/' + @path).gsub(/[\/]+/, '/')
|
||
|
end
|
||
|
end
|
||
|
@change = nil
|
||
|
changeset.filechanges.each{|chg|
|
||
|
@change = chg if ((chg.path == fullpath) or ("/#{chg.path}" == fullpath)) or (chg.path == "/#{@path}")
|
||
|
} unless @path.blank?
|
||
|
|
||
|
@changeset = changeset
|
||
|
if @path
|
||
|
@reviews = CodeReview.where(['file_path = ? and rev = ? and issue_id is NOT NULL', @path, @rev]).all
|
||
|
else
|
||
|
@reviews = CodeReview.where(['rev = ? and issue_id is NOT NULL', @rev]).all
|
||
|
end
|
||
|
@review.change_id = @change.id if @change
|
||
|
|
||
|
#render :partial => 'show_error'
|
||
|
#return
|
||
|
|
||
|
|
||
|
|
||
|
render :partial => 'update_diff_view'
|
||
|
end
|
||
|
|
||
|
def update_attachment_view
|
||
|
@show_review_id = params[:review_id].to_i unless params[:review_id].blank?
|
||
|
@attachment_id = params[:attachment_id].to_i
|
||
|
@show_review = CodeReview.find(@show_review_id) if @show_review_id
|
||
|
@review = CodeReview.new
|
||
|
@action_type = 'attachment'
|
||
|
@attachment = Attachment.find(@attachment_id)
|
||
|
|
||
|
@reviews = CodeReview.where(['attachment_id = (?) and issue_id is NOT NULL', @attachment_id]).all
|
||
|
|
||
|
render :partial => 'update_diff_view'
|
||
|
end
|
||
|
|
||
|
def show
|
||
|
@review = CodeReview.find(params[:review_id].to_i) unless params[:review_id].blank?
|
||
|
@repository = @review.repository if @review
|
||
|
@assignment = CodeReviewAssignment.find(params[:assignment_id].to_i) unless params[:assignment_id].blank?
|
||
|
@repository_id = @assignment.repository_identifier if @assignment
|
||
|
@issue = @review.issue if @review
|
||
|
@allowed_statuses = @review.issue.new_statuses_allowed_to(User.current) if @review
|
||
|
target = @review if @review
|
||
|
target = @assignment if @assignment
|
||
|
@repository_id = target.repository_identifier
|
||
|
if request.xhr? or !params[:update].blank?
|
||
|
render :partial => 'show'
|
||
|
elsif target.path
|
||
|
#@review = @review.root
|
||
|
path = URI.decode(target.path)
|
||
|
#path = '/' + path unless path.match(/^\//)
|
||
|
action_name = target.action_type
|
||
|
rev_to = ''
|
||
|
rev_to = '&rev_to=' + target.rev_to if target.rev_to
|
||
|
if action_name == 'attachment'
|
||
|
attachment = target.attachment
|
||
|
url = url_for(:controller => 'attachments', :action => 'show', :id => attachment.id) + '/' + URI.escape(attachment.filename)
|
||
|
url << '?review_id=' + @review.id.to_s if @review
|
||
|
redirect_to(url)
|
||
|
else
|
||
|
path = nil if target.diff_all
|
||
|
url = url_for(:controller => 'repositories', :action => action_name, :id => @project,
|
||
|
:repository_id => @repository_id, :rev => target.revision, :path => path)
|
||
|
#url = url_for(:controller => 'repositories', :action => action_name, :id => @project, :repository_id => @repository_id) + path + '?rev=' + target.revision
|
||
|
url << '?review_id=' + @review.id.to_s + rev_to if @review
|
||
|
redirect_to url
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def reply
|
||
|
begin
|
||
|
@review = CodeReview.find(params[:review_id].to_i)
|
||
|
@issue = @review.issue
|
||
|
@issue.lock_version = params[:issue][:lock_version]
|
||
|
comment = params[:reply][:comment]
|
||
|
journal = @issue.init_journal(User.current, comment)
|
||
|
@review.safe_attributes = params[:review]
|
||
|
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
|
||
|
|
||
|
@issue.save!
|
||
|
if !journal.new_record?
|
||
|
# Only send notification if something was actually changed
|
||
|
flash[:notice] = l(:notice_successful_update)
|
||
|
end
|
||
|
|
||
|
render :partial => 'show'
|
||
|
rescue ActiveRecord::StaleObjectError
|
||
|
# Optimistic locking exception
|
||
|
@error = l(:notice_locking_conflict)
|
||
|
render :partial => 'show'
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def update
|
||
|
begin
|
||
|
CodeReview.transaction {
|
||
|
@review = CodeReview.find(params[:review_id].to_i)
|
||
|
journal = @review.issue.init_journal(User.current, nil)
|
||
|
@allowed_statuses = @review.issue.new_statuses_allowed_to(User.current)
|
||
|
@issue = @review.issue
|
||
|
@issue.lock_version = params[:issue][:lock_version]
|
||
|
@review.safe_attributes = params[:review]
|
||
|
@review.updated_by_id = @user.id
|
||
|
@review.save!
|
||
|
@review.issue.save!
|
||
|
@notice = l(:notice_review_updated)
|
||
|
lang = current_language
|
||
|
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
|
||
|
set_language lang if respond_to? 'set_language'
|
||
|
render :partial => 'show'
|
||
|
}
|
||
|
rescue ActiveRecord::StaleObjectError
|
||
|
# Optimistic locking exception
|
||
|
@error = l(:notice_locking_conflict)
|
||
|
render :partial => 'show'
|
||
|
rescue
|
||
|
render :partial => 'show'
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
def destroy
|
||
|
@review = CodeReview.find(params[:review_id].to_i)
|
||
|
@review.issue.destroy if @review
|
||
|
render :text => 'delete success.'
|
||
|
end
|
||
|
|
||
|
def forward_to_revision
|
||
|
path = params[:path]
|
||
|
rev = params[:rev]
|
||
|
changesets = @repository.latest_changesets(path, rev, Setting.repository_log_display_limit.to_i)
|
||
|
change = changesets[0]
|
||
|
|
||
|
identifier = change.identifier
|
||
|
redirect_to url_for(:controller => 'repositories', :action => 'entry', :id => @project, :repository_id => @repository_id) + '/' + path + '?rev=' + identifier.to_s
|
||
|
|
||
|
end
|
||
|
|
||
|
def preview
|
||
|
@text = params[:review][:comment]
|
||
|
@text = params[:reply][:comment] unless @text
|
||
|
render :partial => 'common/preview'
|
||
|
end
|
||
|
|
||
|
def update_revisions_view
|
||
|
changeset_ids = []
|
||
|
#changeset_ids = CGI.unescape(params[:changeset_ids]).split(',') unless params[:changeset_ids].blank?
|
||
|
changeset_ids = params[:changeset_ids].split(',') unless params[:changeset_ids].blank?
|
||
|
@changesets = []
|
||
|
changeset_ids.each {|id|
|
||
|
@changesets << @repository.find_changeset_by_name(id) unless id.blank?
|
||
|
}
|
||
|
render :partial => 'update_revisions'
|
||
|
end
|
||
|
|
||
|
private
|
||
|
def find_repository
|
||
|
if params[:repository_id].present? and @project.repositories
|
||
|
@repository = @project.repositories.find_by_identifier_param(params[:repository_id])
|
||
|
else
|
||
|
@repository = @project.repository
|
||
|
end
|
||
|
@repository_id = @repository.identifier_param if @repository.respond_to?("identifier_param")
|
||
|
end
|
||
|
|
||
|
def find_project
|
||
|
# @project variable must be set before calling the authorize filter
|
||
|
@project = Project.find(params[:id])
|
||
|
end
|
||
|
|
||
|
def find_user
|
||
|
@user = User.current
|
||
|
end
|
||
|
|
||
|
|
||
|
def find_setting
|
||
|
@setting = CodeReviewProjectSetting.find_or_create(@project)
|
||
|
end
|
||
|
|
||
|
def get_parent_candidate(revision)
|
||
|
changeset = @repository.find_changeset_by_name(revision)
|
||
|
changeset.issues.each {|issue|
|
||
|
return Issue.find(issue.parent_issue_id) if issue.parent_issue_id
|
||
|
}
|
||
|
nil
|
||
|
end
|
||
|
|
||
|
def create_relation(review, issue, type)
|
||
|
return unless issue.project == @project
|
||
|
relation = IssueRelation.new
|
||
|
relation.relation_type = type
|
||
|
relation.issue_from_id = review.issue.id
|
||
|
relation.issue_to_id = issue.id
|
||
|
relation.save!
|
||
|
end
|
||
|
end
|