From 79c33bbc838fd837e516fd60569dbcf7917bd534 Mon Sep 17 00:00:00 2001
From: Jean-Philippe Lang
Date: Mon, 10 Nov 2008 18:59:06 +0000
Subject: [PATCH] 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
---
app/controllers/repositories_controller.rb | 14 +++++++
app/models/changeset.rb | 30 +++++++-------
app/models/repository.rb | 39 +++++++++++++++++++
app/models/user.rb | 1 +
app/views/issues/_changesets.rhtml | 2 +-
app/views/issues/show.rfpdf | 2 +-
app/views/projects/settings/_repository.rhtml | 5 ++-
.../repositories/_dir_list_content.rhtml | 4 +-
app/views/repositories/_revisions.rhtml | 2 +-
app/views/repositories/committers.rhtml | 29 ++++++++++++++
app/views/repositories/revision.rhtml | 2 +-
db/migrate/100_add_changesets_user_id.rb | 9 +++++
db/migrate/101_populate_changesets_user_id.rb | 18 +++++++++
lang/bg.yml | 1 +
lang/ca.yml | 1 +
lang/cs.yml | 1 +
lang/da.yml | 1 +
lang/de.yml | 1 +
lang/en.yml | 1 +
lang/es.yml | 1 +
lang/fi.yml | 1 +
lang/fr.yml | 1 +
lang/he.yml | 1 +
lang/hu.yml | 1 +
lang/it.yml | 1 +
lang/ja.yml | 1 +
lang/ko.yml | 1 +
lang/lt.yml | 1 +
lang/nl.yml | 1 +
lang/no.yml | 1 +
lang/pl.yml | 1 +
lang/pt-br.yml | 1 +
lang/pt.yml | 1 +
lang/ro.yml | 1 +
lang/ru.yml | 1 +
lang/sk.yml | 1 +
lang/sr.yml | 1 +
lang/sv.yml | 1 +
lang/th.yml | 1 +
lang/tr.yml | 1 +
lang/uk.yml | 1 +
lang/vn.yml | 1 +
lang/zh-tw.yml | 1 +
lang/zh.yml | 1 +
lib/redmine.rb | 2 +-
test/fixtures/changesets.yml | 4 ++
test/fixtures/roles.yml | 1 +
.../repositories_controller_test.rb | 34 ++++++++++++++++
test/unit/repository_git_test.rb | 1 +
test/unit/repository_test.rb | 22 +++++++++++
50 files changed, 228 insertions(+), 24 deletions(-)
create mode 100644 app/views/repositories/committers.rhtml
create mode 100644 db/migrate/100_add_changesets_user_id.rb
create mode 100644 db/migrate/101_populate_changesets_user_id.rb
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index 78576856d..17f836b7b 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -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'
diff --git a/app/models/changeset.rb b/app/models/changeset.rb
index c4258c88b..dd38ce757 100644
--- a/app/models/changeset.rb
+++ b/app/models/changeset.rb
@@ -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
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 81e6647a2..0cae37d06 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -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"
diff --git a/app/models/user.rb b/app/models/user.rb
index 132896ad9..f468063ed 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -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
diff --git a/app/views/issues/_changesets.rhtml b/app/views/issues/_changesets.rhtml
index caa983cbf..15b75c61d 100644
--- a/app/views/issues/_changesets.rhtml
+++ b/app/views/issues/_changesets.rhtml
@@ -2,7 +2,7 @@
<%= link_to("#{l(:label_revision)} #{changeset.revision}",
:controller => 'repositories', :action => 'revision', :id => @project, :rev => changeset.revision) %>
- <%= authoring(changeset.committed_on, changeset.committer) %>
+
<%= authoring(changeset.committed_on, changeset.author) %>
<%= textilizable(changeset, :comments) %>
<% end %>
diff --git a/app/views/issues/show.rfpdf b/app/views/issues/show.rfpdf
index 73d9d66b5..5926ba89d 100644
--- a/app/views/issues/show.rfpdf
+++ b/app/views/issues/show.rfpdf
@@ -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)
diff --git a/app/views/projects/settings/_repository.rhtml b/app/views/projects/settings/_repository.rhtml
index dcfabbbf0..cdfb7feb4 100644
--- a/app/views/projects/settings/_repository.rhtml
+++ b/app/views/projects/settings/_repository.rhtml
@@ -11,10 +11,13 @@
+<% 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 %>
<%= submit_tag((@repository.nil? || @repository.new_record?) ? l(:button_create) : l(:button_save), :disabled => @repository.nil?) %>
diff --git a/app/views/repositories/_dir_list_content.rhtml b/app/views/repositories/_dir_list_content.rhtml
index 20473a264..12ee44dbf 100644
--- a/app/views/repositories/_dir_list_content.rhtml
+++ b/app/views/repositories/_dir_list_content.rhtml
@@ -15,10 +15,10 @@
:class => (entry.is_dir? ? 'icon icon-folder' : 'icon icon-file')%>
<%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %> |
+<% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %>
<%= link_to(format_revision(entry.lastrev.name), :action => 'revision', :id => @project, :rev => entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %> |
<%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %> |
-<%=h(entry.lastrev.author.to_s.split('<').first) if entry.lastrev %> |
-<% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %>
+<%= changeset.nil? ? h(entry.lastrev.author.to_s.split('<').first) : changeset.author if entry.lastrev %> |
<% end %>
diff --git a/app/views/repositories/_revisions.rhtml b/app/views/repositories/_revisions.rhtml
index d8ca8007a..fb0131c82 100644
--- a/app/views/repositories/_revisions.rhtml
+++ b/app/views/repositories/_revisions.rhtml
@@ -17,7 +17,7 @@
<%= 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) %> |
<%= 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) %> |
<%= format_time(changeset.committed_on) %> |
-<%=h changeset.committer.to_s.split('<').first %> |
+<%=h changeset.author %> |
<% line_num += 1 %>
diff --git a/app/views/repositories/committers.rhtml b/app/views/repositories/committers.rhtml
new file mode 100644
index 000000000..6e3ba83b4
--- /dev/null
+++ b/app/views/repositories/committers.rhtml
@@ -0,0 +1,29 @@
+<%= l(:label_repository) %>
+
+<%= simple_format(l(:text_repository_usernames_mapping)) %>
+
+<% if @committers.empty? %>
+<%= l(:label_no_data) %>
+<% else %>
+
+<% form_tag({}) do %>
+
+
+
+ <%= l(:field_login) %> |
+ <%= l(:label_user) %> |
+
+
+
+<% @committers.each do |committer, user_id| -%>
+
+ <%=h committer %> |
+ <%= 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) %> |
+
+<% end -%>
+
+
+<%= submit_tag(l(:button_update)) %>
+<% end %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml
index 123fccf26..c5bac720b 100644
--- a/app/views/repositories/revision.rhtml
+++ b/app/views/repositories/revision.rhtml
@@ -22,7 +22,7 @@
<%= l(:label_revision) %> <%= format_revision(@changeset.revision) %>
<% if @changeset.scmid %>ID: <%= @changeset.scmid %>
<% end %>
-<%= @changeset.committer.to_s.split('<').first %>, <%= format_time(@changeset.committed_on) %>
+<%= authoring(@changeset.committed_on, @changeset.author) %>
<%= textilizable @changeset.comments %>
diff --git a/db/migrate/100_add_changesets_user_id.rb b/db/migrate/100_add_changesets_user_id.rb
new file mode 100644
index 000000000..9b25fd7bc
--- /dev/null
+++ b/db/migrate/100_add_changesets_user_id.rb
@@ -0,0 +1,9 @@
+class AddChangesetsUserId < ActiveRecord::Migration
+ def self.up
+ add_column :changesets, :user_id, :integer, :default => nil
+ end
+
+ def self.down
+ remove_column :changesets, :user_id
+ end
+end
diff --git a/db/migrate/101_populate_changesets_user_id.rb b/db/migrate/101_populate_changesets_user_id.rb
new file mode 100644
index 000000000..dd493d17d
--- /dev/null
+++ b/db/migrate/101_populate_changesets_user_id.rb
@@ -0,0 +1,18 @@
+class PopulateChangesetsUserId < ActiveRecord::Migration
+ def self.up
+ committers = Changeset.connection.select_values("SELECT DISTINCT committer FROM #{Changeset.table_name}")
+ committers.each do |committer|
+ next if committer.blank?
+ if committer.strip =~ /^([^<]+)(<(.*)>)?$/
+ username, email = $1.strip, $3
+ u = User.find_by_login(username)
+ u ||= User.find_by_mail(email) unless email.blank?
+ Changeset.update_all("user_id = #{u.id}", ["committer = ?", committer]) unless u.nil?
+ end
+ end
+ end
+
+ def self.down
+ Changeset.update_all('user_id = NULL')
+ end
+end
diff --git a/lang/bg.yml b/lang/bg.yml
index bc8309845..000f0a977 100644
--- a/lang/bg.yml
+++ b/lang/bg.yml
@@ -689,3 +689,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/ca.yml b/lang/ca.yml
index 793128293..5db547ac5 100644
--- a/lang/ca.yml
+++ b/lang/ca.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/cs.yml b/lang/cs.yml
index 7e49482c2..09ed76ea9 100644
--- a/lang/cs.yml
+++ b/lang/cs.yml
@@ -694,3 +694,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/da.yml b/lang/da.yml
index a5b8ddfb5..b5081e9ee 100644
--- a/lang/da.yml
+++ b/lang/da.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/de.yml b/lang/de.yml
index 7d4b7959f..3e59bcf9e 100644
--- a/lang/de.yml
+++ b/lang/de.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/en.yml b/lang/en.yml
index a76c5d1a5..e9d7a1706 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -665,6 +665,7 @@ text_user_wrote: '%s wrote:'
text_enumeration_destroy_question: '%d objects are assigned to this value.'
text_enumeration_category_reassign_to: 'Reassign them to this value:'
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
default_role_manager: Manager
default_role_developper: Developer
diff --git a/lang/es.yml b/lang/es.yml
index 74ca4119a..c3b9de2b6 100644
--- a/lang/es.yml
+++ b/lang/es.yml
@@ -692,3 +692,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/fi.yml b/lang/fi.yml
index 17423bf86..bdcefef9c 100644
--- a/lang/fi.yml
+++ b/lang/fi.yml
@@ -689,3 +689,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/fr.yml b/lang/fr.yml
index 556bb4548..ab34575b2 100644
--- a/lang/fr.yml
+++ b/lang/fr.yml
@@ -665,6 +665,7 @@ text_user_wrote: '%s a écrit:'
text_enumeration_destroy_question: 'Cette valeur est affectée à %d objets.'
text_enumeration_category_reassign_to: 'Réaffecter les objets à cette valeur:'
text_email_delivery_not_configured: "L'envoi de mail n'est pas configuré, les notifications sont désactivées.\nConfigurez votre serveur SMTP dans config/email.yml et redémarrez l'application pour les activer."
+text_repository_usernames_mapping: "Vous pouvez sélectionner ou modifier l'utilisateur Redmine associé à chaque nom d'utilisateur figurant dans l'historique du dépôt.\nLes utilisateurs avec le même identifiant ou la même adresse mail seront automatiquement associés."
default_role_manager: Manager
default_role_developper: Développeur
diff --git a/lang/he.yml b/lang/he.yml
index 59474f6ce..13eab75c5 100644
--- a/lang/he.yml
+++ b/lang/he.yml
@@ -689,3 +689,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/hu.yml b/lang/hu.yml
index 8a508a1e6..1c4afdcd3 100644
--- a/lang/hu.yml
+++ b/lang/hu.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: IdÅnaplók szerkesztése
permission_edit_own_issue_notes: Saját jegyzetek szerkesztése
setting_gravatar_enabled: Felhasználói fényképek engedélyezése
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/it.yml b/lang/it.yml
index e33e6b8a3..c6ae84a91 100644
--- a/lang/it.yml
+++ b/lang/it.yml
@@ -689,3 +689,4 @@ permission_edit_time_entries: Modifica time logs
permission_edit_own_issue_notes: Modifica proprie note
setting_gravatar_enabled: Usa icone utente Gravatar
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/ja.yml b/lang/ja.yml
index cd55ab896..4092584d7 100644
--- a/lang/ja.yml
+++ b/lang/ja.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/ko.yml b/lang/ko.yml
index 8a1030da7..595acfa9e 100644
--- a/lang/ko.yml
+++ b/lang/ko.yml
@@ -689,3 +689,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/lt.yml b/lang/lt.yml
index f9e5bea14..544cb14f9 100644
--- a/lang/lt.yml
+++ b/lang/lt.yml
@@ -691,3 +691,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/nl.yml b/lang/nl.yml
index 229fb8e77..3434b1481 100644
--- a/lang/nl.yml
+++ b/lang/nl.yml
@@ -691,3 +691,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/no.yml b/lang/no.yml
index 5faa25fc1..ca056f809 100644
--- a/lang/no.yml
+++ b/lang/no.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/pl.yml b/lang/pl.yml
index 246c147e4..df7d525dc 100644
--- a/lang/pl.yml
+++ b/lang/pl.yml
@@ -724,3 +724,4 @@ permission_edit_own_issue_notes: Edycja wÅasnych notatek
setting_gravatar_enabled: Używaj ikon użytkowników Gravatar
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/pt-br.yml b/lang/pt-br.yml
index 3ead43b80..4e34231af 100644
--- a/lang/pt-br.yml
+++ b/lang/pt-br.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: Editar tempo gasto
permission_edit_own_issue_notes: Editar próprias notas
setting_gravatar_enabled: Usar Ãcones do Gravatar
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/pt.yml b/lang/pt.yml
index 878eae1fc..ce1e27a36 100644
--- a/lang/pt.yml
+++ b/lang/pt.yml
@@ -691,3 +691,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/ro.yml b/lang/ro.yml
index bde50a98d..8890f6e2d 100644
--- a/lang/ro.yml
+++ b/lang/ro.yml
@@ -689,3 +689,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/ru.yml b/lang/ru.yml
index f7ca8fc07..363386dc9 100644
--- a/lang/ru.yml
+++ b/lang/ru.yml
@@ -722,3 +722,4 @@ text_user_mail_option: "ÐÐ»Ñ Ð½ÐµÐ²ÑбÑаннÑÑ
пÑоекÑов, ÐÑ Ð±
text_user_wrote: '%s напиÑал(а):'
text_wiki_destroy_confirmation: ÐÑ ÑвеÑенÑ, ÑÑо Ñ
оÑиÑе ÑдалиÑÑ Ð´Ð°Ð½Ð½ÑÑ Wiki и вÑе ее ÑодеÑжимое?
text_workflow_edit: ÐÑбеÑиÑе ÑÐ¾Ð»Ñ Ð¸ ÑÑÐµÐºÐµÑ Ð´Ð»Ñ ÑедакÑиÑÐ¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ÑледоваÑелÑноÑÑи ÑоÑÑоÑний
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/sk.yml b/lang/sk.yml
index 919f13d9a..5c53eda2f 100644
--- a/lang/sk.yml
+++ b/lang/sk.yml
@@ -694,3 +694,4 @@ permission_edit_time_entries: Editácia Äasových záznamov
permission_edit_own_issue_notes: Editácia vlastných poznámok úlohy
setting_gravatar_enabled: Použitie uživateľských Gravatar ikon
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/sr.yml b/lang/sr.yml
index 27466e14e..54b1e70c6 100644
--- a/lang/sr.yml
+++ b/lang/sr.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/sv.yml b/lang/sv.yml
index 29789c124..76b6e6c38 100644
--- a/lang/sv.yml
+++ b/lang/sv.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/th.yml b/lang/th.yml
index 1bb60eacc..fa471101f 100644
--- a/lang/th.yml
+++ b/lang/th.yml
@@ -692,3 +692,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/tr.yml b/lang/tr.yml
index 439303a70..90d4d5f65 100644
--- a/lang/tr.yml
+++ b/lang/tr.yml
@@ -690,3 +690,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/uk.yml b/lang/uk.yml
index 5f18b0e22..a7f3f7347 100644
--- a/lang/uk.yml
+++ b/lang/uk.yml
@@ -691,3 +691,4 @@ permission_edit_time_entries: Edit time logs
permission_edit_own_issue_notes: Edit own notes
setting_gravatar_enabled: Use Gravatar user icons
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/vn.yml b/lang/vn.yml
index 8ddc77e47..81957a53c 100644
--- a/lang/vn.yml
+++ b/lang/vn.yml
@@ -690,3 +690,4 @@ permission_save_queries: Save queries
permission_edit_time_entries: Edit time logs
permission_edit_own_time_entries: Edit own time logs
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/zh-tw.yml b/lang/zh-tw.yml
index 2e4a5f02d..40dc47b8b 100644
--- a/lang/zh-tw.yml
+++ b/lang/zh-tw.yml
@@ -691,3 +691,4 @@ enumeration_issue_priorities: é
ç®åªå
æ¬
enumeration_doc_categories: æ件åé¡
enumeration_activities: æ´»å (æé追蹤)
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lang/zh.yml b/lang/zh.yml
index 00f95dcae..10fd1ac01 100644
--- a/lang/zh.yml
+++ b/lang/zh.yml
@@ -691,3 +691,4 @@ enumeration_issue_priorities: é®é¢ä¼å
级
enumeration_doc_categories: æ档类å«
enumeration_activities: æ´»å¨ï¼æ¶é´è·è¸ªï¼
label_example: Example
+text_repository_usernames_mapping: "Select ou update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
diff --git a/lib/redmine.rb b/lib/redmine.rb
index eeb12e00a..b07d8e90c 100644
--- a/lib/redmine.rb
+++ b/lib/redmine.rb
@@ -88,7 +88,7 @@ Redmine::AccessControl.map do |map|
end
map.project_module :repository do |map|
- map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member
+ map.permission :manage_repository, {:repositories => [:edit, :committers, :destroy]}, :require => :member
map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
map.permission :commit_access, {}
diff --git a/test/fixtures/changesets.yml b/test/fixtures/changesets.yml
index 3b47eecd8..cbc00eb83 100644
--- a/test/fixtures/changesets.yml
+++ b/test/fixtures/changesets.yml
@@ -7,6 +7,7 @@ changesets_001:
comments: My very first commit
repository_id: 10
committer: dlopper
+ user_id: 3
changesets_002:
commit_date: 2007-04-12
committed_on: 2007-04-12 15:14:44 +02:00
@@ -15,6 +16,7 @@ changesets_002:
comments: 'This commit fixes #1, #2 and references #1 & #3'
repository_id: 10
committer: dlopper
+ user_id: 3
changesets_003:
commit_date: 2007-04-12
committed_on: 2007-04-12 15:14:44 +02:00
@@ -25,6 +27,7 @@ changesets_003:
IssueID 666 3
repository_id: 10
committer: dlopper
+ user_id: 3
changesets_004:
commit_date: 2007-04-12
committed_on: 2007-04-12 15:14:44 +02:00
@@ -35,4 +38,5 @@ changesets_004:
IssueID 4 2
repository_id: 10
committer: dlopper
+ user_id: 3
\ No newline at end of file
diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml
index 78e0f0329..5bc6809d7 100644
--- a/test/fixtures/roles.yml
+++ b/test/fixtures/roles.yml
@@ -43,6 +43,7 @@ roles_001:
- :view_files
- :manage_files
- :browse_repository
+ - :manage_repository
- :view_changesets
position: 1
diff --git a/test/functional/repositories_controller_test.rb b/test/functional/repositories_controller_test.rb
index 2892f3bd1..4bb9c3fa3 100644
--- a/test/functional/repositories_controller_test.rb
+++ b/test/functional/repositories_controller_test.rb
@@ -61,4 +61,38 @@ class RepositoriesControllerTest < Test::Unit::TestCase
assert_response :success
assert_equal 'image/svg+xml', @response.content_type
end
+
+ def test_committers
+ @request.session[:user_id] = 2
+ # add a commit with an unknown user
+ Changeset.create!(:repository => Project.find(1).repository, :committer => 'foo', :committed_on => Time.now, :revision => 100, :comments => 'Committed by foo.')
+
+ get :committers, :id => 1
+ assert_response :success
+ assert_template 'committers'
+
+ assert_tag :td, :content => 'dlopper',
+ :sibling => { :tag => 'td',
+ :child => { :tag => 'select', :attributes => { :name => 'committers[dlopper]' },
+ :child => { :tag => 'option', :content => 'Dave Lopper',
+ :attributes => { :value => '3', :selected => 'selected' }}}}
+ assert_tag :td, :content => 'foo',
+ :sibling => { :tag => 'td',
+ :child => { :tag => 'select', :attributes => { :name => 'committers[foo]' }}}
+ assert_no_tag :td, :content => 'foo',
+ :sibling => { :tag => 'td',
+ :descendant => { :tag => 'option', :attributes => { :selected => 'selected' }}}
+ end
+
+ def test_map_committers
+ @request.session[:user_id] = 2
+ # add a commit with an unknown user
+ c = Changeset.create!(:repository => Project.find(1).repository, :committer => 'foo', :committed_on => Time.now, :revision => 100, :comments => 'Committed by foo.')
+
+ assert_no_difference "Changeset.count(:conditions => 'user_id = 3')" do
+ post :committers, :id => 1, :committers => { 'foo' => '2', 'dlopper' => '3'}
+ assert_redirected_to '/repositories/committers/ecookbook'
+ assert_equal User.find(2), c.reload.user
+ end
+ end
end
diff --git a/test/unit/repository_git_test.rb b/test/unit/repository_git_test.rb
index da95d439f..bc997b96c 100644
--- a/test/unit/repository_git_test.rb
+++ b/test/unit/repository_git_test.rb
@@ -40,6 +40,7 @@ class RepositoryGitTest < Test::Unit::TestCase
commit = @repository.changesets.find(:first, :order => 'committed_on ASC')
assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
assert_equal "jsmith ", commit.committer
+ assert_equal User.find_by_login('jsmith'), commit.user
# TODO: add a commit with commit time <> author time to the test repository
assert_equal "2007-12-14 09:22:52".to_time, commit.committed_on
assert_equal "2007-12-14".to_date, commit.commit_date
diff --git a/test/unit/repository_test.rb b/test/unit/repository_test.rb
index 9ea9fdd45..47230cf56 100644
--- a/test/unit/repository_test.rb
+++ b/test/unit/repository_test.rb
@@ -127,4 +127,26 @@ class RepositoryTest < Test::Unit::TestCase
assert_equal ':pserver:login:password@host:/path/to/the/repository', repository.url
assert_equal 'foo', repository.root_url
end
+
+ def test_manual_user_mapping
+ assert_no_difference "Changeset.count(:conditions => 'user_id <> 2')" do
+ c = Changeset.create!(:repository => @repository, :committer => 'foo', :committed_on => Time.now, :revision => 100, :comments => 'Committed by foo.')
+ assert_nil c.user
+ @repository.committer_ids = {'foo' => '2'}
+ assert_equal User.find(2), c.reload.user
+ # committer is now mapped
+ c = Changeset.create!(:repository => @repository, :committer => 'foo', :committed_on => Time.now, :revision => 101, :comments => 'Another commit by foo.')
+ assert_equal User.find(2), c.user
+ end
+ end
+
+ def test_auto_user_mapping_by_username
+ c = Changeset.create!(:repository => @repository, :committer => 'jsmith', :committed_on => Time.now, :revision => 100, :comments => 'Committed by john.')
+ assert_equal User.find(2), c.user
+ end
+
+ def test_auto_user_mapping_by_email
+ c = Changeset.create!(:repository => @repository, :committer => 'john ', :committed_on => Time.now, :revision => 100, :comments => 'Committed by john.')
+ assert_equal User.find(2), c.user
+ end
end
--
2.39.5