Merged #17 "CRUD Milestone Pages"
4 files added
8 files modified
| | |
| | | import com.gitblit.models.TicketModel.Patchset; |
| | | import com.gitblit.models.TicketModel.Status; |
| | | import com.gitblit.tickets.TicketIndexer.Lucene; |
| | | import com.gitblit.utils.DeepCopier; |
| | | import com.gitblit.utils.DiffUtils; |
| | | import com.gitblit.utils.DiffUtils.DiffStat; |
| | | import com.gitblit.utils.StringUtils; |
| | |
| | | public TicketMilestone getMilestone(RepositoryModel repository, String milestone) { |
| | | for (TicketMilestone ms : getMilestones(repository)) { |
| | | if (ms.name.equalsIgnoreCase(milestone)) { |
| | | TicketMilestone tm = DeepCopier.copy(ms); |
| | | String q = QueryBuilder.q(Lucene.rid.matches(repository.getRID())).and(Lucene.milestone.matches(milestone)).build(); |
| | | ms.tickets = indexer.queryFor(q, 1, 0, Lucene.number.name(), true); |
| | | return ms; |
| | | tm.tickets = indexer.queryFor(q, 1, 0, Lucene.number.name(), true); |
| | | return tm; |
| | | } |
| | | } |
| | | return null; |
| | |
| | | * @since 1.4.0 |
| | | */ |
| | | public synchronized boolean renameMilestone(RepositoryModel repository, String oldName, String newName, String createdBy) { |
| | | return renameMilestone(repository, oldName, newName, createdBy, true); |
| | | } |
| | | |
| | | /** |
| | | * Renames a milestone. |
| | | * |
| | | * @param repository |
| | | * @param oldName |
| | | * @param newName |
| | | * @param createdBy |
| | | * @param notifyOpenTickets |
| | | * @return true if successful |
| | | * @since 1.6.0 |
| | | */ |
| | | 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!"); |
| | | } |
| | |
| | | config.setString(MILESTONE, newName, STATUS, milestone.status.name()); |
| | | config.setString(MILESTONE, newName, COLOR, milestone.color); |
| | | if (milestone.due != null) { |
| | | config.setString(MILESTONE, milestone.name, DUE, |
| | | config.setString(MILESTONE, newName, DUE, |
| | | new SimpleDateFormat(DUE_DATE_PATTERN).format(milestone.due)); |
| | | } |
| | | config.save(); |
| | |
| | | Change change = new Change(createdBy); |
| | | change.setField(Field.milestone, newName); |
| | | TicketModel ticket = updateTicket(repository, qr.number, change); |
| | | notifier.queueMailing(ticket); |
| | | if (notifyOpenTickets && ticket.isOpen()) { |
| | | notifier.queueMailing(ticket); |
| | | } |
| | | } |
| | | notifier.sendAll(); |
| | | if (notifyOpenTickets) { |
| | | notifier.sendAll(); |
| | | } |
| | | |
| | | return true; |
| | | } catch (IOException e) { |
| | |
| | | * @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!"); |
| | | } |
| | | Repository db = null; |
| | | try { |
| | | TicketMilestone tm = getMilestone(repository, milestone); |
| | | db = repositoryManager.getRepository(repository.name); |
| | | StoredConfig config = db.getConfig(); |
| | | config.unsetSection(MILESTONE, milestone); |
| | |
| | | |
| | | milestonesCache.remove(repository.name); |
| | | |
| | | TicketNotifier notifier = createNotifier(); |
| | | for (QueryResult qr : tm.tickets) { |
| | | 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); |
| | |
| | | return type != null && Type.Proposal == type; |
| | | } |
| | | |
| | | public boolean isOpen() { |
| | | return !status.isClosed(); |
| | | } |
| | | |
| | | public boolean isClosed() { |
| | | return status.isClosed(); |
| | | } |
| | | |
| | | public boolean isMerged() { |
| | | return Status.Merged == status && !StringUtils.isEmpty(mergeSha); |
| | | } |
| | |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public final String name; |
| | | public String name; |
| | | |
| | | public String color; |
| | | |
| | | public List<QueryResult> tickets; |
| | | |
| | | |
| | | public TicketLabel(String name) { |
| | | setName(name); |
| | | } |
| | | |
| | | public void setName(String name) { |
| | | this.name = name; |
| | | this.color = StringUtils.getColor(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; |
| | | } |
| | | |
| | | public int getProgress() { |
| | | int total = getTotalTickets(); |
| | | if (total == 0) { |
| | |
| | | import com.gitblit.wicket.pages.ComparePage; |
| | | import com.gitblit.wicket.pages.DocPage; |
| | | import com.gitblit.wicket.pages.DocsPage; |
| | | import com.gitblit.wicket.pages.EditMilestonePage; |
| | | import com.gitblit.wicket.pages.EditTicketPage; |
| | | import com.gitblit.wicket.pages.ExportTicketPage; |
| | | import com.gitblit.wicket.pages.FederationRegistrationPage; |
| | |
| | | import com.gitblit.wicket.pages.LuceneSearchPage; |
| | | import com.gitblit.wicket.pages.MetricsPage; |
| | | import com.gitblit.wicket.pages.MyDashboardPage; |
| | | import com.gitblit.wicket.pages.NewMilestonePage; |
| | | import com.gitblit.wicket.pages.NewTicketPage; |
| | | import com.gitblit.wicket.pages.OverviewPage; |
| | | import com.gitblit.wicket.pages.PatchPage; |
| | |
| | | mount("/tickets/new", NewTicketPage.class, "r"); |
| | | mount("/tickets/edit", EditTicketPage.class, "r", "h"); |
| | | mount("/tickets/export", ExportTicketPage.class, "r", "h"); |
| | | mount("/milestones/new", NewMilestonePage.class, "r"); |
| | | mount("/milestones/edit", EditMilestonePage.class, "r", "h"); |
| | | |
| | | // setup the markup document urls |
| | | mount("/docs", DocsPage.class, "r"); |
| | |
| | | gb.ticketIsClosed = This ticket is closed. |
| | | 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.youDoNotHaveClonePermission = You are not permitted to clone this repository. |
| | | gb.newMilestone = new milestone |
| | | gb.editMilestone = edit milestone |
| | | gb.notifyChangedOpenTickets = send notification for changed open tickets |
| | | gb.overdue = overdue |
| | | gb.openMilestones = open milestones |
| | | gb.closedMilestones = closed milestones |
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
| | | <html xmlns="http://www.w3.org/1999/xhtml" |
| | | xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" |
| | | xml:lang="en" |
| | | lang="en"> |
| | |
|
| | | <wicket:extend>
|
| | | <body onload="document.getElementById('name').focus();">
|
| | | |
| | | <div class="container">
|
| | | <!-- page header -->
|
| | | <div class="title" style="font-size: 22px; color: rgb(0, 32, 96);padding: 3px 0px 7px;">
|
| | | <span class="project"><wicket:message key="gb.editMilestone"></wicket:message></span>
|
| | | </div>
|
| | |
|
| | | <form style="padding-top:5px;" wicket:id="editForm">
|
| | | <div class="row">
|
| | | <div class="span12">
|
| | | <!-- 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> <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" /> <span class="help-inline"><wicket:message key="gb.notifyChangedOpenTickets"></wicket:message></span></label></td></tr>
|
| | | </table>
|
| | | </div>
|
| | | </div> |
| | |
|
| | | <div class="row">
|
| | | <div class="span12">
|
| | | <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /> <input class="btn btn-danger" type="submit" value="Delete" wicket:message="value:gb.delete" wicket:id="delete" /></div>
|
| | | </div>
|
| | | </div>
|
| | | </form>
|
| | | </div>
|
| | | </body>
|
| | |
|
| | | </wicket:extend>
|
| | | </html> |
New file |
| | |
| | | /*
|
| | | * Copyright 2014 gitblit.com.
|
| | | *
|
| | | * Licensed under the Apache License, Version 2.0 (the "License");
|
| | | * you may not use this file except in compliance with the License.
|
| | | * You may obtain a copy of the License at
|
| | | *
|
| | | * http://www.apache.org/licenses/LICENSE-2.0
|
| | | *
|
| | | * Unless required by applicable law or agreed to in writing, software
|
| | | * distributed under the License is distributed on an "AS IS" BASIS,
|
| | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| | | * See the License for the specific language governing permissions and
|
| | | * limitations under the License.
|
| | | */
|
| | | package com.gitblit.wicket.pages;
|
| | |
|
| | | import java.util.Arrays;
|
| | | import java.util.Date;
|
| | | import java.util.List;
|
| | |
|
| | | import org.apache.wicket.PageParameters;
|
| | | import org.apache.wicket.RestartResponseException;
|
| | | 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;
|
| | | import org.apache.wicket.model.IModel;
|
| | | import org.apache.wicket.model.Model;
|
| | |
|
| | | import com.gitblit.models.RepositoryModel;
|
| | | import com.gitblit.models.TicketModel;
|
| | | import com.gitblit.models.TicketModel.Status;
|
| | | import com.gitblit.models.UserModel;
|
| | | import com.gitblit.tickets.TicketMilestone;
|
| | | import com.gitblit.utils.StringUtils;
|
| | | import com.gitblit.wicket.GitBlitWebSession;
|
| | | import com.gitblit.wicket.WicketUtils;
|
| | |
|
| | | /**
|
| | | * Page for creating a new milestone.
|
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | | public class EditMilestonePage extends RepositoryPage {
|
| | |
|
| | | private final String oldName;
|
| | |
|
| | | private IModel<String> nameModel;
|
| | |
|
| | | private IModel<Date> dueModel;
|
| | |
|
| | | private IModel<Status> statusModel;
|
| | |
|
| | | private IModel<Boolean> notificationModel;
|
| | |
|
| | | public EditMilestonePage(PageParameters params) {
|
| | | super(params);
|
| | |
|
| | | RepositoryModel model = getRepositoryModel();
|
| | | if (!app().tickets().isAcceptingTicketUpdates(model)) {
|
| | | // ticket service is read-only
|
| | | throw new RestartResponseException(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
|
| | | }
|
| | |
|
| | | UserModel currentUser = GitBlitWebSession.get().getUser();
|
| | | if (currentUser == null) {
|
| | | currentUser = UserModel.ANONYMOUS;
|
| | | }
|
| | |
|
| | | if (!currentUser.isAuthenticated || !currentUser.canAdmin(model)) {
|
| | | // administration prohibited
|
| | | throw new RestartResponseException(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
|
| | | }
|
| | |
|
| | | oldName = WicketUtils.getObject(params);
|
| | | if (StringUtils.isEmpty(oldName)) {
|
| | | // milestone not specified
|
| | | throw new RestartResponseException(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
|
| | | }
|
| | |
|
| | | TicketMilestone tm = app().tickets().getMilestone(getRepositoryModel(), oldName);
|
| | | if (tm == null) {
|
| | | // milestone does not exist
|
| | | throw new RestartResponseException(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
|
| | | }
|
| | |
|
| | | setStatelessHint(false);
|
| | | setOutputMarkupId(true);
|
| | |
|
| | | Form<Void> form = new Form<Void>("editForm");
|
| | | add(form);
|
| | |
|
| | | nameModel = Model.of(tm.name);
|
| | | dueModel = Model.of(tm.due);
|
| | | statusModel = Model.of(tm.status);
|
| | | notificationModel = Model.of(true);
|
| | |
|
| | | 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));
|
| | |
|
| | | form.add(new AjaxButton("save") {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
|
| | | String name = nameModel.getObject();
|
| | | if (StringUtils.isEmpty(name)) {
|
| | | return;
|
| | | }
|
| | |
|
| | | Date due = dueModel.getObject();
|
| | | Status status = statusModel.getObject();
|
| | | boolean rename = !name.equals(oldName);
|
| | | boolean notify = notificationModel.getObject();
|
| | |
|
| | | UserModel currentUser = GitBlitWebSession.get().getUser();
|
| | | String createdBy = currentUser.username;
|
| | |
|
| | | TicketMilestone tm = app().tickets().getMilestone(getRepositoryModel(), oldName);
|
| | | tm.setName(name);
|
| | | tm.setDue(due);
|
| | | tm.status = status;
|
| | |
|
| | | boolean success = true;
|
| | | if (rename) {
|
| | | success = app().tickets().renameMilestone(getRepositoryModel(), oldName, name, createdBy, notify);
|
| | | }
|
| | |
|
| | | if (success && app().tickets().updateMilestone(getRepositoryModel(), tm, createdBy)) {
|
| | | setResponsePage(TicketsPage.class, WicketUtils.newRepositoryParameter(getRepositoryModel().name));
|
| | | } else {
|
| | | // TODO error
|
| | | }
|
| | | }
|
| | | });
|
| | | Button cancel = new Button("cancel") {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public void onSubmit() {
|
| | | setResponsePage(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
|
| | | }
|
| | | };
|
| | | cancel.setDefaultFormProcessing(false);
|
| | | form.add(cancel);
|
| | |
|
| | | Button delete = new Button("delete") {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public void onSubmit() {
|
| | | UserModel currentUser = GitBlitWebSession.get().getUser();
|
| | | String createdBy = currentUser.username;
|
| | | boolean notify = notificationModel.getObject();
|
| | |
|
| | | if (app().tickets().deleteMilestone(getRepositoryModel(), oldName, createdBy, notify)) {
|
| | | setResponsePage(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
|
| | | } else {
|
| | | // TODO error processing
|
| | | }
|
| | | }
|
| | | };
|
| | | delete.setDefaultFormProcessing(false);
|
| | | form.add(delete);
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected String getPageName() {
|
| | | return getString("gb.editMilestone");
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected Class<? extends BasePage> getRepoNavPageClass() {
|
| | | return TicketsPage.class;
|
| | | }
|
| | | }
|
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
| | | <html xmlns="http://www.w3.org/1999/xhtml" |
| | | xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" |
| | | xml:lang="en" |
| | | lang="en"> |
| | |
|
| | | <wicket:extend>
|
| | | <body onload="document.getElementById('name').focus();">
|
| | | |
| | | <div class="container">
|
| | | <!-- page header -->
|
| | | <div class="title" style="font-size: 22px; color: rgb(0, 32, 96);padding: 3px 0px 7px;">
|
| | | <span class="project"><wicket:message key="gb.newMilestone"></wicket:message></span>
|
| | | </div>
|
| | |
|
| | | <form style="padding-top:5px;" wicket:id="editForm">
|
| | | <div class="row">
|
| | | <div class="span12">
|
| | | <!-- 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> <span class="help-inline" wicket:id="dueFormat"></span></td></tr>
|
| | | </table>
|
| | | </div>
|
| | | </div> |
| | |
|
| | | <div class="row">
|
| | | <div class="span12">
|
| | | <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Create" wicket:message="value:gb.create" wicket:id="create" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /></div>
|
| | | </div>
|
| | | </div>
|
| | | </form>
|
| | | </div>
|
| | | </body>
|
| | |
|
| | | </wicket:extend>
|
| | | </html> |
New file |
| | |
| | | /*
|
| | | * Copyright 2014 gitblit.com.
|
| | | *
|
| | | * Licensed under the Apache License, Version 2.0 (the "License");
|
| | | * you may not use this file except in compliance with the License.
|
| | | * You may obtain a copy of the License at
|
| | | *
|
| | | * http://www.apache.org/licenses/LICENSE-2.0
|
| | | *
|
| | | * Unless required by applicable law or agreed to in writing, software
|
| | | * distributed under the License is distributed on an "AS IS" BASIS,
|
| | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| | | * See the License for the specific language governing permissions and
|
| | | * limitations under the License.
|
| | | */
|
| | | package com.gitblit.wicket.pages;
|
| | |
|
| | | import java.util.Date;
|
| | |
|
| | | import org.apache.wicket.PageParameters;
|
| | | import org.apache.wicket.RestartResponseException;
|
| | | 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;
|
| | | import org.apache.wicket.model.IModel;
|
| | | import org.apache.wicket.model.Model;
|
| | |
|
| | | import com.gitblit.models.RepositoryModel;
|
| | | import com.gitblit.models.UserModel;
|
| | | import com.gitblit.tickets.TicketMilestone;
|
| | | import com.gitblit.utils.StringUtils;
|
| | | import com.gitblit.utils.TimeUtils;
|
| | | import com.gitblit.wicket.GitBlitWebSession;
|
| | | import com.gitblit.wicket.WicketUtils;
|
| | |
|
| | | /**
|
| | | * Page for creating a new milestone.
|
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | | public class NewMilestonePage extends RepositoryPage {
|
| | |
|
| | | private IModel<String> nameModel;
|
| | |
|
| | | private IModel<Date> dueModel;
|
| | |
|
| | | public NewMilestonePage(PageParameters params) {
|
| | | super(params);
|
| | |
|
| | | RepositoryModel model = getRepositoryModel();
|
| | | if (!app().tickets().isAcceptingTicketUpdates(model)) {
|
| | | // ticket service is read-only
|
| | | throw new RestartResponseException(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
|
| | | }
|
| | |
|
| | | UserModel currentUser = GitBlitWebSession.get().getUser();
|
| | | if (currentUser == null) {
|
| | | currentUser = UserModel.ANONYMOUS;
|
| | | }
|
| | |
|
| | | if (!currentUser.isAuthenticated || !currentUser.canAdmin(model)) {
|
| | | // administration prohibited
|
| | | throw new RestartResponseException(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
|
| | | }
|
| | |
|
| | | setStatelessHint(false);
|
| | | setOutputMarkupId(true);
|
| | |
|
| | | Form<Void> form = new Form<Void>("editForm");
|
| | | add(form);
|
| | |
|
| | | nameModel = Model.of("");
|
| | | dueModel = Model.of(new Date(System.currentTimeMillis() + TimeUtils.ONEDAY));
|
| | |
|
| | | 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") {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | 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;
|
| | | }
|
| | |
|
| | | Date due = dueModel.getObject();
|
| | |
|
| | | UserModel currentUser = GitBlitWebSession.get().getUser();
|
| | | String createdBy = currentUser.username;
|
| | |
|
| | | milestone = app().tickets().createMilestone(getRepositoryModel(), name, createdBy);
|
| | | if (milestone != null) {
|
| | | milestone.due = due;
|
| | | app().tickets().updateMilestone(getRepositoryModel(), milestone, createdBy);
|
| | | throw new RestartResponseException(TicketsPage.class, WicketUtils.newRepositoryParameter(getRepositoryModel().name));
|
| | | } else {
|
| | | // TODO error
|
| | | }
|
| | | }
|
| | | });
|
| | |
|
| | | Button cancel = new Button("cancel") {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public void onSubmit() {
|
| | | setResponsePage(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
|
| | | }
|
| | | };
|
| | | cancel.setDefaultFormProcessing(false);
|
| | | form.add(cancel);
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected String getPageName() {
|
| | | return getString("gb.newMilestone");
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected Class<? extends BasePage> getRepoNavPageClass() {
|
| | | return TicketsPage.class;
|
| | | }
|
| | | }
|
| | |
| | | </div>
|
| | | <div class="tab-pane" id="milestones">
|
| | | <div class="row">
|
| | | <div class="span9" wicket:id="milestoneList" style="padding-bottom: 10px;">
|
| | | <h3><span wicket:id="milestoneName"></span> <small><span wicket:id="milestoneState"></span></small></h3>
|
| | | <i style="color:#888;"class="fa fa-calendar"></i> <span wicket:id="milestoneDue"></span>
|
| | | <span class="span12" style="padding-bottom:10px;" wicket:id="newMilestone"></span>
|
| | | </div>
|
| | |
|
| | | <div class="row">
|
| | | <div class="span12"><h2><wicket:message key="gb.openMilestones"></wicket:message></h2></div>
|
| | | <div class="span12"><hr/></div>
|
| | | </div> |
| | | <div class="row">
|
| | | <div class="span4" wicket:id="openMilestonesList" style="padding-bottom: 20px;">
|
| | | <div wicket:id="entryPanel"></div>
|
| | | </div>
|
| | | </div>
|
| | |
|
| | | <div class="row">
|
| | | <div class="span12"><h2><wicket:message key="gb.closedMilestones"></wicket:message></h2></div>
|
| | | <div class="span12"><hr/></div>
|
| | | </div> |
| | | <div class="row">
|
| | | <div class="span4" wicket:id="closedMilestonesList" style="padding-bottom: 15px;">
|
| | | <div wicket:id="entryPanel"></div>
|
| | | </div>
|
| | | </div>
|
| | | |
| | | </div>
|
| | | </div>
|
| | |
|
| | | <wicket:fragment wicket:id="milestoneListFragment">
|
| | | <h3><span wicket:id="milestoneName"></span> <small><span wicket:id="milestoneState"></span></small></h3>
|
| | | <i style="color:#888;"class="fa fa-calendar"></i> <span wicket:id="milestoneDue"></span> <span wicket:id="editMilestone"></span>
|
| | | <div wicket:id="milestonePanel"></div>
|
| | | </wicket:fragment>
|
| | |
|
| | | <wicket:fragment wicket:id="openMilestoneFragment">
|
| | | <div style="clear:both;padding-bottom: 10px;">
|
| | | <div style="margin-bottom: 5px;" class="progress progress-success">
|
| | | <div class="bar" wicket:id="progress"></div>
|
| | | </div>
|
| | | <div class="milestoneOverview">
|
| | | <span wicket:id="openTickets" />,
|
| | | <span wicket:id="closedTickets" />,
|
| | | <span wicket:id="totalTickets" />
|
| | | </div>
|
| | | </div>
|
| | | </wicket:fragment>
|
| | |
|
| | | <wicket:fragment wicket:id="noMilestoneFragment">
|
| | | <table style="width: 100%;padding-bottom: 5px;">
|
| | | <tbody>
|
| | |
| | | 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;
|
| | |
| | | import com.gitblit.Constants.AccessPermission;
|
| | | import com.gitblit.Keys;
|
| | | import com.gitblit.models.RegistrantAccessPermission;
|
| | | import com.gitblit.models.RepositoryModel;
|
| | | import com.gitblit.models.TicketModel;
|
| | | import com.gitblit.models.TicketModel.Status;
|
| | | import com.gitblit.models.UserModel;
|
| | |
| | | };
|
| | | add(ticketsView);
|
| | |
|
| | | List<TicketMilestone> allMilestones = app().tickets().getMilestones(getRepositoryModel());
|
| | | ListDataProvider<TicketMilestone> allMilestonesDp = new ListDataProvider<TicketMilestone>(allMilestones);
|
| | | DataView<TicketMilestone> milestonesList = new DataView<TicketMilestone>("milestoneList", allMilestonesDp) {
|
| | | // new milestone link
|
| | | RepositoryModel repositoryModel = getRepositoryModel();
|
| | | final boolean acceptingUpdates = app().tickets().isAcceptingTicketUpdates(repositoryModel)
|
| | | && user != null && user.canAdmin(getRepositoryModel());
|
| | | if (acceptingUpdates) {
|
| | | add(new LinkPanel("newMilestone", null, getString("gb.newMilestone"),
|
| | | NewMilestonePage.class, WicketUtils.newRepositoryParameter(repositoryName)));
|
| | | } else {
|
| | | add(new Label("newMilestone").setVisible(false));
|
| | | }
|
| | |
|
| | | // milestones list
|
| | | List<TicketMilestone> openMilestones = new ArrayList<TicketMilestone>();
|
| | | List<TicketMilestone> closedMilestones = new ArrayList<TicketMilestone>();
|
| | | for (TicketMilestone milestone : app().tickets().getMilestones(repositoryModel)) {
|
| | | if (milestone.isOpen()) {
|
| | | openMilestones.add(milestone);
|
| | | } else {
|
| | | closedMilestones.add(milestone);
|
| | | }
|
| | | }
|
| | | Collections.sort(openMilestones, new Comparator<TicketMilestone>() {
|
| | | @Override
|
| | | public int compare(TicketMilestone o1, TicketMilestone o2) {
|
| | | return o2.due.compareTo(o1.due);
|
| | | }
|
| | | });
|
| | |
|
| | | Collections.sort(closedMilestones, new Comparator<TicketMilestone>() {
|
| | | @Override
|
| | | public int compare(TicketMilestone o1, TicketMilestone o2) {
|
| | | return o2.due.compareTo(o1.due);
|
| | | }
|
| | | });
|
| | |
|
| | | DataView<TicketMilestone> openMilestonesList = milestoneList("openMilestonesList", openMilestones, acceptingUpdates);
|
| | | add(openMilestonesList);
|
| | |
|
| | | DataView<TicketMilestone> closedMilestonesList = milestoneList("closedMilestonesList", closedMilestones, acceptingUpdates);
|
| | | add(closedMilestonesList);
|
| | | }
|
| | |
|
| | | protected DataView<TicketMilestone> milestoneList(String wicketId, List<TicketMilestone> milestones, final boolean acceptingUpdates) {
|
| | | ListDataProvider<TicketMilestone> milestonesDp = new ListDataProvider<TicketMilestone>(milestones);
|
| | | DataView<TicketMilestone> milestonesList = new DataView<TicketMilestone>(wicketId, milestonesDp) {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public void populateItem(final Item<TicketMilestone> item) {
|
| | | Fragment entryPanel = new Fragment("entryPanel", "milestoneListFragment", this);
|
| | | item.add(entryPanel);
|
| | |
|
| | | final TicketMilestone tm = item.getModelObject();
|
| | | PageParameters params = queryParameters(null, tm.name, null, null, null, desc, 1);
|
| | | item.add(new LinkPanel("milestoneName", null, tm.name, TicketsPage.class, params).setRenderBodyOnly(true));
|
| | | PageParameters params = queryParameters(null, tm.name, null, null, null, true, 1);
|
| | | entryPanel.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);
|
| | | entryPanel.add(stateLabel);
|
| | |
|
| | | if (tm.due == null) {
|
| | | item.add(new Label("milestoneDue", getString("gb.notSpecified")));
|
| | | entryPanel.add(new Label("milestoneDue", getString("gb.notSpecified")));
|
| | | } else {
|
| | | item.add(WicketUtils.createDatestampLabel("milestoneDue", tm.due, getTimeZone(), getTimeUtils()));
|
| | | entryPanel.add(WicketUtils.createDatestampLabel("milestoneDue", tm.due, getTimeZone(), getTimeUtils()));
|
| | | }
|
| | | if (acceptingUpdates) {
|
| | | entryPanel.add(new LinkPanel("editMilestone", null, getString("gb.edit"), EditMilestonePage.class,
|
| | | WicketUtils.newObjectParameter(repositoryName, tm.name)));
|
| | | } else {
|
| | | entryPanel.add(new Label("editMilestone").setVisible(false));
|
| | | }
|
| | |
|
| | | if (tm.isOpen()) {
|
| | | // re-load milestone with query results
|
| | | TicketMilestone m = app().tickets().getMilestone(getRepositoryModel(), tm.name);
|
| | |
|
| | | Fragment milestonePanel = new Fragment("milestonePanel", "openMilestoneFragment", this);
|
| | | Label label = new Label("progress");
|
| | | WicketUtils.setCssStyle(label, "width:" + tm.getProgress() + "%;");
|
| | | milestonePanel.add(label);
|
| | |
|
| | | milestonePanel.add(new LinkPanel("openTickets", null,
|
| | | MessageFormat.format(getString("gb.nOpenTickets"), m.getOpenTickets()),
|
| | | TicketsPage.class,
|
| | | queryParameters(null, tm.name, openStatii, null, null, true, 1)));
|
| | |
|
| | | milestonePanel.add(new LinkPanel("closedTickets", null,
|
| | | MessageFormat.format(getString("gb.nClosedTickets"), m.getClosedTickets()),
|
| | | TicketsPage.class,
|
| | | queryParameters(null, tm.name, closedStatii, null, null, true, 1)));
|
| | |
|
| | | milestonePanel.add(new Label("totalTickets", MessageFormat.format(getString("gb.nTotalTickets"), m.getTotalTickets())));
|
| | | entryPanel.add(milestonePanel);
|
| | | } else {
|
| | | entryPanel.add(new Label("milestonePanel").setVisible(false));
|
| | | }
|
| | | }
|
| | | };
|
| | | add(milestonesList);
|
| | | return milestonesList;
|
| | | }
|
| | |
|
| | | protected PageParameters queryParameters(
|