From 2fca824e349f5fecbf71d940c4521644e92cb0dd Mon Sep 17 00:00:00 2001 From: Paul Martin <paul@paulsputer.com> Date: Wed, 06 Apr 2016 14:49:09 -0400 Subject: [PATCH] Merge pull request #1039 from gitblit/962-Patchset-Revision-Delete --- src/main/java/com/gitblit/wicket/pages/TicketPage.java | 258 +++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 197 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.java b/src/main/java/com/gitblit/wicket/pages/TicketPage.java index e4bb41f..b2e63a6 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.java @@ -15,6 +15,7 @@ */ package com.gitblit.wicket.pages; +import java.io.IOException; import java.text.DateFormat; import java.text.MessageFormat; import java.text.SimpleDateFormat; @@ -35,20 +36,25 @@ import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.PageParameters; +import org.apache.wicket.RequestCycle; import org.apache.wicket.RestartResponseException; import org.apache.wicket.ajax.AjaxRequestTarget; -import org.apache.wicket.behavior.IBehavior; import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.image.ContextImage; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.html.link.ExternalLink; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.markup.html.link.StatelessLink; +import org.apache.wicket.markup.html.pages.RedirectPage; import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.ListDataProvider; import org.apache.wicket.model.Model; +import org.apache.wicket.protocol.http.RequestUtils; import org.apache.wicket.protocol.http.WebRequest; +import org.apache.wicket.request.target.basic.RedirectRequestTarget; import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; @@ -57,6 +63,7 @@ import com.gitblit.Constants; import com.gitblit.Constants.AccessPermission; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Keys; import com.gitblit.git.PatchsetCommand; import com.gitblit.git.PatchsetReceivePack; @@ -82,15 +89,19 @@ import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.JGitUtils.MergeStatus; +import com.gitblit.utils.CommitCache; import com.gitblit.utils.MarkdownUtils; +import com.gitblit.utils.RefLogUtils; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; import com.gitblit.wicket.GitBlitWebSession; +import com.gitblit.wicket.TicketsUI; import com.gitblit.wicket.WicketUtils; +import com.gitblit.wicket.panels.AvatarImage; +import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation; import com.gitblit.wicket.panels.BasePanel.JavascriptTextPrompt; import com.gitblit.wicket.panels.CommentPanel; import com.gitblit.wicket.panels.DiffStatPanel; -import com.gitblit.wicket.panels.GravatarImage; import com.gitblit.wicket.panels.IconAjaxLink; import com.gitblit.wicket.panels.LinkPanel; import com.gitblit.wicket.panels.ShockWaveComponent; @@ -102,7 +113,7 @@ * @author James Moger * */ -public class TicketPage extends TicketBasePage { +public class TicketPage extends RepositoryPage { static final String NIL = "<nil>"; @@ -154,7 +165,7 @@ String href = urlFor(TicketsPage.class, params).toString(); add(new ExternalLink("ticketNumber", href, "#" + ticket.number)); Label headerStatus = new Label("headerStatus", ticket.status.toString()); - WicketUtils.setCssClass(headerStatus, getLozengeClass(ticket.status, false)); + WicketUtils.setCssClass(headerStatus, TicketsUI.getLozengeClass(ticket.status, false)); add(headerStatus); add(new Label("ticketTitle", ticket.title)); if (currentPatchset == null) { @@ -247,17 +258,24 @@ add(new Label("milestone")); } else { // link to milestone query - TicketMilestone milestone = app().tickets().getMilestone(repository, ticket.milestone); - PageParameters milestoneParameters = new PageParameters(); - milestoneParameters.put("r", repositoryName); + TicketMilestone tm = app().tickets().getMilestone(repository, ticket.milestone); + if (tm == null) { + tm = new TicketMilestone(ticket.milestone); + } + PageParameters milestoneParameters; + if (tm.isOpen()) { + milestoneParameters = WicketUtils.newOpenTicketsParameter(repositoryName); + } else { + milestoneParameters = WicketUtils.newRepositoryParameter(repositoryName); + } milestoneParameters.put(Lucene.milestone.name(), ticket.milestone); int progress = 0; int open = 0; int closed = 0; - if (milestone != null) { - progress = milestone.getProgress(); - open = milestone.getOpenTickets(); - closed = milestone.getClosedTickets(); + if (tm != null) { + progress = tm.getProgress(); + open = tm.getOpenTickets(); + closed = tm.getClosedTickets(); } Fragment milestoneProgress = new Fragment("milestone", "milestoneProgressFragment", this); @@ -277,7 +295,10 @@ if (StringUtils.isEmpty(ticket.body)) { desc = getString("gb.noDescriptionGiven"); } else { - desc = MarkdownUtils.transformGFM(app().settings(), ticket.body, ticket.repository); + String bugtraq = bugtraqProcessor().processText(getRepository(), repositoryName, ticket.body); + String html = MarkdownUtils.transformGFM(app().settings(), bugtraq, ticket.repository); + String safeHtml = app().xssFilter().relaxed(html); + desc = safeHtml; } add(new Label("ticketDescription", desc).setEscapeModelStrings(false)); @@ -301,7 +322,7 @@ if (user == null) { user = new UserModel(username); } - item.add(new GravatarImage("participant", user.getDisplayName(), + item.add(new AvatarImage("participant", user.getDisplayName(), user.emailAddress, null, 25, true)); } }; @@ -317,10 +338,10 @@ * LARGE STATUS INDICATOR WITH ICON (DISCUSSION TAB->SIDE BAR) */ Fragment ticketStatus = new Fragment("ticketStatus", "ticketStatusFragment", this); - Label ticketIcon = getStateIcon("ticketIcon", ticket); + Label ticketIcon = TicketsUI.getStateIcon("ticketIcon", ticket); ticketStatus.add(ticketIcon); ticketStatus.add(new Label("ticketStatus", ticket.status.toString())); - WicketUtils.setCssClass(ticketStatus, getLozengeClass(ticket.status, false)); + WicketUtils.setCssClass(ticketStatus, TicketsUI.getLozengeClass(ticket.status, false)); add(ticketStatus); @@ -367,10 +388,10 @@ } TicketModel update = app().tickets().updateTicket(repository, ticket.number, change); app().tickets().createNotifier().sendMailing(update); - setResponsePage(TicketsPage.class, getPageParameters()); + redirectTo(TicketsPage.class, getPageParameters()); } }; - String css = getStatusClass(item.getModel().getObject()); + String css = TicketsUI.getStatusClass(item.getModel().getObject()); WicketUtils.setCssClass(link, css); item.add(link); } @@ -381,9 +402,16 @@ * RESPONSIBLE LIST */ Set<String> userlist = new TreeSet<String>(ticket.getParticipants()); - for (RegistrantAccessPermission rp : app().repositories().getUserAccessPermissions(getRepositoryModel())) { - if (rp.permission.atLeast(AccessPermission.PUSH) && !rp.isTeam()) { - userlist.add(rp.registrant); + if (UserModel.ANONYMOUS.canPush(getRepositoryModel()) + || AuthorizationControl.AUTHENTICATED == getRepositoryModel().authorizationControl) { + // authorization is ANONYMOUS or AUTHENTICATED (i.e. all users can be set responsible) + userlist.addAll(app().users().getAllUsernames()); + } else { + // authorization is by NAMED users (users with PUSH permission can be set responsible) + for (RegistrantAccessPermission rp : app().repositories().getUserAccessPermissions(getRepositoryModel())) { + if (rp.permission.atLeast(AccessPermission.PUSH)) { + userlist.add(rp.registrant); + } } } List<TicketResponsible> responsibles = new ArrayList<TicketResponsible>(); @@ -424,7 +452,7 @@ } TicketModel update = app().tickets().updateTicket(repository, ticket.number, change); app().tickets().createNotifier().sendMailing(update); - setResponsePage(TicketsPage.class, getPageParameters()); + redirectTo(TicketsPage.class, getPageParameters()); } }; item.add(link); @@ -469,7 +497,7 @@ } TicketModel update = app().tickets().updateTicket(repository, ticket.number, change); app().tickets().createNotifier().sendMailing(update); - setResponsePage(TicketsPage.class, getPageParameters()); + redirectTo(TicketsPage.class, getPageParameters()); } }; item.add(link); @@ -501,13 +529,20 @@ * TICKET METADATA */ add(new Label("ticketType", ticket.type.toString())); + + add(new Label("priority", ticket.priority.toString())); + add(new Label("severity", ticket.severity.toString())); + if (StringUtils.isEmpty(ticket.topic)) { add(new Label("ticketTopic").setVisible(false)); } else { // process the topic using the bugtraq config to link things - String topic = bugtraqProcessor().processPlainCommitMessage(getRepository(), repositoryName, ticket.topic); - add(new Label("ticketTopic", topic).setEscapeModelStrings(false)); + String topic = bugtraqProcessor().processText(getRepository(), repositoryName, ticket.topic); + String safeTopic = app().xssFilter().relaxed(topic); + add(new Label("ticketTopic", safeTopic).setEscapeModelStrings(false)); } + + /* @@ -541,7 +576,7 @@ change.vote(user.username); } app().tickets().updateTicket(repository, ticket.number, change); - setResponsePage(TicketsPage.class, getPageParameters()); + redirectTo(TicketsPage.class, getPageParameters()); } }; add(link); @@ -581,7 +616,7 @@ change.watch(user.username); } app().tickets().updateTicket(repository, ticket.number, change); - setResponsePage(TicketsPage.class, getPageParameters()); + redirectTo(TicketsPage.class, getPageParameters()); } }; add(link); @@ -665,17 +700,8 @@ */ Fragment frag = new Fragment("entry", "statusFragment", this); Label status = new Label("statusChange", entry.getStatus().toString()); - String css = getLozengeClass(entry.getStatus(), false); + String css = TicketsUI.getLozengeClass(entry.getStatus(), false); WicketUtils.setCssClass(status, css); - for (IBehavior b : status.getBehaviors()) { - if (b instanceof SimpleAttributeModifier) { - SimpleAttributeModifier sam = (SimpleAttributeModifier) b; - if ("class".equals(sam.getAttribute())) { - status.add(new SimpleAttributeModifier("class", "status-change " + sam.getValue())); - break; - } - } - } frag.add(status); addUserAttributions(frag, entry, avatarWidth); addDateAttributions(frag, entry); @@ -684,7 +710,9 @@ /* * COMMENT */ - String comment = MarkdownUtils.transformGFM(app().settings(), entry.comment.text, repositoryName); + String bugtraq = bugtraqProcessor().processText(getRepository(), repositoryName, entry.comment.text); + String comment = MarkdownUtils.transformGFM(app().settings(), bugtraq, repositoryName); + String safeComment = app().xssFilter().relaxed(comment); Fragment frag = new Fragment("entry", "commentFragment", this); Label commentIcon = new Label("commentIcon"); if (entry.comment.src == CommentSource.Email) { @@ -693,7 +721,7 @@ WicketUtils.setCssClass(commentIcon, "iconic-comment-alt2-stroke"); } frag.add(commentIcon); - frag.add(new Label("comment", comment).setEscapeModelStrings(false)); + frag.add(new Label("comment", safeComment).setEscapeModelStrings(false)); addUserAttributions(frag, entry, avatarWidth); addDateAttributions(frag, entry); item.add(frag); @@ -718,7 +746,7 @@ } else { // permit user to comment Fragment newComment = new Fragment("newComment", "newCommentFragment", this); - GravatarImage img = new GravatarImage("newCommentAvatar", user.username, user.emailAddress, + AvatarImage img = new AvatarImage("newCommentAvatar", user.username, user.emailAddress, "gravatar-round", avatarWidth, true); newComment.add(img); CommentPanel commentPanel = new CommentPanel("commentPanel", user, ticket, null, TicketsPage.class); @@ -734,7 +762,7 @@ if (currentPatchset == null) { // no patchset available RepositoryUrl repoUrl = getRepositoryUrl(user, repository); - boolean canPropose = repoUrl != null && repoUrl.permission.atLeast(AccessPermission.CLONE) && !UserModel.ANONYMOUS.equals(user); + boolean canPropose = repoUrl != null && repoUrl.hasPermission() && repoUrl.permission.atLeast(AccessPermission.CLONE) && !UserModel.ANONYMOUS.equals(user); if (ticket.isOpen() && app().tickets().isAcceptingNewPatchsets(repository) && canPropose) { // ticket & repo will accept a proposal patchset // show the instructions for proposing a patchset @@ -774,12 +802,19 @@ Fragment patchsetFrag = new Fragment("patchset", "patchsetFragment", this); patchsetFrag.add(new Label("commitsInPatchset", MessageFormat.format(getString("gb.commitsInPatchsetN"), currentPatchset.number))); - // current revision - MarkupContainer panel = createPatchsetPanel("panel", repository, user); - patchsetFrag.add(panel); - addUserAttributions(patchsetFrag, currentRevision, avatarWidth); - addUserAttributions(panel, currentRevision, 0); - addDateAttributions(panel, currentRevision); + patchsetFrag.add(createMergePanel(user, repository)); + + if (ticket.isOpen()) { + // current revision + MarkupContainer panel = createPatchsetPanel("panel", repository, user); + patchsetFrag.add(panel); + addUserAttributions(patchsetFrag, currentRevision, avatarWidth); + addUserAttributions(panel, currentRevision, 0); + addDateAttributions(panel, currentRevision); + } else { + // current revision + patchsetFrag.add(new Label("panel").setVisible(false)); + } // commits List<RevCommit> commits = JGitUtils.getRevLog(getRepository(), currentPatchset.base, currentPatchset.tip); @@ -791,14 +826,14 @@ public void populateItem(final Item<RevCommit> item) { RevCommit commit = item.getModelObject(); PersonIdent author = commit.getAuthorIdent(); - item.add(new GravatarImage("authorAvatar", author.getName(), author.getEmailAddress(), null, 16, false)); + item.add(new AvatarImage("authorAvatar", author.getName(), author.getEmailAddress(), null, 16, false)); item.add(new Label("author", commit.getAuthorIdent().getName())); item.add(new LinkPanel("commitId", null, getShortObjectId(commit.getName()), CommitPage.class, WicketUtils.newObjectParameter(repositoryName, commit.getName()), true)); item.add(new LinkPanel("diff", "link", getString("gb.diff"), CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, commit.getName()), true)); item.add(new Label("title", StringUtils.trimString(commit.getShortMessage(), Constants.LEN_SHORTLOG_REFS))); - item.add(WicketUtils.createDateLabel("commitDate", JGitUtils.getCommitDate(commit), GitBlitWebSession + item.add(WicketUtils.createDateLabel("commitDate", JGitUtils.getAuthorDate(commit), GitBlitWebSession .get().getTimezone(), getTimeUtils(), false)); item.add(new DiffStatPanel("commitDiffStat", 0, 0, true)); } @@ -828,6 +863,9 @@ if (event.hasPatchset()) { // patchset Patchset patchset = event.patchset; + //In the case of using a cached change list + item.setVisible(!patchset.isDeleted()); + String what; if (event.isStatusChange() && (Status.New == event.getStatus())) { what = getString("gb.proposedThisChange"); @@ -855,6 +893,14 @@ } item.add(typeLabel); + Link<Void> deleteLink = createDeletePatchsetLink(repository, patchset); + + if (user.canDeleteRef(repository)) { + item.add(deleteLink.setVisible(patchset.canDelete)); + } else { + item.add(deleteLink.setVisible(false)); + } + // show commit diffstat item.add(new DiffStatPanel("patchsetDiffStat", patchset.insertions, patchset.deletions, patchset.rev > 1)); } else if (event.hasComment()) { @@ -862,6 +908,7 @@ item.add(new Label("what", getString("gb.commented"))); item.add(new Label("patchsetRevision").setVisible(false)); item.add(new Label("patchsetType").setVisible(false)); + item.add(new Label("deleteRevision").setVisible(false)); item.add(new Label("patchsetDiffStat").setVisible(false)); } else if (event.hasReview()) { // review @@ -881,11 +928,13 @@ .setEscapeModelStrings(false)); item.add(new Label("patchsetRevision").setVisible(false)); item.add(new Label("patchsetType").setVisible(false)); + item.add(new Label("deleteRevision").setVisible(false)); item.add(new Label("patchsetDiffStat").setVisible(false)); } else { // field change item.add(new Label("patchsetRevision").setVisible(false)); item.add(new Label("patchsetType").setVisible(false)); + item.add(new Label("deleteRevision").setVisible(false)); item.add(new Label("patchsetDiffStat").setVisible(false)); String what = ""; @@ -929,7 +978,7 @@ case status: // special handling for status Status status = event.getStatus(); - String css = getLozengeClass(status, true); + String css = TicketsUI.getLozengeClass(status, true); value = String.format("<span class=\"%1$s\">%2$s</span>", css, status.toString()); break; default: @@ -947,7 +996,8 @@ sb.append("</td></tr>"); } sb.append("</tbody></table>"); - item.add(new Label("fields", sb.toString()).setEscapeModelStrings(false)); + String safeHtml = app().xssFilter().relaxed(sb.toString()); + item.add(new Label("fields", safeHtml).setEscapeModelStrings(false)); } else { item.add(new Label("fields").setVisible(false)); } @@ -961,12 +1011,12 @@ UserModel commenter = app().users().getUserModel(entry.author); if (commenter == null) { // unknown user - container.add(new GravatarImage("changeAvatar", entry.author, + container.add(new AvatarImage("changeAvatar", entry.author, entry.author, null, avatarSize, false).setVisible(avatarSize > 0)); container.add(new Label("changeAuthor", entry.author.toLowerCase())); } else { // known user - container.add(new GravatarImage("changeAvatar", commenter.getDisplayName(), + container.add(new AvatarImage("changeAvatar", commenter.getDisplayName(), commenter.emailAddress, avatarSize > 24 ? "gravatar-round" : null, avatarSize, true).setVisible(avatarSize > 0)); container.add(new LinkPanel("changeAuthor", null, commenter.getDisplayName(), @@ -1141,7 +1191,6 @@ WicketUtils.setChangeTypeCssClass(changeType, entry.changeType); setChangeTypeTooltip(changeType, entry.changeType); item.add(changeType); - item.add(new DiffStatPanel("diffStat", entry.insertions, entry.deletions, true)); boolean hasSubmodule = false; String submodulePath = null; @@ -1184,6 +1233,7 @@ item.add(new LinkPanel("pathName", "list", displayPath, BlobDiffPage.class, WicketUtils.newPathParameter(repositoryName, currentPatchset.tip, path), true)); } + item.add(new DiffStatPanel("diffStat", entry.insertions, entry.deletions, true)); } // quick links @@ -1213,7 +1263,6 @@ addPtCheckoutInstructions(user, repository, panel); addGitCheckoutInstructions(user, repository, panel); - panel.add(createMergePanel(user, repository)); return panel; } @@ -1278,7 +1327,7 @@ } TicketModel updatedTicket = app().tickets().updateTicket(getRepositoryModel(), ticket.number, change); app().tickets().createNotifier().sendMailing(updatedTicket); - setResponsePage(TicketsPage.class, getPageParameters()); + redirectTo(TicketsPage.class, getPageParameters()); } protected <X extends MarkupContainer> X setNewTarget(X x) { @@ -1389,8 +1438,8 @@ GitBlitWebSession.get().cacheErrorMessage(msg); logger.error(msg); } - - setResponsePage(TicketsPage.class, getPageParameters()); + + redirectTo(TicketsPage.class, getPageParameters()); } }; mergePanel.add(mergeButton); @@ -1405,6 +1454,12 @@ // patchset already merged Fragment mergePanel = new Fragment("mergePanel", "alreadyMergedFragment", this); mergePanel.add(new Label("mergeTitle", MessageFormat.format(getString("gb.patchsetAlreadyMerged"), ticket.mergeTo))); + return mergePanel; + } else if (MergeStatus.MISSING_INTEGRATION_BRANCH == mergeStatus) { + // target/integration branch is missing + Fragment mergePanel = new Fragment("mergePanel", "notMergeableFragment", this); + mergePanel.add(new Label("mergeTitle", MessageFormat.format(getString("gb.patchsetNotMergeable"), ticket.mergeTo))); + mergePanel.add(new Label("mergeMore", MessageFormat.format(getString("gb.missingIntegrationBranchMore"), ticket.mergeTo))); return mergePanel; } else { // patchset can not be cleanly merged @@ -1484,7 +1539,7 @@ */ protected RepositoryUrl getRepositoryUrl(UserModel user, RepositoryModel repository) { HttpServletRequest req = ((WebRequest) getRequest()).getHttpServletRequest(); - List<RepositoryUrl> urls = app().gitblit().getRepositoryUrls(req, user, repository); + List<RepositoryUrl> urls = app().services().getRepositoryUrls(req, user, repository); if (ArrayUtils.isEmpty(urls)) { return null; } @@ -1519,14 +1574,14 @@ switch (type) { case Rebase: case Rebase_Squash: - typeCss = getLozengeClass(Status.Declined, false); + typeCss = TicketsUI.getLozengeClass(Status.Declined, false); break; case Squash: case Amend: - typeCss = getLozengeClass(Status.On_Hold, false); + typeCss = TicketsUI.getLozengeClass(Status.On_Hold, false); break; case Proposal: - typeCss = getLozengeClass(Status.New, false); + typeCss = TicketsUI.getLozengeClass(Status.New, false); break; case FastForward: default: @@ -1569,4 +1624,85 @@ return copyFragment; } } + + private Link<Void> createDeletePatchsetLink(final RepositoryModel repositoryModel, final Patchset patchset) + { + Link<Void> deleteLink = new Link<Void>("deleteRevision") { + private static final long serialVersionUID = 1L; + + @Override + public void onClick() { + Repository r = app().repositories().getRepository(repositoryModel.name); + UserModel user = GitBlitWebSession.get().getUser(); + + if (r == null) { + if (app().repositories().isCollectingGarbage(repositoryModel.name)) { + error(MessageFormat.format(getString("gb.busyCollectingGarbage"), repositoryModel.name)); + } else { + error(MessageFormat.format("Failed to find repository {0}", repositoryModel.name)); + } + return; + } + + //Construct the ref name based on the patchset + String ticketShard = String.format("%02d", ticket.number); + ticketShard = ticketShard.substring(ticketShard.length() - 2); + final String refName = String.format("%s%s/%d/%d", Constants.R_TICKETS_PATCHSETS, ticketShard, ticket.number, patchset.number); + + Ref ref = null; + boolean success = true; + + try { + ref = r.getRef(refName); + + if (ref != null) { + success = JGitUtils.deleteBranchRef(r, ref.getName()); + } else { + success = false; + } + + if (success) { + // clear commit cache + CommitCache.instance().clear(repositoryModel.name, refName); + + // optionally update reflog + if (RefLogUtils.hasRefLogBranch(r)) { + RefLogUtils.deleteRef(user, r, ref); + } + + TicketModel updatedTicket = app().tickets().deletePatchset(ticket, patchset, user.username); + + if (updatedTicket == null) { + success = false; + } + } + } catch (IOException e) { + logger().error("failed to determine ticket from ref", e); + success = false; + } finally { + r.close(); + } + + if (success) { + getSession().info(MessageFormat.format(getString("gb.deletePatchsetSuccess"), patchset.number)); + logger().info(MessageFormat.format("{0} deleted patchset {1} from ticket {2}", + user.username, patchset.number, ticket.number)); + } else { + getSession().error(MessageFormat.format(getString("gb.deletePatchsetFailure"),patchset.number)); + } + + //Force reload of the page to rebuild ticket change cache + String relativeUrl = urlFor(TicketsPage.class, getPageParameters()).toString(); + String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl); + setResponsePage(new RedirectPage(absoluteUrl)); + } + }; + + WicketUtils.setHtmlTooltip(deleteLink, MessageFormat.format(getString("gb.deletePatchset"), patchset.number)); + + deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(getString("gb.deletePatchset"), patchset.number))); + + return deleteLink; + } + } -- Gitblit v1.9.1