]> source.dussan.org Git - sonarqube.git/commitdiff
Fix merge of release 2.8
authorsimonbrandhof <simon.brandhof@gmail.com>
Thu, 19 May 2011 14:14:21 +0000 (16:14 +0200)
committersimonbrandhof <simon.brandhof@gmail.com>
Thu, 19 May 2011 14:14:21 +0000 (16:14 +0200)
24 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest.java
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest/shouldCloseReviewWithoutCorrespondingViolation-result.xml
plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java
plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviews.java [new file with mode: 0644]
plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest.java [new file with mode: 0644]
plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews-result.xml [new file with mode: 0644]
plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews.xml [new file with mode: 0644]
samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/SamplePlugin.java
samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleQualityProfile.java [deleted file]
samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleRuleRepository.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java
sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/reviews_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/models/project.rb
sonar-server/src/main/webapp/WEB-INF/app/models/review.rb
sonar-server/src/main/webapp/WEB-INF/app/views/resource/_violation.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_review.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_show.html.erb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/reviews/index.html.erb
sonar-server/src/main/webapp/stylesheets/style.css
sonar-testing-harness/src/main/java/org/sonar/test/persistence/DatabaseTestCase.java
sonar-testing-harness/src/main/resources/org/sonar/test/persistence/sonar-test.ddl

index b46108b884030b578e133e7179f0676bf218ec63..0b32e50b288845001d7ac87b2215b763ed860393 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.plugins.core.sensors;
 
+import javax.persistence.Query;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.Decorator;
@@ -29,10 +31,9 @@ import org.sonar.api.database.DatabaseSession;
 import org.sonar.api.database.model.Snapshot;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
 import org.sonar.batch.index.ResourcePersister;
 
-import javax.persistence.Query;
-
 /**
  * Decorator that currently only closes a review when its corresponding violation has been fixed.
  */
@@ -50,7 +51,7 @@ public class CloseReviewsDecorator implements Decorator {
   }
 
   public boolean shouldExecuteOnProject(Project project) {
-    return true;
+    return project.isLatestAnalysis();
   }
 
   public void decorate(Resource resource, DecoratorContext context) {
@@ -58,16 +59,32 @@ public class CloseReviewsDecorator implements Decorator {
     if (currentSnapshot != null) {
       int resourceId = currentSnapshot.getResourceId();
       int snapshotId = currentSnapshot.getId();
-      Query query = databaseSession.createNativeQuery(generateSqlRequest(resourceId, snapshotId));
+      Query query = databaseSession.createNativeQuery(generateUpdateOnResourceSqlRequest(resourceId, snapshotId));
       int rowUpdated = query.executeUpdate();
       LOG.debug("- {} reviews set to 'closed' on resource #{}", rowUpdated, resourceId);
+
+      if (ResourceUtils.isRootProject(resource)) {
+        query = databaseSession.createNativeQuery(generateUpdateOnProjectSqlRequest(resourceId, currentSnapshot.getId()));
+        query.setParameter(1, Boolean.TRUE);
+        rowUpdated = query.executeUpdate();
+        LOG.debug("- {} reviews set to 'closed' on project #{}", rowUpdated, resourceId);
+      }
+
       databaseSession.commit();
     }
   }
 
-  String generateSqlRequest(int resourceId, int snapshotId) {
-    return "UPDATE reviews SET status='CLOSED' WHERE resource_id = " + resourceId + " AND rule_failure_permanent_id NOT IN "
-        + "(SELECT permanent_id FROM rule_failures WHERE snapshot_id = " + snapshotId + " AND permanent_id IS NOT NULL)";
+  protected String generateUpdateOnResourceSqlRequest(int resourceId, int snapshotId) {
+    return "UPDATE reviews SET status='CLOSED', updated_at=CURRENT_TIMESTAMP WHERE resource_id = " + resourceId
+        + " AND rule_failure_permanent_id NOT IN " + "(SELECT permanent_id FROM rule_failures WHERE snapshot_id = " + snapshotId
+        + " AND permanent_id IS NOT NULL)";
+  }
+
+  protected String generateUpdateOnProjectSqlRequest(int projectId, int projectSnapshotId) {
+    return "UPDATE reviews SET status='CLOSED', updated_at=CURRENT_TIMESTAMP WHERE status='OPEN' AND project_id=" + projectId
+        + " AND resource_id IN ( SELECT prev.project_id FROM snapshots prev  WHERE prev.root_project_id=" + projectId
+        + " AND prev.islast=? AND NOT EXISTS ( SELECT cur.id FROM snapshots cur WHERE cur.root_snapshot_id=" + projectSnapshotId
+        + " AND cur.created_at > prev.created_at AND cur.root_project_id=" + projectId + " AND cur.project_id=prev.project_id ) )";
   }
 
 }
index 493352c114766c13669edd9b075b19879896900f..2ea8a7c1de4ae1b8193150f459bea117911315de 100644 (file)
@@ -21,25 +21,47 @@ package org.sonar.plugins.core.sensors;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.sql.Statement;
 
+import junit.framework.ComparisonFailure;
+
 import org.junit.Test;
+import org.sonar.api.resources.Project;
 import org.sonar.test.persistence.DatabaseTestCase;
 
 public class CloseReviewsDecoratorTest extends DatabaseTestCase {
 
+  @Test
+  public void testShouldExecuteOnProject() throws Exception {
+    Project project = mock(Project.class);
+    when(project.isLatestAnalysis()).thenReturn(true);
+    CloseReviewsDecorator reviewsDecorator = new CloseReviewsDecorator(null, null);
+    assertTrue(reviewsDecorator.shouldExecuteOnProject(project));
+  }
+
   @Test
   public void shouldCloseReviewWithoutCorrespondingViolation() throws Exception {
     setupData("fixture");
 
     CloseReviewsDecorator reviewsDecorator = new CloseReviewsDecorator(null, null);
-    String sqlRequest = reviewsDecorator.generateSqlRequest(666, 222);
-    
+    String sqlRequest = reviewsDecorator.generateUpdateOnResourceSqlRequest(666, 222);
+
     Statement stmt = getConnection().createStatement();
     int count = stmt.executeUpdate(sqlRequest);
 
     assertThat(count, is(1));
-    assertTables("shouldCloseReviewWithoutCorrespondingViolation", "reviews");
+    assertTables("shouldCloseReviewWithoutCorrespondingViolation", new String[] { "reviews" }, new String[] { "updated_at" });
+
+    try {
+      assertTables("shouldCloseReviewWithoutCorrespondingViolation", new String[] { "reviews" });
+      fail("'updated_at' columns are identical whereas they should be different.");
+    } catch (ComparisonFailure e) {
+      // "updated_at" column must be different, so the comparison should raise this exception
+    }
   }
 }
index d46429ddf3d3fef97942e6da51fd46b095a488e6..db7bf76f0e01a23967d9f22cc4611c0232778379 100644 (file)
@@ -5,18 +5,18 @@
                        status="OPEN"
                        rule_failure_permanent_id="1"
                        resource_id="555"
-                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/>
+                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]" project_id="[null]"/>
   <reviews
                        id="2"
                        status="CLOSED"
                        rule_failure_permanent_id="2"
                        resource_id="666"
-                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/>
+                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]" project_id="[null]"/>
   <reviews
                        id="3"
                        status="OPEN"
                        rule_failure_permanent_id="3"
                        resource_id="666"
-                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/>
+                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]" project_id="[null]"/>
 
 </dataset>
\ No newline at end of file
index f38a6c39b20b83e092b582f1c25d2ba13a590fb2..a02d8d0a3087bd4320ec247eaee56b9823f12a3a 100644 (file)
  */
 package org.sonar.plugins.dbcleaner;
 
+import java.util.Arrays;
+import java.util.List;
+
 import org.sonar.api.Properties;
 import org.sonar.api.Property;
 import org.sonar.api.SonarPlugin;
 import org.sonar.plugins.dbcleaner.api.DbCleanerConstants;
 import org.sonar.plugins.dbcleaner.period.DefaultPeriodCleaner;
 import org.sonar.plugins.dbcleaner.period.PeriodPurge;
-import org.sonar.plugins.dbcleaner.purges.*;
+import org.sonar.plugins.dbcleaner.purges.PurgeDeletedResources;
+import org.sonar.plugins.dbcleaner.purges.PurgeDependencies;
+import org.sonar.plugins.dbcleaner.purges.PurgeDeprecatedLast;
+import org.sonar.plugins.dbcleaner.purges.PurgeDisabledResources;
+import org.sonar.plugins.dbcleaner.purges.PurgeEntities;
+import org.sonar.plugins.dbcleaner.purges.PurgeEventOrphans;
+import org.sonar.plugins.dbcleaner.purges.PurgeOrphanResources;
+import org.sonar.plugins.dbcleaner.purges.PurgeOrphanReviews;
+import org.sonar.plugins.dbcleaner.purges.PurgePropertyOrphans;
+import org.sonar.plugins.dbcleaner.purges.PurgeResourceRoles;
+import org.sonar.plugins.dbcleaner.purges.PurgeRuleMeasures;
+import org.sonar.plugins.dbcleaner.purges.PurgeUnprocessed;
 import org.sonar.plugins.dbcleaner.runner.PurgeRunner;
 
-import java.util.Arrays;
-import java.util.List;
-
 @Properties({
-    @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_WEEK,
-        defaultValue = DbCleanerConstants.ONE_MONTH, name = "Number of months before starting to keep only one snapshot by week",
+    @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_WEEK, defaultValue = DbCleanerConstants.ONE_MONTH,
+        name = "Number of months before starting to keep only one snapshot by week",
         description = "After this number of months, if there are several snapshots during the same week, "
             + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true),
-    @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH,
-        defaultValue = DbCleanerConstants.ONE_YEAR, name = "Number of months before starting to keep only one snapshot by month",
+    @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH, defaultValue = DbCleanerConstants.ONE_YEAR,
+        name = "Number of months before starting to keep only one snapshot by month",
         description = "After this number of months, if there are several snapshots during the same month, "
             + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true),
     @Property(key = DbCleanerConstants.MONTHS_BEFORE_DELETING_ALL_SNAPSHOTS, defaultValue = DbCleanerConstants.FIVE_YEARS,
         name = "Number of months before starting to delete all remaining snapshots",
-        description = "After this number of months, all snapshots are fully deleted.", global = true, project = true)})
+        description = "After this number of months, all snapshots are fully deleted.", global = true, project = true) })
 public final class DbCleanerPlugin extends SonarPlugin {
 
   public List getExtensions() {
@@ -51,9 +62,9 @@ public final class DbCleanerPlugin extends SonarPlugin {
         DefaultPeriodCleaner.class,
 
         // purges
-        PurgeOrphanResources.class, PurgeEntities.class, PurgeRuleMeasures.class, PurgeUnprocessed.class,
-        PurgeDeletedResources.class, PurgeDeprecatedLast.class, PurgeDisabledResources.class,
-        PurgeResourceRoles.class, PurgeEventOrphans.class, PurgePropertyOrphans.class, PeriodPurge.class, PurgeDependencies.class,
+        PurgeOrphanResources.class, PurgeEntities.class, PurgeRuleMeasures.class, PurgeUnprocessed.class, PurgeDeletedResources.class,
+        PurgeDeprecatedLast.class, PurgeDisabledResources.class, PurgeResourceRoles.class, PurgeEventOrphans.class,
+        PurgePropertyOrphans.class, PeriodPurge.class, PurgeDependencies.class, PurgeOrphanReviews.class,
 
         // post-job
         PurgeRunner.class);
diff --git a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviews.java b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviews.java
new file mode 100644 (file)
index 0000000..8e52416
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 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.dbcleaner.purges;
+
+import javax.persistence.Query;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.plugins.dbcleaner.api.Purge;
+import org.sonar.plugins.dbcleaner.api.PurgeContext;
+
+/**
+ * Purge Review that are attached to projects that have been deleted.
+ * 
+ * @since 2.8
+ */
+public final class PurgeOrphanReviews extends Purge {
+
+  private static final Logger LOG = LoggerFactory.getLogger(PurgeOrphanReviews.class);
+
+  public PurgeOrphanReviews(DatabaseSession session) {
+    super(session);
+  }
+
+  public void purge(PurgeContext context) {
+    DatabaseSession session = getSession();
+    
+    // delete reviews
+    Query query = session.createNativeQuery(getDeleteReviewsSqlRequest());
+    int rowDeleted = query.executeUpdate();
+    LOG.debug("- {} reviews deleted.", rowDeleted);
+    
+    // and delete review comments
+    query = session.createNativeQuery(getDeleteReviewCommentsSqlRequest());
+    rowDeleted = query.executeUpdate();
+    LOG.debug("- {} review comments deleted.", rowDeleted);
+
+    session.commit();
+  }
+
+  protected String getDeleteReviewsSqlRequest() {
+    return "DELETE FROM reviews WHERE project_id not in (SELECT id FROM projects WHERE scope = 'PRJ' and qualifier = 'TRK')";
+  }
+
+  protected String getDeleteReviewCommentsSqlRequest() {
+    return "DELETE FROM review_comments WHERE review_id not in (SELECT id FROM reviews)";
+  }
+}
diff --git a/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest.java b/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest.java
new file mode 100644 (file)
index 0000000..de1632a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 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.dbcleaner.purges;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.sql.Statement;
+
+import org.junit.Test;
+import org.sonar.test.persistence.DatabaseTestCase;
+
+public class PurgeOrphanReviewsTest extends DatabaseTestCase {
+
+  @Test
+  public void shouldCloseReviewWithoutCorrespondingViolation() throws Exception {
+    setupData("purgeOrphanReviews");
+
+    Statement stmt = getConnection().createStatement();
+    int count = stmt.executeUpdate(new PurgeOrphanReviews(null).getDeleteReviewsSqlRequest());
+    assertThat(count, is(1));
+
+    count = stmt.executeUpdate(new PurgeOrphanReviews(null).getDeleteReviewCommentsSqlRequest());
+    assertThat(count, is(1));
+
+    assertTables("purgeOrphanReviews", "reviews");
+  }
+
+}
diff --git a/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews-result.xml b/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews-result.xml
new file mode 100644 (file)
index 0000000..7680bdd
--- /dev/null
@@ -0,0 +1,43 @@
+<dataset>
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="other-project" name="other-project"
+            root_id="[null]" profile_id="[null]"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]"/>
+
+  <!-- project1 has been removed from UI -->
+  <!--<projects long_name="[null]" id="2" scope="PRJ" qualifier="TRK" kee="project1" name="project1"-->
+            <!--root_id="[null]"-->
+            <!--description="[null]"-->
+            <!--enabled="true" language="java" copy_resource_id="[null]"/>-->
+
+  <reviews
+                       id="1"
+                       status="OPEN"
+                       rule_failure_permanent_id="1"
+                       resource_id="555"
+                       project_id="1"
+                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/>
+                       
+  <!--  Following must have been deleted
+  <reviews
+                       id="2"
+                       status="CLOSED"
+                       rule_failure_permanent_id="2"
+                       resource_id="666"
+                       project_id="2"
+                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/>
+  -->
+                       
+  <review_comments
+                       id="1"
+                       review_id="1"
+                       created_at="[null]" updated_at="[null]" user_id="[null]" review_text=""/>
+                       
+  <!--  Following must have been deleted
+  <review_comments
+                       id="2"
+                       review_id="2"
+                       created_at="[null]" updated_at="[null]" user_id="[null]" review_text=""/>
+  -->
+
+</dataset>
\ No newline at end of file
diff --git a/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews.xml b/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews.xml
new file mode 100644 (file)
index 0000000..01da166
--- /dev/null
@@ -0,0 +1,39 @@
+<dataset>
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="other-project" name="other-project"
+            root_id="[null]" profile_id="[null]"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]"/>
+
+  <!-- project1 has been removed from UI -->
+  <!--<projects long_name="[null]" id="2" scope="PRJ" qualifier="TRK" kee="project1" name="project1"-->
+            <!--root_id="[null]"-->
+            <!--description="[null]"-->
+            <!--enabled="true" language="java" copy_resource_id="[null]"/>-->
+
+  <reviews
+                       id="1"
+                       status="OPEN"
+                       rule_failure_permanent_id="1"
+                       resource_id="555"
+                       project_id="1"
+                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/>
+  <reviews
+                       id="2"
+                       status="CLOSED"
+                       rule_failure_permanent_id="2"
+                       resource_id="666"
+                       project_id="2"
+                       created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/>
+                       
+  <review_comments
+                       id="1"
+                       review_id="1"
+                       created_at="[null]" updated_at="[null]" user_id="[null]" review_text=""/>
+                       
+  <review_comments
+                       id="2"
+                       review_id="2"
+                       created_at="[null]" updated_at="[null]" user_id="[null]" review_text=""/>
+
+
+</dataset>
\ No newline at end of file
index f530e6b38b43bbc3d08d890931d42043b5454857..7622e032bd02d6226582d2fc7e5a6bcc41d59fa9 100644 (file)
@@ -2,8 +2,6 @@ package com.mycompany.sonar.standard;
 
 import com.mycompany.sonar.standard.batch.RandomDecorator;
 import com.mycompany.sonar.standard.batch.SampleSensor;
-import com.mycompany.sonar.standard.rules.SampleQualityProfile;
-import com.mycompany.sonar.standard.rules.SampleRuleRepository;
 import com.mycompany.sonar.standard.ui.SampleFooter;
 import com.mycompany.sonar.standard.ui.SampleRubyWidget;
 import org.sonar.api.SonarPlugin;
@@ -20,7 +18,7 @@ public final class SamplePlugin extends SonarPlugin {
   public List getExtensions() {
     return Arrays.asList(
         // Definitions
-        SampleRuleRepository.class, SampleMetrics.class, SampleQualityProfile.class,
+        SampleMetrics.class,
 
         // Batch
         SampleSensor.class, RandomDecorator.class,
diff --git a/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleQualityProfile.java b/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleQualityProfile.java
deleted file mode 100644 (file)
index ac6e981..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.mycompany.sonar.standard.rules;
-
-import org.sonar.api.profiles.ProfileDefinition;
-import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.resources.Java;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RulePriority;
-import org.sonar.api.utils.ValidationMessages;
-
-public class SampleQualityProfile extends ProfileDefinition {
-
-  private SampleRuleRepository ruleRepository;
-
-  // The component ruleRepository is injected at runtime
-  public SampleQualityProfile(SampleRuleRepository ruleRepository) {
-    this.ruleRepository = ruleRepository;
-  }
-
-  @Override
-  public RulesProfile createProfile(ValidationMessages validation) {
-    RulesProfile profile = RulesProfile.create("Sample profile", Java.KEY);
-    profile.activateRule(ruleRepository.getRule1(), RulePriority.MAJOR);
-    profile.activateRule(ruleRepository.getRule2(), null);
-    profile.activateRule(Rule.create("checkstyle", "com.puppycrawl.tools.checkstyle.checks.coding.CovariantEqualsCheck", null), null);
-    return profile;
-  }
-}
diff --git a/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleRuleRepository.java b/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleRuleRepository.java
deleted file mode 100644 (file)
index 4bd909a..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.mycompany.sonar.standard.rules;
-
-import org.sonar.api.resources.Java;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RulePriority;
-import org.sonar.api.rules.RuleRepository;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * This class declares rules. It is not the engine used to execute rules during project analysis.
- */
-public class SampleRuleRepository extends RuleRepository {
-
-  public static final String REPOSITORY_KEY = "sample";
-
-  public SampleRuleRepository() {
-    super(REPOSITORY_KEY, Java.KEY);
-    setName("Sample");
-  }
-
-  @Override
-  public List<Rule> createRules() {
-    // This method is called when server is started. It's used to register rules into database.
-    // Definition of rules can be loaded from any sources, like XML files.
-    return Arrays.asList(
-        getRule1(),
-        getRule2());
-  }
-
-  public Rule getRule1() {
-    return Rule.create(REPOSITORY_KEY, "fake1", "Fake one").setSeverity(RulePriority.CRITICAL);
-  }
-
-  public Rule getRule2() {
-    return Rule.create(REPOSITORY_KEY, "fake2", "Fake two").setDescription("Description of fake two");
-  }
-}
index 7a6ea61b4541880cb3207975564f6033456f0a08..2216a49c1b531d7b0077c95eed44d2c93451f8cd 100644 (file)
@@ -104,7 +104,7 @@ public class ProjectBatch {
 
     @Override
     protected void configure() {
-      ProjectDefinition projectDefinition = getComponent(ProjectTree.class).getProjectDefinition(project.getKey());
+      ProjectDefinition projectDefinition = getComponent(ProjectTree.class).getProjectDefinition(project);
       addComponent(projectDefinition);
       for (Object component : projectDefinition.getContainerExtensions()) {
         addComponent(component);
index 54b97fb2b6f3dcfca9c7ace4c2ff56e1972c6065..1ea52bc57381185397345f912a21751ac652e83a 100644 (file)
@@ -21,10 +21,10 @@ package org.sonar.batch;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import org.apache.commons.lang.ObjectUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.maven.project.MavenProject;
 import org.slf4j.LoggerFactory;
-import org.sonar.api.CoreProperties;
 import org.sonar.api.database.DatabaseSession;
 import org.sonar.api.resources.Project;
 import org.sonar.batch.bootstrapper.ProjectDefinition;
@@ -35,9 +35,10 @@ import java.util.*;
 
 public class ProjectTree {
 
-  private List<Project> projects;
   private ProjectBuilder projectBuilder;
+  private List<Project> projects;
   private List<ProjectDefinition> definitions;
+  private Map<ProjectDefinition, Project> projectsMap;
 
   public ProjectTree(Reactor sonarReactor, DatabaseSession databaseSession) {
     this.projectBuilder = new ProjectBuilder(databaseSession);
@@ -75,24 +76,24 @@ public class ProjectTree {
 
   public void start() throws IOException {
     projects = Lists.newArrayList();
-    Map<ProjectDefinition, Project> map = Maps.newHashMap();
+    projectsMap = Maps.newHashMap();
 
     for (ProjectDefinition def : definitions) {
       Project project = projectBuilder.create(def);
-      map.put(def, project);
+      projectsMap.put(def, project);
       projects.add(project);
     }
 
-    for (Map.Entry<ProjectDefinition, Project> entry : map.entrySet()) {
+    for (Map.Entry<ProjectDefinition, Project> entry : projectsMap.entrySet()) {
       ProjectDefinition def = entry.getKey();
       Project project = entry.getValue();
       for (ProjectDefinition module : def.getModules()) {
-        map.get(module).setParent(project);
+        projectsMap.get(module).setParent(project);
       }
     }
 
     // Configure
-    for (Map.Entry<ProjectDefinition, Project> entry : map.entrySet()) {
+    for (Map.Entry<ProjectDefinition, Project> entry : projectsMap.entrySet()) {
       projectBuilder.configure(entry.getValue(), entry.getKey());
     }
 
@@ -181,26 +182,12 @@ public class ProjectTree {
     throw new IllegalStateException("Can not find the root project from the list of Maven modules");
   }
 
-  private String getProjectKey(ProjectDefinition def) {
-    String key = def.getProperties().getProperty(CoreProperties.PROJECT_KEY_PROPERTY);
-    String branch = def.getProperties().getProperty(CoreProperties.PROJECT_BRANCH_PROPERTY);
-    if (StringUtils.isNotBlank(branch)) {
-      return key + ":" + branch;
-    } else {
-      return key;
-    }
-  }
-
-  public ProjectDefinition getProjectDefinition(String key) {
-    for (ProjectDefinition def : definitions) {
-      if (StringUtils.equals(key, getProjectKey(def))) {
-        return def;
+  public ProjectDefinition getProjectDefinition(Project project) {
+    for (Map.Entry<ProjectDefinition, Project> entry : projectsMap.entrySet()) {
+      if (ObjectUtils.equals(entry.getValue(), project)) {
+        return entry.getKey();
       }
     }
-    return null;
-  }
-
-  public List<Object> getProjectExtensions(String key) {
-    return getProjectDefinition(key).getContainerExtensions();
+    throw new IllegalStateException("Can not find ProjectDefinition for " + project);
   }
 }
index 62b3f3b87596ef48c26b4b2783506cbbeaeb7f58..7a69b96676c76a863bb05a1317e23b48d06b2862 100644 (file)
@@ -53,7 +53,7 @@ class ReviewsController < ApplicationController
   def show
     @review = Review.find(params[:id], :include => ['project'])
     if has_role?(:user, @review.project)
-      render :partial => 'reviews/view'
+      render :partial => 'reviews/show'
     else
       render :text => "access denied"
     end
@@ -76,7 +76,7 @@ class ReviewsController < ApplicationController
     @review.assignee = User.find params[:assignee_id]
     @review.save
 
-    render :partial => 'reviews/view'
+    render :partial => 'reviews/show'
   end
 
   # GET
@@ -106,7 +106,7 @@ class ReviewsController < ApplicationController
       @review.comments.create!(:user => current_user, :text => params[:text])
     end
 
-    render :partial => "reviews/view"
+    render :partial => "reviews/show"
   end
 
   # GET
@@ -134,7 +134,7 @@ class ReviewsController < ApplicationController
       @review.comments.create(:review_text => params[:comment], :user_id => current_user.id)
     end
 
-    render :partial => "reviews/view"
+    render :partial => "reviews/show"
   end
 
   # POST
@@ -149,7 +149,7 @@ class ReviewsController < ApplicationController
       comment=@review.comments.find(params[:comment_id].to_i)
       comment.delete if comment
     end
-    render :partial => "reviews/view"
+    render :partial => "reviews/show"
   end
 
 
index e7f95603788d61a6aa3f695ea1b967b17c242c76..854c5f746db2244de26513bd868203f4eee13676 100644 (file)
@@ -32,6 +32,8 @@ class SettingsController < ApplicationController
       project=Project.by_key(params[:resource_id])
       return access_denied unless is_admin?(project)
       resource_id=project.id
+    else
+      return access_denied unless is_admin?
     end
 
     plugins = java_facade.getPluginsMetadata()
index 8489caea88b6c13e2834b35639f7a7e615d1469c..9658c9dfcb31d38926fed2a961eccf4d0b96b1e8 100644 (file)
@@ -43,6 +43,13 @@ class Project < ActiveRecord::Base
     root||self
   end
   
+  def root_project
+    @root_project ||= 
+      begin
+        parent_module(self)
+      end
+  end
+  
   def last_snapshot
     @last_snapshot ||=
       begin
@@ -153,4 +160,9 @@ class Project < ActiveRecord::Base
     end
     chart_measures
   end
+  
+  def parent_module(current_module)
+    current_module.root ? parent_module(current_module.root) : current_module
+  end
+  
 end
index 46e97ba9c955cf96d8c8d9c3181761b68c11a5a4..1f91e4abe79ccf91bd09fa9f7fd691523b29b7de 100644 (file)
@@ -205,7 +205,7 @@ class Review < ActiveRecord::Base
   
   def assign_project
     if self.project.nil? && self.resource
-      self.project=self.resource.project
+      self.project=self.resource.root_project
     end
   end
   
index 25989a7b1e000e1e7597d0a2f74f23f8dc307acc..3282a0fedee0ef8339f0ce1b1c1a07ae1faf9089 100644 (file)
@@ -2,7 +2,7 @@
 <div class="violation">
   <div class="vtitle">
     <% if violation.review %>
-      <div class="review_permalink"><span class="review_permalink"><%= link_to "Review #"+violation.review.id.to_s, :controller => "reviews", :action => "view", :id => violation.review.id -%></span></div>
+      <div style="float: right"><span class="review_permalink"><%= link_to violation.review.id.to_s, :controller => "reviews", :action => "view", :id => violation.review.id -%></span></div>
     <% end %>
     
     <%= image_tag("priority/" + violation.failure_level.to_s + '.png') -%>
index 5e8fcd7299e83ea095bec5094b5c836f4b4552a7..79b0a2c864d2fd0805bcc9f8356692f8d8e4b20e 100644 (file)
@@ -1,6 +1,7 @@
 <div id="rev_<%= review.id -%>">
   <div class="reportTitle">
-    <h2>Review #<%= h(review.id.to_s) -%> - <%= h(review.title) -%></h2>
+    <div style="float: right"><span class="violation_date"><%= review.id.to_s -%></span></div>  
+    <h2><%= h(review.title) -%></h2>
     
     <% if current_user && review.status != "CLOSED" %>
     <span class="actions" id="rActions">
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_show.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_show.html.erb
new file mode 100644 (file)
index 0000000..b9faf2e
--- /dev/null
@@ -0,0 +1,11 @@
+<div id="backReviewDiv" class="marginbottom10">
+       <a href="#" onclick="backReviews()">&laquo; Back to reviews</a>
+</div>
+<script>
+  if ($('reviews-search')==null) {
+    // This happens when this page results from a call made from the review permalink page
+    $('backReviewDiv').hide();
+  }
+</script>
+
+<%= render :partial => 'reviews/review', :locals => {:review => @review} -%>
\ No newline at end of file
index 9946a4564f88da4df7452fa2aad68bb9fd4ba582..7bd4895f05a3654cdffb59da44e490ef6b755187 100644 (file)
@@ -107,11 +107,11 @@ function reviewIdFieldModified(field) {
         <tr class="<%= cycle('even', 'odd') -%>">
           <td><img src="<%= ApplicationController.root_context -%>/images/status/<%= review.status -%>.png" title="<%= review.status.capitalize -%>"/></td>
           <td>
-            <%= link_to h(review.id), :controller => "reviews", :action => "view", :id => review.id -%>
+            <%= link_to_remote( h(review.id), :update => 'review', :url => {:action => 'show', :id => review.id}, :loading => 'onReviewLoading()', :complete => "onReviewLoaded()") -%>
           </td>
           <td><img src="<%= ApplicationController.root_context -%>/images/priority/<%= review.severity -%>.png" title="<%= review.severity.capitalize -%>"/></td>
           <td>
-            <%= link_to h(review.title), :controller => "reviews", :action => "view", :id => review.id -%>
+            <%= link_to_remote(h(review.title), :update => 'review', :url => {:action => 'show', :id => review.id}, :loading => 'onReviewLoading()', :complete => "onReviewLoaded()") -%>
           </td>
           <td><%= review.project.name -%>
             <br/><span class="note"><%= review.resource.long_name -%></span></td>
@@ -138,6 +138,8 @@ function reviewIdFieldModified(field) {
   
 </div>
 
+<div id="review-loading" style="display: none"><%= image_tag 'loading.gif' -%></div>
+<div id="review" style="display: none"></div>
 <script>
   $('review_id').focus();
 </script>
\ No newline at end of file
index fd11c07a403c2f573d6c1b662077805438e9a69d..a6a1af75d40bcae78b576a8af30bd14a39f00d5e 100644 (file)
@@ -686,7 +686,7 @@ ul.operations li img {
 div.vtitle{
   background-color:#E4ECF3;
   margin:0;
-  padding:0 0 0 10px;
+  padding:0 10px;
   line-height: 2.2em;
   text-shadow: 0 1px 0 #FFF;
   color:#777
@@ -706,24 +706,9 @@ span.violation_date {
   color:#777;
   font-size:90%;
 }
-div.review_permalink {
-  float: right;
-  background-color: #F4F4F4;
-  border-color: #CDCDCD;
-  border-style: none none none solid;
-  border-width: 1px;
-  color: #333333;
-  font-size: 12px;
-  font-weight: bold;
-  margin: 0;
-  padding: 0 10px;
-  text-shadow: 1px 1px 0 #FFFFFF;
-}
 span.review_permalink a {
   color:#777;
   font-size:90%;
-  padding: 0 0 0 20px;
-  background: url('../images/zoom.png') no-repeat left;
 }
 span.rulename a:hover {
   text-decoration: underline;
index ba9e47b6e76133e89a8789404740ed1b1476f191..7c8d4543d6c0256488d854e0b3056abac3ae3931 100644 (file)
@@ -196,6 +196,20 @@ public abstract class DatabaseTestCase {
     }
   }
 
+  protected final void assertTables(String testName, String[] tables, String[] ignoreCols) {
+    try {
+      IDataSet dataSet = getCurrentDataSet();
+      IDataSet expectedDataSet = getExpectedData(testName);
+      for (String table : tables) {
+        Assertion.assertEqualsIgnoreCols(expectedDataSet.getTable(table), dataSet.getTable(table), ignoreCols);
+      }
+    } catch (DataSetException e) {
+      throw translateException("Error while checking results", e);
+    } catch (DatabaseUnitException e) {
+      fail(e.getMessage());
+    }
+  }
+
   protected final void assertEmptyTables(String... emptyTables) {
     for (String table : emptyTables) {
       try {
index c5125179cbfdf82682afc1e255bb6f760e2183ea..54f49a0953becba0732573585abe691ea8a5a565 100644 (file)
@@ -492,6 +492,7 @@ CREATE TABLE REVIEWS (
        STATUS VARCHAR(10),
        SEVERITY VARCHAR(10),
        RULE_FAILURE_PERMANENT_ID INTEGER,
+       PROJECT_ID INTEGER,
        RESOURCE_ID INTEGER,
        RESOURCE_LINE INTEGER,
     primary key (id)