issue_filter.form.name=Name
issue_filter.form.description=Description
issue_filter.form.share=Shared with all users
+issue_filter.form.owner=Owner
issue_filter.favourite_filters=Favourite Filters
issue_filter.manage.my_filters=My Filters
issue_filter.no_filters=No filters
#----- Embedded database H2
# Note : it does not accept connections from remote hosts, so the
-# sonar server and the maven plugin must be executed on the same host.
+# SonarQube server and the maven plugin must be executed on the same host.
# Comment the following line to deactivate the default embedded database.
sonar.jdbc.url: jdbc:h2:tcp://localhost:9092/sonar
-# directory containing H2 database files. By default it's the /data directory in the sonar installation.
+# directory containing H2 database files. By default it's the /data directory in the SonarQube installation.
#sonar.embeddedDatabase.dataDir:
# H2 embedded database server listening port, defaults to 9092
#sonar.embeddedDatabase.port: 9092
shared=#{shared},
description=#{description},
data=#{data},
+ user_login=#{userLogin},
updated_at=current_timestamp
where id=#{id}
</update>
filterDto.setShared(false);
filterDto.setDescription("All closed issues");
filterDto.setData("statuses=CLOSED");
+ filterDto.setUserLogin("bernard");
dao.update(filterDto);
<issue_filters
id="2"
name="Closed issues"
- user_login="michael"
+ user_login="bernard"
shared="[false]"
description="All closed issues"
data="statuses=CLOSED"
String name = params.get("name");
String description = params.get("description");
String data = params.get("data");
+ String user = params.get("user");
Boolean sharedParam = RubyUtils.toBoolean(params.get("shared"));
boolean shared = sharedParam != null ? sharedParam : false;
DefaultIssueFilter defaultIssueFilter = DefaultIssueFilter.create(name)
.setDescription(description)
.setShared(shared)
+ .setUser(user)
.setData(data);
if (isUpdate) {
defaultIssueFilter.setId(Long.valueOf(id));
String user = getNotNullLogin(userSession);
IssueFilterDto issueFilterDto = findIssueFilterDto(issueFilter.id(), user);
verifyCurrentUserCanModifyFilter(issueFilterDto.toIssueFilter(), user);
+ if(issueFilterDto.getUserLogin() != issueFilter.user()) {
+ verifyCurrentUserCanChangeFilterOwnership(user);
+ }
validateFilter(issueFilter, user);
issueFilterDao.update(IssueFilterDto.toIssueFilter(issueFilter));
}
}
+ private void verifyCurrentUserCanChangeFilterOwnership(String user) {
+ if(!isAdmin(user)) {
+ // TODO throw unauthorized
+ throw new IllegalStateException("User is not authorized to change the owner of this filter");
+ }
+ }
+
private void validateFilter(DefaultIssueFilter issueFilter, String user) {
if (issueFilterDao.selectByNameAndUser(issueFilter.name(), user, issueFilter.id()) != null) {
throw new IllegalArgumentException("Name already exists");
verify_post_request
existing_filter = find_filter(params[:id].to_i)
- options = {'id' => params[:id].to_s, 'name' => params[:name], 'description' => params[:description], 'data' => existing_filter.data, 'shared' => params[:shared]=='true' }
+ options = {'id' => params[:id].to_s, 'name' => params[:name], 'description' => params[:description],
+ 'data' => existing_filter.data, 'shared' => params[:shared]=='true', 'user' => params[:user]}
filter_result = Internal.issues.updateIssueFilter(options)
if filter_result.ok
@filter = filter_result.get()
"<a href='#' class='issue-filter-star #{style}' filter-id='#{filter.id.to_s}' title='#{title}'></a>"
end
+ def can_be_reassigned_by(user, filter)
+ user.has_role?(:admin) && filter.shared
+ end
+
end
<div class="modal-head">
<h2><%= message('issue_filter.edit_filter') -%></h2>
</div>
- <%= render :partial => 'shared_form' %>
+
+ <%= render :partial => 'shared_form', :locals => {:display_owner => true} %>
+
<div class="modal-foot">
<input type="submit" value="<%= message('save') -%>" id="save-submit"/>
<a href="#" onclick="return closeModalWindow()" id="save-cancel"><%= message('cancel') -%></a>
+<% if !local_assigns.has_key? :display_owner
+ display_owner = false
+ end %>
<div class="modal-body">
<%= render :partial => 'display_errors' %>
<div class="modal-field">
<label for="description"><%= message('issue_filter.form.description') -%></label>
<input id="description" name="description" type="text" size="50" maxlength="4000" value="<%= params[:description] || (h(@filter.description) if @filter) -%>"/>
</div>
+ <% if display_owner && can_be_reassigned_by(current_user, @filter) %>
+ <% filter_owner = Api.users.findByLogin(@filter.user) %>
+ <div class="modal-field">
+ <label for="user"><%= h message('issue_filter.form.owner') -%></label>
+ <%= user_select_tag('user', :html_id => 'select-filter-owner', :selected_user => filter_owner) -%>
+ </div>
+ <% end %>
<div class="modal-field">
<label for="shared"><%= message('issue_filter.form.share') -%></label>
<input id="shared" name="shared" type="checkbox" value="true" <%= 'checked' if (params[:shared] || (@filter && @filter.shared)) -%>/>
<div class="modal-head">
<h2>Copy Filter</h2>
</div>
- <div class="modal-body">
- <% @filter.errors.each do |attr, msg| %>
- <p class="error"><%= h msg -%></p>
- <% end %>
- <div class="modal-field">
- <label for="name"><%= h message('name') -%> <em class="mandatory">*</em></label>
- <input id="name" name="name" type="text" size="50" maxlength="100" value="" autofocus="autofocus"/>
- </div>
- <div class="modal-field">
- <label for="description"><%= h message('description') -%></label>
- <input id="description" name="description" type="text" size="50" maxlength="4000" value="<%= h @filter.description -%>" />
- </div>
- <div class="modal-field">
- <label for="shared"><%= h message('measure_filter.shared_with_all_users') -%></label>
- <input id="shared" name="shared" type="checkbox" value="true" <%= 'checked' if @filter.shared -%>/>
- </div>
- </div>
+
+ <%= render :partial => 'shared_form' %>
+
<div class="modal-foot">
<input type="submit" value="<%= h message('copy') -%>" id="copy-submit"/>
<a href="#" onclick="return closeModalWindow()" id="copy-cancel"><%= h message('cancel') -%></a>
<div class="modal-head">
<h2>Edit Filter</h2>
</div>
- <div class="modal-body">
- <% @filter.errors.each do |attr, msg| %>
- <p class="error"><%= h "#{attr} #{msg}" -%></p>
- <% end %>
- <div class="modal-field">
- <label for="name"><%= h message('name') -%> <em class="mandatory">*</em></label>
- <input id="name" name="name" type="text" size="50" maxlength="100" value="<%= h @filter.name -%>" autofocus="autofocus"/>
- </div>
- <div class="modal-field">
- <label for="description"><%= h message('description') -%></label>
- <input id="description" name="description" type="text" size="50" maxlength="4000" value="<%= h @filter.description -%>"/>
- </div>
- <% if @filter.can_be_reassigned_by(current_user) %>
- <div class="modal-field">
- <label for="owner"><%= h message('owner') -%></label>
- <%= user_select_tag('owner', :html_id => 'select-filter-owner', :selected_user => @filter.user) -%>
- </div>
- <% end %>
- <div class="modal-field">
- <label for="shared"><%= h message('measure_filter.shared_with_all_users') -%></label>
- <input id="shared" name="shared" type="checkbox" value="true" <%= 'checked' if @filter.shared -%>/>
- </div>
- </div>
+
+ <%= render :partial => 'shared_form', :locals => {:display_owner => true} %>
+
<div class="modal-foot">
<input type="submit" value="<%= h message('save') -%>" id="save-submit"/>
<a href="#" onclick="return closeModalWindow()" id="save-cancel"><%= h message('cancel') -%></a>
<h2>Save Filter</h2>
</div>
- <div class="modal-body">
- <% @filter.errors.each do |attr, msg| %>
- <p class="error"><%= h msg -%></p>
- <% end %>
- <div class="modal-field">
- <label for="name">Name <em class="mandatory">*</em></label>
- <input id="name" name="name" type="text" size="50" maxlength="100" value="<%= h @filter.name -%>" autofocus="autofocus"/>
- </div>
- <div class="modal-field">
- <label for="description">Description</label>
- <input id="description" name="description" type="text" size="50" maxlength="4000" value="<%= h @filter.description -%>"/>
- </div>
- <div class="modal-field">
- <label for="shared">Shared with all users</label>
- <input id="shared" name="shared" type="checkbox" value="true" <%= 'checked' if @filter.shared -%>/>
- </div>
- </div>
+ <%= render :partial => 'shared_form' %>
+
<div class="modal-foot">
<input type="submit" value="<%= h message('save') -%>" id="save-as-submit"/>
<a href="#" onclick="return closeModalWindow()" id="save-as-cancel"><%= h message('cancel') -%></a>
--- /dev/null
+<% if !local_assigns.has_key? :display_owner
+ display_owner = false
+ end %>
+<div class="modal-body">
+ <% @filter.errors.full_messages.each do |msg| %>
+ <p class="error"><%= h msg -%></p>
+ <% end %>
+ <div class="modal-field">
+ <label for="name"><%= h message('name') -%> <em class="mandatory">*</em></label>
+ <input id="name" name="name" type="text" size="50" maxlength="100" value="<%= h @filter.name -%>" autofocus="autofocus"/>
+ </div>
+ <div class="modal-field">
+ <label for="description"><%= h message('description') -%></label>
+ <input id="description" name="description" type="text" size="50" maxlength="4000" value="<%= h @filter.description -%>"/>
+ </div>
+ <% if display_owner && @filter.can_be_reassigned_by(current_user) %>
+ <div class="modal-field">
+ <label for="owner"><%= h message('owner') -%></label>
+ <%= user_select_tag('owner', :html_id => 'select-filter-owner', :selected_user => @filter.user) -%>
+ </div>
+ <% end %>
+ <div class="modal-field">
+ <label for="shared"><%= h message('measure_filter.shared_with_all_users') -%></label>
+ <input id="shared" name="shared" type="checkbox" value="true" <%= 'checked' if @filter.shared -%>/>
+ </div>
+</div>
\ No newline at end of file
package org.sonar.server.issue;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
public void should_update() {
when(issueFilterDao.selectById(1L)).thenReturn(new IssueFilterDto().setId(1L).setName("My Old Filter").setUserLogin("john"));
- DefaultIssueFilter result = service.update(new DefaultIssueFilter().setId(1L).setName("My New Filter"), userSession);
+ DefaultIssueFilter result = service.update(new DefaultIssueFilter().setId(1L).setName("My New Filter").setUser("john"), userSession);
assertThat(result.name()).isEqualTo("My New Filter");
verify(issueFilterDao).update(any(IssueFilterDto.class));
@Test
public void should_not_update_if_name_already_used() {
when(issueFilterDao.selectById(1L)).thenReturn(new IssueFilterDto().setId(1L).setName("My Old Filter").setUserLogin("john"));
- when(issueFilterDao.selectByNameAndUser(eq("My Issue"), eq("john"), eq(1L))).thenReturn(new IssueFilterDto());
+ when(issueFilterDao.selectByNameAndUser("My Issue", "john", 1L)).thenReturn(new IssueFilterDto());
try {
- service.update(new DefaultIssueFilter().setId(1L).setName("My Issue"), userSession);
+ service.update(new DefaultIssueFilter().setId(1L).setUser("john").setName("My Issue"), userSession);
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Name already exists");
verify(issueFilterDao).update(any(IssueFilterDto.class));
}
+
+ @Test
+ public void should_change_shared_filter_ownership_when_admin() throws Exception {
+ String currentUser = "dave.loper";
+ IssueFilterDto sharedFilter = new IssueFilterDto().setId(1L).setName("My filter").setUserLogin("former.owner").setShared(true);
+ IssueFilterDto expectedDto = new IssueFilterDto().setName("My filter").setUserLogin("new.owner").setShared(true);
+
+ when(authorizationDao.selectGlobalPermissions(currentUser)).thenReturn(newArrayList(UserRole.ADMIN));
+ when(issueFilterDao.selectById(1L)).thenReturn(sharedFilter);
+
+ DefaultIssueFilter issueFilter = new DefaultIssueFilter().setId(1L).setName("My filter").setShared(true).setUser("new.owner");
+ service.update(issueFilter, new UserSession(1, currentUser, null));
+
+ verify(issueFilterDao).update(argThat(Matches.filter(expectedDto)));
+ }
+
+ @Test
+ public void should_not_change_own_filter_ownership() throws Exception {
+ String currentUser = "dave.loper";
+ IssueFilterDto sharedFilter = new IssueFilterDto().setId(1L).setName("My filter").setUserLogin(currentUser).setShared(true);
+
+ when(authorizationDao.selectGlobalPermissions(currentUser)).thenReturn(newArrayList(UserRole.USER));
+ when(issueFilterDao.selectById(1L)).thenReturn(sharedFilter);
+
+ try {
+ DefaultIssueFilter issueFilter = new DefaultIssueFilter().setId(1L).setName("My filter").setShared(true).setUser("new.owner");
+ service.update(issueFilter, new UserSession(1, currentUser, null));
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("User is not authorized to change the owner of this filter");
+ }
+
+ verify(issueFilterDao, never()).update(any(IssueFilterDto.class));
+ }
@Test
public void should_delete() {
verify(issueFilterFavouriteDao, never()).delete(anyLong());
}
+ private static class Matches extends BaseMatcher<IssueFilterDto> {
+
+ private final IssueFilterDto referenceFilter;
+
+ private Matches(IssueFilterDto reference) {
+ referenceFilter = reference;
+ }
+
+ static Matches filter(IssueFilterDto filterDto) {
+ return new Matches(filterDto);
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ if(o != null && o instanceof IssueFilterDto) {
+ IssueFilterDto otherFilter = (IssueFilterDto) o;
+ return referenceFilter.isShared() == otherFilter.isShared()
+ && referenceFilter.getUserLogin() == otherFilter.getUserLogin()
+ && referenceFilter.getDescription() == otherFilter.getDescription()
+ && referenceFilter.getName() == otherFilter.getName()
+ && referenceFilter.getData() == otherFilter.getData();
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ }
+ }
+
}