James Moger
2014-04-25 ce048e750f7ae986dddfc8ab9b57750114d2b7b9
Create and update milestone pages with rename support
4 files added
7 files modified
440 ■■■■■ changed files
src/main/java/com/gitblit/tickets/ITicketService.java 31 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/tickets/TicketLabel.java 7 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/tickets/TicketMilestone.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties 4 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html 38 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java 166 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html 37 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java 123 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/TicketsPage.html 5 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/TicketsPage.java 21 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/tickets/ITicketService.java
@@ -49,6 +49,7 @@
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;
@@ -556,9 +557,10 @@
    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;
@@ -639,6 +641,21 @@
     * @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 send ticket notifications
     * @return true if successful
     * @since 1.6.0
     */
    public synchronized boolean renameMilestone(RepositoryModel repository, String oldName, String newName, String createdBy, boolean notify) {
        if (StringUtils.isEmpty(newName)) {
            throw new IllegalArgumentException("new milestone can not be empty!");
        }
@@ -651,7 +668,7 @@
            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();
@@ -663,9 +680,13 @@
                Change change = new Change(createdBy);
                change.setField(Field.milestone, newName);
                TicketModel ticket = updateTicket(repository, qr.number, change);
                notifier.queueMailing(ticket);
                if (notify && ticket.isOpen()) {
                    notifier.queueMailing(ticket);
                }
            }
            notifier.sendAll();
            if (notify) {
                notifier.sendAll();
            }
            return true;
        } catch (IOException e) {
src/main/java/com/gitblit/tickets/TicketLabel.java
@@ -30,14 +30,17 @@
    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);
    }
src/main/java/com/gitblit/tickets/TicketMilestone.java
@@ -37,6 +37,10 @@
        super(name);
        status = Status.Open;
    }
    public void setDue(Date due) {
        this.due = due;
    }
    public int getProgress() {
        int total = getTotalTickets();
src/main/java/com/gitblit/wicket/GitBlitWebApp.java
@@ -51,6 +51,7 @@
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;
@@ -63,6 +64,7 @@
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;
@@ -187,6 +189,8 @@
        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");
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -671,4 +671,6 @@
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
src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html
New file
@@ -0,0 +1,38 @@
<!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></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>
        </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" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /></div>
    </div>
    </div>
    </form>
</div>
</body>
</wicket:extend>
</html>
src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java
New file
@@ -0,0 +1,166 @@
/*
 * 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.extensions.markup.html.form.DateTextField;
import org.apache.wicket.markup.html.form.Button;
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 org.parboiled.common.StringUtils;
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.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") {
            private static final long serialVersionUID = 1L;
            @Override
            protected void onSubmit() {
                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
                }
            }
        };
        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"));
        List<Status> statusChoices = Arrays.asList(Status.Open, Status.Closed);
        form.add(new DropDownChoice<TicketModel.Status>("status", statusModel, statusChoices));
        form.add(new Button("save"));
        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.editMilestone");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return TicketsPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html
New file
@@ -0,0 +1,37 @@
<!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></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" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /></div>
    </div>
    </div>
    </form>
</div>
</body>
</wicket:extend>
</html>
src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java
New file
@@ -0,0 +1,123 @@
/*
 * 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.extensions.markup.html.form.DateTextField;
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.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") {
            private static final long serialVersionUID = 1L;
            @Override
            protected void onSubmit() {
                String name = nameModel.getObject();
                Date due = dueModel.getObject();
                UserModel currentUser = GitBlitWebSession.get().getUser();
                String createdBy = currentUser.username;
                TicketMilestone 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
                }
            }
        };
        add(form);
        nameModel = Model.of("");
        dueModel = Model.of(new Date());
        form.add(new TextField<String>("name", nameModel));
        form.add(new DateTextField("due", dueModel, "yyyy-MM-dd"));
        form.add(new Button("create"));
        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;
    }
}
src/main/java/com/gitblit/wicket/pages/TicketsPage.html
@@ -139,9 +139,12 @@
    </div>
    <div class="tab-pane" id="milestones">
        <div class="row">
            <span class="span12" style="padding-bottom:10px;" wicket:id="newMilestone"></span>
        </div>
        <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>
                <i style="color:#888;"class="fa fa-calendar"></i> <span wicket:id="milestoneDue"></span> <span wicket:id="editMilestone"></span>
            </div>
        </div>
    </div>
src/main/java/com/gitblit/wicket/pages/TicketsPage.java
@@ -42,6 +42,7 @@
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;
@@ -646,7 +647,19 @@
        };
        add(ticketsView);
        List<TicketMilestone> allMilestones = app().tickets().getMilestones(getRepositoryModel());
        // 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> allMilestones = app().tickets().getMilestones(repositoryModel);
        ListDataProvider<TicketMilestone> allMilestonesDp = new ListDataProvider<TicketMilestone>(allMilestones);
        DataView<TicketMilestone> milestonesList = new DataView<TicketMilestone>("milestoneList", allMilestonesDp) {
            private static final long serialVersionUID = 1L;
@@ -675,6 +688,12 @@
                } else {
                    item.add(WicketUtils.createDatestampLabel("milestoneDue", tm.due, getTimeZone(), getTimeUtils()));
                }
                if (acceptingUpdates) {
                    item.add(new LinkPanel("editMilestone", null, getString("gb.edit"), EditMilestonePage.class,
                        WicketUtils.newObjectParameter(repositoryName, tm.name)));
                } else {
                    item.add(new Label("editMilestone").setVisible(false));
                }
            }
        };
        add(milestonesList);