Maps repository users to Redmine users (#1383).
Users with same username or email are automatically mapped. Mapping can be manually adjusted in repository settings. Multiple usernames can be mapped to the same Redmine user. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2006 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -44,6 +44,20 @@ class RepositoriesController < ApplicationController
|
||||
render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
|
||||
end
|
||||
|
||||
def committers
|
||||
@committers = @repository.committers
|
||||
@users = @project.users
|
||||
additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
|
||||
@users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
|
||||
@users.compact!
|
||||
@users.sort!
|
||||
if request.post?
|
||||
@repository.committer_ids = params[:committers]
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to :action => 'committers', :id => @project
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@repository.destroy
|
||||
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
|
||||
|
||||
+14
-16
@@ -1,5 +1,5 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2008 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -19,13 +19,13 @@ require 'iconv'
|
||||
|
||||
class Changeset < ActiveRecord::Base
|
||||
belongs_to :repository
|
||||
belongs_to :user
|
||||
has_many :changes, :dependent => :delete_all
|
||||
has_and_belongs_to_many :issues
|
||||
|
||||
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.comments.blank? ? '' : (': ' + o.comments))},
|
||||
:description => :comments,
|
||||
:datetime => :committed_on,
|
||||
:author => :committer,
|
||||
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project_id, :rev => o.revision}}
|
||||
|
||||
acts_as_searchable :columns => 'comments',
|
||||
@@ -57,6 +57,14 @@ class Changeset < ActiveRecord::Base
|
||||
repository.project
|
||||
end
|
||||
|
||||
def author
|
||||
user || committer.to_s.split('<').first
|
||||
end
|
||||
|
||||
def before_create
|
||||
self.user = repository.find_committer_user(committer)
|
||||
end
|
||||
|
||||
def after_create
|
||||
scan_comment_for_issue_ids
|
||||
end
|
||||
@@ -96,12 +104,11 @@ class Changeset < ActiveRecord::Base
|
||||
issue.reload
|
||||
# don't change the status is the issue is closed
|
||||
next if issue.status.is_closed?
|
||||
user = committer_user || User.anonymous
|
||||
csettext = "r#{self.revision}"
|
||||
if self.scmid && (! (csettext =~ /^r[0-9]+$/))
|
||||
csettext = "commit:\"#{self.scmid}\""
|
||||
end
|
||||
journal = issue.init_journal(user, l(:text_status_changed_by_changeset, csettext))
|
||||
journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext))
|
||||
issue.status = fix_status
|
||||
issue.done_ratio = done_ratio if done_ratio
|
||||
issue.save
|
||||
@@ -113,16 +120,6 @@ class Changeset < ActiveRecord::Base
|
||||
|
||||
self.issues = referenced_issues.uniq
|
||||
end
|
||||
|
||||
# Returns the Redmine User corresponding to the committer
|
||||
def committer_user
|
||||
if committer && committer.strip =~ /^([^<]+)(<(.*)>)?$/
|
||||
username, email = $1.strip, $3
|
||||
u = User.find_by_login(username)
|
||||
u ||= User.find_by_mail(email) unless email.blank?
|
||||
u
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the previous changeset
|
||||
def previous
|
||||
@@ -140,7 +137,8 @@ class Changeset < ActiveRecord::Base
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
||||
|
||||
def self.to_utf8(str)
|
||||
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
|
||||
encoding = Setting.commit_logs_encoding.to_s.strip
|
||||
|
||||
@@ -96,6 +96,45 @@ class Repository < ActiveRecord::Base
|
||||
self.changesets.each(&:scan_comment_for_issue_ids)
|
||||
end
|
||||
|
||||
# Returns an array of committers usernames and associated user_id
|
||||
def committers
|
||||
@committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
|
||||
end
|
||||
|
||||
# Maps committers username to a user ids
|
||||
def committer_ids=(h)
|
||||
if h.is_a?(Hash)
|
||||
committers.each do |committer, user_id|
|
||||
new_user_id = h[committer]
|
||||
if new_user_id && (new_user_id.to_i != user_id.to_i)
|
||||
new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
|
||||
Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
|
||||
end
|
||||
end
|
||||
@committers = nil
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the Redmine User corresponding to the given +committer+
|
||||
# It will return nil if the committer is not yet mapped and if no User
|
||||
# with the same username or email was found
|
||||
def find_committer_user(committer)
|
||||
if committer
|
||||
c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
|
||||
if c && c.user
|
||||
c.user
|
||||
elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
|
||||
username, email = $1.strip, $3
|
||||
u = User.find_by_login(username)
|
||||
u ||= User.find_by_mail(email) unless email.blank?
|
||||
u
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# fetch new changesets for all repositories
|
||||
# can be called periodically by an external script
|
||||
# eg. ruby script/runner "Repository.fetch_changesets"
|
||||
|
||||
@@ -37,6 +37,7 @@ class User < ActiveRecord::Base
|
||||
has_many :members, :dependent => :delete_all
|
||||
has_many :projects, :through => :memberships
|
||||
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
|
||||
has_many :changesets, :dependent => :nullify
|
||||
has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
|
||||
has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
|
||||
belongs_to :auth_source
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="changeset <%= cycle('odd', 'even') %>">
|
||||
<p><%= link_to("#{l(:label_revision)} #{changeset.revision}",
|
||||
:controller => 'repositories', :action => 'revision', :id => @project, :rev => changeset.revision) %><br />
|
||||
<span class="author"><%= authoring(changeset.committed_on, changeset.committer) %></span></p>
|
||||
<span class="author"><%= authoring(changeset.committed_on, changeset.author) %></span></p>
|
||||
<%= textilizable(changeset, :comments) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
pdf.Ln
|
||||
for changeset in @issue.changesets
|
||||
pdf.SetFontStyle('B',8)
|
||||
pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.committer)
|
||||
pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author)
|
||||
pdf.Ln
|
||||
unless changeset.comments.blank?
|
||||
pdf.SetFontStyle('',8)
|
||||
|
||||
@@ -11,10 +11,13 @@
|
||||
</div>
|
||||
|
||||
<div class="contextual">
|
||||
<% if @repository && !@repository.new_record? %>
|
||||
<%= link_to(l(:label_user_plural), {:controller => 'repositories', :action => 'committers', :id => @project}, :class => 'icon icon-user') %>
|
||||
<%= link_to(l(:button_delete), {:controller => 'repositories', :action => 'destroy', :id => @project},
|
||||
:confirm => l(:text_are_you_sure),
|
||||
:method => :post,
|
||||
:class => 'icon icon-del') if @repository && !@repository.new_record? %>
|
||||
:class => 'icon icon-del') %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= submit_tag((@repository.nil? || @repository.new_record?) ? l(:button_create) : l(:button_save), :disabled => @repository.nil?) %>
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
:class => (entry.is_dir? ? 'icon icon-folder' : 'icon icon-file')%>
|
||||
</td>
|
||||
<td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
|
||||
<% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %>
|
||||
<td class="revision"><%= link_to(format_revision(entry.lastrev.name), :action => 'revision', :id => @project, :rev => entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %></td>
|
||||
<td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td>
|
||||
<td class="author"><%=h(entry.lastrev.author.to_s.split('<').first) if entry.lastrev %></td>
|
||||
<% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %>
|
||||
<td class="author"><%= changeset.nil? ? h(entry.lastrev.author.to_s.split('<').first) : changeset.author if entry.lastrev %></td>
|
||||
<td class="comments"><%=h truncate(changeset.comments, 50) unless changeset.nil? %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<td class="checkbox"><%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
|
||||
<td class="checkbox"><%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
|
||||
<td class="committed_on"><%= format_time(changeset.committed_on) %></td>
|
||||
<td class="author"><%=h changeset.committer.to_s.split('<').first %></td>
|
||||
<td class="author"><%=h changeset.author %></td>
|
||||
<td class="comments"><%= textilizable(truncate_at_line_break(changeset.comments)) %></td>
|
||||
</tr>
|
||||
<% line_num += 1 %>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<h2><%= l(:label_repository) %></h2>
|
||||
|
||||
<%= simple_format(l(:text_repository_usernames_mapping)) %>
|
||||
|
||||
<% if @committers.empty? %>
|
||||
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||
<% else %>
|
||||
|
||||
<% form_tag({}) do %>
|
||||
<table class="list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= l(:field_login) %></th>
|
||||
<th><%= l(:label_user) %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @committers.each do |committer, user_id| -%>
|
||||
<tr class="<%= cycle 'odd', 'even' %>">
|
||||
<td><%=h committer %></td>
|
||||
<td><%= select_tag "committers[#{committer}]", content_tag('option', "-- #{l :actionview_instancetag_blank_option} --", :value => '') + options_from_collection_for_select(@users, 'id', 'name', user_id.to_i) %></td>
|
||||
</tr>
|
||||
<% end -%>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><%= submit_tag(l(:button_update)) %></p>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
@@ -22,7 +22,7 @@
|
||||
<h2><%= l(:label_revision) %> <%= format_revision(@changeset.revision) %></h2>
|
||||
|
||||
<p><% if @changeset.scmid %>ID: <%= @changeset.scmid %><br /><% end %>
|
||||
<em><%= @changeset.committer.to_s.split('<').first %>, <%= format_time(@changeset.committed_on) %></em></p>
|
||||
<span class="author"><%= authoring(@changeset.committed_on, @changeset.author) %></span></p>
|
||||
|
||||
<%= textilizable @changeset.comments %>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user