James Moger
2015-11-22 ed552ba47c02779c270ffd62841d6d1048dade70
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;
e2c0c9 78
f9c78c 79     private IModel<TicketModel.Priority> priorityModel;
e2c0c9 80
f9c78c 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));
e2c0c9 114         form.add(new DropDownChoice<TicketModel.Severity>("severity", severityModel, Arrays.asList(TicketModel.Severity.choices())));
5e3521 115
a59627 116         final IModel<String> markdownPreviewModel = Model.of();
5e3521 117         descriptionPreview = new Label("descriptionPreview", markdownPreviewModel);
JM 118         descriptionPreview.setEscapeModelStrings(false);
119         descriptionPreview.setOutputMarkupId(true);
120         form.add(descriptionPreview);
121
122         descriptionEditor = new MarkdownTextArea("description", markdownPreviewModel, descriptionPreview);
123         descriptionEditor.setRepository(repositoryName);
124         form.add(descriptionEditor);
125
cc1c3f 126         if (currentUser.canAdmin(null, getRepositoryModel())) {
5e3521 127             // responsible
0476cf 128             Set<String> userlist = new TreeSet<String>();
JM 129
130             if (UserModel.ANONYMOUS.canPush(getRepositoryModel())
131                     || AuthorizationControl.AUTHENTICATED == getRepositoryModel().authorizationControl) {
132                 //     authorization is ANONYMOUS or AUTHENTICATED (i.e. all users can be set responsible)
133                 userlist.addAll(app().users().getAllUsernames());
134             } else {
135                 // authorization is by NAMED users (users with PUSH permission can be set responsible)
136                 for (RegistrantAccessPermission rp : app().repositories().getUserAccessPermissions(getRepositoryModel())) {
137                     if (rp.permission.atLeast(AccessPermission.PUSH)) {
138                         userlist.add(rp.registrant);
5e3521 139                     }
JM 140                 }
0476cf 141             }
JM 142
143             List<TicketResponsible> responsibles = new ArrayList<TicketResponsible>();
144             for (String username : userlist) {
145                 UserModel user = app().users().getUserModel(username);
146                 if (user != null && !user.disabled) {
147                     TicketResponsible responsible = new TicketResponsible(user);
148                     responsibles.add(responsible);
eb6129 149                 }
5e3521 150             }
JM 151             Collections.sort(responsibles);
152             Fragment responsible = new Fragment("responsible", "responsibleFragment", this);
153             responsible.add(new DropDownChoice<TicketResponsible>("responsible", responsibleModel, responsibles));
154             form.add(responsible.setVisible(!responsibles.isEmpty()));
155
156             // milestone
157             List<TicketMilestone> milestones = app().tickets().getMilestones(getRepositoryModel(), Status.Open);
158             Fragment milestone = new Fragment("milestone", "milestoneFragment", this);
159             milestone.add(new DropDownChoice<TicketMilestone>("milestone", milestoneModel, milestones));
160             form.add(milestone.setVisible(!milestones.isEmpty()));
cc1c3f 161
f9c78c 162             // priority
e2c0c9 163             Fragment priority = new Fragment("priority", "priorityFragment", this);
JM 164             priority.add(new DropDownChoice<TicketModel.Priority>("priority", priorityModel, Arrays.asList(TicketModel.Priority.choices())));
165             form.add(priority);
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));
e2c0c9 186             form.add(new Label("priority").setVisible(false));
5e3521 187         }
JM 188
9d60ff 189         form.add(new AjaxButton("create") {
JM 190
191             private static final long serialVersionUID = 1L;
192
193             @Override
194             protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
195                 String title = titleModel.getObject();
196                 if (StringUtils.isEmpty(title)) {
197                     return;
198                 }
199
200                 String createdBy = GitBlitWebSession.get().getUsername();
201                 Change change = new Change(createdBy);
202                 change.setField(Field.title, title);
203                 change.setField(Field.body, descriptionEditor.getText());
204                 String topic = topicModel.getObject();
205                 if (!StringUtils.isEmpty(topic)) {
206                     change.setField(Field.topic, topic);
207                 }
208
209                 // type
210                 TicketModel.Type type = TicketModel.Type.defaultType;
211                 if (typeModel.getObject() != null) {
212                     type = typeModel.getObject();
213                 }
214                 change.setField(Field.type, type);
215
216                 // responsible
217                 TicketResponsible responsible = responsibleModel == null ? null : responsibleModel.getObject();
218                 if (responsible != null) {
219                     change.setField(Field.responsible, responsible.username);
220                 }
221
222                 // milestone
223                 TicketMilestone milestone = milestoneModel == null ? null : milestoneModel.getObject();
224                 if (milestone != null) {
225                     change.setField(Field.milestone, milestone.name);
226                 }
e2c0c9 227
f9c78c 228                 // severity
PM 229                 TicketModel.Severity severity = TicketModel.Severity.defaultSeverity;
230                 if (severityModel.getObject() != null) {
231                     severity = severityModel.getObject();
232                 }
233                 change.setField(Field.severity, severity);
e2c0c9 234
f9c78c 235                 // priority
PM 236                 TicketModel.Priority priority = TicketModel.Priority.defaultPriority;
237                 if (priorityModel.getObject() != null) {
238                     priority = priorityModel.getObject();
239                 }
240                 change.setField(Field.priority, priority);
9d60ff 241
JM 242                 // integration branch
243                 String mergeTo = mergeToModel.getObject();
244                 if (!StringUtils.isEmpty(mergeTo)) {
245                     change.setField(Field.mergeTo, mergeTo);
246                 }
247
248                 TicketModel ticket = app().tickets().createTicket(getRepositoryModel(), 0L, change);
249                 if (ticket != null) {
250                     TicketNotifier notifier = app().tickets().createNotifier();
251                     notifier.sendMailing(ticket);
2598bf 252
60bbdf 253                     redirectTo(TicketsPage.class, WicketUtils.newObjectParameter(getRepositoryModel().name, "" + ticket.number));
9d60ff 254                 } else {
JM 255                     // TODO error
256                 }
257             }
258         });
259
5e3521 260         Button cancel = new Button("cancel") {
JM 261             private static final long serialVersionUID = 1L;
262
263             @Override
264             public void onSubmit() {
265                 setResponsePage(TicketsPage.class, getPageParameters());
266             }
267         };
268         cancel.setDefaultFormProcessing(false);
269         form.add(cancel);
270
271     }
272
273     @Override
274     protected String getPageName() {
275         return getString("gb.newTicket");
276     }
277
278     @Override
279     protected Class<? extends BasePage> getRepoNavPageClass() {
280         return TicketsPage.class;
281     }
282 }