Fri, 22 May 2020 16:21:59 +0200
some reformatting
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2018 Mike Becker. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29 package de.uapcore.lightpit.modules;
32 import de.uapcore.lightpit.*;
33 import de.uapcore.lightpit.dao.DataAccessObjects;
34 import de.uapcore.lightpit.entities.*;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import javax.servlet.annotation.WebServlet;
39 import javax.servlet.http.HttpServletRequest;
40 import javax.servlet.http.HttpServletResponse;
41 import java.io.IOException;
42 import java.sql.SQLException;
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.NoSuchElementException;
46 import java.util.Optional;
48 import static de.uapcore.lightpit.Functions.fqn;
50 @LightPITModule(
51 bundleBaseName = "localization.projects",
52 modulePath = "projects",
53 defaultPriority = 20
54 )
55 @WebServlet(
56 name = "ProjectsModule",
57 urlPatterns = "/projects/*"
58 )
59 public final class ProjectsModule extends AbstractLightPITServlet {
61 private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
63 public static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected-project");
65 private Project getSelectedProject(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
66 final var projectDao = dao.getProjectDao();
67 final var session = req.getSession();
68 final var projectSelection = getParameter(req, Integer.class, "pid");
69 final Project selectedProject;
70 if (projectSelection.isPresent()) {
71 selectedProject = projectDao.find(projectSelection.get());
72 } else {
73 final var sessionProject = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
74 selectedProject = sessionProject == null ? null : projectDao.find(sessionProject.getId());
75 }
76 session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, selectedProject);
77 return selectedProject;
78 }
81 /**
82 * Creates the breadcrumb menu.
83 *
84 * @param level the current active level
85 * @param selectedProject the selected project, if any, or null
86 * @return a dynamic breadcrumb menu trying to display as many levels as possible
87 */
88 private List<MenuEntry> getBreadcrumbs(int level,
89 Project selectedProject) {
90 MenuEntry entry;
92 final var breadcrumbs = new ArrayList<MenuEntry>();
93 entry = new MenuEntry(new ResourceKey("localization.projects", "menuLabel"),
94 "projects/", 0);
95 breadcrumbs.add(entry);
96 if (level == 0) entry.setActive(true);
98 if (selectedProject == null)
99 return breadcrumbs;
101 entry = new MenuEntry(selectedProject.getName(),
102 "projects/view?pid=" + selectedProject.getId(), 1);
103 if (level == 1) entry.setActive(true);
105 breadcrumbs.add(entry);
106 return breadcrumbs;
107 }
109 @RequestMapping(method = HttpMethod.GET)
110 public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
112 final var projectList = dao.getProjectDao().list();
113 req.setAttribute("projects", projectList);
114 setDynamicFragment(req, "projects");
115 setStylesheet(req, "projects");
117 final var selectedProject = getSelectedProject(req, dao);
118 setBreadcrumbs(req, getBreadcrumbs(0, selectedProject));
120 return ResponseType.HTML;
121 }
123 private void configureEditForm(HttpServletRequest req, DataAccessObjects dao, Optional<Project> project) throws SQLException {
124 if (project.isPresent()) {
125 req.setAttribute("project", project.get());
126 setBreadcrumbs(req, getBreadcrumbs(1, project.get()));
127 } else {
128 req.setAttribute("project", new Project(-1));
129 setBreadcrumbs(req, getBreadcrumbs(0, null));
130 }
132 req.setAttribute("users", dao.getUserDao().list());
133 setDynamicFragment(req, "project-form");
134 }
136 @RequestMapping(requestPath = "edit", method = HttpMethod.GET)
137 public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
139 Optional<Project> project = findByParameter(req, Integer.class, "id", dao.getProjectDao()::find);
140 configureEditForm(req, dao, project);
141 if (project.isPresent()) {
142 req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, project.get());
143 }
145 return ResponseType.HTML;
146 }
148 @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
149 public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
151 Project project = null;
152 try {
153 project = new Project(getParameter(req, Integer.class, "id").orElseThrow());
154 project.setName(getParameter(req, String.class, "name").orElseThrow());
155 getParameter(req, String.class, "description").ifPresent(project::setDescription);
156 getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
157 getParameter(req, Integer.class, "owner").map(
158 ownerId -> ownerId >= 0 ? new User(ownerId) : null
159 ).ifPresent(project::setOwner);
161 dao.getProjectDao().saveOrUpdate(project);
163 setRedirectLocation(req, "./projects/");
164 setDynamicFragment(req, Constants.DYN_FRAGMENT_COMMIT_SUCCESSFUL);
165 LOG.debug("Successfully updated project {}", project.getName());
166 } catch (NoSuchElementException | NumberFormatException | SQLException ex) {
167 // TODO: set request attribute with error text
168 LOG.warn("Form validation failure: {}", ex.getMessage());
169 LOG.debug("Details:", ex);
170 configureEditForm(req, dao, Optional.ofNullable(project));
171 }
173 return ResponseType.HTML;
174 }
176 @RequestMapping(requestPath = "view", method = HttpMethod.GET)
177 public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
178 final var selectedProject = getSelectedProject(req, dao);
179 if (selectedProject == null) {
180 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
181 return ResponseType.NONE;
182 }
184 req.setAttribute("versions", dao.getVersionDao().list(selectedProject));
185 req.setAttribute("issues", dao.getIssueDao().list(selectedProject));
187 // TODO: add more levels depending on last visited location
188 setBreadcrumbs(req, getBreadcrumbs(1, selectedProject));
190 setDynamicFragment(req, "project-details");
192 return ResponseType.HTML;
193 }
195 private void configureEditVersionForm(HttpServletRequest req, Optional<Version> version, Project selectedProject) {
196 req.setAttribute("version", version.orElse(new Version(-1, selectedProject)));
197 req.setAttribute("versionStatusEnum", VersionStatus.values());
199 setDynamicFragment(req, "version-form");
200 }
202 @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET)
203 public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
204 final var selectedProject = getSelectedProject(req, dao);
205 if (selectedProject == null) {
206 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
207 return ResponseType.NONE;
208 }
210 configureEditVersionForm(req,
211 findByParameter(req, Integer.class, "id", dao.getVersionDao()::find),
212 selectedProject);
214 return ResponseType.HTML;
215 }
217 @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST)
218 public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
219 final var selectedProject = getSelectedProject(req, dao);
220 if (selectedProject == null) {
221 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
222 return ResponseType.NONE;
223 }
225 Version version = null;
226 try {
227 version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), selectedProject);
228 version.setName(getParameter(req, String.class, "name").orElseThrow());
229 getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
230 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
231 dao.getVersionDao().saveOrUpdate(version);
233 setRedirectLocation(req, "./projects/versions/");
234 setDynamicFragment(req, Constants.DYN_FRAGMENT_COMMIT_SUCCESSFUL);
235 LOG.debug("Successfully updated version {} for project {}", version.getName(), selectedProject.getName());
236 } catch (NoSuchElementException | NumberFormatException | SQLException ex) {
237 // TODO: set request attribute with error text
238 LOG.warn("Form validation failure: {}", ex.getMessage());
239 LOG.debug("Details:", ex);
240 configureEditVersionForm(req, Optional.ofNullable(version), selectedProject);
241 }
243 return ResponseType.HTML;
244 }
246 private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, Optional<Issue> issue, Project selectedProject) throws SQLException {
248 req.setAttribute("issue", issue.orElse(new Issue(-1, selectedProject)));
249 req.setAttribute("issueStatusEnum", IssueStatus.values());
250 req.setAttribute("issueCategoryEnum", IssueCategory.values());
251 req.setAttribute("versions", dao.getVersionDao().list(selectedProject));
253 setDynamicFragment(req, "issue-form");
254 }
256 @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET)
257 public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
258 final var selectedProject = getSelectedProject(req, dao);
259 if (selectedProject == null) {
260 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
261 return ResponseType.NONE;
262 }
264 configureEditIssueForm(req, dao,
265 findByParameter(req, Integer.class, "id", dao.getIssueDao()::find),
266 selectedProject);
268 return ResponseType.HTML;
269 }
271 @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST)
272 public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
273 final var selectedProject = getSelectedProject(req, dao);
274 if (selectedProject == null) {
275 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
276 return ResponseType.NONE;
277 }
279 Issue issue = null;
280 try {
281 issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), selectedProject);
283 // TODO: implement
285 dao.getIssueDao().saveOrUpdate(issue);
287 setRedirectLocation(req, "./projects/issues/");
288 setDynamicFragment(req, Constants.DYN_FRAGMENT_COMMIT_SUCCESSFUL);
289 LOG.debug("Successfully updated issue {} for project {}", issue.getId(), selectedProject.getName());
290 } catch (NoSuchElementException | NumberFormatException | SQLException ex) {
291 // TODO: set request attribute with error text
292 LOG.warn("Form validation failure: {}", ex.getMessage());
293 LOG.debug("Details:", ex);
294 configureEditIssueForm(req, dao, Optional.ofNullable(issue), selectedProject);
295 }
297 return ResponseType.HTML;
298 }
299 }