return selectByIssuesAndType(session, issueKeys, IssueChangeDto.TYPE_COMMENT);
}
+ public List<IssueChangeDto> selectIssueChangelog(String issueKey) {
+ SqlSession session = mybatis.openSession();
+ try {
+ IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
+ return mapper.selectByIssue(issueKey);
+
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
@CheckForNull
public DefaultIssueComment selectCommentByKey(String commentKey) {
SqlSession session = mybatis.openSession();
*/
List<IssueChangeDto> selectByIssuesAndType(@Param("issueKeys") Collection<String> issueKeys,
@Param("changeType") String changeType);
+
+ List<IssueChangeDto> selectByIssue(String issueKey);
}
from issue_changes c
where c.change_type=#{changeType} and c.kee=#{key}
</select>
+
+ <select id="selectByIssue" parameterType="string" resultType="IssueChange">
+ select
+ <include refid="issueChangeColumns"/>
+ from issue_changes c
+ where c.issue_key=#{id}
+ order by created_at asc
+ </select>
</mapper>
assertThat(second.markdownText()).isEqualTo("recent comment");
}
+ @Test
+ public void selectIssueChangelog() {
+ setupData("shared");
+
+ List<IssueChangeDto> changes = dao.selectIssueChangelog("1000");
+ assertThat(changes).hasSize(3);
+
+ // chronological order
+ assertThat(changes.get(0).getId()).isEqualTo(100);
+ assertThat(changes.get(1).getId()).isEqualTo(101);
+ assertThat(changes.get(2).getId()).isEqualTo(102);
+ }
+
@Test
public void selectCommentsByIssues_empty_input() {
// no need to connect to db
import org.sonar.core.issue.ActionPlanStats;
import org.sonar.core.issue.DefaultActionPlan;
import org.sonar.core.issue.DefaultIssueBuilder;
+import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.issue.workflow.Transition;
import org.sonar.core.resource.ResourceDao;
import org.sonar.core.resource.ResourceDto;
public class InternalRubyIssueService implements ServerComponent {
private final IssueService issueService;
- private final IssueCommentService commentService;
+ private final IssueChangeService changeService;
private final ActionPlanService actionPlanService;
private final IssueStatsFinder issueStatsFinder;
private final ResourceDao resourceDao;
private final ActionService actionService;
public InternalRubyIssueService(IssueService issueService,
- IssueCommentService commentService,
+ IssueChangeService changeService,
ActionPlanService actionPlanService,
IssueStatsFinder issueStatsFinder, ResourceDao resourceDao, ActionService actionService) {
this.issueService = issueService;
- this.commentService = commentService;
+ this.changeService = changeService;
this.actionPlanService = actionPlanService;
this.issueStatsFinder = issueStatsFinder;
this.resourceDao = resourceDao;
return Issue.RESOLUTIONS;
}
+ public List<IssueChangeDto> changelog(String issueKey) {
+ // TODO verify security
+ return changeService.changelog(issueKey);
+ }
+
public Issue doTransition(String issueKey, String transitionKey) {
return issueService.doTransition(issueKey, transitionKey, UserSession.get());
}
}
public IssueComment addComment(String issueKey, String text) {
- return commentService.addComment(issueKey, text, UserSession.get());
+ return changeService.addComment(issueKey, text, UserSession.get());
}
public IssueComment deleteComment(String commentKey) {
- return commentService.deleteComment(commentKey, UserSession.get());
+ return changeService.deleteComment(commentKey, UserSession.get());
}
public IssueComment editComment(String commentKey, String newText) {
- return commentService.editComment(commentKey, newText, UserSession.get());
+ return changeService.editComment(commentKey, newText, UserSession.get());
}
public IssueComment findComment(String commentKey) {
- return commentService.findComment(commentKey);
+ return changeService.findComment(commentKey);
}
/**
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.issue;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Strings;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.issue.IssueComment;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.issue.IssueQueryResult;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.issue.internal.DefaultIssueComment;
+import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.issue.IssueNotifications;
+import org.sonar.core.issue.IssueUpdater;
+import org.sonar.core.issue.db.IssueChangeDao;
+import org.sonar.core.issue.db.IssueChangeDto;
+import org.sonar.core.issue.db.IssueStorage;
+import org.sonar.server.user.UserSession;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+public class IssueChangeService implements ServerComponent {
+
+ private final IssueUpdater updater;
+ private final IssueChangeDao changeDao;
+ private final IssueStorage storage;
+ private final DefaultIssueFinder finder;
+ private final IssueNotifications issueNotifications;
+
+ public IssueChangeService(IssueUpdater updater, IssueChangeDao changeDao, IssueStorage storage, DefaultIssueFinder finder, IssueNotifications issueNotifications) {
+ this.updater = updater;
+ this.changeDao = changeDao;
+ this.storage = storage;
+ this.finder = finder;
+ this.issueNotifications = issueNotifications;
+ }
+
+ public List<IssueChangeDto> changelog(String issueKey) {
+ // TODO verify security
+ return changeDao.selectIssueChangelog(issueKey);
+ }
+
+ public IssueComment findComment(String commentKey) {
+ return changeDao.selectCommentByKey(commentKey);
+ }
+
+ public IssueComment addComment(String issueKey, String text, UserSession userSession) {
+ verifyLoggedIn(userSession);
+
+ IssueQueryResult queryResult = loadIssue(issueKey);
+ DefaultIssue issue = (DefaultIssue) queryResult.first();
+
+ IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login());
+ updater.addComment(issue, text, context);
+ storage.save(issue);
+ issueNotifications.sendChanges(issue, context, queryResult, text);
+ return issue.comments().get(issue.comments().size() - 1);
+ }
+
+ public IssueComment deleteComment(String commentKey, UserSession userSession) {
+ DefaultIssueComment comment = changeDao.selectCommentByKey(commentKey);
+ if (comment == null) {
+ // TODO throw 404
+ throw new IllegalStateException();
+ }
+ if (Strings.isNullOrEmpty(comment.userLogin()) || !Objects.equal(comment.userLogin(), userSession.login())) {
+ // TODO throw unauthorized
+ throw new IllegalStateException();
+ }
+
+ // check authorization
+ finder.findByKey(comment.issueKey(), UserRole.USER);
+
+ changeDao.delete(commentKey);
+ return comment;
+ }
+
+ public IssueComment editComment(String commentKey, String text, UserSession userSession) {
+ DefaultIssueComment comment = changeDao.selectCommentByKey(commentKey);
+ if (comment == null) {
+ // TODO throw 404
+ throw new IllegalStateException();
+ }
+ if (Strings.isNullOrEmpty(comment.userLogin()) || !Objects.equal(comment.userLogin(), userSession.login())) {
+ // TODO throw unauthorized
+ throw new IllegalStateException();
+ }
+
+ // check authorization
+ finder.findByKey(comment.issueKey(), UserRole.USER);
+
+ IssueChangeDto dto = IssueChangeDto.of(comment);
+ dto.setUpdatedAt(new Date());
+ dto.setChangeData(text);
+ changeDao.update(dto);
+
+ return comment;
+ }
+
+ private void verifyLoggedIn(UserSession userSession) {
+ if (!userSession.isLoggedIn()) {
+ // must be logged
+ throw new IllegalStateException("User is not logged in");
+ }
+ }
+
+ public IssueQueryResult loadIssue(String issueKey) {
+ IssueQuery query = IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).requiredRole(UserRole.USER).build();
+ return finder.find(query);
+ }
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.issue;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Strings;
-import org.sonar.api.ServerComponent;
-import org.sonar.api.issue.IssueComment;
-import org.sonar.api.issue.IssueQuery;
-import org.sonar.api.issue.IssueQueryResult;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.IssueChangeContext;
-import org.sonar.api.web.UserRole;
-import org.sonar.core.issue.IssueNotifications;
-import org.sonar.core.issue.IssueUpdater;
-import org.sonar.core.issue.db.IssueChangeDao;
-import org.sonar.core.issue.db.IssueChangeDto;
-import org.sonar.core.issue.db.IssueStorage;
-import org.sonar.server.user.UserSession;
-
-import java.util.Arrays;
-import java.util.Date;
-
-public class IssueCommentService implements ServerComponent {
-
- private final IssueUpdater updater;
- private final IssueChangeDao changeDao;
- private final IssueStorage storage;
- private final DefaultIssueFinder finder;
- private final IssueNotifications issueNotifications;
-
- public IssueCommentService(IssueUpdater updater, IssueChangeDao changeDao, IssueStorage storage, DefaultIssueFinder finder, IssueNotifications issueNotifications) {
- this.updater = updater;
- this.changeDao = changeDao;
- this.storage = storage;
- this.finder = finder;
- this.issueNotifications = issueNotifications;
- }
-
- public IssueComment findComment(String commentKey) {
- return changeDao.selectCommentByKey(commentKey);
- }
-
- public IssueComment addComment(String issueKey, String text, UserSession userSession) {
- verifyLoggedIn(userSession);
-
- IssueQueryResult queryResult = loadIssue(issueKey);
- DefaultIssue issue = (DefaultIssue) queryResult.first();
-
- IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login());
- updater.addComment(issue, text, context);
- storage.save(issue);
- issueNotifications.sendChanges(issue, context, queryResult, text);
- return issue.comments().get(issue.comments().size() - 1);
- }
-
- public IssueComment deleteComment(String commentKey, UserSession userSession) {
- DefaultIssueComment comment = changeDao.selectCommentByKey(commentKey);
- if (comment == null) {
- // TODO throw 404
- throw new IllegalStateException();
- }
- if (Strings.isNullOrEmpty(comment.userLogin()) || !Objects.equal(comment.userLogin(), userSession.login())) {
- // TODO throw unauthorized
- throw new IllegalStateException();
- }
-
- // check authorization
- finder.findByKey(comment.issueKey(), UserRole.USER);
-
- changeDao.delete(commentKey);
- return comment;
- }
-
- public IssueComment editComment(String commentKey, String text, UserSession userSession) {
- DefaultIssueComment comment = changeDao.selectCommentByKey(commentKey);
- if (comment == null) {
- // TODO throw 404
- throw new IllegalStateException();
- }
- if (Strings.isNullOrEmpty(comment.userLogin()) || !Objects.equal(comment.userLogin(), userSession.login())) {
- // TODO throw unauthorized
- throw new IllegalStateException();
- }
-
- // check authorization
- finder.findByKey(comment.issueKey(), UserRole.USER);
-
- IssueChangeDto dto = IssueChangeDto.of(comment);
- dto.setUpdatedAt(new Date());
- dto.setChangeData(text);
- changeDao.update(dto);
-
- return comment;
- }
-
- private void verifyLoggedIn(UserSession userSession) {
- if (!userSession.isLoggedIn()) {
- // must be logged
- throw new IllegalStateException("User is not logged in");
- }
- }
-
- public IssueQueryResult loadIssue(String issueKey) {
- IssueQuery query = IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).requiredRole(UserRole.USER).build();
- return finder.find(query);
- }
-}
servicesContainer.addSingleton(FunctionExecutor.class);
servicesContainer.addSingleton(IssueWorkflow.class);
servicesContainer.addSingleton(IssueService.class);
- servicesContainer.addSingleton(IssueCommentService.class);
+ servicesContainer.addSingleton(IssueChangeService.class);
servicesContainer.addSingleton(DefaultIssueFinder.class);
servicesContainer.addSingleton(IssueStatsFinder.class);
servicesContainer.addSingleton(PublicRubyIssueService.class);
helper SourceHelper
- def view
- require_parameters :id
- init_issue
-
- if request.xhr?
- render :partial => 'issue/view', :locals => {:issue => @issue, :issue_results => @issue_results, :snapshot => @snapshot, :show_source => true}
- else
- render :action => 'view'
- end
- end
-
# GET /issue/show/<key>
# This URL is used by the Eclipse Plugin
#
def show
require_parameters :id
init_issue
- render :action => 'view'
+
+ if params[:modal]
+ render :partial => 'issue/show_modal'
+ elsif request.xhr?
+ render :partial => 'issue/show'
+ else
+ render :action => 'show'
+ end
+
end
# Form used for: assign, comment, transition, change severity and plan
issue_result = Internal.issues.create(params.merge({:component => component_key}))
if issue_result.ok
@issue_results = Api.issues.find(issue_result.get.key)
- render :partial => 'issue/issue', :locals => {:issue => @issue_results.issues.get(0)}
+ render :partial => 'issue/manual_issue_created', :locals => {:issue => @issue_results.first}
else
render :partial => 'shared/result_messages', :status => 500, :locals => {:result => issue_result}
end
render :partial => 'project/widgets/issues/issues_list'
end
+ # Display the rule description in the issue panel
+ def rule
+ verify_ajax_request
+ require_parameters :id
+ rule_key = params[:id].split(':')
+ @rule = Rule.first(:conditions => ['plugin_name=? and plugin_rule_key=?', rule_key[0], rule_key[1]], :include => :rule_note)
+ render :partial => 'issue/rule'
+ end
+
+ # Display the changelog in the issue panel
+ def changelog
+ verify_ajax_request
+ require_parameters :id
+ @issue_results = Api.issues.find(params[:id])
+ @issue = @issue_results.first()
+ @changes = Internal.issues.changelog(params[:id])
+ render :partial => 'issue/changelog'
+ end
+
private
def init_issue
@issue_results = Api.issues.find(params[:id])
- @issue = @issue_results.issues.get(0)
+ @issue = @issue_results.first()
resource = Project.by_key(@issue.componentKey())
@snapshot = resource.last_snapshot if resource.last_snapshot
--- /dev/null
+<ul>
+ <li>
+ <%= format_datetime(@issue.creationDate()) -%>
+ <% if @issue.reporter %>
+ - <%= message('issue.reported_by') -%> <%= @issue_results.user(@issue.reporter).name -%>
+ <% end %>
+ - <%= message('created') -%>
+ </li>
+
+ <% @changes.each do |change| %>
+ <li>
+ <%= format_datetime(change.createdAt()) -%> - <%= change.userLogin -%> - <%= change.changeData -%>
+ </li>
+ <% end %>
+</ul>
+
+<div class="note">
+ <a href="#" onclick="return hideIssueMore(this)">Hide</a><%= image_tag 'asc.png' -%>
+</div>
\ No newline at end of file
-<div class="code-issue" data-issue-key="<%= issue.key -%>" data-issue-component="<%= issue.componentKey() -%>">
+<div class="code-issue" data-issue-key="<%= issue.key -%>" data-issue-component="<%= issue.componentKey() -%>" data-issue-rule="<%= issue.ruleKey().toString() -%>">
<div class="code-issue-name">
- <img src="<%= ApplicationController.root_context -%>/images/priority/<%= issue.severity -%>.png">
+ <div style="float: right">
+ <a target="issue"
+ href="<%= url_for :controller => 'issue', :action => 'show', :id => issue.key -%>">
+ <img src="<%= ApplicationController.root_context -%>/images/new-window-16.gif">
+ </a>
+ </div>
+
+ <img src="<%= ApplicationController.root_context -%>/images/priority/<%= issue.severity -%>.png" title="<%= h message("severity.#{issue.severity}") -%>">
- <span class="rulename">
- <% rule_name = Internal.rules.ruleL10nName(@issue_results.rule(issue)) %>
- <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>
+ <a href="#" onclick="return showIssueRule(this)" class="rulename"><%= h Internal.rules.ruleL10nName(@issue_results.rule(issue)) -%></a>
<% if issue.resolution %>
<%= image_tag 'sep12.png' -%>
<% end %>
<%= image_tag 'sep12.png' -%>
- <%
- created_at = Api::Utils.java_to_ruby_datetime(issue.creationDate())
- updated_at = Api::Utils.java_to_ruby_datetime(issue.updateDate())
- dates_title = "Created at #{format_datetime(created_at)} and updated at #{format_datetime(updated_at)}"
- %>
- <span title="<%= h dates_title -%>"><%= distance_of_time_in_words_to_now(created_at) -%></span>
+ <a href="#" onclick="return showIssueChangelog(this)" class="gray"><%= distance_of_time_in_words_to_now(Api::Utils.java_to_ruby_datetime(issue.creationDate())) -%></a>
<% if issue.reporter %>
<%= image_tag 'sep12.png' -%>
<% end %>
</div>
+ <div class="issue-more" style="display: none"></div>
+
<% unless issue.message.blank? %>
<div class="code-issue-msg">
<%= Api::Utils.split_newlines(h(issue.message)).join('<br/>') -%>
<%= image_tag('reviews/comment.png') -%> <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 %>
-
- <%= image_tag 'sep12.png' -%>
-
- <a class="link-action" href="#" onclick="return formEditIssueComment(this)"><%= message('edit') -%></a>
- <a class="link-action spacer-right" href="#" onclick="return formDeleteIssueComment(this)"><%= message('delete') -%></a>
+
+ <%= image_tag 'sep12.png' -%>
+
+ <a class="link-action" href="#" onclick="return formEditIssueComment(this)"><%= message('edit') -%></a>
+ <a class="link-action spacer-right" href="#" onclick="return formDeleteIssueComment(this)"><%= message('delete') -%></a>
<% end %>
</h4>
<%= Internal.text.markdownToHtml(comment.markdownText) -%>
<% end %>
<% if current_user %>
- <% transitions = Internal.issues.listTransitions(issue) %>
-
<div class="code-issue-actions">
<a href='#' onclick="return issueForm('comment', this)" class="link-action spacer-right"><%= message('issue.comment.formlink') -%></a>
<% unless issue.resolution %>
<%= image_tag 'sep12.png' -%>
<span class="spacer-right">
- <% if !issue.assignee %>
+ <% 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 %>
<a href='#' onclick="return issueForm('assign', this)" class="link-action"><%= message('issue.assign.formlink') -%></a>
- <% if issue.assignee!=current_user.login %>
+ <% if issue.assignee!=current_user.login %>
[<a href="#" onclick="return assignIssueToMe(this)" class="link-action"><%= message('issue.assign.to_me') -%></a>]
- <% end %>
- <% else %>
- <a href='#' onclick="return issueForm('assign', this)" class="link-action"><%= message('assigned_to') -%></a> <%= @issue_results.user(issue.assignee).name -%>
+ <% end %>
<% end %>
</span>
<% end %>
<%= image_tag 'sep12.png' -%>
<span class="spacer-right">
- <% if !@issue_results.actionPlan(issue) %>
- <a href="#" onclick="return issueForm('plan', this)" class="link-action"><%= message('issue.do_plan') -%></a>
- <% else %>
+ <% 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)
if transitions.size > 0 && transitions.first
- transition = transitions.first
+ transition = transitions.first
%>
<%= image_tag 'sep12.png' -%>
<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">
<% unless issue.resolution %>
- <li><a href="#" onclick="return issueForm('severity', this)" class="link-action spacer-right"><%= message("issue.set_severity") -%></a></li>
+ <li>
+ <a href="#" onclick="return issueForm('severity', this)" class="link-action spacer-right"><%= message("issue.set_severity") -%></a>
+ </li>
<% end %>
<% transitions.each do |transition| %>
- <li><a href="#" onclick="return doIssueTransition(this, '<%= transition.key -%>')" class="link-action spacer-right"><%= message("issue.transition.#{transition.key}") -%></a></li>
+ <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 %>
</div>
<div class="code-issue-form hidden"></div>
+ <% elsif issue.assignee || issue.actionPlanKey %>
+ <div class="code-issue-actions">
+ <% if issue.assignee %>
+ <%= message('assigned_to') -%> <%= h @issue_results.user(issue.assignee).name -%>
+
+ <% end %>
+ <% if issue.actionPlanKey %>
+ <%= message('issue.planned_for') -%> <%= h(@issue_results.actionPlan(issue).name()) -%>
+ <% end %>
+ </div>
<% end %>
</div>
\ No newline at end of file
--- /dev/null
+<div class="code-issues">
+ <%= render :partial => 'issue/issue', :locals => {:issue => issue} -%>
+</div>
\ No newline at end of file
--- /dev/null
+<div class="marginbottom10">
+ <% if @rule.description.strip.start_with?('<p>') %>
+ <%= Internal.text.interpretMacros(@rule.description) %>
+ <% else %>
+ <p><%= Internal.text.interpretMacros(@rule.description) %></p>
+ <% end %>
+</div>
+
+<% if @rule.note %>
+ <div class="marginbottom10">
+ <%= @rule.note.html_text -%>
+ </div>
+<% end %>
+
+<div class="note">
+ <a href="#" onclick="return hideIssueMore(this)">Hide</a><%= image_tag 'asc.png' -%>
+ <%= image_tag 'sep12.png' -%>
+ <%= @rule.plugin_name -%>
+ <%= image_tag 'sep12.png' -%>
+ <a onclick="window.open(this.href,'rule','height=800,width=900,scrollbars=1,resizable=1');return false;"
+ href="<%= url_for :controller => 'rules', :action => 'show', :id => @rule.key, :layout => 'false' -%>">
+ <%= @rule.plugin_rule_key -%>
+ </a>
+</div>
\ No newline at end of file
--- /dev/null
+<div class="source_title">
+ <div class="subtitle">
+ <%= h @issue_results.project(@issue).name() -%>
+ </div>
+ <span class="h1"><%= h @issue_results.component(@issue).longName() -%></span>
+</div>
+
+<div class="marginbottom10">
+<%= render :partial => 'issue/issue', :locals => {:issue => @issue_results.first} -%>
+</div>
+
+<% if @snapshot && @issue.line && params[:source]!='false' %>
+ <div class="bordered">
+ <%= snapshot_html_source(@snapshot, {:line_range => (@issue.line-5)..(@issue.line+5), :highlighted_lines => [@issue.line]}) -%>
+ </div>
+<% end %>
--- /dev/null
+<div class="modal-body">
+ <%= render :partial => 'issue/show' -%>
+</div>
+<div class="modal-foot">
+ <input type="button" value="<%= h message('close') -%>" onclick="return closeModalWindow()">
+</div>
\ No newline at end of file
+++ /dev/null
-<div id="content">
- <div style="border: 1px solid #DDDDDD">
- <div class="violations_header tab_header">
- <h3>Issue #<%= issue.key %></h3>
- <h3><%= h issue_results.project(issue).name -%></h3>
- <h3><%= h issue_results.component(issue).name -%></h3>
- </div>
-
- <div class="marginbottom10 issue-content">
- <%= render :partial => 'issue/issue', :locals => {:issue => issue_results.issues.get(0)} -%>
- </div>
-
- <% if snapshot && issue.line && show_source %>
- <div>
- <%= snapshot_html_source(snapshot, {:line_range => (issue.line-5)..(issue.line+5), :highlighted_lines => [issue.line]}) -%>
- </div>
- <% end %>
- </div>
-</div>
-
-<script>
- // Apply modal function on the rule link
- $j('.issue-content .open-modal').modal();
-</script>
--- /dev/null
+<div id="content">
+ <%= render :partial => 'issue/show' -%>
+</div>
+++ /dev/null
-<div>
- <%= render :partial => 'issue/view', :locals => {:issue => @issue, :issue_results => @issue_results, :snapshot => @snapshot, :show_source => params[:source]!='false'} -%>
-</div>
<%= message("issue.resolution.#{issue.resolution}") if issue.resolution -%>
</td>
<td>
- <a class='open-modal rule-modal' modal-width='800' href='<%= url_for :controller => 'issue', :action => 'view', :id => issue.key %>'>
+ <a class='open-modal rule-modal' modal-width='800' href='<%= url_for :controller => 'issue', :action => 'show', :id => issue.key, :modal => true -%>'>
<%= h truncate(issue.message, :length => 100) -%></a>
</td>
<td>
current_display_id = "sources_#{rand(100)}"
%>
-<table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0" border="0">
+<table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0">
<script>
$j("#<%= current_display_id %>").on("click", "span.sym", {id: "<%= current_display_id.to_s() %>"}, highlight_usages);
return false;
}
+function hideIssueMore(elt) {
+ var issueElt = $j(elt).closest('[data-issue-key]');
+ var moreElt = issueElt.find('.issue-more');
+ moreElt.slideUp('fast');
+ return false;
+}
+
+function showIssueRule(elt) {
+ var issueElt = $j(elt).closest('[data-issue-rule]');
+ var ruleKey = issueElt.attr('data-issue-rule');
+ var moreElt = issueElt.find('.issue-more');
+ moreElt.slideUp('fast');
+ $j.get(baseUrl + "/issue/rule/" + ruleKey, function (html) {
+ moreElt.html(html);
+ moreElt.slideDown('fast');
+ });
+ return false;
+}
+
+function showIssueChangelog(elt) {
+ var issueElt = $j(elt).closest('[data-issue-key]');
+ var issueKey = issueElt.attr('data-issue-key');
+ var moreElt = issueElt.find('.issue-more');
+ moreElt.slideUp('fast');
+ $j.get(baseUrl + "/issue/changelog/" + issueKey, function (html) {
+ moreElt.html(html);
+ moreElt.slideDown('fast');
+ });
+ return false;
+}
\ No newline at end of file
color: #333333;
}
+/*
.ui-widget-content a {
color: #333333;
}
+*/
.ui-widget-header {
border: 1px solid #e78f08;
display: block;
}
-.nolayout {
- padding: 10px;
-}
-
.page-split-left {
min-width: 200px;
max-width: 200px;
/* SOURCE */
.sources2 {
width: 100%;
- border-bottom: 1px solid #DDD;
+ border: 0;
margin: 0;
background-color: #FFF;
}
font-size: 12px;
}
-span.rulename, span.rulename a {
+.rulename, .rulename a {
color: #444;
font-weight: bold;
}
-span.rulename a:hover {
+.rulename a:hover {
text-decoration: underline;
}
}
.code-issue-create-form {
- padding-bottom: 10px;
+ padding: 10px;
}
.code-global-issues {
.code-issues {
background-color: #FFF;
- padding-bottom: 10px;
+ padding: 10px;
}
.code-issue {
background-color: #FFF;
margin: 0;
font-size: 12px;
- padding: 10px 10px 0 10px;
}
.code-issue-name {
vertical-align: text-bottom;
}
-.code-issue-comment, .code-issue-msg, .code-issue-actions, .code-issue-form {
+.code-issue-comment, .code-issue-msg, .code-issue-actions, .code-issue-form, .issue-more {
background-color: #EFEFEF;
border: 1px solid #DDD;
border-top: none;
}
div.progress div.note {
- color: #777777;
+ color: #777;
font-size: 93%;
font-weight: normal;
white-space: nowrap;
}
+div.note a {
+ color: #777777;
+}
/* SEARCH AUTOCOMPLETE FIELDS */
#searchInput {
private InternalRubyIssueService internalRubyIssueService;
private IssueService issueService = mock(IssueService.class);
- private IssueCommentService commentService = mock(IssueCommentService.class);
+ private IssueChangeService commentService = mock(IssueChangeService.class);
private ActionPlanService actionPlanService = mock(ActionPlanService.class);
private ResourceDao resourceDao = mock(ResourceDao.class);
private IssueStatsFinder issueStatsFinder = mock(IssueStatsFinder.class);