diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-12-19 10:29:17 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-12-19 10:29:27 +0100 |
commit | 79e8246a6449e1cac91d5c44262e791dba07e9e7 (patch) | |
tree | fdcc96fcde4d0c0309648e4f3b495a92b549875c | |
parent | f1c12f9673545a181712a3c27cd655d59d567c86 (diff) | |
download | sonarqube-79e8246a6449e1cac91d5c44262e791dba07e9e7.tar.gz sonarqube-79e8246a6449e1cac91d5c44262e791dba07e9e7.zip |
SONAR-5830 Possibility to map authors (from SCM) to SonarQube users
9 files changed, 180 insertions, 79 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/DefaultUserService.java b/server/sonar-server/src/main/java/org/sonar/server/user/DefaultUserService.java index 7de63a17920..9bd6465ebf6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/DefaultUserService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/DefaultUserService.java @@ -28,6 +28,7 @@ import org.sonar.api.user.UserQuery; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.user.UserDao; import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.user.index.UserDoc; import org.sonar.server.util.RubyUtils; import javax.annotation.CheckForNull; @@ -69,17 +70,9 @@ public class DefaultUserService implements RubyUserService { return builder.build(); } - public void deactivate(String login) { - if (Strings.isNullOrEmpty(login)) { - throw new BadRequestException("Login is missing"); - } - UserSession userSession = UserSession.get(); - userSession.checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); - if (Objects.equal(userSession.login(), login)) { - throw new BadRequestException("Self-deactivation is not possible"); - } - dao.deactivateUserByLogin(login); - userService.index(); + @CheckForNull + public UserDoc getByLogin(String login) { + return userService.getNullableByLogin(login); } public boolean create(Map<String, Object> params) { @@ -111,6 +104,19 @@ public class DefaultUserService implements RubyUserService { userService.update(updateUser); } + public void deactivate(String login) { + if (Strings.isNullOrEmpty(login)) { + throw new BadRequestException("Login is missing"); + } + UserSession userSession = UserSession.get(); + userSession.checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); + if (Objects.equal(userSession.login(), login)) { + throw new BadRequestException("Self-deactivation is not possible"); + } + dao.deactivateUserByLogin(login); + userService.index(); + } + public void index() { userService.index(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java index dff44ab1916..48bf1c9864d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java @@ -47,6 +47,7 @@ import javax.annotation.Nullable; import java.io.StringWriter; import java.security.SecureRandom; +import java.util.Arrays; import java.util.List; import java.util.Random; @@ -257,6 +258,7 @@ public class UserUpdater implements ServerComponent { @CheckForNull private static String convertScmAccountsToCsv(@Nullable List<String> scmAccounts) { if (scmAccounts != null) { + scmAccounts.removeAll(Arrays.asList(null, "")); int size = scmAccounts.size(); StringWriter writer = new StringWriter(size); CsvWriter csv = CsvWriter.of(writer); diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserServiceTest.java index b8d17b96fa6..98a8f308a8a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/DefaultUserServiceTest.java @@ -231,6 +231,12 @@ public class DefaultUserServiceTest { } @Test + public void get_by_login() throws Exception { + service.getByLogin("john"); + verify(userService).getNullableByLogin("john"); + } + + @Test public void index() throws Exception { service.index(); verify(userService).index(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java index 582eaa1ca37..0ae9a1a9b08 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java @@ -142,6 +142,21 @@ public class UserUpdaterTest { } @Test + public void create_user_with_scm_accounts_containing_blank_entry() throws Exception { + when(system2.now()).thenReturn(1418215735482L); + createDefaultGroup(); + + userUpdater.create(NewUser.create() + .setLogin("user") + .setName("User") + .setPassword("password") + .setPasswordConfirmation("password") + .setScmAccounts(newArrayList("u1", "", null))); + + assertThat(userDao.selectNullableByLogin(session, "user").getScmAccounts()).doesNotContain(","); + } + + @Test public void fail_to_create_user_with_missing_login() throws Exception { try { userUpdater.create(NewUser.create() diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/users_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/users_controller.rb index 948aa8bcbbf..779a828b329 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/users_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/users_controller.rb @@ -63,12 +63,7 @@ class UsersController < ApplicationController end def create_form - if params[:id] - # TODO is it really used ? - @user = User.find(params[:id]) - else - @user = User.new - end + @user = User.new render :partial => 'users/create_form' end @@ -77,8 +72,10 @@ class UsersController < ApplicationController end def edit_form - @user = User.find(params[:id]) - render :partial => 'users/edit_form', :status => 200 + call_backend do + @user = Internal.users_api.getByLogin(params[:id]) + render :partial => 'users/edit_form', :status => 200 + end end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/_create_form.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/_create_form.html.erb index 140d52a7d91..b0bd112c40d 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/_create_form.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/_create_form.html.erb @@ -6,12 +6,7 @@ <div class="modal-body"> <div class="modal-error"/> <div class="modal-field"><label for="user[login]">Login <em class="mandatory">*</em></label> - <% if @user.id %> - <%= @user.login %> - <%= f.hidden_field :login %> - <% else %> - <%= f.text_field :login, :size => 30, :maxLength => 255 %> - <% end %> + <%= f.text_field :login, :size => 30, :maxLength => 255 %> </div> <div class="modal-field"> <label for="user[]">Name <em class="mandatory">*</em></label> @@ -23,6 +18,27 @@ <label for="user[password]">Password <em class="mandatory">*</em></label><%= f.password_field :password, :size => 30, :maxLength => 50, :autocomplete => 'off' %></div> <div class="modal-field"><label for="user[password_confirmation]">Confirm password <em class="mandatory">*</em></label><%= f.password_field :password_confirmation, :size => 30, :maxLength => 50, :autocomplete => 'off' %></div> + + <div class="modal-field"> + <label for="user[scm_accounts]">SCM accounts</label> + <table> + <tr class="scm_account"> + <td> + <input id="user_scm_accounts" type="text" size="30" name="user[scm_accounts][]"/> + </td> + </tr> + <tr class="scm_account template" style="display:none"> + <td> + <input id="user_scm_accounts" type="text" size="30" name="user[scm_accounts][]"/> + <a href="#" class="delete link-action"><%= message('delete') -%></a> + <br/> + </td> + </tr> + </table> + <div class="note marginbottom10">Note that login and email are automatically considered as SCM accounts</div> + <button class="add_value"><%= message('user.add_scm_account') -%></button> + </div> + </div> <div class="modal-foot"> <%= submit_tag 'Create' %> @@ -33,4 +49,15 @@ <script> $j("#user_create_form").modalForm(); + + $j('#user_create_form .delete').click(function () { + $j(this).parents('.scm_account').remove(); + return false; + }); + + $j('#user_create_form .add_value').click(function () { + var template = $j(this).parents('#user_create_form').find('.template').last(); + template.clone().insertBefore(template).show(); + return false; + }); </script> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/_edit_form.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/_edit_form.html.erb index 54144e89ac8..59d0216f32d 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/_edit_form.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/_edit_form.html.erb @@ -1,33 +1,78 @@ -<% form_for :user, @user, :url => { :id => @user.id, :action => 'update'}, :html => { :id =>'user_edit_form', :method => @user.id.nil? ? :post : :put} do |f| %> - <fieldset> +<form id="user_edit_form" method="post" action="<%= ApplicationController.root_context -%>/users/update"> +<fieldset> <div class="modal-head"> - <h2>Edit user: <%= h @user.login -%></h2> + <h2>Edit user: <%= h @user.login() -%></h2> </div> <div class="modal-body"> <div class="modal-error"></div> <div class="modal-field"> - <% if @user.id %> - <%= f.hidden_field :login %> - <% else %> - <%= f.text_field :login, :size => 30, :maxLength => 255 %> - <% end %> + <input type="hidden" value="<%= @user.login() %>" name="user[login]" id="user_name"/> </div> <div class="modal-field"> <label for="user[]">Name<em class="mandatory">*</em></label> - <%= f.text_field :name, :size => 30, :maxLength => 200 %> + <input type="text" value="<%= @user.name() %>" size="30" name="user[name]" maxlength="200" id="user_name"/> </div> <div class="modal-field"> <label for="user[]">Email</label> - <%= f.text_field :email, :size => 30, :maxLength => 100 %> + <input type="text" value="<%= @user.email() %>" size="30" name="user[email]" maxlength="100" id="user_email"> + </div> + <div class="modal-field"> + <label for="user[scm_accounts]">SCM accounts</label> + <table> + <% scmAccounts = @user.scmAccounts().to_a %> + <% if scmAccounts.empty? %> + <tr class="scm_account"> + <td> + <input id="user_scm_accounts" type="text" size="30" name="user[scm_accounts][]"/> + </td> + </tr> + <% else %> + <tr class="scm_account"> + <td> + <input id="user_scm_accounts" value="<%= scmAccounts.first() %>" type="text" size="30" name="user[scm_accounts][]"/> + </td> + </tr> + <% + scmAccounts.shift + scmAccounts.each do |scmAccount| %> + <tr class="scm_account"> + <td> + <input id="user_scm_accounts" value="<%= scmAccount %>" type="text" size="30" name="user[scm_accounts][]"/> + <a href="#" class="delete link-action"><%= message('delete') -%></a> + </td> + </tr> + <% end %> + <% end %> + <tr class="scm_account template" style="display:none"> + <td> + <input id="user_scm_accounts" type="text" size="30" name="user[scm_accounts][]"/> + <a href="#" class="delete link-action"><%= message('delete') -%></a> + <br/> + </td> + </tr> + </table> + <div class="note marginbottom10">Note that login and email are automatically considered as SCM accounts</div> + <button class="add_value"><%= message('user.add_scm_account') -%></button> </div> </div> <div class="modal-foot"> - <%= submit_tag 'Save' %> - <%= link_to 'Cancel', { :controller => 'users', :action => 'index'}, { :class => 'action' } %><br/> + <input type="submit" value="Save" name="commit"> + <a class="action" href="#" onclick="return closeModalWindow()"><%= h message('cancel') -%></a> </div> </fieldset> -<% end %> +</form> <script> $j("#user_edit_form").modalForm(); + + $j('#user_edit_form .delete').click(function () { + $j(this).parents('.scm_account').remove(); + return false; + }); + + $j('#user_edit_form .add_value').click(function () { + var template = $j(this).parents('#user_edit_form').find('.template').last(); + template.clone().insertBefore(template).show(); + return false; + }); </script> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/index.html.erb index 131c2491982..11369d6ed8f 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/index.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/index.html.erb @@ -3,15 +3,16 @@ <% end %> <div> - <% if profiles_administrator? %> - <ul style="float: right" class="horizontal"> - <li class="marginleft10 add"> - <a id="create-link-user" href="<%=ApplicationController.root_context-%>/users/create_form" id="create-link-user" class="open-modal link-action">Add new user</a> - </li> - </ul> - <% end %> - <h1 class="admin-page-title"><%= message('users.page') -%></h1> - <p class="admin-page-description"><%= message('users.page.description') -%> </p> + <% if profiles_administrator? %> + <ul style="float: right" class="horizontal"> + <li class="marginleft10 add"> + <a id="create-link-user" href="<%= ApplicationController.root_context -%>/users/create_form" id="create-link-user" class="open-modal link-action">Add new user</a> + </li> + </ul> + <% end %> + <h1 class="admin-page-title"><%= message('users.page') -%></h1> + + <p class="admin-page-description"><%= message('users.page.description') -%> </p> </div> <table width="100%"> @@ -19,39 +20,40 @@ <td valign="top"> <table class="data width100 sortable" id="users"> <thead> - <tr> - <th class="left"><a>Login</a></th> - <th class="left sortfirstasc"><a>Name</a></th> - <th class="left"><a>Email</a></th> - <th class="left nosort"><a>Groups</a></th> - <th class="right nosort" nowrap><a>Operations</a></th> - </tr> + <tr> + <th class="left"><a>Login</a></th> + <th class="left sortfirstasc"><a>Name</a></th> + <th class="left"><a>Email</a></th> + <th class="left nosort"><a>Groups</a></th> + <th class="right nosort" nowrap><a>Operations</a></th> + </tr> </thead> - <tbody > - <% @users.each do |user|%> - <tr id="user-<%= user.login.parameterize -%>"> - <td class="left" valign="top"><%=h user.login -%></td> - <td class="left" valign="top"><%= h user.name -%></td> - <td class="left" valign="top"><%= h user.email -%></td> - <td class="left" valign="top"> - <%= h user.groups.sort.map(&:name).join(', ') %> (<%= link_to "select", {:action => 'select_group', :id => user.id}, {:id => "select-#{user.login.parameterize}", :class => 'open-modal link-action'} %>) - </td> - <td class="right" valign="top"> - <a id="edit-<%= user.login -%>" class="open-modal link-action" href="<%=ApplicationController.root_context-%>/users/edit_form/<%= user.id -%>">Edit</a> - - <%= link_to 'Change password', { :id => user.id, :action => 'change_password_form'}, {:id => "change-password-#{user.login.parameterize}", :class => 'open-modal link-action'} -%> - - <%= link_to_action message('delete'), "#{ApplicationController.root_context}/users/delete/#{user.id}", - :class => 'link-action link-red', - :id => "delete-#{user.login}", - :confirm_button => message('delete'), - :confirm_title => 'Delete user: '+user.login, - :confirm_msg => 'Warning : are you sure to delete the user "' + user.name+'"?' , - :confirm_msg_params => [user.name] - -%> - </td> - </tr> - <% end %> + <tbody> + <% @users.each do |user| %> + <tr id="user-<%= user.login.parameterize -%>"> + <td class="left" valign="top"><%= h user.login -%></td> + <td class="left" valign="top"><%= h user.name -%></td> + <td class="left" valign="top"><%= h user.email -%></td> + <td class="left" valign="top"> + <%= h user.groups.sort.map(&:name).join(', ') %> + (<%= link_to "select", {:action => 'select_group', :id => user.id}, {:id => "select-#{user.login.parameterize}", :class => 'open-modal link-action'} %>) + </td> + <td class="right" valign="top"> + <a id="edit-<%= user.login -%>" class="open-modal link-action" href="<%= ApplicationController.root_context -%>/users/edit_form/<%= u user.login -%>">Edit</a> + + <%= link_to 'Change password', {:id => user.id, :action => 'change_password_form'}, {:id => "change-password-#{user.login.parameterize}", :class => 'open-modal link-action'} -%> + + <%= link_to_action message('delete'), "#{ApplicationController.root_context}/users/delete/#{user.id}", + :class => 'link-action link-red', + :id => "delete-#{user.login}", + :confirm_button => message('delete'), + :confirm_title => 'Delete user: '+user.login, + :confirm_msg => 'Warning : are you sure to delete the user "' + user.name+'"?', + :confirm_msg_params => [user.name] + -%> + </td> + </tr> + <% end %> </tbody> </table> <script>jQuery('#users').sortable();</script> diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 2a3fa55a00b..08e7ae9e741 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -1968,6 +1968,7 @@ events.name_required=Name (required) user.bad_login=Use only letters, numbers, and .-_@ please. user.password_doesnt_match_confirmation=Password doesn't match confirmation. user.reactivated=The user '{0}' has been reactivated. +user.add_scm_account=Add SCM account #------------------------------------------------------------------------------ |