From 667163976e4e51fc3ebf191525e44d97c8a724dc Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Thu, 01 May 2014 14:31:18 -0400
Subject: [PATCH] Overdue labeling, notify changed tickets control

---
 src/main/java/com/gitblit/wicket/pages/TicketsPage.java       |   26 +++++++++++--
 src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java |    7 +++
 src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html  |    2 
 src/main/java/com/gitblit/tickets/ITicketService.java         |   38 ++++++++++++++----
 src/main/java/com/gitblit/wicket/GitBlitWebApp.properties     |    4 +
 src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html |    3 +
 src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java  |   11 +++++
 src/main/java/com/gitblit/tickets/TicketMilestone.java        |   10 ++++
 8 files changed, 82 insertions(+), 19 deletions(-)

diff --git a/src/main/java/com/gitblit/tickets/ITicketService.java b/src/main/java/com/gitblit/tickets/ITicketService.java
index cce805e..3261ca9 100644
--- a/src/main/java/com/gitblit/tickets/ITicketService.java
+++ b/src/main/java/com/gitblit/tickets/ITicketService.java
@@ -651,11 +651,12 @@
 	 * @param oldName
 	 * @param newName
 	 * @param createdBy
-	 * @param send ticket notifications
+	 * @param notifyOpenTickets
 	 * @return true if successful
 	 * @since 1.6.0
 	 */
-	public synchronized boolean renameMilestone(RepositoryModel repository, String oldName, String newName, String createdBy, boolean notify) {
+	public synchronized boolean renameMilestone(RepositoryModel repository, String oldName,
+			String newName, String createdBy, boolean notifyOpenTickets) {
 		if (StringUtils.isEmpty(newName)) {
 			throw new IllegalArgumentException("new milestone can not be empty!");
 		}
@@ -680,11 +681,11 @@
 				Change change = new Change(createdBy);
 				change.setField(Field.milestone, newName);
 				TicketModel ticket = updateTicket(repository, qr.number, change);
-				if (notify && ticket.isOpen()) {
+				if (notifyOpenTickets && ticket.isOpen()) {
 					notifier.queueMailing(ticket);
 				}
 			}
-			if (notify) {
+			if (notifyOpenTickets) {
 				notifier.sendAll();
 			}
 
@@ -709,6 +710,21 @@
 	 * @since 1.4.0
 	 */
 	public synchronized boolean deleteMilestone(RepositoryModel repository, String milestone, String createdBy) {
+		return deleteMilestone(repository, milestone, createdBy, true);
+	}
+
+	/**
+	 * Deletes a milestone.
+	 *
+	 * @param repository
+	 * @param milestone
+	 * @param createdBy
+	 * @param notifyOpenTickets
+	 * @return true if successful
+	 * @since 1.6.0
+	 */
+	public synchronized boolean deleteMilestone(RepositoryModel repository, String milestone,
+			String createdBy, boolean notifyOpenTickets) {
 		if (StringUtils.isEmpty(milestone)) {
 			throw new IllegalArgumentException("milestone can not be empty!");
 		}
@@ -722,14 +738,18 @@
 
 			milestonesCache.remove(repository.name);
 
+			TicketNotifier notifier = createNotifier();
 			for (QueryResult qr : tm.tickets) {
-				if (qr.isOpen()) {
-					// reset the milestone only for open tickets
-					Change change = new Change(createdBy);
-					change.setField(Field.milestone, "");
-					TicketModel ticket = updateTicket(repository, qr.number, change);
+				Change change = new Change(createdBy);
+				change.setField(Field.milestone, "");
+				TicketModel ticket = updateTicket(repository, qr.number, change);
+				if (notifyOpenTickets && ticket.isOpen()) {
+					notifier.queueMailing(ticket);
 				}
 			}
+			if (notifyOpenTickets) {
+				notifier.sendAll();
+			}
 			return true;
 		} catch (IOException e) {
 			log.error("failed to delete milestone " + milestone + " in " + repository, e);
diff --git a/src/main/java/com/gitblit/tickets/TicketMilestone.java b/src/main/java/com/gitblit/tickets/TicketMilestone.java
index 680615a..dacedda 100644
--- a/src/main/java/com/gitblit/tickets/TicketMilestone.java
+++ b/src/main/java/com/gitblit/tickets/TicketMilestone.java
@@ -37,7 +37,15 @@
 		super(name);
 		status = Status.Open;
 	}
-	
+
+	public boolean isOpen() {
+		return status == Status.Open;
+	}
+
+	public boolean isOverdue() {
+		return due == null ? false : System.currentTimeMillis() > due.getTime();
+	}
+
 	public void setDue(Date due) {
 		this.due = due;
 	}
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
index ce4c0b2..e66e53a 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -673,4 +673,6 @@
 gb.anonymousCanNotPropose = Anonymous users can not propose patchsets.
 gb.youDoNotHaveClonePermission = You are not permitted to clone this repository.
 gb.newMilestone = new milestone
-gb.editMilestone = edit milestone
\ No newline at end of file
+gb.editMilestone = edit milestone
+gb.notifyChangedOpenTickets = send notification for changed open tickets
+gb.overdue = overdue
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html b/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html
index 31f76f1..0897ebe 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html
+++ b/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html
@@ -19,8 +19,9 @@
 		<!-- Edit Milestone Table -->
 		<table class="ticket">
 			<tr><th><wicket:message key="gb.milestone"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="name" id="name"></input></td></tr>
-			<tr><th><wicket:message key="gb.due"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="due"></input></td></tr>
+			<tr><th><wicket:message key="gb.due"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="due"></input> &nbsp;<span class="help-inline" wicket:id="dueFormat"></span></td></tr>
 			<tr><th><wicket:message key="gb.status"></wicket:message><span style="color:red;">*</span></th><td class="edit"><select class="input-large" wicket:id="status"></select></td></tr>
+			<tr><th></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="notify" /> &nbsp;<span class="help-inline"><wicket:message key="gb.notifyChangedOpenTickets"></wicket:message></span></label></td></tr>
 		</table>
 	</div>
 	</div>	
diff --git a/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java b/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java
index 967d8f3..b844442 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java
@@ -24,7 +24,9 @@
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
 import org.apache.wicket.extensions.markup.html.form.DateTextField;
+import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.CheckBox;
 import org.apache.wicket.markup.html.form.DropDownChoice;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.TextField;
@@ -102,6 +104,8 @@
 
 		form.add(new TextField<String>("name", nameModel));
 		form.add(new DateTextField("due", dueModel, "yyyy-MM-dd"));
+		form.add(new Label("dueFormat", "yyyy-MM-dd"));
+		form.add(new CheckBox("notify", notificationModel));
 
 		List<Status> statusChoices = Arrays.asList(Status.Open, Status.Closed);
 		form.add(new DropDownChoice<TicketModel.Status>("status", statusModel, statusChoices));
@@ -160,8 +164,9 @@
 			public void onSubmit() {
 				UserModel currentUser = GitBlitWebSession.get().getUser();
 				String createdBy = currentUser.username;
+				boolean notify = notificationModel.getObject();
 
-				if (app().tickets().deleteMilestone(getRepositoryModel(), oldName, createdBy)) {
+				if (app().tickets().deleteMilestone(getRepositoryModel(), oldName, createdBy, notify)) {
 					setResponsePage(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
 				} else {
 					// TODO error processing
diff --git a/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html b/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html
index 1b7e11a..2ba5d5c 100644
--- a/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html
+++ b/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html
@@ -19,7 +19,7 @@
 		<!-- New Milestone Table -->
 		<table class="ticket">
 			<tr><th><wicket:message key="gb.milestone"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="name" id="name"></input></td></tr>
-			<tr><th><wicket:message key="gb.due"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="due"></input></td></tr>
+			<tr><th><wicket:message key="gb.due"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="due"></input> &nbsp;<span class="help-inline" wicket:id="dueFormat"></span></td></tr>
 		</table>
 	</div>
 	</div>	
diff --git a/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java b/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java
index d6e34cb..a9f76d3 100644
--- a/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java
@@ -22,6 +22,7 @@
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
 import org.apache.wicket.extensions.markup.html.form.DateTextField;
+import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.Button;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.TextField;
@@ -78,6 +79,7 @@
 
 		form.add(new TextField<String>("name", nameModel));
 		form.add(new DateTextField("due", dueModel, "yyyy-MM-dd"));
+		form.add(new Label("dueFormat", "yyyy-MM-dd"));
 
 		form.add(new AjaxButton("create") {
 
@@ -87,6 +89,13 @@
 			protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
 				String name = nameModel.getObject();
 				if (StringUtils.isEmpty(name)) {
+					// invalid name
+					return;
+				}
+
+				TicketMilestone milestone = app().tickets().getMilestone(getRepositoryModel(), name);
+				if (milestone != null) {
+					// milestone already exists
 					return;
 				}
 
@@ -95,7 +104,7 @@
 				UserModel currentUser = GitBlitWebSession.get().getUser();
 				String createdBy = currentUser.username;
 
-				TicketMilestone milestone = app().tickets().createMilestone(getRepositoryModel(), name, createdBy);
+				milestone = app().tickets().createMilestone(getRepositoryModel(), name, createdBy);
 				if (milestone != null) {
 					milestone.due = due;
 					app().tickets().updateMilestone(getRepositoryModel(), milestone, createdBy);
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java
index 984b375..b7e392a 100644
--- a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java
@@ -20,6 +20,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
@@ -657,9 +658,20 @@
 		} else {
 			add(new Label("newMilestone").setVisible(false));
 		}
-		
+
 		// milestones list
-		List<TicketMilestone> allMilestones = app().tickets().getMilestones(repositoryModel);
+		List<TicketMilestone> allMilestones = new ArrayList<TicketMilestone>(app().tickets().getMilestones(repositoryModel));
+		Collections.sort(allMilestones, new Comparator<TicketMilestone>() {
+			@Override
+			public int compare(TicketMilestone o1, TicketMilestone o2) {
+				if (o2.isOpen() && !o1.isOpen()) {
+					return 1;
+				} else if (o1.isOpen() && !o2.isOpen()) {
+					return -1;
+				}
+				return o2.due.compareTo(o1.due);
+			}
+		});
 		ListDataProvider<TicketMilestone> allMilestonesDp = new ListDataProvider<TicketMilestone>(allMilestones);
 		DataView<TicketMilestone> milestonesList = new DataView<TicketMilestone>("milestoneList", allMilestonesDp) {
 			private static final long serialVersionUID = 1L;
@@ -671,15 +683,21 @@
 				item.add(new LinkPanel("milestoneName", null, tm.name, TicketsPage.class, params).setRenderBodyOnly(true));
 
 				String css;
+				String status = tm.status.name();
 				switch (tm.status) {
 				case Open:
-					css = "aui-lozenge aui-lozenge-subtle";
+					if (tm.isOverdue()) {
+						css = "aui-lozenge aui-lozenge-subtle aui-lozenge-error";
+						status = "overdue";
+					} else {
+						css = "aui-lozenge aui-lozenge-subtle";
+					}
 					break;
 				default:
 					css = "aui-lozenge";
 					break;
 				}
-				Label stateLabel = new Label("milestoneState", tm.status.name());
+				Label stateLabel = new Label("milestoneState", status);
 				WicketUtils.setCssClass(stateLabel, css);
 				item.add(stateLabel);
 

--
Gitblit v1.9.1