diff options
author | Simon Brandhof <simon.brandhof@gmail.com> | 2012-04-01 16:29:12 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@gmail.com> | 2012-04-01 16:29:12 +0200 |
commit | 3fe232f417bf8f46eef517b74de596d4c9650006 (patch) | |
tree | 9e7a18c6470f1873f0c119648c16ec0e6665b209 | |
parent | bc058ab2299402a3263dae5fb2ef84eed3c72893 (diff) | |
download | sonarqube-3fe232f417bf8f46eef517b74de596d4c9650006.tar.gz sonarqube-3fe232f417bf8f46eef517b74de596d4c9650006.zip |
Management console of project roles : add pagination and support of new resource types
9 files changed, 235 insertions, 88 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/DefaultResourceTypes.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/DefaultResourceTypes.java index bf2632f271f..3ddd12dc63a 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/DefaultResourceTypes.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/DefaultResourceTypes.java @@ -37,6 +37,7 @@ public final class DefaultResourceTypes extends ExtensionProvider implements Bat .addType(ResourceType.builder(Qualifiers.PROJECT) .setProperty("deletable", "true") .setProperty("modifiable_history", "true") + .setProperty("hasRolePolicy", "true") .build()) .addType(ResourceType.builder(Qualifiers.MODULE).build()) .addType(ResourceType.builder(Qualifiers.DIRECTORY).build()) diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index f2970d4e9d8..134ddfb54de 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -19,6 +19,9 @@ */ package org.sonar.server.ui; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; import org.apache.commons.configuration.Configuration; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; @@ -96,6 +99,16 @@ public final class JRubyFacade { return null; } + public List<String> getQualifiersWithProperty(final String propertyKey) { + List<String> qualifiers = Lists.newArrayList(); + for (ResourceType type : getResourceTypes()) { + if (type.getBooleanProperty(propertyKey) == Boolean.TRUE) { + qualifiers.add(type.getQualifier()); + } + } + return qualifiers; + } + public Boolean getResourceTypeBooleanProperty(String resourceTypeQualifier, String resourceTypeProperty) { ResourceType resourceType = getResourceType(resourceTypeQualifier); if (resourceType != null) { diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/roles_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/roles_controller.rb index fdb1a8d902e..4118f8da517 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/roles_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/roles_controller.rb @@ -19,20 +19,39 @@ # class RolesController < ApplicationController helper RolesHelper - + SECTION=Navigation::SECTION_CONFIGURATION - + PER_PAGE = 2 + before_filter :admin_required - verify :method => :post, :only => [:grant_users, :grant_groups ], :redirect_to => { :action => 'global' } + verify :method => :post, :only => [:grant_users, :grant_groups], :redirect_to => {:action => 'global'} def global end def projects + @qualifiers = java_facade.getQualifiersWithProperty('hasRolePolicy') + @qualifier = params[:qualifier] || 'TRK' + + # it's not possible to paginate directly in database because + # sort would be case-sensitive + + conditions_sql = 'projects.enabled=:enabled and projects.qualifier=:qualifier' + conditions_values = {:enabled => true, :qualifier => @qualifier} + + if params[:q].present? && params[:q].size>=ResourceIndex::MIN_SEARCH_SIZE + conditions_sql += ' and projects.id in (select ri.resource_id from resource_index ri where ri.qualifier=:qualifier and ri.kee like :filter)' + conditions_values[:filter]="#{params[:q].downcase}%" + end + + @pagination = Api::Pagination.new(params) + @pagination.results= Project.count(:conditions => [conditions_sql, conditions_values]) @projects=Project.find(:all, - :conditions => {:enabled=>true, :scope => Project::SCOPE_SET, :qualifier => [Project::QUALIFIER_VIEW, Project::QUALIFIER_SUBVIEW, Project::QUALIFIER_PROJECT]}, - :include => ['user_roles', 'group_roles']) - Api::Utils.insensitive_sort!(@projects){|project| project.name} + :include => %w(user_roles group_roles index), + :conditions => [conditions_sql, conditions_values], + :order => 'resource_index.kee', + :offset => @pagination.offset, + :limit => @pagination.limit) end def edit_users @@ -49,7 +68,7 @@ class RolesController < ApplicationController UserRole.grant_users(params[:users], params[:role], params[:resource]) redirect end - + def grant_groups GroupRole.grant_groups(params[:groups], params[:role], params[:resource]) redirect @@ -57,6 +76,6 @@ class RolesController < ApplicationController private def redirect - redirect_to(:action => params['redirect'] || 'global' ) + redirect_to(:action => params['redirect'] || 'global') end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb index 171edc8a637..c4946d1f4ea 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/search_controller.rb @@ -24,12 +24,10 @@ class SearchController < ApplicationController # Do not exceed 1000 because of the Oracle limition on IN statements MAX_RESULTS = 6 - MIN_SEARCH_SIZE=3 - def index @start_time = Time.now search = params[:s] - bad_request('Minimum search is 3 characters') if search.empty? || search.to_s.size<MIN_SEARCH_SIZE + bad_request("Minimum search is #{ResourceIndex::MIN_SEARCH_SIZE} characters") if search.empty? || search.to_s.size<ResourceIndex::MIN_SEARCH_SIZE key = search.downcase results = ResourceIndex.find(:all, diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/api/pagination.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/api/pagination.rb new file mode 100644 index 00000000000..cb74b5299ea --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/api/pagination.rb @@ -0,0 +1,62 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2008-2012 SonarSource +# mailto:contact AT sonarsource DOT com +# +# Sonar 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. +# +# Sonar 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 Sonar; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 +# +class Api::Pagination + + DEFAULT_PER_PAGE=25 + attr_accessor :per_page, :page, :results + + def initialize(options={}) + @per_page=options[:per_page]||DEFAULT_PER_PAGE + @page=options[:page].to_i + @page=1 if @page<1 + @results = options[:results].to_i + end + + def pages + @pages ||= + begin + p=(@results / @per_page) + p+=1 if @results % @per_page > 0 + p + end + end + + def offset + (page-1) * per_page + end + + # alias + def limit + per_page + end + + # inclusive index + #def to_index + # [results-1, (page * per_page)-1].min + #end + + def previous? + page>1 + end + + def next? + page<pages + end +end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/project.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/project.rb index 844b4fb0070..d482c8670aa 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/project.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/project.rb @@ -33,6 +33,7 @@ class Project < ActiveRecord::Base belongs_to :copy_resource, :class_name => 'Project', :foreign_key => 'copy_resource_id' belongs_to :person, :class_name => 'Project', :foreign_key => 'person_id' has_many :authors, :foreign_key => 'person_id', :dependent => :delete_all + has_one :index, :class_name => 'ResourceIndex', :foreign_key => 'resource_id', :conditions => 'position=0', :select => 'kee' def self.by_key(k) begin diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb index 7415dc249bf..934649be8b8 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/resource_index.rb @@ -24,6 +24,8 @@ class ResourceIndex < ActiveRecord::Base belongs_to :resource, :class_name => 'Project', :foreign_key => 'resource_id' belongs_to :root_project, :class_name => 'Project', :foreign_key => 'root_project_id' + MIN_SEARCH_SIZE=3 + def resource_id_for_authorization root_project_id end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb index 3331b8dadca..0d7cda524ac 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb @@ -1,99 +1,128 @@ -<h1 class="marginbottom10">Default roles for new projects</h1> +<h1 class="marginbottom10">Default roles for new <%= @qualifiers.map { |q| message("qualifiers.#{q}") }.join(', ') -%></h1> <table class="data width100" id="default-project-roles"> <thead> - <tr > + <tr> <th>Role</th> <th width="35%">Users</th> <th width="35%">Groups</th> </tr> </thead> <tbody> - <tr class="even" > - <td valign="top"><b>Administrators</b><br/><span class="small gray">Ability to perform administration functions for a project by accessing its settings.</span></td> - <td valign="top" style="word-break:break-all;width:35%;"> + <tr class="even"> + <td valign="top"><b>Administrators</b><br/><span class="small gray">Ability to perform administration functions for a project by accessing its settings.</span></td> + <td valign="top" style="word-break:break-all;width:35%;"> <div style="vertical-align:top"> <span><%= users('default-admin').map(&:login).join(', ') %></span> (<%= link_to "select", {:action => 'edit_users', :role => 'default-admin', :redirect => 'projects'}, :class => 'link-action' %>) - </div> - </td> - <td valign="top" style="word-break:break-all;width:35%;"> - <span><%= groups('default-admin').map{|g| group_name(g)}.join(', ') %></span> + </div> + </td> + <td valign="top" style="word-break:break-all;width:35%;"> + <span><%= groups('default-admin').map { |g| group_name(g) }.join(', ') %></span> (<%= link_to "select", {:action => 'edit_groups', :role => 'default-admin', :redirect => 'projects'}, :class => 'link-action' %>) - </td> - </tr> - <tr class="odd" > - <td valign="top"><b>Users</b><br/><span class="small gray">Ability to navigate through every service of a project, except viewing source code and settings.</span></td> - <td valign="top" style="word-break:break-all;width:35%;"> - <span><%= users('default-user').map(&:login).join(', ') %></span> - (<%= link_to "select", {:action => 'edit_users', :role => 'default-user', :redirect => 'projects'}, :class => 'link-action' %>) - </td> - <td valign="top" style="word-break:break-all;width:35%;"> - <span><%= groups('default-user').map{|g| group_name(g)}.join(', ') %></span> - (<%= link_to "select", {:action => 'edit_groups', :role => 'default-user', :redirect => 'projects'}, :class => 'link-action' %>) - </td> - </tr> - <tr class="even" > - <td valign="top"><b>Code viewers</b><br/><span class="small gray">Ability to view source code of a project.</span></td> - <td valign="top" style="word-break:break-all;width:35%;"> - <span><%= users('default-codeviewer').map(&:login).join(', ') %></span> - (<%= link_to "select", {:action => 'edit_users', :role => 'default-codeviewer', :redirect => 'projects'}, :class => 'link-action' %>) - </td> - <td valign="top" style="word-break:break-all;width:35%;"> - <span><%= groups('default-codeviewer').map{|g| group_name(g)}.join(', ') %></span> + </td> + </tr> + <tr class="odd"> + <td valign="top"><b>Users</b><br/><span class="small gray">Ability to navigate through every service of a project, except viewing source code and settings.</span></td> + <td valign="top" style="word-break:break-all;width:35%;"> + <span><%= users('default-user').map(&:login).join(', ') %></span> + (<%= link_to "select", {:action => 'edit_users', :role => 'default-user', :redirect => 'projects'}, :class => 'link-action' %>) + </td> + <td valign="top" style="word-break:break-all;width:35%;"> + <span><%= groups('default-user').map { |g| group_name(g) }.join(', ') %></span> + (<%= link_to "select", {:action => 'edit_groups', :role => 'default-user', :redirect => 'projects'}, :class => 'link-action' %>) + </td> + </tr> + <tr class="even"> + <td valign="top"><b>Code viewers</b><br/><span class="small gray">Ability to view source code of a project.</span></td> + <td valign="top" style="word-break:break-all;width:35%;"> + <span><%= users('default-codeviewer').map(&:login).join(', ') %></span> + (<%= link_to "select", {:action => 'edit_users', :role => 'default-codeviewer', :redirect => 'projects'}, :class => 'link-action' %>) + </td> + <td valign="top" style="word-break:break-all;width:35%;"> + <span><%= groups('default-codeviewer').map { |g| group_name(g) }.join(', ') %></span> (<%= link_to "select", {:action => 'edit_groups', :role => 'default-codeviewer', :redirect => 'projects'}, :class => 'link-action' %>) - </td> - </tr> + </td> + </tr> </tbody> </table> <br/><br/> -<h1 class="marginbottom10">Projects</h1> +<% if @qualifiers.size>1 %> -<table class="data width100" > - <thead> - <tr > - <th>Project</th> - <th>Role: Administrators</th> - <th>Role: Users</th> - <th>Role: Code viewers</th> - </tr> - </thead> - <tbody> +<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} -%> + </li> + <% end %> +</ul> +<% else %> + <h1 class="spacer-bottom"><%= message("qualifiers.#{@qualifiers[0]}") -%></h1> +<% end %> + +<div class="<%= @qualifiers.size>1 ? 'tabs-panel' : '' -%>"> + + <table class="data width100 "> + <thead> + <tr> + <th>Project</th> + <th>Role: Administrators</th> + <th>Role: Users</th> + <th>Role: Code viewers</th> + </tr> + </thead> + + <%= render :partial => 'utils/tfoot_pagination', :locals => {:pagination => @pagination, :colspan => 4} %> + + <tbody> <% if @projects.empty? %> - <tr class="even"><td colspan="4" align="left">No data</td></tr> - <% else - @projects.each do |project| %> - <tr class="<%= cycle('even', 'odd') -%>"> - <td valign="top"><b><%= h project.name %></b><br/> - <span class="small gray"><%= project.key -%></span> - </td> - <td valign="top"> - <% - users=project.user_roles.select{|ur| ur.role=='admin'}.map{|ur| ur.user.login} - groups=project.group_roles.select{|gr| gr.role=='admin'}.map{|gr| group_name(gr.group) } - %> - <%= users.join(', ') %> (<%= link_to "select users", {:action => 'edit_users', :role => 'admin', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>)<br/> - <%= groups.join(', ') %> (<%= link_to "select groups", {:action => 'edit_groups', :role => 'admin', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>) - </td> - <td valign="top"> - <% - users=project.user_roles.select{|ur| ur.role=='user'}.map{|ur| ur.user.login} - groups=project.group_roles.select{|gr| gr.role=='user'}.map{|gr| group_name(gr.group) } - %> - <%= users.join(', ') %> (<%= link_to "select users", {:action => 'edit_users', :role => 'user', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>)<br/> - <%= groups.join(', ') %> (<%= link_to "select groups", {:action => 'edit_groups', :role => 'user', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>) - </td> - <td valign="top"> - <% - users=project.user_roles.select{|ur| ur.role=='codeviewer'}.map{|ur| ur.user.login} - groups=project.group_roles.select{|gr| gr.role=='codeviewer'}.map{|gr| group_name(gr.group) } - %> - <%= users.join(', ') %> (<%= link_to "select users", {:action => 'edit_users', :role => 'codeviewer', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>)<br/> - <%= groups.join(', ') %> (<%= link_to "select groups", {:action => 'edit_groups', :role => 'codeviewer', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>) - </td> + <tr class="even"> + <td colspan="4" align="left"><%= message('no_results') %></td> </tr> <% end - end %> - </tbody> -</table>
\ No newline at end of file + + @projects.each do |project| + %> + <tr class="<%= cycle('even', 'odd') -%>"> + <td valign="top"><b><%= h project.name %></b><br/> + <span class="small gray"><%= project.key -%></span> + </td> + <td valign="top"> + <% + users=project.user_roles.select { |ur| ur.role=='admin' }.map { |ur| ur.user.login } + groups=project.group_roles.select { |gr| gr.role=='admin' }.map { |gr| group_name(gr.group) } + %> + <%= users.join(', ') %> + (<%= link_to "select users", {:action => 'edit_users', :role => 'admin', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>)<br/> + <%= groups.join(', ') %> + (<%= link_to "select groups", {:action => 'edit_groups', :role => 'admin', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>) + </td> + <td valign="top"> + <% + users=project.user_roles.select { |ur| ur.role=='user' }.map { |ur| ur.user.login } + groups=project.group_roles.select { |gr| gr.role=='user' }.map { |gr| group_name(gr.group) } + %> + <%= users.join(', ') %> + (<%= link_to "select users", {:action => 'edit_users', :role => 'user', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>)<br/> + <%= groups.join(', ') %> + (<%= link_to "select groups", {:action => 'edit_groups', :role => 'user', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>) + </td> + <td valign="top"> + <% + users=project.user_roles.select { |ur| ur.role=='codeviewer' }.map { |ur| ur.user.login } + groups=project.group_roles.select { |gr| gr.role=='codeviewer' }.map { |gr| group_name(gr.group) } + %> + <%= users.join(', ') %> + (<%= link_to "select users", {:action => 'edit_users', :role => 'codeviewer', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>)<br/> + <%= groups.join(', ') %> + (<%= link_to "select groups", {:action => 'edit_groups', :role => 'codeviewer', :resource => project.id, :redirect => 'projects'}, :class => 'link-action' %>) + </td> + </tr> + <% + end %> + </tbody> + </table> +</div>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/utils/_tfoot_pagination.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/utils/_tfoot_pagination.html.erb new file mode 100644 index 00000000000..3e27189cc4b --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/utils/_tfoot_pagination.html.erb @@ -0,0 +1,22 @@ +<tfoot> +<tr> + <td colspan="<%= colspan || 1 -%>"> + <% if pagination.results>0 %> + <%= pagination.results -%> <%= message('results').downcase -%> + <% end %> + <% + if pagination.pages>1 + %> + | + <%= link_to_if pagination.previous?, message('paging_previous'), params.merge(:page => pagination.page-1) %> + <%= link_to_if pagination.next?, message('paging_next'), params.merge(:page => pagination.page+1) %> + | + <% for index in 1..pagination.pages %> + <%= link_to_if index!=pagination.page, index.to_s, params.merge(:page => index) %> + <% end %> + <% + end + %> + </td> +</tr> +</tfoot>
\ No newline at end of file |