]> source.dussan.org Git - sonarqube.git/commitdiff
Update issue ui in resource viewer update to be consistent with issues filter
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 7 Feb 2014 11:25:32 +0000 (12:25 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 7 Feb 2014 11:25:32 +0000 (12:25 +0100)
12 files changed:
plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
sonar-core/src/main/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtManager.java
sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtManagerTest.java
sonar-server/src/main/java/org/sonar/server/rule/ws/RuleShowWsHandler.java
sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtService.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/views/issue/_issue.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/issue/_rule.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/issue/_technicaldebt.html.erb [deleted file]
sonar-server/src/main/webapp/javascripts/issue.js
sonar-server/src/main/webapp/stylesheets/style.css
sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtServiceTest.java

index 4a467aa054d93058a05c26189528bd7d6b0ab2a5..cbe50409e85c1ccf98bf8e746d00aa215cf087e1 100644 (file)
@@ -582,6 +582,7 @@ issue.manual.no_rules.non_admin=At least one manual rule must exist before manua
 issue.reported_by=Reported by
 issue.authorLogin=Author:
 issue.component_deleted=Removed
+issue.debt=Debt:
 issue.technical_debt=Technical Debt:
 issue.technical_debt_short=Debt
 issue.technical_debt.x_days={0} days
index 86913704cf3ca5e674d3d1daa4d3afded0cc31fa..3bb7b5bae7d94176c6d220e33340339ef82b6d71 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.core.technicaldebt;
 
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.technicaldebt.server.Characteristic;
 import org.sonar.api.technicaldebt.server.TechnicalDebtManager;
 import org.sonar.api.technicaldebt.server.internal.DefaultCharacteristic;
@@ -31,6 +32,7 @@ import org.sonar.core.technicaldebt.db.CharacteristicDto;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+
 import java.util.List;
 
 import static com.google.common.collect.Lists.newArrayList;
@@ -41,9 +43,11 @@ import static com.google.common.collect.Lists.newArrayList;
 public class DefaultTechnicalDebtManager implements TechnicalDebtManager {
 
   private final CharacteristicDao dao;
+  private final RuleFinder ruleFinder;
 
-  public DefaultTechnicalDebtManager(CharacteristicDao dao) {
+  public DefaultTechnicalDebtManager(CharacteristicDao dao, RuleFinder ruleFinder) {
     this.dao = dao;
+    this.ruleFinder = ruleFinder;
   }
 
   public List<Characteristic> findRootCharacteristics() {
@@ -64,6 +68,16 @@ public class DefaultTechnicalDebtManager implements TechnicalDebtManager {
     return null;
   }
 
+  @CheckForNull
+  public Characteristic findRequirementByRuleId(int ruleId) {
+    CharacteristicDto requirementDto = dao.selectByRuleId(ruleId);
+    if (requirementDto != null) {
+      Rule rule = ruleFinder.findById(ruleId);
+      return toCharacteristic(requirementDto, RuleKey.of(rule.getRepositoryKey(), rule.getKey()));
+    }
+    return null;
+  }
+
   @CheckForNull
   public Characteristic findRequirementByRule(Rule rule) {
     CharacteristicDto requirementDto = dao.selectByRuleId(rule.getId());
index a20cb142ee960d790d5434f5692596e207584bd9..7ac3d43530930179904400f312a19d136eb90d2c 100644 (file)
@@ -27,6 +27,7 @@ import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.technicaldebt.server.Characteristic;
 import org.sonar.api.utils.WorkUnit;
 import org.sonar.core.technicaldebt.db.CharacteristicDao;
@@ -44,11 +45,14 @@ public class DefaultTechnicalDebtManagerTest {
   @Mock
   CharacteristicDao dao;
 
+  @Mock
+  RuleFinder ruleFinder;
+
   DefaultTechnicalDebtManager finder;
 
   @Before
   public void setUp() throws Exception {
-    finder = new DefaultTechnicalDebtManager(dao);
+    finder = new DefaultTechnicalDebtManager(dao, ruleFinder);
   }
 
   @Test
@@ -126,4 +130,37 @@ public class DefaultTechnicalDebtManagerTest {
     Characteristic result = finder.findCharacteristicById(2);
     assertThat(result).isNull();
   }
+
+  @Test
+  public void find_requirement_by_rule_id() throws Exception {
+    Rule rule = Rule.create("repo", "key");
+    rule.setId(1);
+
+    when(ruleFinder.findById(1)).thenReturn(rule);
+
+    when(dao.selectByRuleId(rule.getId())).thenReturn(
+      new CharacteristicDto().setId(3).setRuleId(10).setParentId(2).setRootId(1).setFunction("linear").setFactorValue(30.0).setFactorUnit("mn"));
+
+    Characteristic result = finder.findRequirementByRuleId(1);
+
+    assertThat(result.id()).isEqualTo(3);
+    assertThat(result.parentId()).isEqualTo(2);
+    assertThat(result.rootId()).isEqualTo(1);
+    assertThat(result.ruleKey()).isEqualTo(RuleKey.of("repo", "key"));
+    assertThat(result.function()).isEqualTo("linear");
+    assertThat(result.factor()).isEqualTo(WorkUnit.create(30.0, WorkUnit.MINUTES));
+    assertThat(result.offset()).isEqualTo(WorkUnit.create());
+  }
+
+  @Test
+  public void not_find_requirement_by_rule_id_on_unknown_requirement() throws Exception {
+    Rule rule = Rule.create("repo", "key");
+    rule.setId(1);
+
+    when(ruleFinder.findById(1)).thenReturn(rule);
+
+    when(dao.selectByRuleId(rule.getId())).thenReturn(null);
+
+    assertThat(finder.findRequirementByRuleId(1)).isNull();
+  }
 }
index 6efedab37c1a1ac6a1b6f51d2bf111fe2f055169..ccb97dc0ce8c2b1bde9cad9c85a1e880bfa16145 100644 (file)
@@ -74,6 +74,7 @@ public class RuleShowWsHandler implements RequestHandler {
 
   @CheckForNull
   private Rule findRule(RuleKey ruleKey) {
+    // TODO remove this when manual rules when be indexed in E/S
     if (ruleKey.repository().equals(Rule.MANUAL_REPOSITORY_KEY)) {
       org.sonar.api.rules.Rule rule = ruleFinder.findByKey(ruleKey);
       if (rule != null) {
index 464214c03e32eb173c26a7b20b9da81d9870500d..fe89ec33b3be04e4aeb19b0daad07fc3943cbef4 100644 (file)
@@ -22,12 +22,12 @@ package org.sonar.server.technicaldebt;
 
 import org.sonar.api.ServerComponent;
 import org.sonar.api.issue.internal.WorkDayDuration;
-import org.sonar.api.rules.Rule;
 import org.sonar.api.technicaldebt.server.Characteristic;
 import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
 import org.sonar.server.user.UserSession;
 
 import javax.annotation.CheckForNull;
+
 import java.util.List;
 
 public class DebtService implements ServerComponent {
@@ -52,8 +52,8 @@ public class DebtService implements ServerComponent {
     return finder.findRootCharacteristics();
   }
 
-  public Characteristic findRequirement(Rule rule) {
-    return finder.findRequirementByRule(rule);
+  public Characteristic findRequirementByRuleId(int ruleId) {
+    return finder.findRequirementByRuleId(ruleId);
   }
 
   @CheckForNull
index 58df6c0e39fd731cae017f793dd1897b714143f1..a9b04bcd49b29691b26d1367b65e89b3932c8171 100644 (file)
@@ -187,6 +187,12 @@ class IssueController < ApplicationController
     require_parameters :id
     rule_key = params[:id].split(':')
     @rule = Rule.first(:conditions => ['plugin_name=? and plugin_rule_key=?', rule_key[0], rule_key[1]])
+    @requirement = Internal.technical_debt.findRequirementByRuleId(@rule.id)
+    # Requirement can be null if it's disabled or if there's no requirement on this rule
+    if @requirement
+      @characteristic = Internal.technical_debt.findCharacteristic(@requirement.parentId)
+      @root_characteristic = Internal.technical_debt.findCharacteristic(@requirement.rootId)
+    end
     render :partial => 'issue/rule'
   end
 
@@ -200,21 +206,6 @@ class IssueController < ApplicationController
     render :partial => 'issue/changelog'
   end
 
-  # Display the technical debt detail in the issue panel
-  def technicaldebt
-    verify_ajax_request
-    require_parameters :id
-    @issue_results = Api.issues.find(params[:id])
-    @issue = @issue_results.first()
-
-    rule = @issue_results.rule(@issue)
-    @requirement = Internal.technical_debt.findRequirement(rule)
-    # Requirement can be null if it's disabled
-    @characteristic = Internal.technical_debt.findCharacteristic(@requirement.parentId) if @requirement
-    @root_characteristic = Internal.technical_debt.findCharacteristic(@requirement.rootId) if @requirement
-    render :partial => 'issue/technicaldebt'
-  end
-
 
   private
 
index 0d790ae6585f0a7ecd074bb40efa2198d7d3c394..18aca66d630e626f733a7fa1a9f04bfd1e18a2bf 100644 (file)
-<div class="code-issue" data-issue-key="<%= issue.key -%>" data-issue-component="<%= issue.componentKey() -%>" data-issue-rule="<%= u issue.ruleKey().toString() -%>">
-  <div class="code-issue-name">
-    <div style="float: right">
+<div id="issue-<%= u issue.key -%>" class="code-issue code-issue-collapsed" data-issue-key="<%= issue.key -%>" data-issue-component="<%= issue.componentKey() -%>" data-issue-rule="<%= u issue.ruleKey().toString() -%>">
+  <div class="code-issue-name code-issue-toggle">
+    <div class="code-issue-name-rule">
+      <i class="icon-severity-<%= issue.severity.downcase -%>"></i>
+      <span class="rulename">
+        <%= h !issue.message.blank? ? Api::Utils.split_newlines(issue.message).join('<br/>') : @issue_results.rule(issue).getName() -%>
+      </span>
+    </div>
+    <div class="code-issue-permalink">
       <a href="#" onclick="return openIssuePopup(this)" class="issue-permalink"><img src="<%= ApplicationController.root_context -%>/images/new-window-16.gif"></a>
     </div>
+  </div>
 
-    <i class="icon-severity-<%= issue.severity.downcase -%>"></i>
-    &nbsp;
-    <a href="#" onclick="return toggleIssueRule(this)" class="rulename issue-rule-link"><%= h @issue_results.rule(issue).getName() -%></a>
-    &nbsp;
-    <% if issue.resolution %>
-      <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
-      &nbsp;
-      <span><%= message("issue.resolution.#{issue.resolution}") -%></span>
-      &nbsp;
-    <% else %>
-      <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
-      &nbsp;
-      <span><%= message("issue.status.#{issue.status}") -%></span>
-      &nbsp;
-    <% end %>
-    <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
-    &nbsp;
-    <%= message('issue.updated') -%>&nbsp;<a href="#" onclick="return toggleIssueChangelog(this)" class="gray issue-changelog-link"
-       id="toggle-issue-changelog"><%= distance_of_time_in_words_to_now(Api::Utils.java_to_ruby_datetime(issue.updateDate())) -%></a>
-    &nbsp;
-    <% if issue.reporter %>
-      <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
-      &nbsp;
-      <span><%= message('issue.reported_by') -%> <%= @issue_results.user(issue.reporter).name -%></span>
-      &nbsp;
+  <ul class="code-issue-actions code-issue-list">
+    <% if current_user %>
+      <li><a href='#' onclick="return issueForm('comment', this)" class="link-action spacer-right" autofocus><%= message('issue.comment.formlink') -%></a></li>
     <% end %>
-    <% if issue.authorLogin %>
-      <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
-      &nbsp;
-     <span><%= message('issue.authorLogin') -%>&nbsp;<%= issue.authorLogin -%></span>
-      &nbsp;
-    <% end %>
-    <% if issue.technicalDebt %>
-      <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
-      &nbsp;
-      <%= message('issue.technical_debt') -%>&nbsp;<a href="#" onclick="return toggleTechnicalDebt(this)" class="gray issue-technicaldebt-link"
-           id="toggle-issue-technicaldebt"><%= Internal.technical_debt.format(issue.technicalDebt) -%></a>
-        &nbsp;
+    <li>
+      <i class="icon-status-<%= issue.status.downcase -%>"></i><%= message("issue.status.#{issue.status}") -%> <%= '(' + message("issue.resolution.#{issue.resolution}")  + ')' if issue.resolution -%>
+    </li>
+    <% if current_user %>
+      <% transitions = Internal.issues.listTransitions(issue).to_a
+         if !transitions.empty? && transitions.first
+           first_transition = transitions.first %>
+          <li>
+             <!-- Display only the first transition -->
+            <a href="#" onclick="return doIssueTransition(this, '<%= first_transition.key -%>')" class="link-action issue-transition spacer-left">
+              <%= message("issue.transition.#{first_transition.key}") -%></a>
+            <!-- Display remaining transitions -->
+            <% if transitions.size > 1 %>
+              <div class="dropdown">
+                <a href="#" class="link-action link-more" onclick="showDropdownMenuOnElement($j(this).next('.dropdown-menu')); return false;"/></a>
+                <ul style="display: none" class="dropdown-menu">
+                  <% transitions[1..-1].each do |transition| %>
+                    <li>
+                      <a href="#" onclick="return doIssueTransition(this, '<%= transition.key -%>')" class="link-action spacer-right"><%= message("issue.transition.#{transition.key}") -%></a>
+                    </li>
+                  <% end %>
+                </ul>
+              </div>
+            <% end %>
+          </li>
+      <% end %>
     <% end %>
-  </div>
-
-  <div class="issue-rule rule-desc" style="display: none"></div>
-  <div class="issue-changelog" id="issue-changelog" style="display: none"></div>
-  <div class="issue-technicaldebt" id="issue-technicaldebt" style="display: none"></div>
-
-  <% unless issue.message.blank? %>
-    <div class="code-issue-msg">
-      <%= Api::Utils.split_newlines(h(issue.message)).join('<br/>') -%>
-    </div>
-  <% end %>
-
-  <%
-     issue.comments.each do |comment|
-       comment_html_id = "comment-#{comment.key}-#{rand(100)}"
-  %>
-    <div class="code-issue-comment" id="<%= comment_html_id -%>" data-comment-key="<%= comment.key -%>">
-      <h4>
-        <%= image_tag('reviews/comment.png') -%> &nbsp;<b><%= @issue_results.user(comment.userLogin()).name() -%></b>
-        (<%= distance_of_time_in_words_to_now(Api::Utils.java_to_ruby_datetime(comment.createdAt)) -%>)
-        <% if current_user && current_user.login==comment.userLogin %>
-          &nbsp;
-          <%= image_tag 'sep12.png' -%>
-          &nbsp;
-          <a class="link-action" href="#" onclick="return formEditIssueComment(this)"><%= message('edit') -%></a>
-          <a class="link-action spacer-right" href="#" onclick="return deleteIssueComment(this, '<%= escape_javascript(message('issue.comment.delete_confirm_message')) -%>')"><%= message('delete') -%></a>
-        <% end %>
-      </h4>
-      <%= Internal.text.markdownToHtml(comment.markdownText) -%>
-    </div>
-  <% end %>
-
-  <% if current_user %>
-
-    <div class="code-issue-actions">
-      <a href='#' onclick="return issueForm('comment', this)" class="link-action spacer-right" autofocus><%= message('issue.comment.formlink') -%></a>
-      <% unless issue.resolution %>
-        <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
-        &nbsp;
-        <span class="spacer-right">
+    <% unless issue.resolution %>
+      <li>
         <% if issue.assignee %>
-          <a href='#' onclick="return issueForm('assign', this)" class="link-action"><%= message('assigned_to') -%></a> <%= h @issue_results.user(issue.assignee).name -%>
-        <% else %>
+          <% if current_user %>
+            <a href='#' onclick="return issueForm('assign', this)" class="link-action"><%= message('assigned_to') -%></a> <%= h @issue_results.user(issue.assignee).name -%>
+          <% else %>
+            <%= message('assigned_to') -%> <strong><%= h @issue_results.user(issue.assignee).name -%></strong>
+          <% end %>
+        <% elsif current_user %>
           <a href='#' onclick="return issueForm('assign', this)" class="link-action"><%= message('issue.assign.formlink') -%></a>
             <% if issue.assignee != current_user.login %>
             [<a href="#" onclick="return assignIssueToMe(this)" class="link-action"><%= message('issue.assign.to_me') -%></a>]
             <% end %>
         <% end %>
-        </span>
-        <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
-        &nbsp;
-        <span class="spacer-right">
-          <% if issue.actionPlanKey %>
-            <a href="#" onclick="return issueForm('plan', this)" class="link-action"><%= message('issue.planned_for') -%></a> <%= h(@issue_results.actionPlan(issue).name()) -%>
-          <% else %>
-            <a href="#" onclick="return issueForm('plan', this)" class="link-action"><%= message('issue.do_plan') -%></a>
-          <% end %>
-        </span>
-      <% end %>
-
-      <%
-         transitions = Internal.issues.listTransitions(issue).to_a
-
-         # Display only the first transition
-         if !transitions.empty? && transitions.first
-           first_transition = transitions.first
-      %>
-        <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
-        &nbsp;
-        <a href="#" onclick="return doIssueTransition(this, '<%= first_transition.key -%>')" class="link-action spacer-right"><%= message("issue.transition.#{first_transition.key}") -%></a>
+      </li>
+      <li>
+      <% if issue.actionPlanKey %>
+        <% if current_user %>
+          <a href="#" onclick="return issueForm('plan', this)" class="link-action"><%= message('issue.planned_for') -%></a> <%= h(@issue_results.actionPlan(issue).name()) -%>
+        <% else %>
+          <%= message('issue.planned_for') -%> <strong><%= h(@issue_results.actionPlan(issue).name()) -%></strong>
+        <% end %>
+      <% elsif current_user %>
+        <a href="#" onclick="return issueForm('plan', this)" class="link-action"><%= message('issue.do_plan') -%></a>
       <% end %>
+      </li>
+    <% end %>
+    <% if current_user %>
+      <% plugin_actions = Internal.issues.listActions(issue)
+        if !issue.resolution || !plugin_actions.empty? %>
+        <li>
+          <div class="dropdown">
+            <a href="#" class="link-action link-more" onclick="showDropdownMenuOnElement($j(this).next('.dropdown-menu')); return false;"><%= message('more_actions') -%></a>
+            <ul style="display: none" class="dropdown-menu">
+              <% if Java::OrgSonarServerUser::UserSession.get().hasProjectPermission('issueadmin', issue.projectKey) %>
+                <% unless issue.resolution %>
+                  <li>
+                    <a href="#" onclick="return issueForm('severity', this)" class="link-action spacer-right"><%= message("issue.set_severity") -%></a>
+                  </li>
+                <% end %>
+              <% end %>
 
-      <%
-         plugin_actions = Internal.issues.listActions(issue)
-         shouldDisplayDropDown = transitions.size > 1 || !issue.resolution || !plugin_actions.empty?
-         if shouldDisplayDropDown
-      %>
-        <div class="dropdown">
-          <a href="#" class="link-action link-more" onclick="showDropdownMenuOnElement($j(this).next('.dropdown-menu')); return false;"><%= message('more_actions') -%></a>
-          <ul style="display: none" class="dropdown-menu">
-            <% if Java::OrgSonarServerUser::UserSession.get().hasProjectPermission('issueadmin', issue.projectKey) %>
-              <% unless issue.resolution %>
+              <% # Display actions defined by plugins
+                 plugin_actions.each do |action| %>
                 <li>
-                  <a href="#" onclick="return issueForm('severity', this)" class="link-action spacer-right"><%= message("issue.set_severity") -%></a>
+                  <a href="#" onclick="return doPluginIssueAction(this, '<%= action.key -%>')" class="link-action spacer-right"><%= message("issue.action.#{action.key}.formlink") -%></a>
                 </li>
               <% end %>
-            <% end %>
+            </ul>
+          </div>
+        </li>
+      <% end %>
+    <% end %>
+    <% if issue.technicalDebt %>
+      <li><%= message('issue.debt') -%>&nbsp;<%= Internal.technical_debt.format(issue.technicalDebt) -%></li>
+    <% end %>
+    <% if issue.authorLogin %>
+      <li><%= message('issue.authorLogin') -%>&nbsp;<%= issue.authorLogin -%></li>
+    <% end %>
+    <% if issue.reporter %>
+      <li><%= message('issue.reported_by') -%>&nbsp;<%= @issue_results.user(issue.reporter).name -%></li>
+    <% end %>
+  </ul>
+  <div class="code-issue-form hidden"></div>
 
-            <% # Display remaining transitions
-               if transitions.size > 1
-                 transitions[1..-1].each do |transition| %>
-              <li>
-                <a href="#" onclick="return doIssueTransition(this, '<%= transition.key -%>')" class="link-action spacer-right"><%= message("issue.transition.#{transition.key}") -%></a>
-              </li>
-            <%  end
-               end %>
+  <div class="code-issue-details">
+    <ul class="tabs">
+      <li>
+        <a href="#tab-issue-rule"><%= message('rule') -%></a>
+      </li>
+      <li>
+        <a href="#tab-issue-changelog"><%= message('changelog') -%></a>
+      </li>
+    </ul>
 
-            <% # Display actions defined by plugins
-               plugin_actions.each do |action| %>
-              <li>
-                <a href="#" onclick="return doPluginIssueAction(this, '<%= action.key -%>')" class="link-action spacer-right"><%= message("issue.action.#{action.key}.formlink") -%></a>
-              </li>
-            <% end %>
-          </ul>
-        </div>
-      <% end %>
+    <div id="tab-issue-rule">
+      <%= image_tag 'loading.gif', :class => 'rule-loading hidden' -%>
+      <div class="issue-rule rule-desc"></div>
     </div>
-    <div class="code-issue-form hidden"></div>
-  <% elsif issue.assignee || issue.actionPlanKey %>
-    <div class="code-issue-actions">
-      <% if issue.assignee %>
-        <span class="gray"><%= message('assigned_to') -%> <%= h @issue_results.user(issue.assignee).name -%></span>
-        &nbsp;
-      <% end %>
-      <% if issue.actionPlanKey %>
-        <% if issue.assignee %><img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>&nbsp;<% end %>
-        <span class="gray"><%= message('issue.planned_for') -%> <%= h(@issue_results.actionPlan(issue).name()) -%></span>
-      <% end %>
+
+    <div id="tab-issue-changelog">
+      <%= image_tag 'loading.gif', :class => 'changelog-loading hidden' -%>
+      <table class="issue-changelog spaced">
+      </table>
     </div>
-  <% end %>
+  </div>
+
+  <div class="code-issue-comments">
+    <% issue.comments.each do |comment|
+         comment_html_id = "comment-#{comment.key}-#{rand(100)}" %>
+      <div class="code-issue-comment" id="<%= comment_html_id -%>" data-comment-key="<%= comment.key -%>">
+        <h4>
+          <%= image_tag('reviews/comment.png') -%> &nbsp;<b><%= @issue_results.user(comment.userLogin()).name() -%></b>
+          (<%= distance_of_time_in_words_to_now(Api::Utils.java_to_ruby_datetime(comment.createdAt)) -%>)
+          <% if current_user && current_user.login==comment.userLogin %>
+            &nbsp;
+            <%= image_tag 'sep12.png' -%>
+            &nbsp;
+            <a class="link-action" href="#" onclick="return formEditIssueComment(this)"><%= message('edit') -%></a>
+            <a class="link-action spacer-right" href="#" onclick="return deleteIssueComment(this, '<%= escape_javascript(message('issue.comment.delete_confirm_message')) -%>')"><%= message('delete') -%></a>
+          <% end %>
+        </h4>
+        <%= Internal.text.markdownToHtml(comment.markdownText) -%>
+      </div>
+    <% end %>
+  </div>
 </div>
+
+<script>
+  $j('#issue-<%= u issue.key -%> .code-issue-details').tabs();
+  $j('#issue-<%= u issue.key -%> .code-issue-toggle').click(function() {
+    toggleIssueCollapsed(this);
+  });
+</script>
index f93e91898f2955308b34aecafc76f6764d42a16b..19a919026172d9f59eb7a81d780540ad0f1e9b20 100644 (file)
@@ -1,3 +1,5 @@
+<h1 class="marginbottom10"><%= h @rule.name %></h1>
+
 <div class="marginbottom10">
   <% if @rule.description.strip.start_with?('<p>') %>
     <%= Internal.text.interpretMacros(@rule.description) %>
   </div>
 <% end %>
 
-<div class="note">
-  <%= h @rule.plugin_name -%>
+<p class="note">
+  <%= h @rule.plugin_name -%>:<%= h @rule.plugin_rule_key -%>
   &nbsp;<%= image_tag 'sep12.png' -%>&nbsp;
-  <a href="#" onclick="return openIssueRulePopup(this)"><%= h @rule.plugin_rule_key -%></a>
-</div>
+  <% if @requirement %>
+    <%= @root_characteristic.name -%>&nbsp;&gt;&nbsp;<%= @characteristic.name -%>
+  <% else %>
+    <%= message 'issue.technical_debt_deleted' %>
+  <% end %>
+</p>
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_technicaldebt.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_technicaldebt.html.erb
deleted file mode 100644 (file)
index 09078b4..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<div>
-  <% if @requirement %>
-    <%= @root_characteristic.name %>&nbsp;&gt;&nbsp;<%= @characteristic.name %>
-  <% else %>
-    <%= message 'issue.technical_debt_deleted' %>
-  <% end %>
-</div>
-
index e38027e80dcfa7f9d41c29892f5cd3c68225f13b..3a91c7401bae5cf05af6b9505d1f04053b2d98f7 100644 (file)
@@ -203,67 +203,45 @@ function submitCreateIssueForm(elt) {
   return false;
 }
 
-function toggleIssueRule(elt) {
+function toggleIssueCollapsed(elt) {
   var issueElt = $j(elt).closest('[data-issue-rule]');
-  var ruleElt = issueElt.find('.issue-rule');
-  if (ruleElt.is(':visible')) {
-    ruleElt.slideUp('fast');
-  } else {
-    issueElt.find('.issue-changelog').slideUp('fast');
-    issueElt.find('.issue-technicaldebt').slideUp('fast');
+  issueElt.toggleClass('code-issue-collapsed');
+
+  if (!issueElt.hasClass('code-issue-collapsed')) {
+
+    // Load rule desc
+    // Display loading images and hide existing content
+    var ruleLoading = issueElt.find('.rule-loading');
+    ruleLoading.removeClass('hidden');
+    var ruleElt = issueElt.find('.issue-rule');
+    ruleElt.addClass('hidden');
     var ruleKey = issueElt.attr('data-issue-rule');
     $j.get(baseUrl + "/issue/rule/" + ruleKey, function (html) {
       ruleElt.html(html);
-      ruleElt.slideDown('fast');
-
       // re-enable the links opening modal popups
       ruleElt.find('.open-modal').modal();
+    }).always(function () {
+      ruleLoading.addClass('hidden');
+      ruleElt.removeClass('hidden');
     });
-  }
-  return false;
-}
 
-function toggleIssueChangelog(elt) {
-  var issueElt = $j(elt).closest('[data-issue-key]');
-  var changelogElt = issueElt.find('.issue-changelog');
-  if (changelogElt.is(':visible')) {
-    changelogElt.slideUp('fast');
-  } else {
-    issueElt.find('.issue-rule').slideUp('fast');
-    issueElt.find('.issue-technicaldebt').slideUp('fast');
+    // Load changelog
+    // Display loading images and hide existing content
+    var cangelogLoading = issueElt.find('.changelog-loading');
+    cangelogLoading.removeClass('hidden');
+    var changelogElt = issueElt.find('.issue-changelog');
+    changelogElt.addClass('hidden');
     var issueKey = issueElt.attr('data-issue-key');
     $j.get(baseUrl + "/issue/changelog/" + issueKey, function (html) {
       changelogElt.html(html);
-      changelogElt.slideDown('fast');
-    });
-  }
-  return false;
-}
-
-function toggleTechnicalDebt(elt) {
-  var issueElt = $j(elt).closest('[data-issue-key]');
-  var debtElt = issueElt.find('.issue-technicaldebt');
-  if (debtElt.is(':visible')) {
-    debtElt.slideUp('fast');
-  } else {
-    issueElt.find('.issue-changelog').slideUp('fast');
-    issueElt.find('.issue-rule').slideUp('fast');
-    var issueKey = issueElt.attr('data-issue-key');
-    $j.get(baseUrl + "/issue/technicaldebt/" + issueKey, function (html) {
-      debtElt.html(html);
-      debtElt.slideDown('fast');
+    }).always(function () {
+      cangelogLoading.addClass('hidden');
+      changelogElt.removeClass('hidden');
     });
   }
   return false;
 }
 
-function openIssueRulePopup(elt) {
-  var issueElt = $j(elt).closest('[data-issue-rule]');
-  var ruleKey = issueElt.attr('data-issue-rule');
-  openPopup(baseUrl + "/rules/show/" + ruleKey + "?layout=false", 'rule');
-  return false;
-}
-
 function openIssuePopup(elt) {
   var issueElt = $j(elt).closest('[data-issue-key]');
   var issueKey = issueElt.attr('data-issue-key');
index 2edc06c0cb00ce1be9d16588cefc792d379454f8..84b5ba0a23e6178d665facb58cd308c4a1f402ca 100644 (file)
@@ -814,6 +814,7 @@ th.operations, td.operations {
   line-height: 16px;
   color: #777;
   border: 1px solid #DDD;
+  border-bottom: medium none;
 }
 
 .code-issue-msg {
@@ -854,7 +855,7 @@ th.operations, td.operations {
   top: 5px; right: 26px;
 }
 
-.code-issue-comment, .code-issue-msg, .code-issue-actions, .code-issue-form, .issue-rule, .issue-changelog, .issue-technicaldebt {
+.code-issue-comment, .code-issue-msg, .code-issue-actions, .code-issue-form {
   background-color: #EFEFEF;
   border: 1px solid #DDD;
   border-top: none;
@@ -944,6 +945,7 @@ th.operations, td.operations {
 .code-issue-list {
   font-size: 0;
   white-space: nowrap;
+  background-color: #E4ECF3;
 }
 
 .code-issue-list > li {
index b0a1cd61cb2fac44ff0a5cc590769d5291507e14..586444b6f5e8fe1dad93b0bb9263b1c9ac07f2c1 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.server.technicaldebt;
 
 import org.junit.Test;
 import org.sonar.api.issue.internal.WorkDayDuration;
-import org.sonar.api.rules.Rule;
 import org.sonar.api.technicaldebt.server.Characteristic;
 import org.sonar.api.technicaldebt.server.internal.DefaultCharacteristic;
 import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
@@ -61,11 +60,9 @@ public class DebtServiceTest {
   }
 
   @Test
-  public void find_requirement() {
-    Rule rule = Rule.create("repo", "key");
-    Characteristic requirement = new DefaultCharacteristic();
-    when(finder.findRequirementByRule(rule)).thenReturn(requirement);
-    assertThat(service.findRequirement(rule)).isEqualTo(requirement);
+  public void find_requirement_by_rule_id() {
+    service.findRequirementByRuleId(1);
+    verify(finder).findRequirementByRuleId(1);
   }
 
   @Test