List of usage examples for org.eclipse.jgit.lib PersonIdent getEmailAddress
public String getEmailAddress()
From source file:at.ac.tuwien.inso.subcat.miner.GitMiner.java
License:Open Source License
private Identity resolveIdentity(PersonIdent author) throws SQLException { String mail = author.getEmailAddress(); String name = author.getName(); assert (mail != null || name != null); String mapKey = (mail != null) ? mail : name; name = (name != null) ? name : mail; Identity identity = identities.get(mapKey); if (identity == null) { User user = model.addUser(project, name); identity = model.addIdentity(null, Model.CONTEXT_SRC, mail, name, user); identities.put(mapKey, identity); }/* www.j a v a 2 s . c o m*/ return identity; }
From source file:com.binarybirchtree.contributionart.RepositoryTest.java
License:Open Source License
@Test public void validate_commits() throws IOException, Matrix.FileFormatException, Repository.GitException, GitAPIException { final int factor = 20; final String name = "name"; final String email = "email"; /////from w w w .j a va 2 s . c o m /// Encapsulates commit-validation logic. /// class CommitValidator { final ZonedDateTime timestamp; /// /// @param[in] commit Commit to validate. /// @param[in] timestamp Expected timestamp. /// @param[in] message Expected message. /// CommitValidator(RevCommit commit, ZonedDateTime timestamp, String message) { this.timestamp = timestamp; Assert.assertEquals(timestamp.toInstant(), Instant.ofEpochSecond(commit.getCommitTime())); Assert.assertEquals(message, commit.getFullMessage()); new IdentityValidator(commit.getAuthorIdent()); new IdentityValidator(commit.getCommitterIdent()); } /// /// Contains shared validation logic used for both Author and Committer identities. /// class IdentityValidator { private PersonIdent identity; /// /// @param[in] identity Identity to validate. /// public IdentityValidator(PersonIdent identity) { this.identity = identity; validate_name(); validate_email(); validate_timestamp(); } private void validate_name() { Assert.assertEquals(name, identity.getName()); } private void validate_email() { Assert.assertEquals(email, identity.getEmailAddress()); } private void validate_timestamp() { Assert.assertEquals(timestamp.toInstant(), identity.getWhen().toInstant()); } } } Path repo = folder.newFolder().toPath(); Matrix matrix = new Matrix(file); ZonedDateTime today = ZonedDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.DAYS); // Generate commits in a temporary repository. try (Repository repository = new Repository(repo, name, email)) { repository.illustrate(matrix, factor); } // Verify that the state of the repository is as expected. try (Git git = Git.open(repo.toFile())) { // Start from the earliest date for which commits were generated. ZonedDateTime current = today.with(WeekFields.SUNDAY_START.dayOfWeek(), DayOfWeek.values().length) .minusDays(Matrix.AREA); // Prepare to iterate through the definition matrix alongside the commit log, // as the values in the definition matrix affect how many commits should have been generated. Iterator<Matrix.Value> values = matrix.iterator(); Matrix.Value value; int cell_iterations = 0; int commit_count = 0; // Retrieve the list of commits, sorted from earliest to latest. List<RevCommit> commits = Lists.reverse(Lists.newArrayList(git.log().call())); Assert.assertFalse(commits.isEmpty()); // Validate the README commit. String readme = "README.md"; new CommitValidator(Iterables.getLast(commits), today, String.format("Added %s.", readme)); commits.remove(commits.size() - 1); Assert.assertEquals(Repository.README, new String(Files.readAllBytes(repo.resolve(readme)))); // Iterate through the commit log, starting from the earliest commit. for (RevCommit commit : commits) { if (cell_iterations == 0) { Assert.assertTrue(values.hasNext()); value = values.next(); cell_iterations = value.weight() * factor; current = current.plusDays(1); } new CommitValidator(commit, current, ""); ++commit_count; --cell_iterations; } // Determine the expected commit count and compare it with the actual commit count. int expected_commit_count = StreamSupport.stream(matrix.spliterator(), false).map(Matrix.Value::weight) .reduce(0, (accumulator, element) -> accumulator + element * factor); while (values.hasNext()) { expected_commit_count -= values.next().weight() * factor; } Assert.assertEquals(expected_commit_count, commit_count); } }
From source file:com.centurylink.mdw.dataaccess.file.VersionControlGit.java
License:Apache License
/** * Does not fetch.//from w w w .ja v a 2s . c om */ public CommitInfo getCommitInfo(String path) throws Exception { Iterator<RevCommit> revCommits = git.log().addPath(path).setMaxCount(1).call().iterator(); if (revCommits.hasNext()) { RevCommit revCommit = revCommits.next(); CommitInfo commitInfo = new CommitInfo(revCommit.getId().name()); PersonIdent committerIdent = revCommit.getCommitterIdent(); commitInfo.setCommitter(committerIdent.getName()); commitInfo.setEmail(committerIdent.getEmailAddress()); if ((commitInfo.getCommitter() == null || commitInfo.getCommitter().isEmpty()) && commitInfo.getEmail() != null) commitInfo.setCommitter(commitInfo.getEmail()); commitInfo.setDate(committerIdent.getWhen()); commitInfo.setMessage(revCommit.getShortMessage()); return commitInfo; } return null; }
From source file:com.gitblit.git.GitblitReceivePack.java
License:Apache License
/** * Instrumentation point where the incoming push event has been parsed, * validated, objects created BUT refs have not been updated. You might * use this to enforce a branch-write permissions model. *//*from www . j a v a 2 s .c om*/ @Override public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) { if (commands.size() == 0) { // no receive commands to process // this can happen if receive pack subclasses intercept and filter // the commands LOGGER.debug("skipping pre-receive processing, no refs created, updated, or removed"); return; } if (repository.isMirror) { // repository is a mirror for (ReceiveCommand cmd : commands) { sendRejection(cmd, "Gitblit does not allow pushes to \"{0}\" because it is a mirror!", repository.name); } return; } if (repository.isFrozen) { // repository is frozen/readonly for (ReceiveCommand cmd : commands) { sendRejection(cmd, "Gitblit does not allow pushes to \"{0}\" because it is frozen!", repository.name); } return; } if (!repository.isBare) { // repository has a working copy for (ReceiveCommand cmd : commands) { sendRejection(cmd, "Gitblit does not allow pushes to \"{0}\" because it has a working copy!", repository.name); } return; } if (!canPush(commands)) { // user does not have push permissions for (ReceiveCommand cmd : commands) { sendRejection(cmd, "User \"{0}\" does not have push permissions for \"{1}\"!", user.username, repository.name); } return; } if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH) && repository.verifyCommitter) { // enforce committer verification if (StringUtils.isEmpty(user.emailAddress)) { // reject the push because the pushing account does not have an email address for (ReceiveCommand cmd : commands) { sendRejection(cmd, "Sorry, the account \"{0}\" does not have an email address set for committer verification!", user.username); } return; } // Optionally enforce that the committer of first parent chain // match the account being used to push the commits. // // This requires all merge commits are executed with the "--no-ff" // option to force a merge commit even if fast-forward is possible. // This ensures that the chain first parents has the commit // identity of the merging user. boolean allRejected = false; for (ReceiveCommand cmd : commands) { String firstParent = null; try { List<RevCommit> commits = JGitUtils.getRevLog(rp.getRepository(), cmd.getOldId().name(), cmd.getNewId().name()); for (RevCommit commit : commits) { if (firstParent != null) { if (!commit.getName().equals(firstParent)) { // ignore: commit is right-descendant of a merge continue; } } // update expected next commit id if (commit.getParentCount() == 0) { firstParent = null; } else { firstParent = commit.getParents()[0].getId().getName(); } PersonIdent committer = commit.getCommitterIdent(); if (!user.is(committer.getName(), committer.getEmailAddress())) { // verification failed String reason = MessageFormat.format( "{0} by {1} <{2}> was not committed by {3} ({4}) <{5}>", commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?" : committer.getEmailAddress(), user.getDisplayName(), user.username, user.emailAddress); LOGGER.warn(reason); cmd.setResult(Result.REJECTED_OTHER_REASON, reason); allRejected &= true; break; } else { allRejected = false; } } } catch (Exception e) { LOGGER.error("Failed to verify commits were made by pushing user", e); } } if (allRejected) { // all ref updates rejected, abort return; } } for (ReceiveCommand cmd : commands) { String ref = cmd.getRefName(); if (ref.startsWith(Constants.R_HEADS)) { switch (cmd.getType()) { case UPDATE_NONFASTFORWARD: case DELETE: // reset branch commit cache on REWIND and DELETE CommitCache.instance().clear(repository.name, ref); break; default: break; } } else if (ref.equals(BranchTicketService.BRANCH)) { // ensure pushing user is an administrator OR an owner // i.e. prevent ticket tampering boolean permitted = user.canAdmin() || repository.isOwner(user.username); if (!permitted) { sendRejection(cmd, "{0} is not permitted to push to {1}", user.username, ref); } } else if (ref.startsWith(Constants.R_FOR)) { // prevent accidental push to refs/for sendRejection(cmd, "{0} is not configured to receive patchsets", repository.name); } } // call pre-receive plugins for (ReceiveHook hook : gitblit.getExtensions(ReceiveHook.class)) { try { hook.onPreReceive(this, commands); } catch (Exception e) { LOGGER.error("Failed to execute extension", e); } } Set<String> scripts = new LinkedHashSet<String>(); scripts.addAll(gitblit.getPreReceiveScriptsInherited(repository)); if (!ArrayUtils.isEmpty(repository.preReceiveScripts)) { scripts.addAll(repository.preReceiveScripts); } runGroovy(commands, scripts); for (ReceiveCommand cmd : commands) { if (!Result.NOT_ATTEMPTED.equals(cmd.getResult())) { LOGGER.warn(MessageFormat.format("{0} {1} because \"{2}\"", cmd.getNewId().getName(), cmd.getResult(), cmd.getMessage())); } } }
From source file:com.gitblit.git.ReceiveHook.java
License:Apache License
/** * Instrumentation point where the incoming push event has been parsed, * validated, objects created BUT refs have not been updated. You might * use this to enforce a branch-write permissions model. *///from w w w . ja v a2 s . c o m @Override public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) { if (repository.isFrozen) { // repository is frozen/readonly String reason = MessageFormat.format("Gitblit does not allow pushes to \"{0}\" because it is frozen!", repository.name); logger.warn(reason); for (ReceiveCommand cmd : commands) { cmd.setResult(Result.REJECTED_OTHER_REASON, reason); } return; } if (!repository.isBare) { // repository has a working copy String reason = MessageFormat.format( "Gitblit does not allow pushes to \"{0}\" because it has a working copy!", repository.name); logger.warn(reason); for (ReceiveCommand cmd : commands) { cmd.setResult(Result.REJECTED_OTHER_REASON, reason); } return; } if (!user.canPush(repository)) { // user does not have push permissions String reason = MessageFormat.format("User \"{0}\" does not have push permissions for \"{1}\"!", user.username, repository.name); logger.warn(reason); for (ReceiveCommand cmd : commands) { cmd.setResult(Result.REJECTED_OTHER_REASON, reason); } return; } if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH) && repository.verifyCommitter) { // enforce committer verification if (StringUtils.isEmpty(user.emailAddress)) { // emit warning if user does not have an email address logger.warn(MessageFormat.format( "Consider setting an email address for {0} ({1}) to improve committer verification.", user.getDisplayName(), user.username)); } // Optionally enforce that the committer of the left parent chain // match the account being used to push the commits. // // This requires all merge commits are executed with the "--no-ff" // option to force a merge commit even if fast-forward is possible. // This ensures that the chain of left parents has the commit // identity of the merging user. boolean allRejected = false; for (ReceiveCommand cmd : commands) { String linearParent = null; try { List<RevCommit> commits = JGitUtils.getRevLog(rp.getRepository(), cmd.getOldId().name(), cmd.getNewId().name()); for (RevCommit commit : commits) { if (linearParent != null) { if (!commit.getName().equals(linearParent)) { // ignore: commit is right-descendant of a merge continue; } } // update expected next commit id if (commit.getParentCount() == 0) { linearParent = null; } else { linearParent = commit.getParents()[0].getId().getName(); } PersonIdent committer = commit.getCommitterIdent(); if (!user.is(committer.getName(), committer.getEmailAddress())) { String reason; if (StringUtils.isEmpty(user.emailAddress)) { // account does not have an email address reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4})", commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?" : committer.getEmailAddress(), user.getDisplayName(), user.username); } else { // account has an email address reason = MessageFormat.format( "{0} by {1} <{2}> was not committed by {3} ({4}) <{5}>", commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?" : committer.getEmailAddress(), user.getDisplayName(), user.username, user.emailAddress); } logger.warn(reason); cmd.setResult(Result.REJECTED_OTHER_REASON, reason); allRejected &= true; break; } else { allRejected = false; } } } catch (Exception e) { logger.error("Failed to verify commits were made by pushing user", e); } } if (allRejected) { // all ref updates rejected, abort return; } } // reset branch commit cache on REWIND and DELETE for (ReceiveCommand cmd : commands) { String ref = cmd.getRefName(); if (ref.startsWith(Constants.R_HEADS)) { switch (cmd.getType()) { case UPDATE_NONFASTFORWARD: case DELETE: CommitCache.instance().clear(repository.name, ref); break; default: break; } } } Set<String> scripts = new LinkedHashSet<String>(); scripts.addAll(GitBlit.self().getPreReceiveScriptsInherited(repository)); if (!ArrayUtils.isEmpty(repository.preReceiveScripts)) { scripts.addAll(repository.preReceiveScripts); } runGroovy(repository, user, commands, rp, scripts); for (ReceiveCommand cmd : commands) { if (!Result.NOT_ATTEMPTED.equals(cmd.getResult())) { logger.warn(MessageFormat.format("{0} {1} because \"{2}\"", cmd.getNewId().getName(), cmd.getResult(), cmd.getMessage())); } } }
From source file:com.gitblit.plugin.flowdock.FlowDockReceiveHook.java
License:Apache License
@Override public void onPostReceive(GitblitReceivePack receivePack, Collection<ReceiveCommand> commands) { if (!shallPost(receivePack, commands)) { return;// w ww . j av a 2s . c o m } IRuntimeManager runtimeManager = GitblitContext.getManager(IRuntimeManager.class); try { for (ReceiveCommand cmd : commands) { if (cmd.getRefName().startsWith(Constants.R_TAGS)) { boolean shallPostTag = runtimeManager.getSettings().getBoolean(Plugin.SETTING_POST_TAGS, true); if (!shallPostTag) { continue; } } else if (cmd.getRefName().startsWith(Constants.R_HEADS)) { boolean shallPostBranch = runtimeManager.getSettings().getBoolean(Plugin.SETTING_POST_BRANCHES, true); if (!shallPostBranch) { continue; } } else { // ignore other refs continue; } RepositoryModel repo = receivePack.getRepositoryModel(); String repoUrl = getUrl(repo.name, null, null); String diffUrl = getUrl(repo.name, cmd.getOldId().getName(), cmd.getNewId().getName()); GitPayload payload = new GitPayload().pusher(receivePack.getUserModel()).repository(repo.name) .repoUrl(repoUrl).tags(getTags(repo)).ref(cmd.getRefName()) .refName(Repository.shortenRefName(cmd.getRefName())).diffUrl(diffUrl) .before(cmd.getOldId().getName()).after(cmd.getNewId().getName()); List<RevCommit> commits = getCommits(receivePack, cmd.getOldId().name(), cmd.getNewId().name()); for (RevCommit commit : commits) { Commit c = new Commit(); c.id = commit.getName(); c.url = getUrl(repo.name, null, commit.getName()); c.message = commit.getFullMessage().trim(); PersonIdent author = commit.getAuthorIdent(); c.author = new Ident(author.getName(), author.getEmailAddress()); c.timestamp = author.getWhen(); if (c.timestamp == null) { c.timestamp = commit.getCommitterIdent().getWhen(); } List<PathChangeModel> paths = JGitUtils.getFilesInCommit(receivePack.getRepository(), commit); c.added = filter(paths, ChangeType.ADD); c.modified = filter(paths, ChangeType.MODIFY); c.removed = filter(paths, ChangeType.DELETE); payload.add(c); } flowdock.setFlow(repo, payload); flowdock.sendAsync(payload); } } catch (Exception e) { log.error("Failed to notify FlowDock!", e); } }
From source file:com.gitblit.utils.JGitUtils.java
License:Apache License
/** * Returns the displayable name of the person in the form "Real Name <email * address>". If the email address is empty, just "Real Name" is returned. * * @param person// www. j a va 2 s.c o m * @return "Real Name <email address>" or "Real Name" */ public static String getDisplayName(PersonIdent person) { if (StringUtils.isEmpty(person.getEmailAddress())) { return person.getName(); } final StringBuilder r = new StringBuilder(); r.append(person.getName()); r.append(" <"); r.append(person.getEmailAddress()); r.append('>'); return r.toString().trim(); }
From source file:com.gitblit.utils.RefLogUtils.java
License:Apache License
private static UserModel newUserModelFrom(PersonIdent ident) { String name = ident.getName(); String username;//from www . j av a 2 s . c o m String displayname; if (name.indexOf('/') > -1) { int slash = name.indexOf('/'); displayname = name.substring(0, slash); username = name.substring(slash + 1); } else { displayname = name; username = ident.getEmailAddress(); } UserModel user = new UserModel(username); user.displayName = displayname; user.emailAddress = ident.getEmailAddress(); return user; }
From source file:com.gitblit.wicket.pages.RepositoryPage.java
License:Apache License
protected Component createPersonPanel(String wicketId, PersonIdent identity, Constants.SearchType searchType) { String name = identity == null ? "" : identity.getName(); String address = identity == null ? "" : identity.getEmailAddress(); name = StringUtils.removeNewlines(name); address = StringUtils.removeNewlines(address); boolean showEmail = app().settings().getBoolean(Keys.web.showEmailAddresses, false); if (!showEmail || StringUtils.isEmpty(name) || StringUtils.isEmpty(address)) { String value = name;// ww w . j a v a 2 s .c om if (StringUtils.isEmpty(value)) { if (showEmail) { value = address; } else { value = getString("gb.missingUsername"); } } Fragment partial = new Fragment(wicketId, "partialPersonIdent", RepositoryPage.this); LinkPanel link = new LinkPanel("personName", "list", value, GitSearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType)); setPersonSearchTooltip(link, value, searchType); partial.add(link); return partial; } else { Fragment fullPerson = new Fragment(wicketId, "fullPersonIdent", RepositoryPage.this); LinkPanel nameLink = new LinkPanel("personName", "list", name, GitSearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, name, searchType)); setPersonSearchTooltip(nameLink, name, searchType); fullPerson.add(nameLink); LinkPanel addressLink = new LinkPanel("personAddress", "hidden-phone list", "<" + address + ">", GitSearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, address, searchType)); setPersonSearchTooltip(addressLink, address, searchType); fullPerson.add(addressLink); return fullPerson; } }
From source file:com.gitblit.wicket.pages.TicketPage.java
License:Apache License
public TicketPage(PageParameters params) { super(params); final UserModel user = GitBlitWebSession.get().getUser() == null ? UserModel.ANONYMOUS : GitBlitWebSession.get().getUser(); final RepositoryModel repository = getRepositoryModel(); final String id = WicketUtils.getObject(params); long ticketId = Long.parseLong(id); ticket = app().tickets().getTicket(repository, ticketId); if (ticket == null) { // ticket not found throw new RestartResponseException(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName)); }/*from www . j av a2 s . c om*/ final List<Change> revisions = new ArrayList<Change>(); List<Change> comments = new ArrayList<Change>(); List<Change> statusChanges = new ArrayList<Change>(); List<Change> discussion = new ArrayList<Change>(); for (Change change : ticket.changes) { if (change.hasComment() || (change.isStatusChange() && (change.getStatus() != Status.New))) { discussion.add(change); } if (change.hasComment()) { comments.add(change); } if (change.hasPatchset()) { revisions.add(change); } if (change.isStatusChange() && !change.hasPatchset()) { statusChanges.add(change); } } final Change currentRevision = revisions.isEmpty() ? null : revisions.get(revisions.size() - 1); final Patchset currentPatchset = ticket.getCurrentPatchset(); /* * TICKET HEADER */ 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, TicketsUI.getLozengeClass(ticket.status, false)); add(headerStatus); add(new Label("ticketTitle", ticket.title)); if (currentPatchset == null) { add(new Label("diffstat").setVisible(false)); } else { // calculate the current diffstat of the patchset add(new DiffStatPanel("diffstat", ticket.insertions, ticket.deletions)); } /* * TAB TITLES */ add(new Label("commentCount", "" + comments.size()).setVisible(!comments.isEmpty())); add(new Label("commitCount", "" + (currentPatchset == null ? 0 : currentPatchset.commits)) .setVisible(currentPatchset != null)); /* * TICKET AUTHOR and DATE (DISCUSSION TAB) */ UserModel createdBy = app().users().getUserModel(ticket.createdBy); if (createdBy == null) { add(new Label("whoCreated", ticket.createdBy)); } else { add(new LinkPanel("whoCreated", null, createdBy.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(createdBy.username))); } if (ticket.isProposal()) { // clearly indicate this is a change ticket add(new Label("creationMessage", getString("gb.proposedThisChange"))); } else { // standard ticket add(new Label("creationMessage", getString("gb.createdThisTicket"))); } String dateFormat = app().settings().getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy"); String timestampFormat = app().settings().getString(Keys.web.datetimestampLongFormat, "EEEE, MMMM d, yyyy"); final TimeZone timezone = getTimeZone(); final DateFormat df = new SimpleDateFormat(dateFormat); df.setTimeZone(timezone); final DateFormat tsf = new SimpleDateFormat(timestampFormat); tsf.setTimeZone(timezone); final Calendar cal = Calendar.getInstance(timezone); String fuzzydate; TimeUtils tu = getTimeUtils(); Date createdDate = ticket.created; if (TimeUtils.isToday(createdDate, timezone)) { fuzzydate = tu.today(); } else if (TimeUtils.isYesterday(createdDate, timezone)) { fuzzydate = tu.yesterday(); } else { // calculate a fuzzy time ago date cal.setTime(createdDate); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); createdDate = cal.getTime(); fuzzydate = getTimeUtils().timeAgo(createdDate); } Label when = new Label("whenCreated", fuzzydate + ", " + df.format(createdDate)); WicketUtils.setHtmlTooltip(when, tsf.format(ticket.created)); add(when); String exportHref = urlFor(ExportTicketPage.class, params).toString(); add(new ExternalLink("exportJson", exportHref, "json")); /* * RESPONSIBLE (DISCUSSION TAB) */ if (StringUtils.isEmpty(ticket.responsible)) { add(new Label("responsible")); } else { UserModel responsible = app().users().getUserModel(ticket.responsible); if (responsible == null) { add(new Label("responsible", ticket.responsible)); } else { add(new LinkPanel("responsible", null, responsible.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(responsible.username))); } } /* * MILESTONE PROGRESS (DISCUSSION TAB) */ if (StringUtils.isEmpty(ticket.milestone)) { add(new Label("milestone")); } else { // link to milestone query 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.add(Lucene.milestone.name(), ticket.milestone); int progress = 0; int open = 0; int closed = 0; if (tm != null) { progress = tm.getProgress(); open = tm.getOpenTickets(); closed = tm.getClosedTickets(); } Fragment milestoneProgress = new Fragment("milestone", "milestoneProgressFragment", TicketPage.this); milestoneProgress .add(new LinkPanel("link", null, ticket.milestone, TicketsPage.class, milestoneParameters)); Label label = new Label("progress"); WicketUtils.setCssStyle(label, "width:" + progress + "%;"); milestoneProgress.add(label); WicketUtils.setHtmlTooltip(milestoneProgress, MessageFormat.format(getString("gb.milestoneProgress"), open, closed)); add(milestoneProgress); } /* * TICKET DESCRIPTION (DISCUSSION TAB) */ String desc; if (StringUtils.isEmpty(ticket.body)) { desc = getString("gb.noDescriptionGiven"); } else { 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)); /* * PARTICIPANTS (DISCUSSION TAB) */ if (app().settings().getBoolean(Keys.web.allowGravatar, true)) { // gravatar allowed List<String> participants = ticket.getParticipants(); add(new Label("participantsLabel", MessageFormat.format( getString(participants.size() > 1 ? "gb.nParticipants" : "gb.oneParticipant"), "<b>" + participants.size() + "</b>")).setEscapeModelStrings(false)); ListDataProvider<String> participantsDp = new ListDataProvider<String>(participants); DataView<String> participantsView = new DataView<String>("participants", participantsDp) { private static final long serialVersionUID = 1L; @Override public void populateItem(final Item<String> item) { String username = item.getModelObject(); UserModel user = app().users().getUserModel(username); if (user == null) { user = new UserModel(username); } item.add(new AvatarImage("participant", user.getDisplayName(), user.emailAddress, null, 25, true)); } }; add(participantsView); } else { // gravatar prohibited add(new Label("participantsLabel").setVisible(false)); add(new Label("participants").setVisible(false)); } /* * LARGE STATUS INDICATOR WITH ICON (DISCUSSION TAB->SIDE BAR) */ Fragment ticketStatus = new Fragment("ticketStatus", "ticketStatusFragment", TicketPage.this); Label ticketIcon = TicketsUI.getStateIcon("ticketIcon", ticket); ticketStatus.add(ticketIcon); ticketStatus.add(new Label("ticketStatus", ticket.status.toString())); WicketUtils.setCssClass(ticketStatus, TicketsUI.getLozengeClass(ticket.status, false)); add(ticketStatus); /* * UPDATE FORM (DISCUSSION TAB) */ if (user.canEdit(ticket, repository) && app().tickets().isAcceptingTicketUpdates(repository)) { if (user.canAdmin(ticket, repository) && ticket.isOpen()) { /* * OPEN TICKET */ Fragment controls = new Fragment("controls", "openControlsFragment", TicketPage.this); /* * STATUS */ List<Status> choices = new ArrayList<Status>(); if (ticket.isProposal()) { choices.addAll(Arrays.asList(TicketModel.Status.proposalWorkflow)); } else if (ticket.isBug()) { choices.addAll(Arrays.asList(TicketModel.Status.bugWorkflow)); } else { choices.addAll(Arrays.asList(TicketModel.Status.requestWorkflow)); } choices.remove(ticket.status); ListDataProvider<Status> workflowDp = new ListDataProvider<Status>(choices); DataView<Status> statusView = new DataView<Status>("newStatus", workflowDp) { private static final long serialVersionUID = 1L; @Override public void populateItem(final Item<Status> item) { SimpleAjaxLink<Status> link = new SimpleAjaxLink<Status>("link", item.getModel()) { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget target) { Status status = getModel().getObject(); Change change = new Change(user.username); change.setField(Field.status, status); if (!ticket.isWatching(user.username)) { change.watch(user.username); } TicketModel update = app().tickets().updateTicket(repository, ticket.number, change); app().tickets().createNotifier().sendMailing(update); redirectTo(TicketsPage.class, getPageParameters()); } }; String css = TicketsUI.getStatusClass(item.getModel().getObject()); WicketUtils.setCssClass(link, css); item.add(link); } }; controls.add(statusView); /* * RESPONSIBLE LIST */ Set<String> userlist = new TreeSet<String>(ticket.getParticipants()); 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>(); if (!StringUtils.isEmpty(ticket.responsible)) { // exclude the current responsible userlist.remove(ticket.responsible); } for (String username : userlist) { UserModel u = app().users().getUserModel(username); if (u != null) { responsibles.add(new TicketResponsible(u)); } } Collections.sort(responsibles); responsibles.add(new TicketResponsible(ESC_NIL, "", "")); ListDataProvider<TicketResponsible> responsibleDp = new ListDataProvider<TicketResponsible>( responsibles); DataView<TicketResponsible> responsibleView = new DataView<TicketResponsible>("newResponsible", responsibleDp) { private static final long serialVersionUID = 1L; @Override public void populateItem(final Item<TicketResponsible> item) { SimpleAjaxLink<TicketResponsible> link = new SimpleAjaxLink<TicketResponsible>("link", item.getModel()) { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget target) { TicketResponsible responsible = getModel().getObject(); Change change = new Change(user.username); change.setField(Field.responsible, responsible.username); if (!StringUtils.isEmpty(responsible.username)) { if (!ticket.isWatching(responsible.username)) { change.watch(responsible.username); } } if (!ticket.isWatching(user.username)) { change.watch(user.username); } TicketModel update = app().tickets().updateTicket(repository, ticket.number, change); app().tickets().createNotifier().sendMailing(update); redirectTo(TicketsPage.class, getPageParameters()); } }; item.add(link); } }; controls.add(responsibleView); /* * MILESTONE LIST */ List<TicketMilestone> milestones = app().tickets().getMilestones(repository, Status.Open); if (!StringUtils.isEmpty(ticket.milestone)) { for (TicketMilestone milestone : milestones) { if (milestone.name.equals(ticket.milestone)) { milestones.remove(milestone); break; } } } milestones.add(new TicketMilestone(ESC_NIL)); ListDataProvider<TicketMilestone> milestoneDp = new ListDataProvider<TicketMilestone>(milestones); DataView<TicketMilestone> milestoneView = new DataView<TicketMilestone>("newMilestone", milestoneDp) { private static final long serialVersionUID = 1L; @Override public void populateItem(final Item<TicketMilestone> item) { SimpleAjaxLink<TicketMilestone> link = new SimpleAjaxLink<TicketMilestone>("link", item.getModel()) { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget target) { TicketMilestone milestone = getModel().getObject(); Change change = new Change(user.username); if (NIL.equals(milestone.name) || ESC_NIL.equals(milestone.name)) { change.setField(Field.milestone, ""); } else { change.setField(Field.milestone, milestone.name); } if (!ticket.isWatching(user.username)) { change.watch(user.username); } TicketModel update = app().tickets().updateTicket(repository, ticket.number, change); app().tickets().createNotifier().sendMailing(update); redirectTo(TicketsPage.class, getPageParameters()); } }; item.add(link); } }; controls.add(milestoneView); String editHref = urlFor(EditTicketPage.class, params).toString(); controls.add(new ExternalLink("editLink", editHref, getString("gb.edit"))); add(controls); } else { /* * CLOSED TICKET */ Fragment controls = new Fragment("controls", "closedControlsFragment", TicketPage.this); String editHref = urlFor(EditTicketPage.class, params).toString(); controls.add(new ExternalLink("editLink", editHref, getString("gb.edit"))); add(controls); } } else { add(new Label("controls").setVisible(false)); } /* * 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().processText(getRepository(), repositoryName, ticket.topic); String safeTopic = app().xssFilter().relaxed(topic); add(new Label("ticketTopic", safeTopic).setEscapeModelStrings(false)); } /* * VOTERS */ List<String> voters = ticket.getVoters(); Label votersCount = new Label("votes", "" + voters.size()); if (voters.size() == 0) { WicketUtils.setCssClass(votersCount, "badge"); } else { WicketUtils.setCssClass(votersCount, "badge badge-info"); } add(votersCount); if (user.isAuthenticated && app().tickets().isAcceptingTicketUpdates(repository)) { Model<String> model; if (ticket.isVoter(user.username)) { model = Model.of(getString("gb.removeVote")); } else { model = Model.of(MessageFormat.format(getString("gb.vote"), ticket.type.toString())); } SimpleAjaxLink<String> link = new SimpleAjaxLink<String>("voteLink", model) { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget target) { Change change = new Change(user.username); if (ticket.isVoter(user.username)) { change.unvote(user.username); } else { change.vote(user.username); } app().tickets().updateTicket(repository, ticket.number, change); redirectTo(TicketsPage.class, getPageParameters()); } }; add(link); } else { add(new Label("voteLink").setVisible(false)); } /* * WATCHERS */ List<String> watchers = ticket.getWatchers(); Label watchersCount = new Label("watchers", "" + watchers.size()); if (watchers.size() == 0) { WicketUtils.setCssClass(watchersCount, "badge"); } else { WicketUtils.setCssClass(watchersCount, "badge badge-info"); } add(watchersCount); if (user.isAuthenticated && app().tickets().isAcceptingTicketUpdates(repository)) { Model<String> model; if (ticket.isWatching(user.username)) { model = Model.of(getString("gb.stopWatching")); } else { model = Model.of(MessageFormat.format(getString("gb.watch"), ticket.type.toString())); } SimpleAjaxLink<String> link = new SimpleAjaxLink<String>("watchLink", model) { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget target) { Change change = new Change(user.username); if (ticket.isWatching(user.username)) { change.unwatch(user.username); } else { change.watch(user.username); } app().tickets().updateTicket(repository, ticket.number, change); redirectTo(TicketsPage.class, getPageParameters()); } }; add(link); } else { add(new Label("watchLink").setVisible(false)); } /* * TOPIC & LABELS (DISCUSSION TAB->SIDE BAR) */ ListDataProvider<String> labelsDp = new ListDataProvider<String>(ticket.getLabels()); DataView<String> labelsView = new DataView<String>("labels", labelsDp) { private static final long serialVersionUID = 1L; @Override public void populateItem(final Item<String> item) { final String value = item.getModelObject(); Label label = new Label("label", value); TicketLabel tLabel = app().tickets().getLabel(repository, value); String background = MessageFormat.format("background-color:{0};", tLabel.color); label.add(new AttributeModifier("style", background)); item.add(label); } }; add(labelsView); /* * COMMENTS & STATUS CHANGES (DISCUSSION TAB) */ if (comments.size() == 0) { add(new Label("discussion").setVisible(false)); } else { Fragment discussionFragment = new Fragment("discussion", "discussionFragment", TicketPage.this); ListDataProvider<Change> discussionDp = new ListDataProvider<Change>(discussion); DataView<Change> discussionView = new DataView<Change>("discussion", discussionDp) { private static final long serialVersionUID = 1L; @Override public void populateItem(final Item<Change> item) { final Change entry = item.getModelObject(); if (entry.isMerge()) { /* * MERGE */ String resolvedBy = entry.getString(Field.mergeSha); // identify the merged patch, it is likely the last Patchset mergedPatch = null; for (Change c : revisions) { if (c.patchset.tip.equals(resolvedBy)) { mergedPatch = c.patchset; break; } } String commitLink; if (mergedPatch == null) { // shouldn't happen, but just-in-case int len = app().settings().getInteger(Keys.web.shortCommitIdLength, 6); commitLink = resolvedBy.substring(0, len); } else { // expected result commitLink = mergedPatch.toString(); } Fragment mergeFragment = new Fragment("entry", "mergeFragment", TicketPage.this); mergeFragment.add(new LinkPanel("commitLink", null, commitLink, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, resolvedBy))); mergeFragment.add(new Label("toBranch", MessageFormat.format(getString("gb.toBranch"), "<b>" + ticket.mergeTo + "</b>")) .setEscapeModelStrings(false)); addUserAttributions(mergeFragment, entry, 0); addDateAttributions(mergeFragment, entry); item.add(mergeFragment); } else if (entry.isStatusChange()) { /* * STATUS CHANGE */ Fragment frag = new Fragment("entry", "statusFragment", TicketPage.this); Label status = new Label("statusChange", entry.getStatus().toString()); String css = TicketsUI.getLozengeClass(entry.getStatus(), false); WicketUtils.setCssClass(status, css); frag.add(status); addUserAttributions(frag, entry, avatarWidth); addDateAttributions(frag, entry); item.add(frag); } else { /* * COMMENT */ 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", TicketPage.this); Label commentIcon = new Label("commentIcon"); if (entry.comment.src == CommentSource.Email) { WicketUtils.setCssClass(commentIcon, "iconic-mail"); } else { WicketUtils.setCssClass(commentIcon, "iconic-comment-alt2-stroke"); } frag.add(commentIcon); frag.add(new Label("comment", safeComment).setEscapeModelStrings(false)); addUserAttributions(frag, entry, avatarWidth); addDateAttributions(frag, entry); item.add(frag); } } }; discussionFragment.add(discussionView); add(discussionFragment); } /* * ADD COMMENT PANEL */ if (UserModel.ANONYMOUS.equals(user) || !repository.isBare || repository.isFrozen || repository.isMirror) { // prohibit comments for anonymous users, local working copy repos, // frozen repos, and mirrors add(new Label("newComment").setVisible(false)); } else { // permit user to comment Fragment newComment = new Fragment("newComment", "newCommentFragment", TicketPage.this); 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); commentPanel.setRepository(repositoryName); newComment.add(commentPanel); add(newComment); } /* * PATCHSET TAB */ if (currentPatchset == null) { // no patchset available RepositoryUrl repoUrl = getRepositoryUrl(user, repository); 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 Fragment changeIdFrag = new Fragment("patchset", "proposeFragment", TicketPage.this); changeIdFrag.add(new Label("proposeInstructions", MarkdownUtils.transformMarkdown(getString("gb.proposeInstructions"))) .setEscapeModelStrings(false)); changeIdFrag .add(new Label("ptWorkflow", MessageFormat.format(getString("gb.proposeWith"), "Barnum"))); changeIdFrag.add(new Label("ptWorkflowSteps", getProposeWorkflow("propose_pt.md", repoUrl.url, ticket.number)) .setEscapeModelStrings(false)); changeIdFrag .add(new Label("gitWorkflow", MessageFormat.format(getString("gb.proposeWith"), "Git"))); changeIdFrag.add(new Label("gitWorkflowSteps", getProposeWorkflow("propose_git.md", repoUrl.url, ticket.number)) .setEscapeModelStrings(false)); add(changeIdFrag); } else { // explain why you can't propose a patchset Fragment fragment = new Fragment("patchset", "canNotProposeFragment", TicketPage.this); String reason = ""; if (ticket.isClosed()) { reason = getString("gb.ticketIsClosed"); } else if (repository.isMirror) { reason = getString("gb.repositoryIsMirror"); } else if (repository.isFrozen) { reason = getString("gb.repositoryIsFrozen"); } else if (!repository.acceptNewPatchsets) { reason = getString("gb.repositoryDoesNotAcceptPatchsets"); } else if (!canPropose) { if (UserModel.ANONYMOUS.equals(user)) { reason = getString("gb.anonymousCanNotPropose"); } else { reason = getString("gb.youDoNotHaveClonePermission"); } } else { reason = getString("gb.serverDoesNotAcceptPatchsets"); } fragment.add(new Label("reason", reason)); add(fragment); } } else { // show current patchset Fragment patchsetFrag = new Fragment("patchset", "patchsetFragment", TicketPage.this); patchsetFrag.add(new Label("commitsInPatchset", MessageFormat.format(getString("gb.commitsInPatchsetN"), currentPatchset.number))); 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); ListDataProvider<RevCommit> commitsDp = new ListDataProvider<RevCommit>(commits); DataView<RevCommit> commitsView = new DataView<RevCommit>("commit", commitsDp) { private static final long serialVersionUID = 1L; @Override public void populateItem(final Item<RevCommit> item) { RevCommit commit = item.getModelObject(); PersonIdent author = commit.getAuthorIdent(); 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.getAuthorDate(commit), GitBlitWebSession.get().getTimezone(), getTimeUtils(), false)); item.add(new DiffStatPanel("commitDiffStat", 0, 0, true)); } }; patchsetFrag.add(commitsView); add(patchsetFrag); } /* * ACTIVITY TAB */ Fragment revisionHistory = new Fragment("activity", "activityFragment", TicketPage.this); List<Change> events = new ArrayList<Change>(ticket.changes); Collections.sort(events); Collections.reverse(events); ListDataProvider<Change> eventsDp = new ListDataProvider<Change>(events); DataView<Change> eventsView = new DataView<Change>("event", eventsDp) { private static final long serialVersionUID = 1L; @Override public void populateItem(final Item<Change> item) { Change event = item.getModelObject(); addUserAttributions(item, event, 16); if (event.hasPatchset()) { // patchset Patchset patchset = event.patchset; String what; if (event.isStatusChange() && (Status.New == event.getStatus())) { what = getString("gb.proposedThisChange"); } else if (patchset.rev == 1) { what = MessageFormat.format(getString("gb.uploadedPatchsetN"), patchset.number); } else { if (patchset.added == 1) { what = getString("gb.addedOneCommit"); } else { what = MessageFormat.format(getString("gb.addedNCommits"), patchset.added); } } item.add(new Label("what", what)); LinkPanel psr = new LinkPanel("patchsetRevision", null, patchset.number + "-" + patchset.rev, ComparePage.class, WicketUtils.newRangeParameter(repositoryName, patchset.parent == null ? patchset.base : patchset.parent, patchset.tip), true); WicketUtils.setHtmlTooltip(psr, patchset.toString()); WicketUtils.setCssClass(psr, "aui-lozenge aui-lozenge-subtle"); item.add(psr); String typeCss = getPatchsetTypeCss(patchset.type); Label typeLabel = new Label("patchsetType", patchset.type.toString()); if (typeCss == null) { typeLabel.setVisible(false); } else { WicketUtils.setCssClass(typeLabel, typeCss); } 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()) { // comment 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.hasReference()) { // reference switch (event.reference.getSourceType()) { case Commit: { final int shaLen = app().settings().getInteger(Keys.web.shortCommitIdLength, 6); item.add(new Label("what", getString("gb.referencedByCommit"))); LinkPanel psr = new LinkPanel("patchsetRevision", null, event.reference.toString().substring(0, shaLen), CommitPage.class, WicketUtils.newObjectParameter(repositoryName, event.reference.toString()), true); WicketUtils.setHtmlTooltip(psr, event.reference.toString()); WicketUtils.setCssClass(psr, "ticketReference-commit shortsha1"); item.add(psr); } break; case Ticket: { final String text = MessageFormat.format("ticket/{0}", event.reference.ticketId); item.add(new Label("what", getString("gb.referencedByTicket"))); //NOTE: Ideally reference the exact comment using reference.toString, // however anchor hash is used and is escaped resulting in broken link LinkPanel psr = new LinkPanel("patchsetRevision", null, text, TicketsPage.class, WicketUtils.newObjectParameter(repositoryName, event.reference.ticketId.toString()), true); WicketUtils.setCssClass(psr, "ticketReference-comment"); item.add(psr); } break; default: { item.add(new Label("what").setVisible(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 if (event.hasReview()) { // review String score; switch (event.review.score) { case approved: score = "<span style='color:darkGreen'>" + getScoreDescription(event.review.score) + "</span>"; break; case vetoed: score = "<span style='color:darkRed'>" + getScoreDescription(event.review.score) + "</span>"; break; default: score = getScoreDescription(event.review.score); } item.add(new Label("what", MessageFormat.format(getString("gb.reviewedPatchsetRev"), event.review.patchset, event.review.rev, score)).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 = ""; if (event.isStatusChange()) { switch (event.getStatus()) { case New: if (ticket.isProposal()) { what = getString("gb.proposedThisChange"); } else { what = getString("gb.createdThisTicket"); } break; default: break; } } item.add(new Label("what", what).setVisible(what.length() > 0)); } addDateAttributions(item, event); if (event.hasFieldChanges()) { StringBuilder sb = new StringBuilder(); sb.append("<table class=\"summary\"><tbody>"); for (Map.Entry<Field, String> entry : event.fields.entrySet()) { String value; switch (entry.getKey()) { case body: String body = entry.getValue(); if (event.isStatusChange() && Status.New == event.getStatus() && StringUtils.isEmpty(body)) { // ignore initial empty description continue; } // trim body changes if (StringUtils.isEmpty(body)) { value = "<i>" + ESC_NIL + "</i>"; } else { value = StringUtils.trimString(body, Constants.LEN_SHORTLOG_REFS); } break; case status: // special handling for status Status status = event.getStatus(); String css = TicketsUI.getLozengeClass(status, true); value = String.format("<span class=\"%1$s\">%2$s</span>", css, status.toString()); break; default: value = StringUtils.isEmpty(entry.getValue()) ? ("<i>" + ESC_NIL + "</i>") : StringUtils.escapeForHtml(entry.getValue(), false); break; } sb.append("<tr><th style=\"width:70px;\">"); try { sb.append(getString("gb." + entry.getKey().name())); } catch (Exception e) { sb.append(entry.getKey().name()); } sb.append("</th><td>"); sb.append(value); sb.append("</td></tr>"); } sb.append("</tbody></table>"); String safeHtml = app().xssFilter().relaxed(sb.toString()); item.add(new Label("fields", safeHtml).setEscapeModelStrings(false)); } else { item.add(new Label("fields").setVisible(false)); } } }; revisionHistory.add(eventsView); add(revisionHistory); }