src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java

changeset 86
0a658e53177c
parent 83
24a3596b8f98
child 88
1438e5a22c55
equal deleted inserted replaced
85:3d16ad54b3dc 86:0a658e53177c
30 30
31 31
32 import de.uapcore.lightpit.*; 32 import de.uapcore.lightpit.*;
33 import de.uapcore.lightpit.dao.DataAccessObjects; 33 import de.uapcore.lightpit.dao.DataAccessObjects;
34 import de.uapcore.lightpit.entities.*; 34 import de.uapcore.lightpit.entities.*;
35 import de.uapcore.lightpit.viewmodel.*;
35 import org.slf4j.Logger; 36 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory; 37 import org.slf4j.LoggerFactory;
37 38
38 import javax.servlet.annotation.WebServlet; 39 import javax.servlet.annotation.WebServlet;
39 import javax.servlet.http.HttpServletRequest; 40 import javax.servlet.http.HttpServletRequest;
40 import javax.servlet.http.HttpServletResponse; 41 import javax.servlet.http.HttpServletResponse;
41 import javax.servlet.http.HttpSession; 42 import javax.servlet.http.HttpSession;
42 import java.io.IOException; 43 import java.io.IOException;
43 import java.sql.Date; 44 import java.sql.Date;
44 import java.sql.SQLException; 45 import java.sql.SQLException;
45 import java.util.*; 46 import java.util.ArrayList;
47 import java.util.List;
48 import java.util.NoSuchElementException;
49 import java.util.Objects;
46 import java.util.stream.Collectors; 50 import java.util.stream.Collectors;
47 import java.util.stream.Stream; 51 import java.util.stream.Stream;
48 52
49 import static de.uapcore.lightpit.Functions.fqn; 53 import static de.uapcore.lightpit.Functions.fqn;
50 54
57 private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class); 61 private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
58 62
59 public static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project"); 63 public static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project");
60 public static final String SESSION_ATTR_SELECTED_ISSUE = fqn(ProjectsModule.class, "selected_issue"); 64 public static final String SESSION_ATTR_SELECTED_ISSUE = fqn(ProjectsModule.class, "selected_issue");
61 public static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version"); 65 public static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version");
62 public static final String SESSION_ATTR_HIDE_ZEROS = fqn(ProjectsModule.class, "stats_hide_zeros");
63 66
64 private class SessionSelection { 67 private class SessionSelection {
65 final HttpSession session; 68 final HttpSession session;
69 final HttpServletRequest req;
70 final DataAccessObjects dao;
66 Project project; 71 Project project;
67 Version version; 72 Version version;
68 Issue issue; 73 Issue issue;
69 74
70 SessionSelection(HttpServletRequest req, Project project) { 75 SessionSelection(HttpServletRequest req, DataAccessObjects dao) {
71 this.session = req.getSession(); 76 this.req = req;
72 this.project = project; 77 this.dao = dao;
78 session = req.getSession();
79 }
80
81 void newProject() {
82 project = null;
73 version = null; 83 version = null;
74 issue = null; 84 issue = null;
75 updateAttributes(); 85 updateAttributes();
76 } 86 project = new Project(-1);
77 87 updateAttributes();
78 SessionSelection(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 88 }
79 this.session = req.getSession(); 89
80 final var issueDao = dao.getIssueDao(); 90 void newVersion() throws SQLException {
81 final var projectDao = dao.getProjectDao(); 91 project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
82 final var issueSelection = getParameter(req, Integer.class, "issue"); 92 syncProject();
83 if (issueSelection.isPresent()) { 93 version = null;
84 issue = issueDao.find(issueSelection.get()); 94 issue = null;
85 } else { 95 updateAttributes();
86 final var issue = (Issue) session.getAttribute(SESSION_ATTR_SELECTED_ISSUE); 96 version = new Version(-1);
87 this.issue = issue == null ? null : issueDao.find(issue.getId()); 97 version.setProject(project);
88 } 98 updateAttributes();
89 if (issue != null) { 99 }
90 version = null; // show the issue globally 100
91 project = projectDao.find(issue.getProject().getId()); 101 void newIssue() throws SQLException {
92 } 102 project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
93 103 syncProject();
104 version = null;
105 issue = null;
106 updateAttributes();
107 issue = new Issue(-1);
108 issue.setProject(project);
109 updateAttributes();
110 }
111
112 void selectVersion(Version selectedVersion) throws SQLException {
113 issue = null;
114 version = selectedVersion;
115 if (!version.getProject().equals(project)) {
116 project = dao.getProjectDao().find(version.getProject().getId());
117 }
118 // our object contains more details
119 version.setProject(project);
120 updateAttributes();
121 }
122
123 void selectIssue(Issue selectedIssue) throws SQLException {
124 issue = selectedIssue;
125 if (!issue.getProject().equals(project)) {
126 project = dao.getProjectDao().find(issue.getProject().getId());
127 }
128 // our object contains more details
129 issue.setProject(project);
130 if (!issue.getResolvedVersions().contains(version) && !issue.getScheduledVersions().contains(version)
131 && !issue.getAffectedVersions().contains(version)) {
132 version = null;
133 }
134 updateAttributes();
135 }
136
137 void syncProject() throws SQLException {
94 final var projectSelection = getParameter(req, Integer.class, "pid"); 138 final var projectSelection = getParameter(req, Integer.class, "pid");
95 if (projectSelection.isPresent()) { 139 if (projectSelection.isPresent()) {
96 final var selectedProject = projectDao.find(projectSelection.get()); 140 final var selectedProject = dao.getProjectDao().find(projectSelection.get());
97 if (!Objects.equals(selectedProject, project)) { 141 if (!Objects.equals(selectedProject, project)) {
98 // reset version and issue if project changed 142 // reset version and issue if project changed
99 version = null; 143 version = null;
100 issue = null; 144 issue = null;
101 } 145 }
102 project = selectedProject; 146 project = selectedProject;
103 } else { 147 } else {
104 final var sessionProject = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); 148 project = project == null ? null : dao.getProjectDao().find(project.getId());
105 project = sessionProject == null ? null : projectDao.find(sessionProject.getId()); 149 }
106 } 150 }
107 updateAttributes(); 151
108 } 152 void syncVersion() throws SQLException {
109 153 final var versionSelection = getParameter(req, Integer.class, "vid");
110 void selectVersion(Version version) { 154 if (versionSelection.isPresent()) {
111 this.project = version.getProject(); 155 if (versionSelection.get() < 0) {
112 this.version = version; 156 version = null;
113 this.issue = null; 157 } else {
114 updateAttributes(); 158 final var selectedVersion = dao.getVersionDao().find(versionSelection.get());
115 } 159 if (!Objects.equals(selectedVersion, version)) {
116 160 issue = null;
117 void selectIssue(Issue issue) { 161 }
118 this.project = issue.getProject(); 162 selectVersion(selectedVersion);
119 this.issue = issue; 163 }
120 this.version = null; 164 } else {
121 updateAttributes(); 165 version = version == null ? null : dao.getVersionDao().find(version.getId());
122 } 166 }
123 167 }
124 void updateAttributes() { 168
169 void syncIssue() throws SQLException {
170 final var issueSelection = getParameter(req, Integer.class, "issue");
171 if (issueSelection.isPresent()) {
172 final var selectedIssue = dao.getIssueDao().find(issueSelection.get());
173 dao.getIssueDao().joinVersionInformation(selectedIssue);
174 selectIssue(selectedIssue);
175 } else {
176 issue = issue == null ? null : dao.getIssueDao().find(issue.getId());
177 }
178 }
179
180 void sync() throws SQLException {
181 project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
182 version = (Version) session.getAttribute(SESSION_ATTR_SELECTED_VERSION);
183 issue = (Issue) session.getAttribute(SESSION_ATTR_SELECTED_ISSUE);
184
185 syncProject();
186 syncVersion();
187 syncIssue();
188
189 updateAttributes();
190 }
191
192 private void updateAttributes() {
125 session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, project); 193 session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, project);
126 session.setAttribute(SESSION_ATTR_SELECTED_VERSION, version); 194 session.setAttribute(SESSION_ATTR_SELECTED_VERSION, version);
127 session.setAttribute(SESSION_ATTR_SELECTED_ISSUE, issue); 195 session.setAttribute(SESSION_ATTR_SELECTED_ISSUE, issue);
128 } 196 }
129 }
130
131 private void setAttributeHideZeros(HttpServletRequest req) {
132 final Boolean value;
133 final var param = getParameter(req, Boolean.class, "reduced");
134 if (param.isPresent()) {
135 value = param.get();
136 req.getSession().setAttribute(SESSION_ATTR_HIDE_ZEROS, value);
137 } else {
138 final var sessionValue = req.getSession().getAttribute(SESSION_ATTR_HIDE_ZEROS);
139 if (sessionValue != null) {
140 value = (Boolean) sessionValue;
141 } else {
142 value = false;
143 req.getSession().setAttribute(SESSION_ATTR_HIDE_ZEROS, value);
144 }
145 }
146 req.setAttribute("statsHideZeros", value);
147 } 197 }
148 198
149 @Override 199 @Override
150 protected String getResourceBundleName() { 200 protected String getResourceBundleName() {
151 return "localization.projects"; 201 return "localization.projects";
160 210
161 /** 211 /**
162 * Creates the breadcrumb menu. 212 * Creates the breadcrumb menu.
163 * 213 *
164 * @param level the current active level (0: root, 1: project, 2: version, 3: issue list, 4: issue) 214 * @param level the current active level (0: root, 1: project, 2: version, 3: issue list, 4: issue)
165 * @param sessionSelection the currently selected objects 215 * @param selection the currently selected objects
166 * @return a dynamic breadcrumb menu trying to display as many levels as possible 216 * @return a dynamic breadcrumb menu trying to display as many levels as possible
167 */ 217 */
168 private List<MenuEntry> getBreadcrumbs(int level, SessionSelection sessionSelection) { 218 private List<MenuEntry> getBreadcrumbs(int level, SessionSelection selection) {
169 MenuEntry entry; 219 MenuEntry entry;
170 220
171 final var breadcrumbs = new ArrayList<MenuEntry>(); 221 final var breadcrumbs = new ArrayList<MenuEntry>();
172 entry = new MenuEntry(new ResourceKey("localization.lightpit", "menu.projects"), 222 entry = new MenuEntry(new ResourceKey("localization.lightpit", "menu.projects"),
173 "projects/"); 223 "projects/");
174 breadcrumbs.add(entry); 224 breadcrumbs.add(entry);
175 if (level == BREADCRUMB_LEVEL_ROOT) entry.setActive(true); 225 if (level == BREADCRUMB_LEVEL_ROOT) entry.setActive(true);
176 226
177 if (sessionSelection.project != null) { 227 if (selection.project != null) {
178 if (sessionSelection.project.getId() < 0) { 228 if (selection.project.getId() < 0) {
179 entry = new MenuEntry(new ResourceKey("localization.projects", "button.create"), 229 entry = new MenuEntry(new ResourceKey("localization.projects", "button.create"),
180 "projects/edit"); 230 "projects/edit");
181 } else { 231 } else {
182 entry = new MenuEntry(sessionSelection.project.getName(), 232 entry = new MenuEntry(selection.project.getName(),
183 "projects/view?pid=" + sessionSelection.project.getId()); 233 "projects/view?pid=" + selection.project.getId());
184 } 234 }
185 if (level == BREADCRUMB_LEVEL_PROJECT) entry.setActive(true); 235 if (level == BREADCRUMB_LEVEL_PROJECT) entry.setActive(true);
186 breadcrumbs.add(entry); 236 breadcrumbs.add(entry);
187 } 237 }
188 238
189 if (sessionSelection.version != null) { 239 if (selection.version != null) {
190 if (sessionSelection.version.getId() < 0) { 240 if (selection.version.getId() < 0) {
191 entry = new MenuEntry(new ResourceKey("localization.projects", "button.version.create"), 241 entry = new MenuEntry(new ResourceKey("localization.projects", "button.version.create"),
192 "projects/versions/edit"); 242 "projects/versions/edit");
193 } else { 243 } else {
194 entry = new MenuEntry(sessionSelection.version.getName(), 244 entry = new MenuEntry(selection.version.getName(),
195 // TODO: change link to issue overview for that version 245 "projects/versions/view?vid=" + selection.version.getId());
196 "projects/versions/edit?id=" + sessionSelection.version.getId());
197 } 246 }
198 if (level == BREADCRUMB_LEVEL_VERSION) entry.setActive(true); 247 if (level == BREADCRUMB_LEVEL_VERSION) entry.setActive(true);
199 breadcrumbs.add(entry); 248 breadcrumbs.add(entry);
200 } 249 }
201 250
202 if (sessionSelection.project != null) { 251 if (selection.project != null) {
252 String path = "projects/issues/?pid=" + selection.project.getId();
253 if (selection.version != null) {
254 path += "&vid="+selection.version.getId();
255 }
203 entry = new MenuEntry(new ResourceKey("localization.projects", "menu.issues"), 256 entry = new MenuEntry(new ResourceKey("localization.projects", "menu.issues"),
204 // TODO: maybe also add selected version 257 path);
205 "projects/issues/?pid=" + sessionSelection.project.getId());
206 if (level == BREADCRUMB_LEVEL_ISSUE_LIST) entry.setActive(true); 258 if (level == BREADCRUMB_LEVEL_ISSUE_LIST) entry.setActive(true);
207 breadcrumbs.add(entry); 259 breadcrumbs.add(entry);
208 } 260 }
209 261
210 if (sessionSelection.issue != null) { 262 if (selection.issue != null) {
211 if (sessionSelection.issue.getId() < 0) { 263 if (selection.issue.getId() < 0) {
212 entry = new MenuEntry(new ResourceKey("localization.projects", "button.issue.create"), 264 entry = new MenuEntry(new ResourceKey("localization.projects", "button.issue.create"),
213 "projects/issues/edit"); 265 "projects/issues/edit");
214 } else { 266 } else {
215 entry = new MenuEntry("#" + sessionSelection.issue.getId(), 267 entry = new MenuEntry("#" + selection.issue.getId(),
216 // TODO: maybe change link to a view rather than directly opening the editor 268 // TODO: maybe change link to a view rather than directly opening the editor
217 "projects/issues/edit?id=" + sessionSelection.issue.getId()); 269 "projects/issues/edit?issue=" + selection.issue.getId());
218 } 270 }
219 if (level == BREADCRUMB_LEVEL_ISSUE) entry.setActive(true); 271 if (level == BREADCRUMB_LEVEL_ISSUE) entry.setActive(true);
220 breadcrumbs.add(entry); 272 breadcrumbs.add(entry);
221 } 273 }
222 274
224 } 276 }
225 277
226 @RequestMapping(method = HttpMethod.GET) 278 @RequestMapping(method = HttpMethod.GET)
227 public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 279 public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
228 final var sessionSelection = new SessionSelection(req, dao); 280 final var sessionSelection = new SessionSelection(req, dao);
229 final var projectList = dao.getProjectDao().list(); 281 sessionSelection.sync();
230 req.setAttribute("projects", projectList); 282
283 final var projectDao = dao.getProjectDao();
284 final var versionDao = dao.getVersionDao();
285
286 final var projectList = projectDao.list();
287
288 final var viewModel = new ProjectIndexView();
289 for (var project : projectList) {
290 final var info = new ProjectInfo(project);
291 info.setVersions(versionDao.list(project));
292 info.setIssueSummary(projectDao.getIssueSummary(project));
293 viewModel.getProjects().add(info);
294 }
295
296 setViewModel(req, viewModel);
231 setContentPage(req, "projects"); 297 setContentPage(req, "projects");
232 setStylesheet(req, "projects"); 298 setStylesheet(req, "projects");
233 299
234 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ROOT, sessionSelection)); 300 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ROOT, sessionSelection));
235 301
236 return ResponseType.HTML; 302 return ResponseType.HTML;
237 } 303 }
238 304
239 private void configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 305 private ProjectEditView configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
240 req.setAttribute("project", selection.project); 306 final var viewModel = new ProjectEditView();
241 req.setAttribute("users", dao.getUserDao().list()); 307 viewModel.setProject(selection.project);
308 viewModel.setUsers(dao.getUserDao().list());
309 setViewModel(req, viewModel);
242 setContentPage(req, "project-form"); 310 setContentPage(req, "project-form");
243 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, selection)); 311 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, selection));
312 return viewModel;
244 } 313 }
245 314
246 @RequestMapping(requestPath = "edit", method = HttpMethod.GET) 315 @RequestMapping(requestPath = "edit", method = HttpMethod.GET)
247 public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 316 public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
248 final var selection = new SessionSelection(req, findByParameter(req, Integer.class, "id", 317 final var selection = new SessionSelection(req, dao);
249 dao.getProjectDao()::find).orElse(new Project(-1))); 318 if (getParameter(req, Integer.class, "pid").isEmpty()) {
319 selection.newProject();
320 } else {
321 selection.sync();
322 }
250 323
251 configureEditForm(req, dao, selection); 324 configureEditForm(req, dao, selection);
252 325
253 return ResponseType.HTML; 326 return ResponseType.HTML;
254 } 327 }
270 343
271 setRedirectLocation(req, "./projects/"); 344 setRedirectLocation(req, "./projects/");
272 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 345 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
273 LOG.debug("Successfully updated project {}", project.getName()); 346 LOG.debug("Successfully updated project {}", project.getName());
274 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 347 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
275 // TODO: set request attribute with error text
276 LOG.warn("Form validation failure: {}", ex.getMessage()); 348 LOG.warn("Form validation failure: {}", ex.getMessage());
277 LOG.debug("Details:", ex); 349 LOG.debug("Details:", ex);
278 configureEditForm(req, dao, new SessionSelection(req, project)); 350 final var selection = new SessionSelection(req, dao);
351 selection.project = project;
352 final var vm = configureEditForm(req, dao, selection);
353 vm.setErrorText(ex.getMessage()); // TODO: error text
279 } 354 }
280 355
281 return ResponseType.HTML; 356 return ResponseType.HTML;
282 } 357 }
283 358
284 @RequestMapping(requestPath = "view", method = HttpMethod.GET) 359 @RequestMapping(requestPath = "view", method = HttpMethod.GET)
285 public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { 360 public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
286 final var sessionSelection = new SessionSelection(req, dao); 361 final var selection = new SessionSelection(req, dao);
287 if (sessionSelection.project == null) { 362 selection.sync();
363
364 if (selection.project == null) {
288 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); 365 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
289 return ResponseType.NONE; 366 return ResponseType.NONE;
290 } 367 }
291 368
292 final var versionDao = dao.getVersionDao(); 369 final var versionDao = dao.getVersionDao();
293 final var versions = versionDao.list(sessionSelection.project); 370 final var issueDao = dao.getIssueDao();
294 final var statsAffected = new ArrayList<VersionStatistics>(); 371
295 final var statsScheduled = new ArrayList<VersionStatistics>(); 372 final var viewModel = new ProjectView(selection.project);
296 final var statsResolved = new ArrayList<VersionStatistics>(); 373 final var issues = issueDao.list(selection.project);
297 for (Version version : versions) { 374 for (var issue : issues) issueDao.joinVersionInformation(issue);
298 statsAffected.add(versionDao.statsOpenedIssues(version)); 375 viewModel.setIssues(issues);
299 statsScheduled.add(versionDao.statsScheduledIssues(version)); 376 viewModel.setVersions(versionDao.list(selection.project));
300 statsResolved.add(versionDao.statsResolvedIssues(version)); 377 viewModel.updateVersionInfo();
301 } 378 setViewModel(req, viewModel);
302 379
303 setAttributeHideZeros(req); 380 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, selection));
304
305 req.setAttribute("project", sessionSelection.project);
306 req.setAttribute("versions", versions);
307 req.setAttribute("statsAffected", statsAffected);
308 req.setAttribute("statsScheduled", statsScheduled);
309 req.setAttribute("statsResolved", statsResolved);
310
311 req.setAttribute("issueStatusEnum", IssueStatus.values());
312 req.setAttribute("issueCategoryEnum", IssueCategory.values());
313
314 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, sessionSelection));
315 setContentPage(req, "project-details"); 381 setContentPage(req, "project-details");
316 setStylesheet(req, "projects"); 382 setStylesheet(req, "projects");
317 383
318 return ResponseType.HTML; 384 return ResponseType.HTML;
319 } 385 }
320 386
321 private void configureEditVersionForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 387 @RequestMapping(requestPath = "versions/view", method = HttpMethod.GET)
322 final var versionDao = dao.getVersionDao(); 388 public ResponseType viewVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
323 req.setAttribute("projects", dao.getProjectDao().list()); 389 final var selection = new SessionSelection(req, dao);
324 req.setAttribute("version", selection.version); 390 selection.sync();
325 req.setAttribute("versionStatusEnum", VersionStatus.values()); 391 if (selection.version == null) {
326 392 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
327 req.setAttribute("issueStatusEnum", IssueStatus.values()); 393 return ResponseType.NONE;
328 req.setAttribute("issueCategoryEnum", IssueCategory.values()); 394 }
329 req.setAttribute("statsAffected", versionDao.statsOpenedIssues(selection.version)); 395
330 req.setAttribute("statsScheduled", versionDao.statsScheduledIssues(selection.version)); 396 final var issueDao = dao.getIssueDao();
331 req.setAttribute("statsResolved", versionDao.statsResolvedIssues(selection.version)); 397 final var viewModel = new VersionView(selection.version);
332 setAttributeHideZeros(req); 398 final var issues = issueDao.list(selection.version);
333 399 for (var issue : issues) issueDao.joinVersionInformation(issue);
400 viewModel.setIssues(issues);
401 setViewModel(req, viewModel);
402
403 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_VERSION, selection));
404 setContentPage(req, "version");
405 setStylesheet(req, "projects");
406
407 return ResponseType.HTML;
408 }
409
410 private VersionEditView configureEditVersionForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
411 final var viewModel = new VersionEditView(selection.version);
412 if (selection.version.getProject() == null) {
413 viewModel.setProjects(dao.getProjectDao().list());
414 }
415 setViewModel(req, viewModel);
334 setContentPage(req, "version-form"); 416 setContentPage(req, "version-form");
335 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_VERSION, selection)); 417 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_VERSION, selection));
418 return viewModel;
336 } 419 }
337 420
338 @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET) 421 @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET)
339 public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 422 public ResponseType editVersion(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
340 final var sessionSelection = new SessionSelection(req, dao); 423 final var selection = new SessionSelection(req, dao);
341 424 if (getParameter(req, Integer.class, "vid").isEmpty()) {
342 sessionSelection.selectVersion(findByParameter(req, Integer.class, "id", dao.getVersionDao()::find) 425 selection.newVersion();
343 .orElse(new Version(-1, sessionSelection.project))); 426 } else {
344 configureEditVersionForm(req, dao, sessionSelection); 427 selection.sync();
428 }
429
430 configureEditVersionForm(req, dao, selection);
345 431
346 return ResponseType.HTML; 432 return ResponseType.HTML;
347 } 433 }
348 434
349 @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST) 435 @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST)
350 public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 436 public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
351 final var sessionSelection = new SessionSelection(req, dao); 437
352 438 var version = new Version(-1);
353 var version = new Version(-1, sessionSelection.project);
354 try { 439 try {
355 version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project); 440 version = new Version(getParameter(req, Integer.class, "id").orElseThrow());
441 version.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
356 version.setName(getParameter(req, String.class, "name").orElseThrow()); 442 version.setName(getParameter(req, String.class, "name").orElseThrow());
357 getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal); 443 getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
358 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); 444 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
359 dao.getVersionDao().saveOrUpdate(version); 445 dao.getVersionDao().saveOrUpdate(version);
360 446
361 // specifying the pid parameter will purposely reset the session selected version! 447 // specifying the pid parameter will purposely reset the session selected version!
362 setRedirectLocation(req, "./projects/view?pid="+sessionSelection.project.getId()); 448 setRedirectLocation(req, "./projects/view?pid="+version.getProject().getId());
363 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 449 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
364 LOG.debug("Successfully updated version {} for project {}", version.getName(), sessionSelection.project.getName());
365 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 450 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
366 // TODO: set request attribute with error text
367 LOG.warn("Form validation failure: {}", ex.getMessage()); 451 LOG.warn("Form validation failure: {}", ex.getMessage());
368 LOG.debug("Details:", ex); 452 LOG.debug("Details:", ex);
369 sessionSelection.selectVersion(version); 453 final var selection = new SessionSelection(req, dao);
370 configureEditVersionForm(req, dao, sessionSelection); 454 selection.selectVersion(version);
371 } 455 final var viewModel = configureEditVersionForm(req, dao, selection);
372 456 // TODO: set Error Text
373 return ResponseType.HTML; 457 }
374 } 458
375 459 return ResponseType.HTML;
376 private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 460 }
377 461
378 if (selection.issue.getProject() == null || selection.issue.getProject().getId() < 0) { 462 private IssueEditView configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
379 req.setAttribute("projects", dao.getProjectDao().list()); 463 final var viewModel = new IssueEditView(selection.issue);
380 req.setAttribute("versions", Collections.<Version>emptyList()); 464
465 if (selection.issue.getProject() == null) {
466 viewModel.setProjects(dao.getProjectDao().list());
381 } else { 467 } else {
382 req.setAttribute("projects", Collections.<Project>emptyList()); 468 viewModel.setVersions(dao.getVersionDao().list(selection.issue.getProject()));
383 req.setAttribute("versions", dao.getVersionDao().list(selection.issue.getProject())); 469 }
384 } 470 viewModel.setUsers(dao.getUserDao().list());
385 471 setViewModel(req, viewModel);
386 dao.getIssueDao().joinVersionInformation(selection.issue);
387 req.setAttribute("issue", selection.issue);
388 req.setAttribute("issueStatusEnum", IssueStatus.values());
389 req.setAttribute("issueCategoryEnum", IssueCategory.values());
390 req.setAttribute("users", dao.getUserDao().list());
391 472
392 setContentPage(req, "issue-form"); 473 setContentPage(req, "issue-form");
393 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE, selection)); 474 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE, selection));
475 return viewModel;
394 } 476 }
395 477
396 @RequestMapping(requestPath = "issues/", method = HttpMethod.GET) 478 @RequestMapping(requestPath = "issues/", method = HttpMethod.GET)
397 public ResponseType issues(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { 479 public ResponseType issues(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
398 final var sessionSelection = new SessionSelection(req, dao); 480 final var selection = new SessionSelection(req, dao);
399 if (sessionSelection.project == null) { 481 selection.sync();
482 if (selection.project == null) {
400 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); 483 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
401 return ResponseType.NONE; 484 return ResponseType.NONE;
402 } 485 }
403 486
404 req.setAttribute("issues", dao.getIssueDao().list(sessionSelection.project)); 487 final var viewModel = new IssuesView();
405 488 viewModel.setProject(selection.project);
406 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE_LIST, sessionSelection)); 489 if (selection.version == null) {
490 viewModel.setIssues(dao.getIssueDao().list(selection.project));
491 } else {
492 viewModel.setVersion(selection.version);
493 viewModel.setIssues(dao.getIssueDao().list(selection.version));
494 }
495 setViewModel(req, viewModel);
496
497 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE_LIST, selection));
407 setContentPage(req, "issues"); 498 setContentPage(req, "issues");
408 setStylesheet(req, "projects"); 499 setStylesheet(req, "projects");
409 500
410 return ResponseType.HTML; 501 return ResponseType.HTML;
411 } 502 }
412 503
413 @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET) 504 @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET)
414 public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 505 public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
415 final var sessionSelection = new SessionSelection(req, dao); 506 final var selection = new SessionSelection(req, dao);
416 507 if (getParameter(req, Integer.class, "issue").isEmpty()) {
417 sessionSelection.selectIssue(findByParameter(req, Integer.class, "id", 508 selection.newIssue();
418 dao.getIssueDao()::find).orElse(new Issue(-1, sessionSelection.project))); 509 } else {
419 configureEditIssueForm(req, dao, sessionSelection); 510 selection.sync();
511 }
512
513 configureEditIssueForm(req, dao, selection);
420 514
421 return ResponseType.HTML; 515 return ResponseType.HTML;
422 } 516 }
423 517
424 @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST) 518 @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST)
425 public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 519 public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
426 final var sessionSelection = new SessionSelection(req, dao); 520
427 521 Issue issue = new Issue(-1);
428 Issue issue = new Issue(-1, sessionSelection.project);
429 try { 522 try {
430 issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project); 523 issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
524 issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
431 getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory); 525 getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
432 getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus); 526 getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
433 issue.setSubject(getParameter(req, String.class, "subject").orElseThrow()); 527 issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
434 getParameter(req, Integer.class, "assignee").map( 528 getParameter(req, Integer.class, "assignee").map(
435 userid -> userid >= 0 ? new User(userid) : null 529 userid -> userid >= 0 ? new User(userid) : null
438 getParameter(req, Date.class, "eta").ifPresent(issue::setEta); 532 getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
439 533
440 getParameter(req, Integer[].class, "affected") 534 getParameter(req, Integer[].class, "affected")
441 .map(Stream::of) 535 .map(Stream::of)
442 .map(stream -> 536 .map(stream ->
443 stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) 537 stream.map(Version::new).collect(Collectors.toList())
444 ).ifPresent(issue::setAffectedVersions); 538 ).ifPresent(issue::setAffectedVersions);
445 getParameter(req, Integer[].class, "scheduled") 539 getParameter(req, Integer[].class, "scheduled")
446 .map(Stream::of) 540 .map(Stream::of)
447 .map(stream -> 541 .map(stream ->
448 stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) 542 stream.map(Version::new).collect(Collectors.toList())
449 ).ifPresent(issue::setScheduledVersions); 543 ).ifPresent(issue::setScheduledVersions);
450 getParameter(req, Integer[].class, "resolved") 544 getParameter(req, Integer[].class, "resolved")
451 .map(Stream::of) 545 .map(Stream::of)
452 .map(stream -> 546 .map(stream ->
453 stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) 547 stream.map(Version::new).collect(Collectors.toList())
454 ).ifPresent(issue::setResolvedVersions); 548 ).ifPresent(issue::setResolvedVersions);
455 549
456 dao.getIssueDao().saveOrUpdate(issue); 550 dao.getIssueDao().saveOrUpdate(issue);
457 551
458 // specifying the issue parameter keeps the edited issue as breadcrumb 552 // specifying the issue parameter keeps the edited issue as breadcrumb
459 setRedirectLocation(req, "./projects/issues/?issue="+issue.getId()); 553 setRedirectLocation(req, "./projects/issues/?issue="+issue.getId());
460 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 554 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
461 LOG.debug("Successfully updated issue {} for project {}", issue.getId(), sessionSelection.project.getName());
462 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 555 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
463 // TODO: set request attribute with error text 556 // TODO: set request attribute with error text
464 LOG.warn("Form validation failure: {}", ex.getMessage()); 557 LOG.warn("Form validation failure: {}", ex.getMessage());
465 LOG.debug("Details:", ex); 558 LOG.debug("Details:", ex);
466 sessionSelection.selectIssue(issue); 559 final var selection = new SessionSelection(req, dao);
467 configureEditIssueForm(req, dao, sessionSelection); 560 selection.selectIssue(issue);
561 final var viewModel = configureEditIssueForm(req, dao, selection);
562 // TODO: set Error Text
468 } 563 }
469 564
470 return ResponseType.HTML; 565 return ResponseType.HTML;
471 } 566 }
472 } 567 }

mercurial