Paul Martin
2014-09-29 f9c78c0ccc709509cdf7f83c45c898883d329db2
commit | author | age
5e3521 1 /*
JM 2  * Copyright 2014 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit.wicket.pages;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.List;
0476cf 22 import java.util.Set;
JM 23 import java.util.TreeSet;
5e3521 24
JM 25 import org.apache.wicket.PageParameters;
9d60ff 26 import org.apache.wicket.ajax.AjaxRequestTarget;
JM 27 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
5e3521 28 import org.apache.wicket.markup.html.basic.Label;
JM 29 import org.apache.wicket.markup.html.form.Button;
30 import org.apache.wicket.markup.html.form.DropDownChoice;
31 import org.apache.wicket.markup.html.form.Form;
32 import org.apache.wicket.markup.html.form.TextField;
33 import org.apache.wicket.markup.html.panel.Fragment;
34 import org.apache.wicket.model.IModel;
35 import org.apache.wicket.model.Model;
cc1c3f 36 import org.eclipse.jgit.lib.Repository;
5e3521 37
cc1c3f 38 import com.gitblit.Constants;
5e3521 39 import com.gitblit.Constants.AccessPermission;
0476cf 40 import com.gitblit.Constants.AuthorizationControl;
5e3521 41 import com.gitblit.models.RegistrantAccessPermission;
JM 42 import com.gitblit.models.TicketModel;
43 import com.gitblit.models.TicketModel.Change;
44 import com.gitblit.models.TicketModel.Field;
45 import com.gitblit.models.TicketModel.Status;
46 import com.gitblit.models.UserModel;
47 import com.gitblit.tickets.TicketMilestone;
48 import com.gitblit.tickets.TicketNotifier;
49 import com.gitblit.tickets.TicketResponsible;
50 import com.gitblit.utils.StringUtils;
51 import com.gitblit.wicket.GitBlitWebSession;
52 import com.gitblit.wicket.WicketUtils;
53 import com.gitblit.wicket.panels.MarkdownTextArea;
54
55 /**
56  * Page for creating a new ticket.
57  *
58  * @author James Moger
59  *
60  */
61 public class NewTicketPage extends RepositoryPage {
62
63     private IModel<TicketModel.Type> typeModel;
64
65     private IModel<String> titleModel;
66
67     private MarkdownTextArea descriptionEditor;
68
69     private IModel<String> topicModel;
70
cc1c3f 71     private IModel<String> mergeToModel;
JM 72
5e3521 73     private IModel<TicketResponsible> responsibleModel;
JM 74
75     private IModel<TicketMilestone> milestoneModel;
76
77     private Label descriptionPreview;
f9c78c 78     
PM 79     private IModel<TicketModel.Priority> priorityModel;
80     
81     private IModel<TicketModel.Severity> severityModel;
5e3521 82
JM 83     public NewTicketPage(PageParameters params) {
84         super(params);
85
86         UserModel currentUser = GitBlitWebSession.get().getUser();
87         if (currentUser == null) {
88             currentUser = UserModel.ANONYMOUS;
89         }
90
91         if (!currentUser.isAuthenticated || !app().tickets().isAcceptingNewTickets(getRepositoryModel())) {
92             // tickets prohibited
93             setResponsePage(TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName));
94         }
95
96         typeModel = Model.of(TicketModel.Type.defaultType);
a59627 97         titleModel = Model.of();
JM 98         topicModel = Model.of();
f1b882 99         mergeToModel = Model.of(Repository.shortenRefName(getRepositoryModel().mergeTo));
5e3521 100         responsibleModel = Model.of();
JM 101         milestoneModel = Model.of();
f9c78c 102         severityModel = Model.of(TicketModel.Severity.defaultSeverity);
PM 103         priorityModel = Model.of(TicketModel.Priority.defaultPriority);
5e3521 104
JM 105         setStatelessHint(false);
106         setOutputMarkupId(true);
107
9d60ff 108         Form<Void> form = new Form<Void>("editForm");
5e3521 109         add(form);
JM 110
111         form.add(new DropDownChoice<TicketModel.Type>("type", typeModel, Arrays.asList(TicketModel.Type.choices())));
112         form.add(new TextField<String>("title", titleModel));
113         form.add(new TextField<String>("topic", topicModel));
114
a59627 115         final IModel<String> markdownPreviewModel = Model.of();
5e3521 116         descriptionPreview = new Label("descriptionPreview", markdownPreviewModel);
JM 117         descriptionPreview.setEscapeModelStrings(false);
118         descriptionPreview.setOutputMarkupId(true);
119         form.add(descriptionPreview);
120
121         descriptionEditor = new MarkdownTextArea("description", markdownPreviewModel, descriptionPreview);
122         descriptionEditor.setRepository(repositoryName);
123         form.add(descriptionEditor);
124
cc1c3f 125         if (currentUser.canAdmin(null, getRepositoryModel())) {
5e3521 126             // responsible
0476cf 127             Set<String> userlist = new TreeSet<String>();
JM 128
129             if (UserModel.ANONYMOUS.canPush(getRepositoryModel())
130                     || AuthorizationControl.AUTHENTICATED == getRepositoryModel().authorizationControl) {
131                 //     authorization is ANONYMOUS or AUTHENTICATED (i.e. all users can be set responsible)
132                 userlist.addAll(app().users().getAllUsernames());
133             } else {
134                 // authorization is by NAMED users (users with PUSH permission can be set responsible)
135                 for (RegistrantAccessPermission rp : app().repositories().getUserAccessPermissions(getRepositoryModel())) {
136                     if (rp.permission.atLeast(AccessPermission.PUSH)) {
137                         userlist.add(rp.registrant);
5e3521 138                     }
JM 139                 }
0476cf 140             }
JM 141
142             List<TicketResponsible> responsibles = new ArrayList<TicketResponsible>();
143             for (String username : userlist) {
144                 UserModel user = app().users().getUserModel(username);
145                 if (user != null && !user.disabled) {
146                     TicketResponsible responsible = new TicketResponsible(user);
147                     responsibles.add(responsible);
eb6129 148                 }
5e3521 149             }
JM 150             Collections.sort(responsibles);
151             Fragment responsible = new Fragment("responsible", "responsibleFragment", this);
152             responsible.add(new DropDownChoice<TicketResponsible>("responsible", responsibleModel, responsibles));
153             form.add(responsible.setVisible(!responsibles.isEmpty()));
154
155             // milestone
156             List<TicketMilestone> milestones = app().tickets().getMilestones(getRepositoryModel(), Status.Open);
157             Fragment milestone = new Fragment("milestone", "milestoneFragment", this);
158             milestone.add(new DropDownChoice<TicketMilestone>("milestone", milestoneModel, milestones));
159             form.add(milestone.setVisible(!milestones.isEmpty()));
cc1c3f 160
f9c78c 161             // priority
PM 162             form.add(new DropDownChoice<TicketModel.Priority>("priority", priorityModel, Arrays.asList(TicketModel.Priority.choices())));
163             
164             //severity
165             form.add(new DropDownChoice<TicketModel.Severity>("severity", severityModel, Arrays.asList(TicketModel.Severity.choices())));
166             
cc1c3f 167             // integration branch
JM 168             List<String> branches = new ArrayList<String>();
169             for (String branch : getRepositoryModel().getLocalBranches()) {
170                 // exclude ticket branches
171                 if (!branch.startsWith(Constants.R_TICKET)) {
172                     branches.add(Repository.shortenRefName(branch));
173                 }
174             }
175             branches.remove(Repository.shortenRefName(getRepositoryModel().HEAD));
176             branches.add(0, Repository.shortenRefName(getRepositoryModel().HEAD));
177
178             Fragment mergeto = new Fragment("mergeto", "mergeToFragment", this);
179             mergeto.add(new DropDownChoice<String>("mergeto", mergeToModel, branches));
180             form.add(mergeto.setVisible(!branches.isEmpty()));
5e3521 181         } else {
cc1c3f 182             // user does not have permission to assign milestone, responsible, or mergeto
5e3521 183             form.add(new Label("responsible").setVisible(false));
JM 184             form.add(new Label("milestone").setVisible(false));
cc1c3f 185             form.add(new Label("mergeto").setVisible(false));
5e3521 186         }
JM 187
9d60ff 188         form.add(new AjaxButton("create") {
JM 189
190             private static final long serialVersionUID = 1L;
191
192             @Override
193             protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
194                 String title = titleModel.getObject();
195                 if (StringUtils.isEmpty(title)) {
196                     return;
197                 }
198
199                 String createdBy = GitBlitWebSession.get().getUsername();
200                 Change change = new Change(createdBy);
201                 change.setField(Field.title, title);
202                 change.setField(Field.body, descriptionEditor.getText());
203                 String topic = topicModel.getObject();
204                 if (!StringUtils.isEmpty(topic)) {
205                     change.setField(Field.topic, topic);
206                 }
207
208                 // type
209                 TicketModel.Type type = TicketModel.Type.defaultType;
210                 if (typeModel.getObject() != null) {
211                     type = typeModel.getObject();
212                 }
213                 change.setField(Field.type, type);
214
215                 // responsible
216                 TicketResponsible responsible = responsibleModel == null ? null : responsibleModel.getObject();
217                 if (responsible != null) {
218                     change.setField(Field.responsible, responsible.username);
219                 }
220
221                 // milestone
222                 TicketMilestone milestone = milestoneModel == null ? null : milestoneModel.getObject();
223                 if (milestone != null) {
224                     change.setField(Field.milestone, milestone.name);
225                 }
f9c78c 226                 
PM 227                 // severity
228                 TicketModel.Severity severity = TicketModel.Severity.defaultSeverity;
229                 if (severityModel.getObject() != null) {
230                     severity = severityModel.getObject();
231                 }
232                 change.setField(Field.severity, severity);
233                 
234                 // priority
235                 TicketModel.Priority priority = TicketModel.Priority.defaultPriority;
236                 if (priorityModel.getObject() != null) {
237                     priority = priorityModel.getObject();
238                 }
239                 change.setField(Field.priority, priority);
9d60ff 240
JM 241                 // integration branch
242                 String mergeTo = mergeToModel.getObject();
243                 if (!StringUtils.isEmpty(mergeTo)) {
244                     change.setField(Field.mergeTo, mergeTo);
245                 }
246
247                 TicketModel ticket = app().tickets().createTicket(getRepositoryModel(), 0L, change);
248                 if (ticket != null) {
249                     TicketNotifier notifier = app().tickets().createNotifier();
250                     notifier.sendMailing(ticket);
2598bf 251
60bbdf 252                     redirectTo(TicketsPage.class, WicketUtils.newObjectParameter(getRepositoryModel().name, "" + ticket.number));
9d60ff 253                 } else {
JM 254                     // TODO error
255                 }
256             }
257         });
258
5e3521 259         Button cancel = new Button("cancel") {
JM 260             private static final long serialVersionUID = 1L;
261
262             @Override
263             public void onSubmit() {
264                 setResponsePage(TicketsPage.class, getPageParameters());
265             }
266         };
267         cancel.setDefaultFormProcessing(false);
268         form.add(cancel);
269
270     }
271
272     @Override
273     protected String getPageName() {
274         return getString("gb.newTicket");
275     }
276
277     @Override
278     protected Class<? extends BasePage> getRepoNavPageClass() {
279         return TicketsPage.class;
280     }
281 }