summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/user/RubyUserService.java2
-rw-r--r--sonar-server/pom.xml1
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java6
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java7
-rw-r--r--sonar-server/src/main/java/org/sonar/server/user/DefaultRubyUserService.java2
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb33
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb91
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/issue/_assign_form.html.erb40
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/issue/_comment_form.html.erb18
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb7
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/resource/_index_issues.html.erb9
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/resource/_issue.html.erb162
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_issues.html.erb28
-rw-r--r--sonar-server/src/main/webapp/javascripts/issue.js54
-rw-r--r--sonar-server/src/main/webapp/stylesheets/style.css94
-rw-r--r--sonar-server/src/test/java/org/sonar/server/user/DefaultRubyUserServiceTest.java9
-rw-r--r--sonar-ws-client/src/main/java/org/sonar/wsclient/user/UserQuery.java4
-rw-r--r--sonar-ws-client/src/test/java/org/sonar/wsclient/user/UserQueryTest.java2
18 files changed, 380 insertions, 189 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/user/RubyUserService.java b/sonar-plugin-api/src/main/java/org/sonar/api/user/RubyUserService.java
index cb46f2fa95c..c7dd59f0e9e 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/user/RubyUserService.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/user/RubyUserService.java
@@ -38,7 +38,7 @@ public interface RubyUserService extends ServerComponent {
* <p/>
* Optional parameters are:
* <ul>
- * <li><code>q</code> to match all the logins or names containing this search query</li>
+ * <li><code>s</code> to match all the logins or names containing this search query</li>
* <li><code>logins</code>, as an array of strings (['simon', 'julien']) or a comma-separated list of logins ('simon,julien')</li>
* <li><code>includeDeactivated</code> as a boolean. By Default deactivated users are excluded from query.</li>
* </ul>
diff --git a/sonar-server/pom.xml b/sonar-server/pom.xml
index 7d4eaa0cd69..ba963f5be3b 100644
--- a/sonar-server/pom.xml
+++ b/sonar-server/pom.xml
@@ -256,6 +256,7 @@
<include>**/dashboard-min.js</include>
<include>**/duplication-min.js</include>
<include>**/resource-min.js</include>
+ <include>**/issue-min.js</include>
<include>**/recent-history.js</include>
</includes>
<output>${project.build.directory}/${project.build.finalName}/javascripts/sonar.js</output>
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
index 9eb7358570e..f01dff84114 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
@@ -21,6 +21,7 @@ package org.sonar.server.issue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
+import org.apache.commons.lang.StringUtils;
import org.sonar.api.ServerComponent;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
@@ -37,7 +38,6 @@ import org.sonar.core.resource.ResourceQuery;
import org.sonar.server.platform.UserSession;
import javax.annotation.Nullable;
-
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
@@ -72,8 +72,8 @@ public class InternalRubyIssueService implements ServerComponent {
return issueService.doTransition(issueKey, transitionKey, UserSession.get());
}
- public Issue assign(String issueKey, String transitionKey) {
- return issueService.assign(issueKey, transitionKey, UserSession.get());
+ public Issue assign(String issueKey, @Nullable String assignee) {
+ return issueService.assign(issueKey, StringUtils.defaultIfBlank(assignee, null), UserSession.get());
}
public Issue setSeverity(String issueKey, String severity) {
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
index 6377d61fa9d..072972b58e5 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
@@ -32,7 +32,7 @@ import org.sonar.api.web.UserRole;
import org.sonar.server.util.RubyUtils;
import javax.annotation.Nullable;
-
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -50,6 +50,10 @@ public class PublicRubyIssueService implements RubyIssueService {
this.finder = f;
}
+ public IssueQueryResult find(String issueKey) {
+ return finder.find(IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).build());
+ }
+
/**
* Requires the role {@link org.sonar.api.web.UserRole#CODEVIEWER}
*/
@@ -109,7 +113,6 @@ public class PublicRubyIssueService implements RubyIssueService {
}
-
public void start() {
// used to force pico to instantiate the singleton at startup
}
diff --git a/sonar-server/src/main/java/org/sonar/server/user/DefaultRubyUserService.java b/sonar-server/src/main/java/org/sonar/server/user/DefaultRubyUserService.java
index d561d7230a5..8a24eb59d63 100644
--- a/sonar-server/src/main/java/org/sonar/server/user/DefaultRubyUserService.java
+++ b/sonar-server/src/main/java/org/sonar/server/user/DefaultRubyUserService.java
@@ -52,7 +52,7 @@ public class DefaultRubyUserService implements RubyUserService {
builder.includeDeactivated();
}
builder.logins(RubyUtils.toStrings(params.get("logins")));
- builder.searchText((String)params.get("q"));
+ builder.searchText((String)params.get("s"));
return builder.build();
}
}
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
index 76e60d23963..648f4c5d92c 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
@@ -109,7 +109,7 @@ class IssueController < ApplicationController
require_parameters :issue
init_issue(params[:issue])
init_resource
- @action_plans = Internal.issues.findOpenActionPlans(@resource.key)
+ @action_plans = Internal.issues.findOpenActionPlans(@resource.key)
render :partial => 'issue/plan_form'
end
@@ -132,6 +132,35 @@ class IssueController < ApplicationController
render_issue_detail
end
+ def action_form
+ verify_ajax_request
+ require_parameters :id, :issue
+
+ action_type = params[:id]
+
+ # not used yet
+ issue_key = params[:issue]
+
+ render :partial => "issue/#{action_type}_form"
+ end
+
+ def do_action
+ verify_post_request
+ require_parameters :id, :issue
+
+ issue_key = params[:issue]
+ action_type = params[:id]
+
+ if action_type=='comment'
+ Internal.issues.addComment(issue_key, params[:text])
+ elsif action_type=='assign'
+ Internal.issues.assign(issue_key, params[:assignee])
+ end
+
+ @issue_results = Api.issues.find(issue_key)
+ render :partial => 'resource/issue', :locals => {:issue => @issue_results.issues.get(0)}
+ end
+
#
#
# ACTIONS FROM ISSUES TAB OF CODE VIEWER
@@ -206,7 +235,7 @@ class IssueController < ApplicationController
require_parameters :issue
init_issue(params[:issue])
init_resource
- @action_plans = Internal.issues.findOpenActionPlans(@resource.key)
+ @action_plans = Internal.issues.findOpenActionPlans(@resource.key)
render :partial => 'issue/code_viewer/plan_form'
end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
index ac0a1819192..4b580f6caaf 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
@@ -589,30 +589,12 @@ module ApplicationHelper
html += "\">X</a></span>"
end
- #
- # Creates an enhanced dropdown selection box of resources. Values are loaded on the fly via Ajax requests.
- # ==== Options
- # * <tt>:width</tt> - The width suffixed with unit, for example '300px' or '100%'. Default is '250px'
- # * <tt>:html_id</tt> - The id of the HTML element. Default is the name.
- # * <tt>:html_class</tt> - The class of the HTML element. Default is empty.
- # * <tt>:qualifiers</tt> - Array of resource qualifiers to filter.
- # * <tt>:resource_type_property</tt> -Filter on resource types on which the property is enabled, for example 'supportsGlobalDashboards'.
- # * <tt>:selected_resource</tt> - the resource that is selected by default.
- # * <tt>:placeholder</tt> - the label to display when nothing is selected
- # * <tt>:allow_clear</tt> - true if resource can be de-selected. Default is false.
- # * <tt>:select2_options</tt> - hash of select2 options
- #
- def resource_select_tag(name, options={})
- width=options[:width]
+
+ def select2_tag(name, ws_url, options={})
+ width=options[:width]||'250px'
html_id=options[:html_id]||name
html_class=options[:html_class]||''
-
- ws_url="#{ApplicationController::root_context}/api/resources/search?f=s2&"
- if options[:qualifiers]
- ws_url+="q=#{options[:qualifiers].join(',')}"
- elsif options[:resource_type_property]
- ws_url+="qp=#{options[:resource_type_property]}"
- end
+ min_length=options[:min_length]
ajax_options={
'quietMillis' => 300,
@@ -622,7 +604,6 @@ module ApplicationHelper
}
ajax_options.merge!(options[:select2_ajax_options]) if options[:select2_ajax_options]
- min_length = 3 # see limitation in /api/resources/search
js_options={
'minimumInputLength' => min_length,
'allowClear' => options[:allow_clear]||false,
@@ -638,9 +619,10 @@ module ApplicationHelper
html = "<input type='hidden' id='#{html_id}' class='#{html_class}' name='#{name}'/>"
js = "$j('##{html_id}').select2({#{js_options.map { |k, v| "#{k}:#{v}" }.join(',')}});"
- resource = options[:selected_resource]
- if resource
- js += "$j('##{html_id}').select2('data', {id: #{resource.id}, text: '#{escape_javascript(resource.name(true))}'});"
+ selected_id=options[:selected_id]
+ selected_text=options[:selected_text]
+ if selected_id && selected_text
+ js += "$j('##{html_id}').select2('data', {id: #{selected_id}, text: '#{escape_javascript(selected_text)}'});"
end
"#{html}<script>#{js}</script>"
@@ -648,6 +630,63 @@ module ApplicationHelper
#
+ # Creates an enhanced dropdown selection box of resources. Values are loaded on the fly via Ajax requests.
+ # ==== Options
+ # * <tt>:width</tt> - The width suffixed with unit, for example '300px' or '100%'. Default is '250px'
+ # * <tt>:html_id</tt> - The id of the HTML element. Default is the name.
+ # * <tt>:html_class</tt> - The class of the HTML element. Default is empty.
+ # * <tt>:qualifiers</tt> - Array of resource qualifiers to filter.
+ # * <tt>:resource_type_property</tt> -Filter on resource types on which the property is enabled, for example 'supportsGlobalDashboards'.
+ # * <tt>:selected_resource</tt> - the resource that is selected by default.
+ # * <tt>:placeholder</tt> - the label to display when nothing is selected
+ # * <tt>:allow_clear</tt> - true if resource can be de-selected. Default is false.
+ # * <tt>:select2_options</tt> - hash of select2 options
+ #
+ def resource_select_tag(name, options={})
+ # see limitation in /api/resources/search
+ options[:min_length]=3
+
+ ws_url="#{ApplicationController::root_context}/api/resources/search?f=s2&"
+ if options[:qualifiers]
+ ws_url+="q=#{options[:qualifiers].join(',')}"
+ elsif options[:resource_type_property]
+ ws_url+="qp=#{options[:resource_type_property]}"
+ end
+
+ selected_resource = options[:selected_resource]
+ if selected_resource
+ options[:selected_id]=resource.id
+ options[:selected_text]=resource.name(true)
+ end
+
+ select2_tag(name, ws_url, options)
+ end
+
+ #
+ # Creates an enhanced dropdown selection box of users. Values are loaded on the fly via Ajax requests.
+ # ==== Options
+ # * <tt>:width</tt> - The width suffixed with unit, for example '300px' or '100%'. Default is '250px'
+ # * <tt>:html_id</tt> - The id of the HTML element. Default is the name.
+ # * <tt>:html_class</tt> - The class of the HTML element. Default is empty.
+ # * <tt>:selected_user</tt> - the user that is selected by default.
+ # * <tt>:placeholder</tt> - the label to display when nothing is selected
+ # * <tt>:allow_clear</tt> - true if resource can be de-selected. Default is false.
+ # * <tt>:select2_options</tt> - hash of select2 options
+ #
+ def user_select_tag(name, options={})
+ ws_url="#{ApplicationController::root_context}/api/users/search?f=s2"
+ options[:min_length]=2
+
+ user = options[:selected_user]
+ if user
+ options[:selected_id]=user.login
+ options[:selected_text]=user.name
+ end
+
+ select2_tag(name, ws_url, options)
+ end
+
+ #
# Creates an enhanced dropdown selection box of metrics.
# ==== Options
# * <tt>:selected_key</tt> - the key of the metric that is selected by default.
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_assign_form.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_assign_form.html.erb
index 294c88cc82c..60890ab5def 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_assign_form.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_assign_form.html.erb
@@ -1,40 +1,14 @@
-<%
- assignee_check_script = "if ($('autocompleteText-issue_assignee_login').value != '' && $('issue_assignee_login').value == '') { alert($('autocompleteText-issue_assignee_login').value + '" + message('issues.user_does_not_exist') + "'); return false;}"
-%>
-
-<form method="post"
- onsubmit="<%= assignee_check_script -%> new Ajax.Updater('issue', '<%= url_for :action => 'assign' -%>', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">
- <%= hidden_field_tag :issue, params[:issue] -%>
-
+<form action="">
+ <input type="hidden" name="issue" value="<%= params[:issue] -%>"/>
+ <input type="hidden" name="id" value="assign"/>
<table class="width100">
<tr>
<td style="vertical-align:top">
- <textarea id="comment" rows="4" name="text" style="width: 100%"></textarea>
- </td>
- <td class="sep"></td>
- <td style="vertical-align:top;width: 90px">
- <%= render :partial => 'markdown/tips' -%>
+ <%= user_select_tag('assignee') -%>
+ <input type="button" value="<%= message('issues.action.assign.button') -%>" onclick="doIssueAction(this)">
+ &nbsp;<%= link_to_function message('cancel'), 'cancelIssueForm(this)' -%>&nbsp;
+ <span class="hidden"><%= image_tag 'loading.gif' -%></span>
</td>
</tr>
</table>
-
- <%= user_autocomplete_field 'issue_assignee_login', '' -%>
-
- &nbsp;&nbsp;
- <%= submit_to_remote "submit_btn", message('issues.action.assign.button'),
- :url => {:action => 'assign'},
- :update => "issue",
- :before => assignee_check_script -%>
- &nbsp;
- <%= image_tag 'sep12.png' -%>
- &nbsp;
- <%= submit_to_remote "submit_me_btn", message('issues.action.assign_to_me.button'),
- :url => {:action => 'assign', :issue => params[:issue], :me => true},
- :update => "issue",
- :html => {:disabled => (@issue.assignee == current_user.login)} -%>
- &nbsp;
- <%= link_to_remote message('cancel'), :url => {:controller => 'issue', :action => 'show', :key => params[:issue]}, :update => 'issue' -%>
- <script>
- $('autocompleteText-issue_assignee_login').focus()
- </script>
</form> \ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_comment_form.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_comment_form.html.erb
new file mode 100644
index 00000000000..447a0258999
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_comment_form.html.erb
@@ -0,0 +1,18 @@
+<form action="">
+ <input type="hidden" name="issue" value="<%= params[:issue] -%>"/>
+ <input type="hidden" name="id" value="comment"/>
+ <table class="width100">
+ <tr>
+ <td style="vertical-align:top">
+ <textarea rows="4" name="text" style="width: 100%" autofocus="autofocus"></textarea>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <input type="submit" value="<%= message('reviews.comment_submit') -%>" onclick="return doIssueAction(this)">
+ &nbsp;<%= link_to_function message('cancel'), 'cancelIssueForm(this)' -%>&nbsp;
+ <span class="loading hidden"></span>
+ </td>
+ </tr>
+ </table>
+</form>
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb
index a429508df4d..fbcd228b259 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb
@@ -17,13 +17,13 @@
end
if @page_title
- title="Sonar - #{h(@page_title)}"
+ title="SonarQube - #{h(@page_title)}"
elsif @project
- title="Sonar - #{h(@project.name)}"
+ title="SonarQube - #{h(@project.name)}"
elsif @resource
title="#{h(@resource.long_name)}"
else
- title='Sonar'
+ title='SonarQube'
end
%>
<title><%= title -%></title>
@@ -53,6 +53,7 @@
<%= javascript_include_tag 'dashboard' %>
<%= javascript_include_tag 'duplication' %>
<%= javascript_include_tag 'resource' %>
+ <%= javascript_include_tag 'issue' %>
<%= javascript_include_tag 'recent-history' %>
<% end %>
<!--[if lte IE 8]><%= javascript_include_tag 'protovis-msie' -%><![endif]-->
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_index_issues.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_index_issues.html.erb
index 778ed831a06..53e88b0b344 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_index_issues.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_index_issues.html.erb
@@ -1,4 +1,5 @@
-<div>
+<% accordionId = rand(10000) %>
+<div id="accordion<%= accordionId -%>">
<div class="accordion-item-header">
<%= render :partial => 'tabs' -%>
</div>
@@ -16,7 +17,6 @@
<% end %>
<% if @lines && @lines.size>0 %>
-
<%= render :partial => "shared/source_display", :locals => { :display_manual_violation_form => @display_manual_violation_form, \
:scm_available => @scm_available, \
:display_coverage => @display_coverage, \
@@ -29,7 +29,6 @@
:review_screens_by_vid => @review_screens_by_vid, \
:filtered => @filtered}
%>
-
<% end %>
<% if @duplication_groups %>
@@ -38,5 +37,7 @@
</div>
</div>
-
+<script>
+ $j('#accordion<%= accordionId -%> .open-modal').modal();
+</script>
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_issue.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_issue.html.erb
index 602e9eb82bc..662a6635323 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_issue.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_issue.html.erb
@@ -1,111 +1,89 @@
-<div id="issue-key<%= issue.key -%>">
- <div class="violation">
- <div class="vtitle">
- <div class="review_permalink">
- <span class="review_permalink"><%= link_to image_tag('zoom.png'), :controller => "issue", :action => "view", :id => issue.key -%></span>
- </div>
+<div class="code-issue" data-issue-key="<%= issue.key -%>">
+ <div class="code-issue-name">
+ <div class="code-issue-link">
+ <%= link_to image_tag('zoom.png'), :controller => "issue", :action => "view", :id => issue.key -%>
+ </div>
- <img src="<%= ApplicationController.root_context -%>/images/priority/<%= issue.severity -%>.png">
- &nbsp;
+ <img src="<%= ApplicationController.root_context -%>/images/priority/<%= issue.severity -%>.png">
+ &nbsp;
<span class="rulename">
<% rule_name = Internal.rules.ruleL10nName(@issue_results.rule(issue)) %>
- <a class="open-modal issue-rule-modal" modal-width="800" href="<%= url_for :controller => 'rules', :action => 'show', :id => issue.rule_key.to_s, :modal => 'true', :layout => 'false' -%>">
- <%= h rule_name -%>
- </a>
+ <a class="open-modal" modal-width="800" href="<%= url_for :controller => 'rules', :action => 'show', :id => issue.rule_key.to_s, :modal => 'true', :layout => 'false' -%>"><%= h rule_name -%></a>
</span>
+ &nbsp;
+ <%= image_tag 'sep12.png' -%>
+ &nbsp;
+
+ <span><%= distance_of_time_in_words_to_now(Api::Utils.java_to_ruby_datetime(issue.creationDate())) -%></span>
+ &nbsp;
+ <%
+ if issue.resolution
+ %>
+ <%= image_tag 'sep12.png' -%>
&nbsp;
+ <span><%= message("issues.resolution.#{issue.resolution}") -%></span>
+ &nbsp;
+ <% end %>
+ <%
+ if issue.assignee
+ %>
<%= image_tag 'sep12.png' -%>
&nbsp;
-
- <span><%= distance_of_time_in_words_to_now(Api::Utils.java_to_ruby_datetime(issue.creationDate())) -%></span>
+ <%= message('assigned_to') -%> <%= h(@issue_results.user(issue.assignee).name) -%>
&nbsp;
- <%
- if issue.resolution == 'FALSE-POSITIVE'
- %>
- <%= image_tag 'sep12.png' -%>
- &nbsp;
- <span class="reviewResolution<%= issue.resolution %>"><%= message("issues.resolution.#{issue.resolution}") -%></span>
- &nbsp;
- <% end %>
- <%
- if issue.status == 'RESOLVED'
- %>
- <%= image_tag 'sep12.png' -%>
- &nbsp;
- <span class="reviewStatus<%= issue.status -%>"><%= message("issues.status.#{issue.status}") -%></span>
- &nbsp;
- <% end %>
- <%
- if issue.assignee
- %>
- <%= image_tag 'sep12.png' -%>
- &nbsp;
- <%= message('assigned_to') -%> <%= h(@issue_results.user(issue.assignee).name) -%>
- &nbsp;
- <% end %>
- <%
- if issue.actionPlanKey()
- %>
- <%= image_tag 'sep12.png' -%>
- &nbsp;
- <%= message('issues.planned_for_x', :params => h(@issue_results.actionPlan(issue).name())) if @issue_results.actionPlan(issue) -%>
- &nbsp;
- <% end %>
+ <% end %>
+ <%
+ if issue.actionPlanKey()
+ %>
+ <%= image_tag 'sep12.png' -%>
+ &nbsp;
+ <%= message('issues.planned_for_x', :params => h(@issue_results.actionPlan(issue).name())) if @issue_results.actionPlan(issue) -%>
+ &nbsp;
+ <% end %>
+ </div>
+
+ <% unless issue.description.blank? %>
+ <div class="code-issue-msg">
+ <%= Api::Utils.split_newlines(h(issue.description)).join('<br/>') -%>
</div>
+ <% end %>
- <div class="discussionComment first">
- <div id="issue-description<%= issue.key -%>">
- <% issue_description = issue.description ? Api::Utils.split_newlines(h(issue.description)).join('<br/>') : '' %>
- <%= issue_description || '&nbsp;' -%>
- </div>
+ <% issue.comments.each do |comment| %>
+ <div class="code-issue-comment">
+ <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)) -%>)
+ </h4>
+ <%= Internal.text.markdownToHtml(comment.markdownText) -%>
</div>
+ <% end %>
- <% if current_user %>
- <div class="vActions" id="issue-actions<%= issue.key -%>">
- <% if defined?(error_message) && error_message %>
- <div id="issue-error-<%= issue.key -%>" class="error"><%= h error_message -%>
- <a href="#" onclick="$('issue-error-<%= issue.key -%>').hide(); return false;"><%= message('issues.hide_this_message') -%></a></div>
- <% end %>
+ <% if current_user %>
+ <div class="code-issue-actions">
+ <a href='#' onclick="return issueForm('comment', this)" class="link-action spacer-right"><%= message('reviews.comment') -%></a>
- <% unless issue.status == 'RESOLVED' %>
- <%= link_to_function message('issues.action.assign.button'), "displayIssueAssignForm('#{issue.key}')",
- :name => message('issues.action.assign.button'), :class => 'link-action spacer-right' -%>
- <% end %>
+ <% unless issue.resolution %>
+ <a href='#' onclick="return issueForm('assign', this)" class="link-action spacer-right"><%= message('issues.action.assign.button') -%></a>
+ <% end %>
- <% Internal.issues.listTransitions(issue.key).each do |transition| %>
+ <% Internal.issues.listTransitions(issue.key).each do |transition| %>
<%= link_to_function message("issues.transition.#{transition.key}.button"), "displayIssueTransitionForm('#{issue.key}', '#{transition.key}')",
:name => message("issues.transition.#{transition.key}.button"), :class => 'link-action spacer-right' -%>
- <% end %>
+ <% end %>
- <% unless issue.resolution && issue.status == 'RESOLVED' %>
- <div class="dropdown">
- <a href="#" class="link-action link-more" onclick="showDropdownMenu('more<%= issue.key -%>');return false;"><%= message('more_actions') -%></a>
- <ul style="display: none" class="dropdown-menu" id="more<%= issue.key -%>">
- <li><%= link_to_function message('issues.action.change_severity.button'), "displayIssueChangeSeverityForm('#{issue.key}')",
- :name => message('issues.action.change_severity.button') -%></li>
- <li><%= link_to_function message('issues.action.plan.button'), "displayIssuePlanForm('#{issue.key}')",
- :name => message('issues.action.plan.button') -%></li>
- </ul>
- </div>
- <% end %>
- </div>
- <% end %>
- <% issue.comments.each do |comment| %>
- <div class="discussionComment">
- <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)) -%>)
- </h4>
- <%= Internal.text.markdownToHtml(comment.markdownText) -%>
+ <% unless issue.resolution && issue.status == 'RESOLVED' %>
+ <div class="dropdown">
+ <a href="#" class="link-action link-more" onclick="showDropdownMenu('more<%= issue.key -%>');return false;"><%= message('more_actions') -%></a>
+ <ul style="display: none" class="dropdown-menu" id="more<%= issue.key -%>">
+ <li><%= link_to_function message('issues.action.change_severity.button'), "displayIssueChangeSeverityForm('#{issue.key}')",
+ :name => message('issues.action.change_severity.button') -%></li>
+ <li><%= link_to_function message('issues.action.plan.button'), "displayIssuePlanForm('#{issue.key}')",
+ :name => message('issues.action.plan.button') -%></li>
+ </ul>
+ </div>
+ <% end %>
</div>
- <% end %>
- </div>
- <div class="discussionComment" id="issue-form<%= issue.key -%>" style="display:none"></div>
-</div>
-
-<script type="text/javascript">
- $j(document).ready(function () {
- // As rule links will be loaded after open-modal has been processed by jquery, we have to process manually issue-rule-modal classes
- $j('.issue-rule-modal').modal()
- });
-</script> \ No newline at end of file
+ <div class="code-issue-form hidden"></div>
+ <% end %>
+</div> \ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_issues.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_issues.html.erb
index 37d4a4da779..c0a0f827a4f 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_issues.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_issues.html.erb
@@ -1,17 +1,17 @@
<tr>
- <% if display_manual_violation_form %>
- <td class="gray"></td>
- <% end
- if scm_available %>
- <td class="scm"></td>
+ <% if display_manual_violation_form %>
+ <td class="gray"></td>
+ <% end
+ if scm_available %>
+ <td class="scm"></td>
+ <% end %>
+ <td class="lid"></td>
+ <td class="code-issues">
+ <% line.issues.each_with_index do |issue, index| %>
+ <%= render :partial => 'issue', :locals => {:issue => issue} -%>
+ <% if index < line.issues.size-1 %>
+ &nbsp;
+ <% end %>
<% end %>
- <td class="lid"></td>
- <td class="violations">
- <% line.issues.each_with_index do |issue, index| %>
- <%= render :partial => 'issue', :locals => {:issue => issue} -%>
- <% if index < line.issues.size-1 %>
- &nbsp;
- <% end %>
- <% end %>
- </td>
+ </td>
</tr> \ No newline at end of file
diff --git a/sonar-server/src/main/webapp/javascripts/issue.js b/sonar-server/src/main/webapp/javascripts/issue.js
new file mode 100644
index 00000000000..47cb33c632a
--- /dev/null
+++ b/sonar-server/src/main/webapp/javascripts/issue.js
@@ -0,0 +1,54 @@
+function issueForm(actionType, elt) {
+ var issueElt = $j(elt).closest('[data-issue-key]');
+ var issueKey = issueElt.attr('data-issue-key');
+ var actionsElt = issueElt.find('.code-issue-actions');
+ var formElt = issueElt.find('.code-issue-form');
+
+ actionsElt.addClass('hidden');
+ formElt.html("<img src='" + baseUrl + "/images/loading-small.gif'>").removeClass('hidden');
+
+ $j.ajax(baseUrl + "/issue/action_form/" + actionType + "?issue=" + issueKey)
+ .done(function (msg) {
+ formElt.html(msg);
+ var focusField = formElt.find('[autofocus]');
+ if (focusField != null) {
+ focusField.focus();
+ }
+ })
+ .fail(function (jqXHR, textStatus) {
+ alert(textStatus);
+ });
+ return false;
+}
+
+function cancelIssueForm(elt) {
+ var issueElt = $j(elt).closest('[data-issue-key]');
+ var actionsElt = issueElt.find('.code-issue-actions');
+ var formElt = issueElt.find('.code-issue-form');
+
+ formElt.addClass('hidden');
+ actionsElt.removeClass('hidden');
+ return false;
+}
+
+function doIssueAction(elt) {
+ var formElt = $j(elt).closest('form');
+ formElt.find('.loading').removeClass('hidden');
+ formElt.find(':submit').prop('disabled', true);
+ $j.ajax({
+ type: "POST",
+ url: baseUrl + '/issue/do_action',
+ data: formElt.serialize()}
+ ).success(function (htmlResponse) {
+ var issueElt = formElt.closest('[data-issue-key]');
+ issueElt.html(htmlResponse);
+ // re-enable the links opening modal popups
+ issueElt.find('.open-modal').modal();
+ }
+ ).fail(function (jqXHR, textStatus) {
+ cancelIssueForm(elt);
+ alert(textStatus);
+ });
+ return false;
+}
+
diff --git a/sonar-server/src/main/webapp/stylesheets/style.css b/sonar-server/src/main/webapp/stylesheets/style.css
index d974c51814b..77768ce5488 100644
--- a/sonar-server/src/main/webapp/stylesheets/style.css
+++ b/sonar-server/src/main/webapp/stylesheets/style.css
@@ -176,7 +176,7 @@ button, .button, input[type="submit"], input[type="button"] {
-moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
- color: #333333;
+ color: #333;
border: 1px solid #CCC;
background: #EBEBEB;
background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#E7E7E7));
@@ -742,6 +742,18 @@ ul.operations li img {
padding: 10px;
}
+
+
+
+
+
+
+
+
+
+
+
+
div.vtitle {
background-color: #E4ECF3;
margin: 0;
@@ -788,7 +800,7 @@ span.rulename, span.rulename a {
div.review_permalink {
float: right;
- color: #333333;
+ color: #333;
font-weight: bold;
margin: 0;
padding: 0 10px;
@@ -880,6 +892,84 @@ span.rulename a:hover {
vertical-align: text-bottom;
}
+.code-issues {
+ background-color: #FFF;
+ padding: 10px;
+}
+
+.code-issue {
+ background-color: #FFF;
+ margin: 0;
+ font-size: 12px;
+}
+
+.code-issue-name {
+ background-color: #E4ECF3;
+ margin: 0;
+ padding: 5px 10px;
+ line-height: 16px;
+ color: #777;
+ border: 1px solid #DDD;
+}
+
+.code-issue-name img {
+ vertical-align: text-bottom;
+}
+
+.code-issue-link {
+ float: right;
+ color: #333;
+ font-weight: bold;
+ margin: 0;
+ padding: 0 10px;
+ text-shadow: 1px 1px 0 #FFFFFF;
+}
+
+.code-issue-link a {
+ color: #777;
+ font-size: 11px;
+ padding: 0 0 0 20px;
+}
+
+.code-issue-comment, .code-issue-msg, .code-issue-actions, .code-issue-form {
+ background-color: #EFEFEF;
+ border: 1px solid #DDD;
+ border-top: none;
+ line-height: 1.5em;
+ margin: 0;
+ padding: 5px 10px;
+}
+
+.code-issue-msg h4 {
+ font-size: 90%;
+ margin-bottom: 2px;
+}
+
+.code-issue-msg h4 img {
+ vertical-align: sub;
+}
+
+.code-issue-msg li {
+ list-style: square inside;
+}
+
+.code-issue-msg pre {
+ padding: 10px;
+ border: 1px dashed #DDD;
+ color: #444;
+ font-size: 12px;
+}
+
+
+
+
+
+
+
+
+
+
+
.tab_header {
border-bottom: 1px solid #DDD;
background-color: #EFEFEF;
diff --git a/sonar-server/src/test/java/org/sonar/server/user/DefaultRubyUserServiceTest.java b/sonar-server/src/test/java/org/sonar/server/user/DefaultRubyUserServiceTest.java
index 1dbf3b95f90..5dac4625e4d 100644
--- a/sonar-server/src/test/java/org/sonar/server/user/DefaultRubyUserServiceTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/user/DefaultRubyUserServiceTest.java
@@ -38,14 +38,17 @@ public class DefaultRubyUserServiceTest {
public void parse_query() throws Exception {
service.find(ImmutableMap.<String, Object>of(
"logins", "simon,loic",
- "includeDeactivated", "true"
+ "includeDeactivated", "true",
+ "s", "sim"
));
verify(finder, times(1)).find(argThat(new ArgumentMatcher<UserQuery>() {
@Override
public boolean matches(Object o) {
UserQuery query = (UserQuery) o;
- return query.includeDeactivated() && query.logins().contains("simon") && query.logins().contains("loic") && query.logins().size() == 2;
+ return query.includeDeactivated() &&
+ query.logins().contains("simon") && query.logins().contains("loic") && query.logins().size() == 2 &&
+ query.searchText().equals("sim");
}
}));
}
@@ -58,7 +61,7 @@ public class DefaultRubyUserServiceTest {
@Override
public boolean matches(Object o) {
UserQuery query = (UserQuery) o;
- return !query.includeDeactivated() && query.logins() == null;
+ return !query.includeDeactivated() && query.logins() == null && query.searchText()==null;
}
}));
}
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/user/UserQuery.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/user/UserQuery.java
index 07f802fb8a9..43d0e33c6ac 100644
--- a/sonar-ws-client/src/main/java/org/sonar/wsclient/user/UserQuery.java
+++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/user/UserQuery.java
@@ -52,9 +52,9 @@ public class UserQuery {
public UserQuery searchText(@Nullable String s) {
if (s != null) {
- params.put("q", s);
+ params.put("s", s);
} else {
- params.remove("q");
+ params.remove("s");
}
return this;
}
diff --git a/sonar-ws-client/src/test/java/org/sonar/wsclient/user/UserQueryTest.java b/sonar-ws-client/src/test/java/org/sonar/wsclient/user/UserQueryTest.java
index fbb9b4d26a1..475e95c4f70 100644
--- a/sonar-ws-client/src/test/java/org/sonar/wsclient/user/UserQueryTest.java
+++ b/sonar-ws-client/src/test/java/org/sonar/wsclient/user/UserQueryTest.java
@@ -52,6 +52,6 @@ public class UserQueryTest {
@Test
public void should_search_by_text() throws Exception {
UserQuery query = UserQuery.create().searchText("sim");
- assertThat(query.urlParams().get("q")).isEqualTo("sim");
+ assertThat(query.urlParams().get("s")).isEqualTo("sim");
}
}