]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3618 support old versions of Views plugin
authorSimon Brandhof <simon.brandhof@gmail.com>
Fri, 6 Jul 2012 15:09:30 +0000 (17:09 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Fri, 6 Jul 2012 16:14:32 +0000 (18:14 +0200)
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/ApplyProjectRolesDecorator.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/DefaultResourcePermissioning.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/ApplyProjectRolesDecoratorTest.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/DefaultResourcePermissioningTest.java
sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceType.java
sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceTypes.java
sonar-plugin-api/src/test/java/org/sonar/api/resources/ResourceTypeTest.java
sonar-plugin-api/src/test/java/org/sonar/api/resources/ResourceTypesTest.java
sonar-server/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb

index 1e6a5519782290b52e46c4450f48127eabe78fd2..4e6fc53d52b74709fa2574c8fa28a91b059986f7 100644 (file)
  */
 package org.sonar.plugins.core.security;
 
+import com.google.common.collect.ImmutableSet;
+import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.Decorator;
 import org.sonar.api.batch.DecoratorContext;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.security.ResourcePermissioning;
 
+import java.util.Set;
+
 public class ApplyProjectRolesDecorator implements Decorator {
 
   private final ResourcePermissioning resourcePermissioning;
+  private final Set<String> QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.VIEW, Qualifiers.SUBVIEW);
 
   public ApplyProjectRolesDecorator(ResourcePermissioning resourcePermissioning) {
     this.resourcePermissioning = resourcePermissioning;
@@ -40,15 +46,13 @@ public class ApplyProjectRolesDecorator implements Decorator {
 
   public void decorate(Resource resource, DecoratorContext context) {
     if (shouldDecorateResource(resource)) {
+      LoggerFactory.getLogger(ApplyProjectRolesDecorator.class).info("Grant default permissions to {}", resource.getKey());
       resourcePermissioning.grantDefaultRoles(resource);
     }
   }
 
   private boolean shouldDecorateResource(Resource resource) {
-    return resource.getId() != null && isProject(resource) && !resourcePermissioning.hasRoles(resource);
+    return resource.getId() != null && QUALIFIERS.contains(resource.getQualifier()) && !resourcePermissioning.hasRoles(resource);
   }
 
-  private boolean isProject(Resource resource) {
-    return Qualifiers.PROJECT.equals(resource.getQualifier()) || Qualifiers.VIEW.equals(resource.getQualifier());
-  }
 }
index 15777e3075cfd7af0ebe5f22aeed534045527e2a..123869b49d0b081f3fb64e4f398355236c19028c 100644 (file)
  */
 package org.sonar.plugins.core.security;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.ibatis.session.SqlSession;
 import org.sonar.api.BatchExtension;
 import org.sonar.api.Properties;
 import org.sonar.api.Property;
 import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.security.DefaultGroups;
 import org.sonar.api.security.ResourcePermissioning;
@@ -143,7 +145,9 @@ public class DefaultResourcePermissioning implements ResourcePermissioning, Batc
   private void grantDefaultRoles(Resource resource, String role, SqlSession session) {
     UserMapper userMapper = session.getMapper(UserMapper.class);
     RoleMapper roleMapper = session.getMapper(RoleMapper.class);
-    String[] groupNames = settings.getStringArrayBySeparator("sonar.role." + role + "." + resource.getQualifier() + ".defaultGroups", ",");
+
+    String strategy = getStrategy(resource);
+    String[] groupNames = settings.getStringArrayBySeparator("sonar.role." + role + "." + strategy + ".defaultGroups", ",");
     for (String groupName : groupNames) {
       GroupRoleDto groupRole = new GroupRoleDto().setRole(role).setResourceId(new Long(resource.getId()));
       if (DefaultGroups.isAnyone(groupName)) {
@@ -156,7 +160,7 @@ public class DefaultResourcePermissioning implements ResourcePermissioning, Batc
       }
     }
 
-    String[] logins = settings.getStringArrayBySeparator("sonar.role." + role + "." + resource.getQualifier() + ".defaultUsers", ",");
+    String[] logins = settings.getStringArrayBySeparator("sonar.role." + role + "." + strategy + ".defaultUsers", ",");
     for (String login : logins) {
       UserDto user = userMapper.selectUserByLogin(login);
       if (user != null) {
@@ -164,4 +168,28 @@ public class DefaultResourcePermissioning implements ResourcePermissioning, Batc
       }
     }
   }
+
+  /**
+   * This is workaround to support old versions of the Views plugin.
+   * If the Views plugin does not define default permissions, then the standard permissions are re-used for new views.
+   */
+  @VisibleForTesting
+  String getStrategy(Resource resource) {
+    String qualifier = resource.getQualifier();
+    String result;
+    if (Qualifiers.PROJECT.equals(qualifier)) {
+      result = qualifier;
+
+    } else if (hasRoleSettings(UserRole.ADMIN, qualifier) || hasRoleSettings(UserRole.USER, qualifier) || hasRoleSettings(UserRole.CODEVIEWER, qualifier)) {
+      result = qualifier;
+    } else {
+      result = Qualifiers.PROJECT;
+    }
+    return result;
+  }
+
+  private boolean hasRoleSettings(String role, String qualifier) {
+    return settings.getString("sonar.role." + role + "." + qualifier + ".defaultGroups") != null
+      || settings.getString("sonar.role." + role + "." + qualifier + ".defaultUsers") != null;
+  }
 }
index 1541f7eb6b805a5810dfd0c3a0598e87475543c0..424e2d97e0b9ff7d925693632af9c8f88b67e841 100644 (file)
@@ -1,22 +1,22 @@
 /*
- * Sonar, open source software quality management 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
- */
+* Sonar, open source software quality management 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
+*/
 package org.sonar.plugins.core.security;
 
 import org.junit.Before;
@@ -24,6 +24,7 @@ import org.junit.Test;
 import org.sonar.api.resources.Project;
 import org.sonar.api.security.ResourcePermissioning;
 
+import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.*;
 
 public class ApplyProjectRolesDecoratorTest {
@@ -32,13 +33,18 @@ public class ApplyProjectRolesDecoratorTest {
   private ApplyProjectRolesDecorator decorator;
 
   @Before
-  public void before() {
+  public void init() {
     resourcePermissioning = mock(ResourcePermissioning.class);
     decorator = new ApplyProjectRolesDecorator(resourcePermissioning);
   }
 
   @Test
-  public void doNotApplySecurityWhenExistingPermissions() {
+  public void alwaysExecute() {
+    assertThat(decorator.shouldExecuteOnProject(new Project("project"))).isTrue();
+  }
+
+  @Test
+  public void doNotGrantDefaultRolesWhenExistingPermissions() {
     Project project = new Project("project");
     project.setId(10);
     when(resourcePermissioning.hasRoles(project)).thenReturn(true);
@@ -61,7 +67,7 @@ public class ApplyProjectRolesDecoratorTest {
   }
 
   @Test
-  public void applySecurityWhenNoPermissions() {
+  public void grantDefaultRolesWhenNoPermissions() {
     Project project = new Project("project");
     project.setId(10);
     when(resourcePermissioning.hasRoles(project)).thenReturn(false);
index e671f9bdc196df493eb385e34447afa241bd8c16..a67277fc3763e159537d378fb3748b533abac886 100644 (file)
 package org.sonar.plugins.core.security;
 
 import org.junit.Test;
+import org.sonar.api.Properties;
+import org.sonar.api.Property;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.security.DefaultGroups;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 
 import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class DefaultResourcePermissioningTest extends AbstractDaoTestCase {
 
@@ -129,4 +134,43 @@ public class DefaultResourcePermissioningTest extends AbstractDaoTestCase {
     // does not exist
     assertThat(permissioning.hasRoles(new Project("not_found"))).isFalse();
   }
+
+  @Test
+  public void use_default_project_roles_when_old_version_of_views_plugin() {
+    DefaultResourcePermissioning permissioning = new DefaultResourcePermissioning(new Settings(), getMyBatis());
+    Resource view = mock(Resource.class);
+    when(view.getQualifier()).thenReturn(Qualifiers.VIEW);
+
+    assertThat(permissioning.getStrategy(view)).isEqualTo(Qualifiers.PROJECT);
+  }
+
+  @Test
+  public void use_existing_view_roles() {
+    Settings settings = new Settings();
+    settings.setProperty("sonar.role.admin.VW.defaultUsers", "sonar-administrators");
+
+    DefaultResourcePermissioning permissioning = new DefaultResourcePermissioning(settings, getMyBatis());
+    Resource view = mock(Resource.class);
+    when(view.getQualifier()).thenReturn(Qualifiers.VIEW);
+
+    assertThat(permissioning.getStrategy(view)).isEqualTo(Qualifiers.VIEW);
+  }
+
+  @Test
+  public void use_existing_default_view_roles() {
+    Settings settings = new Settings(new PropertyDefinitions(RecentViewPlugin.class));
+
+    DefaultResourcePermissioning permissioning = new DefaultResourcePermissioning(settings, getMyBatis());
+    Resource view = mock(Resource.class);
+    when(view.getQualifier()).thenReturn(Qualifiers.VIEW);
+
+    assertThat(permissioning.getStrategy(view)).isEqualTo(Qualifiers.VIEW);
+  }
+
+  @Properties({
+    @Property(key = "sonar.role.user.VW.defaultUsers", defaultValue = "sonar-users", name = "")
+  })
+  static class RecentViewPlugin {
+
+  }
 }
\ No newline at end of file
index e5444ca5452b10cc668edf971df2877ad5082432..c876d9f75b59b464a36fe47a19512e041fda7677 100644 (file)
@@ -174,6 +174,11 @@ public final class ResourceType {
     return hasSourceCode;
   }
 
+  public boolean hasProperty(String key) {
+    Preconditions.checkNotNull(key);
+    return properties.containsKey(key);
+  }
+
   /**
    * Returns the value of the property for this resource type.
    *
index 306042d40d002b90b41866a0df61c36639256f07..5eb933c725b6ef0c44e232d40d7ffb0a8ce73d45 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.api.resources;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
@@ -44,7 +45,7 @@ public final class ResourceTypes implements BatchComponent, ServerComponent {
 
   public static final Predicate<ResourceType> AVAILABLE_FOR_FILTERS = new Predicate<ResourceType>() {
     public boolean apply(@Nullable ResourceType input) {
-      return input != null && Boolean.TRUE.equals(input.getBooleanProperty("availableForFilters"));
+      return input != null && input.getBooleanProperty("availableForFilters");
     }
   };
 
@@ -83,6 +84,30 @@ public final class ResourceTypes implements BatchComponent, ServerComponent {
     return Collections2.filter(typeByQualifier.values(), predicate);
   }
 
+  public Collection<ResourceType> getAllWithPropertyKey(final String propertyKey) {
+    return Collections2.filter(typeByQualifier.values(), new Predicate<ResourceType>() {
+      public boolean apply(@Nullable ResourceType input) {
+        return input != null && input.hasProperty(propertyKey);
+      }
+    });
+  }
+
+  public Collection<ResourceType> getAllWithPropertyValue(final String propertyKey, final String propertyValue) {
+    return Collections2.filter(typeByQualifier.values(), new Predicate<ResourceType>() {
+      public boolean apply(@Nullable ResourceType input) {
+        return input != null && Objects.equal(propertyValue, input.getStringProperty(propertyKey));
+      }
+    });
+  }
+
+  public Collection<ResourceType> getAllWithPropertyValue(final String propertyKey, final boolean propertyValue) {
+    return Collections2.filter(typeByQualifier.values(), new Predicate<ResourceType>() {
+      public boolean apply(@Nullable ResourceType input) {
+        return input != null && input.getBooleanProperty(propertyKey) == propertyValue;
+      }
+    });
+  }
+
   public List<String> getChildrenQualifiers(String qualifier) {
     ResourceTypeTree tree = getTree(qualifier);
     if (tree != null) {
index 998017b75f8a8049287e319dd8762cfe105654fe..5d3559e9100eabd6dc14b4e13f23a74416abfb65 100644 (file)
@@ -98,4 +98,13 @@ public class ResourceTypeTest {
     ResourceType def = ResourceType.builder("qualifier").build();
     assertThat(def.getBooleanProperty("availableForFilters")).isFalse();
   }
+
+  @Test
+  public void hasProperty() {
+    ResourceType def = ResourceType.builder("qualifier").build();
+    assertThat(def.hasProperty("foo")).isFalse();
+
+    def = ResourceType.builder("qualifier").setProperty("foo", "bar").build();
+    assertThat(def.hasProperty("foo")).isTrue();
+  }
 }
index 6c34aa65a38c240e7856c62bd3b61d2a8224ad9f..eeb96d7402fefd1b81064b4dc623bb22d8e00944 100644 (file)
@@ -25,98 +25,99 @@ import org.junit.Test;
 
 import java.util.Collection;
 
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNull.nullValue;
-import static org.junit.Assert.assertThat;
-import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
-import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
+import static org.fest.assertions.Assertions.assertThat;
 
 public class ResourceTypesTest {
 
   private ResourceTypeTree viewsTree = ResourceTypeTree.builder()
-      .addType(ResourceType.builder(Qualifiers.VIEW).setProperty("availableForFilters", "true").build())
-      .addType(ResourceType.builder(Qualifiers.SUBVIEW).build())
-      .addRelations(Qualifiers.VIEW, Qualifiers.SUBVIEW)
-      .addRelations(Qualifiers.SUBVIEW, Qualifiers.PROJECT)
-      .build();
+    .addType(ResourceType.builder(Qualifiers.VIEW).setProperty("availableForFilters", "true").build())
+    .addType(ResourceType.builder(Qualifiers.SUBVIEW).build())
+    .addRelations(Qualifiers.VIEW, Qualifiers.SUBVIEW)
+    .addRelations(Qualifiers.SUBVIEW, Qualifiers.PROJECT)
+    .build();
 
   private ResourceTypeTree defaultTree = ResourceTypeTree.builder()
-      .addType(ResourceType.builder(Qualifiers.PROJECT).setProperty("availableForFilters", "true").build())
-      .addType(ResourceType.builder(Qualifiers.DIRECTORY).build())
-      .addType(ResourceType.builder(Qualifiers.FILE).build())
-      .addRelations(Qualifiers.PROJECT, Qualifiers.DIRECTORY)
-      .addRelations(Qualifiers.DIRECTORY, Qualifiers.FILE)
-      .build();
+    .addType(ResourceType.builder(Qualifiers.PROJECT).setProperty("availableForFilters", "true").build())
+    .addType(ResourceType.builder(Qualifiers.DIRECTORY).build())
+    .addType(ResourceType.builder(Qualifiers.FILE).build())
+    .addRelations(Qualifiers.PROJECT, Qualifiers.DIRECTORY)
+    .addRelations(Qualifiers.DIRECTORY, Qualifiers.FILE)
+    .build();
 
-  private ResourceTypes types = new ResourceTypes(new ResourceTypeTree[] {viewsTree, defaultTree});
+  private ResourceTypes types = new ResourceTypes(new ResourceTypeTree[]{viewsTree, defaultTree});
 
   @Test
   public void get() {
-    assertThat(types.get(Qualifiers.PROJECT).getQualifier(), is(Qualifiers.PROJECT));
+    assertThat(types.get(Qualifiers.PROJECT).getQualifier()).isEqualTo(Qualifiers.PROJECT);
 
     // does not return null
-    assertThat(types.get("xxx").getQualifier(), is("xxx"));
+    assertThat(types.get("xxx").getQualifier()).isEqualTo("xxx");
   }
 
   @Test
   public void getAll() {
-    assertThat(types.getAll().size(), is(5));
-    assertThat(qualifiers(types.getAll()), hasItems(
-        Qualifiers.PROJECT, Qualifiers.DIRECTORY, Qualifiers.FILE, Qualifiers.VIEW, Qualifiers.SUBVIEW));
+    assertThat(qualifiers(types.getAll())).containsOnly(Qualifiers.PROJECT, Qualifiers.DIRECTORY, Qualifiers.FILE, Qualifiers.VIEW, Qualifiers.SUBVIEW);
   }
 
   @Test
   public void getAll_predicate() {
     Collection<ResourceType> forFilters = types.getAll(ResourceTypes.AVAILABLE_FOR_FILTERS);
-    assertThat(forFilters.size(), is(2));
-    assertThat(qualifiers(forFilters), hasItems(Qualifiers.PROJECT, Qualifiers.VIEW));
+    assertThat(qualifiers(forFilters)).containsOnly(Qualifiers.PROJECT, Qualifiers.VIEW).doesNotHaveDuplicates();
+  }
+
+  @Test
+  public void getAllWithPropertyKey() {
+    assertThat(qualifiers(types.getAllWithPropertyKey("availableForFilters"))).containsOnly(Qualifiers.VIEW, Qualifiers.PROJECT);
+  }
+
+  @Test
+  public void getAllWithPropertyValue() {
+    assertThat(qualifiers(types.getAllWithPropertyValue("availableForFilters", "true"))).containsOnly(Qualifiers.VIEW, Qualifiers.PROJECT);
+    assertThat(qualifiers(types.getAllWithPropertyValue("availableForFilters", true))).containsOnly(Qualifiers.VIEW, Qualifiers.PROJECT);
+    assertThat(qualifiers(types.getAllWithPropertyValue("availableForFilters", false))).containsOnly(Qualifiers.SUBVIEW, Qualifiers.DIRECTORY, Qualifiers.FILE);
   }
 
   @Test
   public void getChildrenQualifiers() {
-    assertThat(types.getChildrenQualifiers(Qualifiers.PROJECT).size(), is(1));
-    assertThat(types.getChildrenQualifiers(Qualifiers.PROJECT), hasItem(Qualifiers.DIRECTORY));
-    assertThat(types.getChildrenQualifiers(Qualifiers.SUBVIEW), hasItem(Qualifiers.PROJECT));
-    assertThat(types.getChildrenQualifiers("xxx").isEmpty(), is(true));
-    assertThat(types.getChildrenQualifiers(Qualifiers.FILE).isEmpty(), is(true));
+    assertThat(types.getChildrenQualifiers(Qualifiers.PROJECT)).containsExactly(Qualifiers.DIRECTORY);
+    assertThat(types.getChildrenQualifiers(Qualifiers.SUBVIEW)).containsExactly(Qualifiers.PROJECT);
+    assertThat(types.getChildrenQualifiers("xxx")).isEmpty();
+    assertThat(types.getChildrenQualifiers(Qualifiers.FILE)).isEmpty();
   }
 
   @Test
   public void getChildren() {
-    assertThat(qualifiers(types.getChildren(Qualifiers.PROJECT)), hasItem(Qualifiers.DIRECTORY));
-    assertThat(qualifiers(types.getChildren(Qualifiers.SUBVIEW)), hasItem(Qualifiers.PROJECT));
+    assertThat(qualifiers(types.getChildren(Qualifiers.PROJECT))).contains(Qualifiers.DIRECTORY);
+    assertThat(qualifiers(types.getChildren(Qualifiers.SUBVIEW))).contains(Qualifiers.PROJECT);
   }
 
   @Test
   public void getLeavesQualifiers() {
-    assertThat(types.getLeavesQualifiers(Qualifiers.PROJECT).size(), is(1));
-    assertThat(types.getLeavesQualifiers(Qualifiers.PROJECT), hasItem(Qualifiers.FILE));
+    assertThat(types.getLeavesQualifiers(Qualifiers.PROJECT)).containsExactly(Qualifiers.FILE);
 
-    assertThat(types.getLeavesQualifiers(Qualifiers.DIRECTORY).size(), is(1));
-    assertThat(types.getLeavesQualifiers(Qualifiers.DIRECTORY), hasItem(Qualifiers.FILE));
+    assertThat(types.getLeavesQualifiers(Qualifiers.DIRECTORY)).containsExactly(Qualifiers.FILE);
 
-    assertThat(types.getLeavesQualifiers(Qualifiers.VIEW).size(), is(1));
-    assertThat(types.getLeavesQualifiers(Qualifiers.VIEW), hasItem(Qualifiers.PROJECT));
+    assertThat(types.getLeavesQualifiers(Qualifiers.VIEW)).containsExactly(Qualifiers.PROJECT);
 
-    assertThat(types.getLeavesQualifiers("xxx").size(), is(0));
+    assertThat(types.getLeavesQualifiers("xxx")).isEmpty();
   }
 
   @Test
   public void getTree() {
-    assertThat(qualifiers(types.getTree(Qualifiers.VIEW).getTypes()), hasItems(Qualifiers.VIEW, Qualifiers.SUBVIEW));
-    assertThat(types.getTree("xxx"), nullValue());
+    assertThat(qualifiers(types.getTree(Qualifiers.VIEW).getTypes())).containsOnly(Qualifiers.VIEW, Qualifiers.SUBVIEW).doesNotHaveDuplicates();
+    assertThat(types.getTree("xxx")).isNull();
   }
 
   @Test(expected = IllegalStateException.class)
   public void failOnDuplicatedQualifier() {
     ResourceTypeTree tree1 = ResourceTypeTree.builder()
-        .addType(ResourceType.builder("foo").build())
-        .build();
+      .addType(ResourceType.builder("foo").build())
+      .build();
     ResourceTypeTree tree2 = ResourceTypeTree.builder()
-        .addType(ResourceType.builder("foo").build())
-        .build();
+      .addType(ResourceType.builder("foo").build())
+      .build();
 
-    new ResourceTypes(new ResourceTypeTree[] {tree1, tree2});
+    new ResourceTypes(new ResourceTypeTree[]{tree1, tree2});
   }
 
   static Collection<String> qualifiers(Collection<ResourceType> types) {
index 70d25884d470a4e79757628ef263d74debed06ab..2875542762fbd03a0e50e48812cd14dc63c72615 100644 (file)
@@ -51,7 +51,7 @@
   </tr>
   <tr class="even">
     <td valign="top">
-      <b>Role: Code viewers</b><br/><span class="small gray">Ability to view source code of a project.</span></td>
+      <b>Role: 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 id="u-codeviewer"><%= default_project_users('codeviewer', @qualifier).map{|u| h u.name}.join(', ') -%></span>
       (<%= link_to "select", {:action => 'edit_default_project_users', :role => 'codeviewer', :redirect => 'projects', :qualifier => @qualifier}, :class => 'link-action', :id => 'selectu-codeviewer' -%>)