to=To
treemap=Treemap
true=True
+type=Type
unfollow=Unfollow
unit_test=Unit test
unit_tests=Unit tests
manual_measures.page=Manual Measures
manual_rules.page=Manual Rules
my_profile.page=My Profile
-roles.page=Roles
+roles.page=Project Permissions
project_settings.page=Settings
project_links.page=Links
project_history.page=History
# PROJECTS ROLES
#
#------------------------------------------------------------------------------
+projects_role.criteria.name=Name contains
+projects_role.criteria.key=Key contains
projects_role.role=Role Membership For New
projects_role.users=Users
projects_role.groups=Groups
*/
package org.sonar.core.resource;
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.ibatis.session.SqlSession;
import org.sonar.api.component.Component;
}
}
- public ComponentDto toComponent(ResourceDto resourceDto){
+ public static ComponentDto toComponent(ResourceDto resourceDto){
return new ComponentDto()
.setKey(resourceDto.getKey())
.setLongName(resourceDto.getLongName())
.setQualifier(resourceDto.getQualifier());
}
+ public static List<ComponentDto> toComponents(List<ResourceDto> resourceDto){
+ return newArrayList(Iterables.transform(resourceDto, new Function<ResourceDto, ComponentDto>() {
+ @Override
+ public ComponentDto apply(ResourceDto resourceDto) {
+ return toComponent(resourceDto);
+ }
+ }));
+ }
+
public void insertUsingExistingSession(ResourceDto resourceDto, SqlSession session) {
ResourceMapper resourceMapper = session.getMapper(ResourceMapper.class);
resourceMapper.insert(resourceDto);
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+package org.sonar.server.component;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @since 3.7
+ */
+public class ComponentQuery {
+
+ public static final int DEFAULT_PAGE_INDEX = 1;
+ public static final int DEFAULT_PAGE_SIZE = 100;
+
+ public static final String SORT_BY_NAME = "NAME";
+ public static final Set<String> SORTS = ImmutableSet.of(SORT_BY_NAME);
+
+ private final Collection<String> keys;
+ private final Collection<String> names;
+ private final Collection<String> qualifiers;
+ private final String sort;
+ private final Boolean asc;
+
+ // max results per page
+ private final int pageSize;
+
+ // index of selected page. Start with 1.
+ private final int pageIndex;
+
+ private ComponentQuery(Builder builder) {
+ this.keys = defaultCollection(builder.keys);
+ this.names = defaultCollection(builder.names);
+ this.qualifiers = defaultCollection(builder.qualifiers);
+
+ this.sort = builder.sort;
+ this.asc = builder.asc;
+ this.pageSize = builder.pageSize;
+ this.pageIndex = builder.pageIndex;
+ }
+
+ /**
+ * Pattern of component keys to search. Can contain a sub part of keys.
+ * Example : 'org.codehaus' will return 'org.codehaus.sonar', 'org.codehaus.tike', etc.
+ */
+ public Collection<String> keys() {
+ return keys;
+ }
+
+ /**
+ * Pattern of component name to search. Can contain a sub part of names.
+ * Example : 'Sona' will return 'Sonar', 'SonarJ', etc.
+ */
+ public Collection<String> names() {
+ return names;
+ }
+
+ /**
+ * Qualifiers of components to search.
+ */
+ public Collection<String> qualifiers() {
+ return qualifiers;
+ }
+
+ @CheckForNull
+ public String sort() {
+ return sort;
+ }
+
+ @CheckForNull
+ public Boolean asc() {
+ return asc;
+ }
+
+ public int pageSize() {
+ return pageSize;
+ }
+
+ public int pageIndex() {
+ return pageIndex;
+ }
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilder.toString(this);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private Collection<String> keys;
+ private Collection<String> names;
+ private Collection<String> qualifiers;
+ private String sort = SORT_BY_NAME;
+ private Boolean asc = true;
+ private Integer pageSize;
+ private Integer pageIndex;
+
+ private Builder() {
+ }
+
+ public Builder keys(@Nullable Collection<String> l) {
+ this.keys = l;
+ return this;
+ }
+
+ public Builder names(@Nullable Collection<String> l) {
+ this.names = l;
+ return this;
+ }
+
+ public Builder qualifiers(@Nullable Collection<String> l) {
+ this.qualifiers = l;
+ return this;
+ }
+
+ public Builder sort(@Nullable String s) {
+ if (s != null && !SORTS.contains(s)) {
+ throw new IllegalArgumentException("Bad sort field: " + s);
+ }
+ this.sort = s;
+ return this;
+ }
+
+ public Builder asc(@Nullable Boolean asc) {
+ this.asc = asc;
+ return this;
+ }
+
+ public Builder pageSize(@Nullable Integer i) {
+ this.pageSize = i;
+ return this;
+ }
+
+ public Builder pageIndex(@Nullable Integer i) {
+ this.pageIndex = i;
+ return this;
+ }
+
+ public ComponentQuery build() {
+ initPageIndex();
+ initPageSize();
+ return new ComponentQuery(this);
+ }
+
+ private void initPageSize() {
+ if (pageSize == null) {
+ pageSize = DEFAULT_PAGE_SIZE;
+ }
+ }
+
+ private void initPageIndex() {
+ if (pageIndex == null) {
+ pageIndex = DEFAULT_PAGE_INDEX;
+ }
+ Preconditions.checkArgument(pageIndex > 0, "Page index must be greater than 0 (got " + pageIndex + ")");
+ }
+ }
+
+ private static <T> Collection<T> defaultCollection(@Nullable Collection<T> c) {
+ return c == null ? Collections.<T>emptyList() : Collections.unmodifiableCollection(c);
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+package org.sonar.server.component;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Ordering;
+import org.sonar.api.component.Component;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @since 3.7
+ */
+class ComponentsFinderSort {
+
+ private Collection<? extends Component> components;
+ private ComponentQuery query;
+
+ public ComponentsFinderSort(Collection<? extends Component> components, ComponentQuery query) {
+ this.components = components;
+ this.query = query;
+ }
+
+ public Collection<? extends Component> sort() {
+ String sort = query.sort();
+ Boolean asc = query.asc();
+ if (sort != null && asc != null) {
+ return getComponentProcessor(sort).sort(components, asc);
+ }
+ return components;
+ }
+
+ private ComponentProcessor getComponentProcessor(String sort) {
+ if (ComponentQuery.SORT_BY_NAME.equals(sort)) {
+ return new NameSort();
+ }
+ throw new IllegalArgumentException("Cannot sort on field : " + sort);
+ }
+
+ abstract static class ComponentProcessor {
+ abstract Function sortFieldFunction();
+
+ abstract Ordering sortFieldOrdering(boolean ascending);
+
+ final List<? extends Component> sort(Collection<? extends Component> components, boolean ascending) {
+ Ordering<Component> ordering = sortFieldOrdering(ascending).onResultOf(sortFieldFunction());
+ return ordering.immutableSortedCopy(components);
+ }
+ }
+
+ abstract static class TextSort extends ComponentProcessor {
+ @Override
+ Function sortFieldFunction() {
+ return new Function<Component, String>() {
+ public String apply(Component component) {
+ return sortField(component);
+ }
+ };
+ }
+
+ abstract String sortField(Component component);
+
+ @Override
+ Ordering sortFieldOrdering(boolean ascending) {
+ Ordering<String> ordering = Ordering.from(String.CASE_INSENSITIVE_ORDER).nullsLast();
+ if (!ascending) {
+ ordering = ordering.reverse();
+ }
+ return ordering;
+ }
+ }
+
+ static class NameSort extends TextSort {
+ @Override
+ String sortField(Component component) {
+ return component.name();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+package org.sonar.server.component;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.component.Component;
+import org.sonar.api.utils.Paging;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceQuery;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * @since 3.7
+ */
+public class DefaultComponentFinder {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultComponentFinder.class);
+ private final ResourceDao resourceDao;
+
+ public DefaultComponentFinder(ResourceDao resourceDao) {
+ this.resourceDao = resourceDao;
+ }
+
+ public DefaultComponentQueryResult find(ComponentQuery query) {
+ LOG.debug("ComponentQuery : {}", query);
+ long start = System.currentTimeMillis();
+ try {
+ // 1. Search components
+ ResourceQuery resourceQuery = ResourceQuery.create().setQualifiers(query.qualifiers().toArray(new String[]{}));
+ List<ComponentDto> dtos = ResourceDao.toComponents(resourceDao.getResources(resourceQuery));
+ Collection<Component> foundComponents = search(query, dtos);
+
+ // 2. Sort components
+ Collection<? extends Component> sortedComponents = new ComponentsFinderSort(foundComponents, query).sort();
+
+ // 3. Apply pagination
+ Paging paging = Paging.create(query.pageSize(), query.pageIndex(), foundComponents.size());
+ Collection<? extends Component> pagedComponents = pagedComponents(sortedComponents, paging);
+
+ return new DefaultComponentQueryResult(pagedComponents).setPaging(paging).setQuery(query);
+ } finally {
+ LOG.debug("ComponentQuery execution time : {} ms", System.currentTimeMillis() - start);
+ }
+ }
+
+ private Collection<Component> search(final ComponentQuery query, List<? extends Component> allComponents) {
+ return newArrayList(Iterables.filter(allComponents, new Predicate<Component>() {
+ @Override
+ public boolean apply(Component component) {
+ return new KeyFilter().accept(component, query.keys()) &&
+ new NameFilter().accept(component, query.names());
+ }
+ }));
+ }
+
+ abstract static class Filter {
+
+ abstract String field(Component component);
+
+ final boolean accept(Component component, Collection<String> collections) {
+ if (!collections.isEmpty()) {
+ for (String item : collections) {
+ if (field(component).toLowerCase().contains(item.toLowerCase())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+ }
+
+ static class NameFilter extends Filter {
+ @Override
+ String field(Component component) {
+ return component.name();
+ }
+ }
+
+ static class KeyFilter extends Filter {
+ @Override
+ String field(Component component) {
+ return component.key();
+ }
+ }
+
+ private Collection<? extends Component> pagedComponents(Collection<? extends Component> components, Paging paging) {
+ Set<Component> pagedComponents = Sets.newLinkedHashSet();
+ int index = 0;
+ for (Component component : components) {
+ if (index >= paging.offset() && pagedComponents.size() < paging.pageSize()) {
+ pagedComponents.add(component);
+ } else if (pagedComponents.size() >= paging.pageSize()) {
+ break;
+ }
+ index++;
+ }
+ return pagedComponents;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+package org.sonar.server.component;
+
+import org.sonar.api.component.Component;
+import org.sonar.api.utils.Paging;
+
+import java.util.Collection;
+
+public class DefaultComponentQueryResult {
+
+ private Collection<? extends Component> components;
+ private ComponentQuery query;
+ private Paging paging;
+
+ public DefaultComponentQueryResult(Collection<? extends Component> components) {
+ this.components = components;
+ }
+
+ public DefaultComponentQueryResult setPaging(Paging paging) {
+ this.paging = paging;
+ return this;
+ }
+
+ public ComponentQuery query() {
+ return query;
+ }
+
+ public DefaultComponentQueryResult setQuery(ComponentQuery query) {
+ this.query = query;
+ return this;
+ }
+
+ public Collection<? extends Component> components() {
+ return components;
+ }
+
+ public Paging paging() {
+ return paging;
+ }
+
+}
*/
package org.sonar.server.component;
+import com.google.common.base.Strings;
import org.sonar.api.component.Component;
import org.sonar.api.component.RubyComponentService;
import org.sonar.core.resource.ResourceDao;
+import org.sonar.server.util.RubyUtils;
+
+import java.util.Map;
public class DefaultRubyComponentService implements RubyComponentService {
private final ResourceDao resourceDao;
+ private final DefaultComponentFinder finder;
- public DefaultRubyComponentService(ResourceDao resourceDao) {
+ public DefaultRubyComponentService(ResourceDao resourceDao, DefaultComponentFinder finder) {
this.resourceDao = resourceDao;
+ this.finder = finder;
}
@Override
return resourceDao.findByKey(key);
}
+ public DefaultComponentQueryResult find(Map<String, Object> params) {
+ return finder.find(toQuery(params));
+ }
+
+ static ComponentQuery toQuery(Map<String, Object> props) {
+ ComponentQuery.Builder builder = ComponentQuery.builder()
+ .keys(RubyUtils.toStrings(props.get("keys")))
+ .names(RubyUtils.toStrings(props.get("names")))
+ .qualifiers(RubyUtils.toStrings(props.get("qualifiers")))
+ .pageSize(RubyUtils.toInteger(props.get("pageSize")))
+ .pageIndex(RubyUtils.toInteger(props.get("pageIndex")));
+ String sort = (String) props.get("sort");
+ if (!Strings.isNullOrEmpty(sort)) {
+ builder.sort(sort);
+ builder.asc(RubyUtils.toBoolean(props.get("asc")));
+ }
+ return builder.build();
+ }
+
}
import org.sonar.jpa.session.DefaultDatabaseConnector;
import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory;
import org.sonar.server.charts.ChartFactory;
+import org.sonar.server.component.DefaultComponentFinder;
import org.sonar.server.component.DefaultRubyComponentService;
import org.sonar.server.configuration.Backup;
import org.sonar.server.configuration.ProfilesManager;
servicesContainer.addSingleton(InternalPermissionTemplateService.class);
// components
+ servicesContainer.addSingleton(DefaultComponentFinder.class);
servicesContainer.addSingleton(DefaultRubyComponentService.class);
// issues
end
def projects
- # for backward-compatibility with versions of views plugin that do not depend on sonar 3.0
- if java_facade.hasPlugin('views')
- @qualifiers = (['VW'] + java_facade.getQualifiersWithProperty('hasRolePolicy').to_a).compact.uniq
- else
- @qualifiers = java_facade.getQualifiersWithProperty('hasRolePolicy')
- end
- @qualifier = params[:qualifier] || 'TRK'
-
-
- conditions_sql = 'projects.enabled=:enabled and projects.qualifier=:qualifier and projects.copy_resource_id is null'
- conditions_values = {:enabled => true, :qualifier => @qualifier}
- joins = "INNER JOIN resource_index on resource_index.resource_id=projects.id "
- joins += "and resource_index.qualifier=#{ActiveRecord::Base::sanitize(@qualifier)} "
- if params[:q].present?
- query = params[:q].downcase + '%'
- joins +="and resource_index.kee like #{ActiveRecord::Base::sanitize(query)}"
- else
- joins += "and resource_index.position=0"
- end
-
- @pagination = Api::Pagination.new(params)
- @projects=Project.all(:select => 'distinct(resource_index.resource_id),projects.id,projects.kee,projects.name,resource_index.kee as resource_index_key',
- :include => ['user_roles','group_roles'],
- :joins => joins,
- :conditions => [conditions_sql, conditions_values],
- :order => 'resource_index.kee',
- :offset => @pagination.offset,
- :limit => @pagination.limit)
- @pagination.count=Project.count(
- :select => 'distinct(projects.id)',
- :joins => joins,
- :conditions => [conditions_sql, conditions_values])
+ params['pageSize'] = 25
+ params['qualifiers'] ||= 'TRK'
+ @query_result = Internal.component_api.find(params)
+
+ @available_qualifiers = java_facade.getQualifiersWithProperty('hasRolePolicy').collect { |qualifier| [message("qualifiers.#{qualifier}"), qualifier] }.sort
+
+ # For the moment, we return projects from rails models, but it should be replaced to return java components (this will need methods on ComponentQueryResult to return roles from component)
+ @projects = Project.all(
+ :include => ['user_roles','group_roles'],
+ :conditions => ['kee in (?)', @query_result.components().to_a.collect{|component| component.key()}],
+ # Even if components are already sorted, we must sort them again as this SQL query will not keep order
+ :order => 'name'
+ )
end
def edit_users
<a href="<%= ApplicationController.root_context -%>/groups/index"><%= message('user_groups.page') -%></a></li>
<li class="<%= 'active' if request.request_uri.include?('/roles/global') -%>">
<a href="<%= ApplicationController.root_context -%>/roles/global"><%= message('global_permissions.page') -%></a></li>
- <li class="<%= 'active' if request.request_uri.include?('/permission_templates') -%>">
- <a href="<%= ApplicationController.root_context -%>/permission_templates"><%= message('permission_templates.page') -%></a></li>
- <li class="<%= 'active' if request.request_uri.include?('/roles/projects') -%>">
+ <li class="<%= 'active' if request.request_uri.include?('/roles/projects') || request.request_uri.include?('/permission_templates') -%>">
<a href="<%= ApplicationController.root_context -%>/roles/projects"><%= message('roles.page') -%></a></li>
<li class="spacer"></li>
-<h1><%= message('permission_templates') -%></h1>
+<%= render :partial => 'roles/tabs', :locals => {:selected_tab=>'Permission templates'} %>
<br/>
<h2>Default templates</h2>
<div class="box">
--- /dev/null
+<%
+ selected_tab = nil unless defined?(:selected_tab)
+%>
+<ul class="tabs">
+ <li>
+ <a href="<%= url_for :controller => 'roles', :action => 'projects' %>" <%= "class='selected'" if selected_tab=='Projects' -%>
+ id="tab-projects"><%= message('projects') -%></a>
+ </li>
+ <li>
+ <a href="<%= url_for :controller => 'permission_templates', :action => 'index' -%>" <%= "class='selected'" if selected_tab=='Permission templates' -%>
+ id="tab-perm-templates"><%= message('permission_templates') -%></a>
+ </li>
+</ul>
\ No newline at end of file
-<h1><%= message('roles.page') -%></h1>
+<%= render :partial => 'roles/tabs', :locals => {:selected_tab=>'Projects'} %>
-<% if @qualifiers.size>1 %>
- <ul class="tabs" id="qualifier-tabs">
- <% @qualifiers.each do |q|
- css_class = (q==@qualifier ? 'selected' : '')
- %>
- <li>
- <%= link_to message("qualifiers.#{q}"), {:action => 'projects', :qualifier => q}, {:class => css_class, :id => "tab-#{u q}"} -%>
- </li>
- <% end %>
- </ul>
- <div class="tabs-panel">
-<% end %>
+<div class="tabs-panel marginbottom10 background-gray">
+ <% form_tag({:action => 'projects'}, {:id => 'project-search-form', :method => 'get'}) do %>
+ <div class="table">
+ <div class="project-search top">
+ <span class="note"><%= message('projects_role.criteria.name') -%></span><br/>
+ <% selected_name = @query_result.query.names.to_a.first if @query_result.query.names && @query_result.query.names.size == 1 %>
+ <%= text_field_tag 'names', selected_name, :id => 'search-text' %>
+ </div>
+ <div class="project-search top">
+ <span class="note"><%= message('projects_role.criteria.key') -%></span><br/>
+ <% selected_key = @query_result.query.keys.to_a.first if @query_result.query.keys && @query_result.query.keys.size == 1 %>
+ <%= text_field_tag 'keys', selected_key, :id => 'search-text' %>
+ </div>
+ <div class="project-search top">
+ <span class="note"><%= message('type') -%></span><br/>
+ <% selected_qualifier = @query_result.query.qualifiers.to_a.first if @query_result.query.qualifiers && @query_result.query.qualifiers.size == 1 %>
+ <%= dropdown_tag 'qualifiers', options_for_select(@available_qualifiers, selected_qualifier), {
+ :width => '150px'
+ }, {:id => 'search-qualifier'} -%>
+ </div>
+ <div class="project-search">
+ <br/>
+ <%= submit_tag message('search_verb'), :id => 'submit-search', :onclick => 'submitSearch();' %>
+ </div>
+ </div>
+ <% end %>
+</div>
<div id="project-roles-operations" style="float: right;">
<ul class="operations">
</ul>
</div>
- <table class="data width100" id="projects">
- <thead>
- <tr>
- <th>
- <form action="<%= url_for :action => 'projects', :qualifier => @qualifier -%>" method="GET">
- <input type="hidden" name="qualifier" value="<%= @qualifier -%>"/>
- <input type="text" name="q" value="<%= params[:q] -%>" id="search_text"/>
- <input type="submit" value="<%= message('search_verb') -%>" id="search_submit"/>
- </form>
- </th>
- <th><%= message('projects_role.admin') -%></th>
- <th><%= message('projects_role.user') -%></th>
- <th><%= message('projects_role.codeviewer') -%></th>
- </tr>
- </thead>
+<table class="data width100" id="projects">
+ <thead>
+ <tr>
+ <th> </th>
+ <th><%= message('projects_role.admin') -%></th>
+ <th><%= message('projects_role.user') -%></th>
+ <th><%= message('projects_role.codeviewer') -%></th>
+ </tr>
+ </thead>
- <%= table_pagination(@pagination, :colspan => 4) { |label, page_id| link_to(label, params.merge({:page => page_id}))} -%>
+ <%= paginate_java(@query_result.paging, :colspan => 4, :id => 'project-roles-foot', :include_loading_icon => true) { |label, page_id|
+ link_to(label, params.merge({:pageIndex => page_id}))
+ }
+ %>
- <tbody>
- <% if @projects.empty? %>
- <tr class="even">
- <td colspan="4" align="left"><%= message('no_results') %></td>
- </tr>
- <% end
+ <tbody>
+ <% if @projects.empty? %>
+ <tr class="even">
+ <td colspan="4" align="left"><%= message('no_results') %></td>
+ </tr>
+ <% end
- @projects.each do |project|
- %>
- <tr class="<%= cycle('even', 'odd') -%>">
- <td valign="top"><b><%= h project.name %></b><br/>
- <span class="small gray"><%= h project.key -%></span>
- </td>
- <td valign="top">
- <%
- users=Api::Utils.insensitive_sort(project.user_roles.select { |ur| ur.role=='admin' }.map { |ur| ur.user.name })
- groups=Api::Utils.insensitive_sort(project.group_roles.select { |gr| gr.role=='admin' }.map { |gr| group_name(gr.group) })
- %>
- <span id="u-admin-<%= u project.kee -%>"><%= users.join(', ') %></span>
- (<a href="<%= ApplicationController.root_context -%>/roles/edit_users?redirect=projects&role=admin&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectu-admin-<%= u project.kee -%>">select users</a>)<br/>
- <span id="g-admin-<%= u project.kee -%>"><%= groups.join(', ') %></span>
- (<a href="<%= ApplicationController.root_context -%>/roles/edit_groups?redirect=projects&role=admin&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectg-admin-<%= u project.kee -%>">select groups</a>)
- </td>
- <td valign="top">
- <%
- users=Api::Utils.insensitive_sort(project.user_roles.select { |ur| ur.role=='user' }.map { |ur| ur.user.name })
- groups=Api::Utils.insensitive_sort(project.group_roles.select { |gr| gr.role=='user' }.map { |gr| group_name(gr.group) })
- %>
- <span id="u-user-<%= u project.kee -%>"><%= users.join(', ') %></span>
- (<a href="<%= ApplicationController.root_context -%>/roles/edit_users?redirect=projects&role=user&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectu-user-<%= u project.kee -%>">select users</a>)<br/>
- <span id="g-user-<%= u project.kee -%>"><%= groups.join(', ') %></span>
- (<a href="<%= ApplicationController.root_context -%>/roles/edit_groups?redirect=projects&role=user&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectg-user-<%= u project.kee -%>">select groups</a>)
- </td>
- <td valign="top">
- <%
- users=Api::Utils.insensitive_sort(project.user_roles.select { |ur| ur.role=='codeviewer' }.map { |ur| ur.user.name })
- groups=Api::Utils.insensitive_sort(project.group_roles.select { |gr| gr.role=='codeviewer' }.map { |gr| group_name(gr.group) })
- %>
- <span id="u-codeviewer-<%= u project.kee -%>"><%= users.join(', ') %></span>
- (<a href="<%= ApplicationController.root_context -%>/roles/edit_users?redirect=projects&role=codeviewer&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectu-codeviewer-<%= u project.kee -%>">select users</a>)<br/>
- <span id="g-codeviewer-<%= u project.kee -%>"><%= groups.join(', ') %></span>
- (<a href="<%= ApplicationController.root_context -%>/roles/edit_groups?redirect=projects&role=codeviewer&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectg-codeviewer-<%= u project.kee -%>">select groups</a>)
- </td>
- </tr>
- <%
- end %>
- </tbody>
- </table>
+ @projects.each do |project|
+ %>
+ <tr class="<%= cycle('even', 'odd') -%>">
+ <td valign="top"><b><%= h project.name %></b><br/>
+ <span class="small gray"><%= h project.key -%></span>
+ </td>
+ <td valign="top">
+ <%
+ users=Api::Utils.insensitive_sort(project.user_roles.select { |ur| ur.role=='admin' }.map { |ur| ur.user.name })
+ groups=Api::Utils.insensitive_sort(project.group_roles.select { |gr| gr.role=='admin' }.map { |gr| group_name(gr.group) })
+ %>
+ <span id="u-admin-<%= u project.kee -%>"><%= users.join(', ') %></span>
+ (<a href="<%= ApplicationController.root_context -%>/roles/edit_users?redirect=projects&role=admin&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectu-admin-<%= u project.kee -%>">select users</a>)<br/>
+ <span id="g-admin-<%= u project.kee -%>"><%= groups.join(', ') %></span>
+ (<a href="<%= ApplicationController.root_context -%>/roles/edit_groups?redirect=projects&role=admin&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectg-admin-<%= u project.kee -%>">select groups</a>)
+ </td>
+ <td valign="top">
+ <%
+ users=Api::Utils.insensitive_sort(project.user_roles.select { |ur| ur.role=='user' }.map { |ur| ur.user.name })
+ groups=Api::Utils.insensitive_sort(project.group_roles.select { |gr| gr.role=='user' }.map { |gr| group_name(gr.group) })
+ %>
+ <span id="u-user-<%= u project.kee -%>"><%= users.join(', ') %></span>
+ (<a href="<%= ApplicationController.root_context -%>/roles/edit_users?redirect=projects&role=user&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectu-user-<%= u project.kee -%>">select users</a>)<br/>
+ <span id="g-user-<%= u project.kee -%>"><%= groups.join(', ') %></span>
+ (<a href="<%= ApplicationController.root_context -%>/roles/edit_groups?redirect=projects&role=user&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectg-user-<%= u project.kee -%>">select groups</a>)
+ </td>
+ <td valign="top">
+ <%
+ users=Api::Utils.insensitive_sort(project.user_roles.select { |ur| ur.role=='codeviewer' }.map { |ur| ur.user.name })
+ groups=Api::Utils.insensitive_sort(project.group_roles.select { |gr| gr.role=='codeviewer' }.map { |gr| group_name(gr.group) })
+ %>
+ <span id="u-codeviewer-<%= u project.kee -%>"><%= users.join(', ') %></span>
+ (<a href="<%= ApplicationController.root_context -%>/roles/edit_users?redirect=projects&role=codeviewer&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectu-codeviewer-<%= u project.kee -%>">select users</a>)<br/>
+ <span id="g-codeviewer-<%= u project.kee -%>"><%= groups.join(', ') %></span>
+ (<a href="<%= ApplicationController.root_context -%>/roles/edit_groups?redirect=projects&role=codeviewer&resource=<%= project.id -%>&q=<%= u params[:q] -%>&qualifier=<%= @qualifier -%>&page=<%= params[:page] -%>" class="link-action" id="selectg-codeviewer-<%= u project.kee -%>">select groups</a>)
+ </td>
+ </tr>
+ <%
+ end %>
+ </tbody>
+</table>
-<% if @qualifiers.size>1 %>
- </div>
-<% end %>
<script>
- $('search_text').focus();
+ function submitSearch() {
+ $j("#project-search-form").submit();
+ }
+
+ $j('#search-text').focus();
</script>
\ No newline at end of file
font-size: 85%;
color: #777;
}
+
+/* ------------------- Role search styles ------------------- */
+
+.project-search {
+ display: inline-block;
+ line-height: 16px;
+ padding: 4px 2px;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+package org.sonar.server.component;
+
+import org.junit.Test;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ComponentQueryTest {
+
+ @Test
+ public void should_build_query() throws Exception {
+ ComponentQuery query = ComponentQuery.builder()
+ .keys(newArrayList("org.codehaus"))
+ .names(newArrayList("Sona"))
+ .qualifiers(newArrayList("TRK"))
+ .pageSize(10)
+ .pageIndex(2)
+ .sort(ComponentQuery.SORT_BY_NAME)
+ .asc(true)
+ .build();
+ assertThat(query.keys()).containsOnly("org.codehaus");
+ assertThat(query.names()).containsOnly("Sona");
+ assertThat(query.qualifiers()).containsOnly("TRK");
+ assertThat(query.sort()).isEqualTo(ComponentQuery.SORT_BY_NAME);
+ assertThat(query.asc()).isTrue();
+ assertThat(query.pageSize()).isEqualTo(10);
+ assertThat(query.pageIndex()).isEqualTo(2);
+ }
+
+ @Test
+ public void should_accept_null_sort() throws Exception {
+ ComponentQuery query = ComponentQuery.builder().sort(null).build();
+ assertThat(query.sort()).isNull();
+ }
+
+ @Test
+ public void should_sort_by_name_asc_by_default() throws Exception {
+ ComponentQuery query = ComponentQuery.builder().build();
+ assertThat(query.sort()).isEqualTo(ComponentQuery.SORT_BY_NAME);
+ assertThat(query.asc()).isTrue();
+ }
+
+ @Test
+ public void should_throw_exception_if_sort_is_not_valid() throws Exception {
+ try {
+ ComponentQuery.builder()
+ .sort("UNKNOWN")
+ .build();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Bad sort field: UNKNOWN");
+ }
+ }
+
+ @Test
+ public void test_default_page_index_and_size() throws Exception {
+ ComponentQuery query = ComponentQuery.builder().build();
+ assertThat(query.pageSize()).isEqualTo(ComponentQuery.DEFAULT_PAGE_SIZE);
+ assertThat(query.pageIndex()).isEqualTo(ComponentQuery.DEFAULT_PAGE_INDEX);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+package org.sonar.server.component;
+
+import org.junit.Test;
+import org.sonar.api.component.Component;
+import org.sonar.core.component.ComponentDto;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ComponentsFinderSortTest {
+
+ @Test
+ public void should_sort_by_name() {
+ List<? extends Component> dtoList = newArrayList(
+ new ComponentDto().setKey("org.codehaus.sonar").setName("Sonar"),
+ new ComponentDto().setKey("org.apache.tika:tika").setName("Apache Tika"),
+ new ComponentDto().setKey("org.picocontainer:picocontainer-parent").setName("PicoContainer Parent"),
+ new ComponentDto().setKey("org.codehaus.sample")
+ );
+
+ ComponentQuery query = ComponentQuery.builder().sort(ComponentQuery.SORT_BY_NAME).asc(true).build();
+ ComponentsFinderSort issuesFinderSort = new ComponentsFinderSort(dtoList, query);
+
+ List<Component> result = newArrayList(issuesFinderSort.sort());
+
+ assertThat(result).hasSize(4);
+ assertThat(result.get(0).name()).isEqualTo("Apache Tika");
+ assertThat(result.get(1).name()).isEqualTo("PicoContainer Parent");
+ assertThat(result.get(2).name()).isEqualTo("Sonar");
+ assertThat(result.get(3).name()).isNull();
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+package org.sonar.server.component;
+
+import org.junit.Test;
+import org.sonar.api.component.Component;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceDto;
+import org.sonar.core.resource.ResourceQuery;
+
+import java.util.Iterator;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultComponentFinderTest {
+
+ ResourceDao dao = mock(ResourceDao.class);
+ DefaultComponentFinder finder = new DefaultComponentFinder(dao);
+
+ @Test
+ public void should_return_all_components_when_no_parameter() {
+ List<ResourceDto> dtoList = newArrayList(
+ new ResourceDto().setKey("org.codehaus.sonar").setName("Sonar").setQualifier("TRK"),
+ new ResourceDto().setKey("org.apache.tika:tika").setName("Apache Tika").setQualifier("TRK"),
+ new ResourceDto().setKey("org.picocontainer:picocontainer-parent").setName("PicoContainer Parent").setQualifier("TRK")
+ );
+ when(dao.getResources(any(ResourceQuery.class))).thenReturn(dtoList);
+
+ ComponentQuery query = ComponentQuery.builder().build();
+ DefaultComponentQueryResult results = finder.find(query);
+
+ assertThat(results.components()).hasSize(3);
+ Component component = results.components().iterator().next();
+ assertThat(component.name()).isEqualTo("Apache Tika");
+ assertThat(component.key()).isEqualTo("org.apache.tika:tika");
+ assertThat(component.qualifier()).isEqualTo("TRK");
+ }
+
+ @Test
+ public void should_find_components_by_key_pattern() {
+ List<ResourceDto> dtoList = newArrayList(
+ new ResourceDto().setKey("org.codehaus.sonar").setName("Sonar").setQualifier("TRK"),
+ new ResourceDto().setKey("org.apache.tika:tika").setName("Apache Tika").setQualifier("TRK"),
+ new ResourceDto().setKey("org.apache.jackrabbit:jackrabbit").setName("Apache Jackrabbit").setQualifier("TRK")
+ );
+ when(dao.getResources(any(ResourceQuery.class))).thenReturn(dtoList);
+
+ ComponentQuery query = ComponentQuery.builder().keys(newArrayList("org.apache")).build();
+ assertThat(finder.find(query).components()).hasSize(2);
+ }
+
+ @Test
+ public void should_find_components_by_name_pattern() {
+ List<ResourceDto> dtoList = newArrayList(
+ new ResourceDto().setKey("org.codehaus.sonar").setName("Sonar").setQualifier("TRK"),
+ new ResourceDto().setKey("org.apache.tika:tika").setName("Apache Tika").setQualifier("TRK"),
+ new ResourceDto().setKey("org.apache.jackrabbit:jackrabbit").setName("Apache Jackrabbit").setQualifier("TRK")
+ );
+ when(dao.getResources(any(ResourceQuery.class))).thenReturn(dtoList);
+
+ ComponentQuery query = ComponentQuery.builder().names(newArrayList("Apache")).build();
+ assertThat(finder.find(query).components()).hasSize(2);
+ }
+
+ @Test
+ public void should_sort_result_by_name() {
+ List<ResourceDto> dtoList = newArrayList(
+ new ResourceDto().setKey("org.codehaus.sonar").setName("Sonar").setQualifier("TRK"),
+ new ResourceDto().setKey("org.apache.tika:tika").setName("Apache Tika").setQualifier("TRK"),
+ new ResourceDto().setKey("org.picocontainer:picocontainer-parent").setName("PicoContainer Parent").setQualifier("TRK")
+ );
+ when(dao.getResources(any(ResourceQuery.class))).thenReturn(dtoList);
+
+ ComponentQuery query = ComponentQuery.builder().build();
+ DefaultComponentQueryResult results = finder.find(query);
+
+ assertThat(results.components()).hasSize(3);
+ Iterator<? extends Component> iterator = results.components().iterator();
+ assertThat(iterator.next().name()).isEqualTo("Apache Tika");
+ assertThat(iterator.next().name()).isEqualTo("PicoContainer Parent");
+ assertThat(iterator.next().name()).isEqualTo("Sonar");
+ }
+
+ @Test
+ public void should_find_paginate_result() {
+ ComponentQuery query = ComponentQuery.builder().pageSize(1).pageIndex(1).build();
+
+ List<ResourceDto> dtoList = newArrayList(
+ new ResourceDto().setKey("org.codehaus.sonar").setName("Sonar").setQualifier("TRK"),
+ new ResourceDto().setKey("org.apache.tika:tika").setName("Apache Tika").setQualifier("TRK"),
+ new ResourceDto().setKey("org.picocontainer:picocontainer-parent").setName("PicoContainer Parent").setQualifier("TRK")
+ );
+ when(dao.getResources(any(ResourceQuery.class))).thenReturn(dtoList);
+
+ DefaultComponentQueryResult results = finder.find(query);
+ assertThat(results.paging().offset()).isEqualTo(0);
+ assertThat(results.paging().pages()).isEqualTo(3);
+ assertThat(results.paging().total()).isEqualTo(3);
+ }
+
+}
package org.sonar.server.component;
import org.junit.Before;
+import org.junit.Test;
import org.sonar.api.component.Component;
import org.sonar.core.resource.ResourceDao;
+import java.util.Map;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
public class DefaultRubyComponentServiceTest {
private ResourceDao resourceDao;
+ private DefaultComponentFinder finder;
private DefaultRubyComponentService componentService;
@Before
- public void before(){
+ public void before() {
resourceDao = mock(ResourceDao.class);
- componentService = new DefaultRubyComponentService(resourceDao);
+ finder = mock(DefaultComponentFinder.class);
+ componentService = new DefaultRubyComponentService(resourceDao, finder);
}
- @Before
+ @Test
public void should_find_by_key() {
Component component = mock(Component.class);
when(resourceDao.findByKey("struts")).thenReturn(component);
assertThat(componentService.findByKey("struts")).isEqualTo(component);
}
+
+ @Test
+ public void should_find() {
+ Map<String, Object> map = newHashMap();
+ map.put("keys", newArrayList("org.codehaus.sonar"));
+ map.put("names", newArrayList("Sonar"));
+ map.put("qualifiers", newArrayList("TRK"));
+ map.put("pageSize", 10l);
+ map.put("pageIndex", 50);
+ map.put("sort", "NAME");
+ map.put("asc", true);
+
+ componentService.find(map);
+ verify(finder).find(any(ComponentQuery.class));
+ }
+
+ @Test
+ public void should_create_query_from_parameters() {
+ Map<String, Object> map = newHashMap();
+ map.put("keys", newArrayList("org.codehaus.sonar"));
+ map.put("names", newArrayList("Sonar"));
+ map.put("qualifiers", newArrayList("TRK"));
+ map.put("pageSize", 10l);
+ map.put("pageIndex", 50);
+ map.put("sort", "NAME");
+ map.put("asc", true);
+
+ ComponentQuery query = DefaultRubyComponentService.toQuery(map);
+ assertThat(query.keys()).containsOnly("org.codehaus.sonar");
+ assertThat(query.names()).containsOnly("Sonar");
+ assertThat(query.qualifiers()).containsOnly("TRK");
+ assertThat(query.pageSize()).isEqualTo(10);
+ assertThat(query.pageIndex()).isEqualTo(50);
+ assertThat(query.sort()).isEqualTo(ComponentQuery.SORT_BY_NAME);
+ assertThat(query.asc()).isTrue();
+ }
}