]> source.dussan.org Git - gitblit.git/commitdiff
Overdue labeling, notify changed tickets control
authorJames Moger <james.moger@gitblit.com>
Mon, 28 Apr 2014 15:08:50 +0000 (11:08 -0400)
committerJames Moger <james.moger@gitblit.com>
Thu, 1 May 2014 18:31:18 +0000 (14:31 -0400)
src/main/java/com/gitblit/tickets/ITicketService.java
src/main/java/com/gitblit/tickets/TicketMilestone.java
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html
src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java
src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html
src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java
src/main/java/com/gitblit/wicket/pages/TicketsPage.java

index cce805ed0e006a6c43f3ce12e16952752d5fdec0..3261ca963d32b1bdb4ede4c22071ed3f32b267a5 100644 (file)
@@ -651,11 +651,12 @@ public abstract class ITicketService {
         * @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 @@ public abstract class ITicketService {
                                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 @@ public abstract class ITicketService {
         * @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 @@ public abstract class ITicketService {
 
                        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);
index 680615a9672d743b7186a61b04b6d156a036185c..dacedda69efebde3b62c367cc1d8f1ae54dd6a5d 100644 (file)
@@ -37,7 +37,15 @@ public class TicketMilestone extends TicketLabel {
                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;
        }
index ce4c0b22a8b97e80f37e4d0ec0a1dd75e57523cd..e66e53a7e0641fd0dcd1de5be86bc6880eea782e 100644 (file)
@@ -673,4 +673,6 @@ gb.mergeToDescription = default integration branch for merging ticket patchsets
 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
index 31f76f1c50e9813aff37ff13d6461e8b2423e8ab..0897ebee57c4110fbf9017df6810ae496eb7297d 100644 (file)
@@ -19,8 +19,9 @@
                <!-- Edit Milestone Table -->\r
                <table class="ticket">\r
                        <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>\r
-                       <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>\r
+                       <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>\r
                        <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>\r
+                       <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>\r
                </table>\r
        </div>\r
        </div>  \r
index 967d8f35af53657b4f767895ccc100086a3c29ec..b844442a073283b7b1fbe2666f60c2c52548289f 100644 (file)
@@ -24,7 +24,9 @@ import org.apache.wicket.RestartResponseException;
 import org.apache.wicket.ajax.AjaxRequestTarget;\r
 import org.apache.wicket.ajax.markup.html.form.AjaxButton;\r
 import org.apache.wicket.extensions.markup.html.form.DateTextField;\r
+import org.apache.wicket.markup.html.basic.Label;\r
 import org.apache.wicket.markup.html.form.Button;\r
+import org.apache.wicket.markup.html.form.CheckBox;\r
 import org.apache.wicket.markup.html.form.DropDownChoice;\r
 import org.apache.wicket.markup.html.form.Form;\r
 import org.apache.wicket.markup.html.form.TextField;\r
@@ -102,6 +104,8 @@ public class EditMilestonePage extends RepositoryPage {
 \r
                form.add(new TextField<String>("name", nameModel));\r
                form.add(new DateTextField("due", dueModel, "yyyy-MM-dd"));\r
+               form.add(new Label("dueFormat", "yyyy-MM-dd"));\r
+               form.add(new CheckBox("notify", notificationModel));\r
 \r
                List<Status> statusChoices = Arrays.asList(Status.Open, Status.Closed);\r
                form.add(new DropDownChoice<TicketModel.Status>("status", statusModel, statusChoices));\r
@@ -160,8 +164,9 @@ public class EditMilestonePage extends RepositoryPage {
                        public void onSubmit() {\r
                                UserModel currentUser = GitBlitWebSession.get().getUser();\r
                                String createdBy = currentUser.username;\r
+                               boolean notify = notificationModel.getObject();\r
 \r
-                               if (app().tickets().deleteMilestone(getRepositoryModel(), oldName, createdBy)) {\r
+                               if (app().tickets().deleteMilestone(getRepositoryModel(), oldName, createdBy, notify)) {\r
                                        setResponsePage(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));\r
                                } else {\r
                                        // TODO error processing\r
index 1b7e11ae8883ccb0b74c997aa3c70b0a672fe980..2ba5d5ccd2d1e79a9484458e15b718b496b3530c 100644 (file)
@@ -19,7 +19,7 @@
                <!-- New Milestone Table -->\r
                <table class="ticket">\r
                        <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>\r
-                       <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>\r
+                       <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>\r
                </table>\r
        </div>\r
        </div>  \r
index d6e34cb08011d55b650a1b871154282fd147b481..a9f76d3a75e7d08eb05f46babf9a2db5a13944ea 100644 (file)
@@ -22,6 +22,7 @@ import org.apache.wicket.RestartResponseException;
 import org.apache.wicket.ajax.AjaxRequestTarget;\r
 import org.apache.wicket.ajax.markup.html.form.AjaxButton;\r
 import org.apache.wicket.extensions.markup.html.form.DateTextField;\r
+import org.apache.wicket.markup.html.basic.Label;\r
 import org.apache.wicket.markup.html.form.Button;\r
 import org.apache.wicket.markup.html.form.Form;\r
 import org.apache.wicket.markup.html.form.TextField;\r
@@ -78,6 +79,7 @@ public class NewMilestonePage extends RepositoryPage {
 \r
                form.add(new TextField<String>("name", nameModel));\r
                form.add(new DateTextField("due", dueModel, "yyyy-MM-dd"));\r
+               form.add(new Label("dueFormat", "yyyy-MM-dd"));\r
 \r
                form.add(new AjaxButton("create") {\r
 \r
@@ -87,6 +89,13 @@ public class NewMilestonePage extends RepositoryPage {
                        protected void onSubmit(AjaxRequestTarget target, Form<?> form) {\r
                                String name = nameModel.getObject();\r
                                if (StringUtils.isEmpty(name)) {\r
+                                       // invalid name\r
+                                       return;\r
+                               }\r
+\r
+                               TicketMilestone milestone = app().tickets().getMilestone(getRepositoryModel(), name);\r
+                               if (milestone != null) {\r
+                                       // milestone already exists\r
                                        return;\r
                                }\r
 \r
@@ -95,7 +104,7 @@ public class NewMilestonePage extends RepositoryPage {
                                UserModel currentUser = GitBlitWebSession.get().getUser();\r
                                String createdBy = currentUser.username;\r
 \r
-                               TicketMilestone milestone = app().tickets().createMilestone(getRepositoryModel(), name, createdBy);\r
+                               milestone = app().tickets().createMilestone(getRepositoryModel(), name, createdBy);\r
                                if (milestone != null) {\r
                                        milestone.due = due;\r
                                        app().tickets().updateMilestone(getRepositoryModel(), milestone, createdBy);\r
index 984b37544939319f1d60801bd87371a02bdc3beb..b7e392a2958f5449f32f9ec17a7b632c22604714 100644 (file)
@@ -20,6 +20,7 @@ import java.text.MessageFormat;
 import java.util.ArrayList;\r
 import java.util.Arrays;\r
 import java.util.Collections;\r
+import java.util.Comparator;\r
 import java.util.List;\r
 import java.util.Set;\r
 import java.util.TreeSet;\r
@@ -657,9 +658,20 @@ public class TicketsPage extends TicketBasePage {
                } else {\r
                        add(new Label("newMilestone").setVisible(false));\r
                }\r
-               \r
+\r
                // milestones list\r
-               List<TicketMilestone> allMilestones = app().tickets().getMilestones(repositoryModel);\r
+               List<TicketMilestone> allMilestones = new ArrayList<TicketMilestone>(app().tickets().getMilestones(repositoryModel));\r
+               Collections.sort(allMilestones, new Comparator<TicketMilestone>() {\r
+                       @Override\r
+                       public int compare(TicketMilestone o1, TicketMilestone o2) {\r
+                               if (o2.isOpen() && !o1.isOpen()) {\r
+                                       return 1;\r
+                               } else if (o1.isOpen() && !o2.isOpen()) {\r
+                                       return -1;\r
+                               }\r
+                               return o2.due.compareTo(o1.due);\r
+                       }\r
+               });\r
                ListDataProvider<TicketMilestone> allMilestonesDp = new ListDataProvider<TicketMilestone>(allMilestones);\r
                DataView<TicketMilestone> milestonesList = new DataView<TicketMilestone>("milestoneList", allMilestonesDp) {\r
                        private static final long serialVersionUID = 1L;\r
@@ -671,15 +683,21 @@ public class TicketsPage extends TicketBasePage {
                                item.add(new LinkPanel("milestoneName", null, tm.name, TicketsPage.class, params).setRenderBodyOnly(true));\r
 \r
                                String css;\r
+                               String status = tm.status.name();\r
                                switch (tm.status) {\r
                                case Open:\r
-                                       css = "aui-lozenge aui-lozenge-subtle";\r
+                                       if (tm.isOverdue()) {\r
+                                               css = "aui-lozenge aui-lozenge-subtle aui-lozenge-error";\r
+                                               status = "overdue";\r
+                                       } else {\r
+                                               css = "aui-lozenge aui-lozenge-subtle";\r
+                                       }\r
                                        break;\r
                                default:\r
                                        css = "aui-lozenge";\r
                                        break;\r
                                }\r
-                               Label stateLabel = new Label("milestoneState", tm.status.name());\r
+                               Label stateLabel = new Label("milestoneState", status);\r
                                WicketUtils.setCssClass(stateLabel, css);\r
                                item.add(stateLabel);\r
 \r