From 841450f2e72d492c194b6a611828464d678bd749 Mon Sep 17 00:00:00 2001 From: clam Date: Mon, 13 Jun 2022 11:13:15 +0200 Subject: [PATCH 01/26] Update version to 1.5.2.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d906df697..5aef4f7ae 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ eusurvey eusurvey war - 1.5.2.7 + 1.5.2.8 1.8 4.3.20.RELEASE From 59b8594d839831e288036b2339f78d11544e24e8 Mon Sep 17 00:00:00 2001 From: clam Date: Tue, 14 Jun 2022 06:52:49 +0200 Subject: [PATCH 02/26] ESURVEY-7735 Access charts issue for privileged survey organizers --- src/main/java/com/ec/survey/controller/DelphiController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ec/survey/controller/DelphiController.java b/src/main/java/com/ec/survey/controller/DelphiController.java index 324f26a0a..135429c75 100644 --- a/src/main/java/com/ec/survey/controller/DelphiController.java +++ b/src/main/java/com/ec/survey/controller/DelphiController.java @@ -223,7 +223,8 @@ public ResponseEntity delphiGraph(HttpServletRequest re User user = sessionService.getCurrentUser(request); if (user != null) { - sessionService.upgradePrivileges(survey, user, request); + Survey draft = surveyService.getSurveyByShortname(survey.getShortname(), true, user, request, false, true, true, false); + sessionService.upgradePrivileges(draft, user, request); } boolean resultsview = request.getParameter("resultsview") != null && request.getParameter("resultsview").equalsIgnoreCase("true"); From 5ecda8e55b4e052b76c45a95d327f8321c7a8945 Mon Sep 17 00:00:00 2001 From: clam Date: Mon, 27 Jun 2022 11:23:32 +0200 Subject: [PATCH 03/26] web proxy and more logging form smt service call --- src/main/java/com/ec/survey/controller/HomeController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/ec/survey/controller/HomeController.java b/src/main/java/com/ec/survey/controller/HomeController.java index baf3cc38e..096874ad6 100644 --- a/src/main/java/com/ec/survey/controller/HomeController.java +++ b/src/main/java/com/ec/survey/controller/HomeController.java @@ -339,6 +339,7 @@ private String sendSupportSmt(HttpServletRequest request, Locale locale, ModelMa try { + sessionService.initializeProxy(); HttpClient httpclient = HttpClients.createDefault(); HttpPost httppost = new HttpPost(incidentHost); @@ -350,6 +351,8 @@ private String sendSupportSmt(HttpServletRequest request, Locale locale, ModelMa HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); + + logger.info(response.getStatusLine()); if (entity != null) { String strResponse = EntityUtils.toString(entity); From 879aed6fc8eecf0b8d7c4b5d9646e323afee3523 Mon Sep 17 00:00:00 2001 From: clam Date: Mon, 27 Jun 2022 16:20:14 +0200 Subject: [PATCH 04/26] soapAction for smt web service --- src/main/java/com/ec/survey/controller/HomeController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ec/survey/controller/HomeController.java b/src/main/java/com/ec/survey/controller/HomeController.java index 096874ad6..b02b8d087 100644 --- a/src/main/java/com/ec/survey/controller/HomeController.java +++ b/src/main/java/com/ec/survey/controller/HomeController.java @@ -348,11 +348,12 @@ private String sendSupportSmt(HttpServletRequest request, Locale locale, ModelMa if (smtpAuth != null) { httppost.addHeader("Authorization", "Basic " + smtpAuth); } + httppost.addHeader("SOAPAction", "Create"); HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); - logger.info(response.getStatusLine()); + logger.info("HTTP Code: " + response.getStatusLine().getStatusCode()); if (entity != null) { String strResponse = EntityUtils.toString(entity); From dcb6ffcb048e65501768d38826de1b7cb3fe278d Mon Sep 17 00:00:00 2001 From: clam Date: Wed, 29 Jun 2022 06:57:55 +0200 Subject: [PATCH 05/26] trim for url in smt web service --- src/main/java/com/ec/survey/controller/HomeController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/ec/survey/controller/HomeController.java b/src/main/java/com/ec/survey/controller/HomeController.java index b02b8d087..ba7388f3e 100644 --- a/src/main/java/com/ec/survey/controller/HomeController.java +++ b/src/main/java/com/ec/survey/controller/HomeController.java @@ -341,7 +341,7 @@ private String sendSupportSmt(HttpServletRequest request, Locale locale, ModelMa sessionService.initializeProxy(); HttpClient httpclient = HttpClients.createDefault(); - HttpPost httppost = new HttpPost(incidentHost); + HttpPost httppost = new HttpPost(incidentHost.trim()); httppost.setEntity(new StringEntity(createTemplate)); From e7dc86c65cd04feed701174224b2e2319f722e71 Mon Sep 17 00:00:00 2001 From: clam Date: Wed, 29 Jun 2022 10:14:59 +0200 Subject: [PATCH 06/26] minor changes and more logging for smt service call --- .../com/ec/survey/controller/HomeController.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/ec/survey/controller/HomeController.java b/src/main/java/com/ec/survey/controller/HomeController.java index ba7388f3e..34d34d736 100644 --- a/src/main/java/com/ec/survey/controller/HomeController.java +++ b/src/main/java/com/ec/survey/controller/HomeController.java @@ -343,20 +343,20 @@ private String sendSupportSmt(HttpServletRequest request, Locale locale, ModelMa HttpClient httpclient = HttpClients.createDefault(); HttpPost httppost = new HttpPost(incidentHost.trim()); - httppost.setEntity(new StringEntity(createTemplate)); + httppost.addHeader("Content-type", "text/xml;charset=UTF-8"); if (smtpAuth != null) { httppost.addHeader("Authorization", "Basic " + smtpAuth); } httppost.addHeader("SOAPAction", "Create"); - + + httppost.setEntity(new StringEntity(createTemplate)); + HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); - - logger.info("HTTP Code: " + response.getStatusLine().getStatusCode()); if (entity != null) { - String strResponse = EntityUtils.toString(entity); + String strResponse = EntityUtils.toString(entity, "UTF-8"); if (!strResponse.contains("message=\"success\"")) { logger.error(strResponse); throw new MessageException("Calling SMT web service failed."); @@ -364,6 +364,9 @@ private String sendSupportSmt(HttpServletRequest request, Locale locale, ModelMa logger.info(strResponse); } + logger.info("Mime Type: " + EntityUtils.getContentMimeType(entity)); + logger.info("Char Set: " + EntityUtils.getContentCharSet(entity)); + } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); //fallback to email From dad0b19c2cadccee488ea692d63b9b4c5bcfff7f Mon Sep 17 00:00:00 2001 From: clam Date: Wed, 29 Jun 2022 10:22:10 +0200 Subject: [PATCH 07/26] more logging for smt service call --- src/main/java/com/ec/survey/controller/HomeController.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/ec/survey/controller/HomeController.java b/src/main/java/com/ec/survey/controller/HomeController.java index 34d34d736..d0e645b2d 100644 --- a/src/main/java/com/ec/survey/controller/HomeController.java +++ b/src/main/java/com/ec/survey/controller/HomeController.java @@ -363,6 +363,11 @@ private String sendSupportSmt(HttpServletRequest request, Locale locale, ModelMa } logger.info(strResponse); } + + org.apache.http.Header[] headers = response.getAllHeaders(); + for(org.apache.http.Header h:headers){ + logger.info("Response Header " + h.getName() + ": " + h.getValue()); + } logger.info("Mime Type: " + EntityUtils.getContentMimeType(entity)); logger.info("Char Set: " + EntityUtils.getContentCharSet(entity)); From 13383ff1d4843f32f32eaecb35e8624d6c478f3d Mon Sep 17 00:00:00 2001 From: clam Date: Wed, 29 Jun 2022 11:39:24 +0200 Subject: [PATCH 08/26] Adding missing properties to smt web service call --- src/main/webapp/WEB-INF/Content/createIncident.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/webapp/WEB-INF/Content/createIncident.xml b/src/main/webapp/WEB-INF/Content/createIncident.xml index 7f861845b..dd33f5b7c 100644 --- a/src/main/webapp/WEB-INF/Content/createIncident.xml +++ b/src/main/webapp/WEB-INF/Content/createIncident.xml @@ -18,6 +18,7 @@ ZZZ_EXTERNAL USER EXTERNE + DIGIT [SUBJECT] Assigned [REASON] @@ -26,6 +27,7 @@ OTHER 2 + EUSurvey From 06356abd10821ca9e69e1617cd74fa2b05c265e6 Mon Sep 17 00:00:00 2001 From: clam Date: Wed, 29 Jun 2022 11:55:30 +0200 Subject: [PATCH 09/26] different category for smt web service call --- src/main/webapp/WEB-INF/Content/createIncident.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/WEB-INF/Content/createIncident.xml b/src/main/webapp/WEB-INF/Content/createIncident.xml index dd33f5b7c..858e4ac6a 100644 --- a/src/main/webapp/WEB-INF/Content/createIncident.xml +++ b/src/main/webapp/WEB-INF/Content/createIncident.xml @@ -27,7 +27,7 @@ OTHER 2 - EUSurvey + INFORMATION SYSTEMS INFRASTRUCTURE From aaa85ebd1b3bf4652aac458aa10e2471efedadd7 Mon Sep 17 00:00:00 2001 From: clam Date: Thu, 30 Jun 2022 07:55:03 +0200 Subject: [PATCH 10/26] different category and company for smt web service call --- src/main/webapp/WEB-INF/Content/createIncident.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/WEB-INF/Content/createIncident.xml b/src/main/webapp/WEB-INF/Content/createIncident.xml index 858e4ac6a..c18891f11 100644 --- a/src/main/webapp/WEB-INF/Content/createIncident.xml +++ b/src/main/webapp/WEB-INF/Content/createIncident.xml @@ -18,7 +18,7 @@ ZZZ_EXTERNAL USER EXTERNE - DIGIT + EXTERNE [SUBJECT] Assigned [REASON] @@ -27,7 +27,7 @@ OTHER 2 - INFORMATION SYSTEMS INFRASTRUCTURE + INCIDENT From f832731c862331d8dabe63d0afaaf12ddd329cc0 Mon Sep 17 00:00:00 2001 From: clam Date: Thu, 30 Jun 2022 08:15:01 +0200 Subject: [PATCH 11/26] updated template for smt web service call --- src/main/webapp/WEB-INF/Content/createIncident.xml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/WEB-INF/Content/createIncident.xml b/src/main/webapp/WEB-INF/Content/createIncident.xml index c18891f11..6eb31659e 100644 --- a/src/main/webapp/WEB-INF/Content/createIncident.xml +++ b/src/main/webapp/WEB-INF/Content/createIncident.xml @@ -4,9 +4,9 @@ - 2 + 3 DIGIT EUSURVEY SUPPORT - SMT + SURVEYS [MESSAGE] User name: [ADDITIONALINFOUSERNAME] @@ -15,9 +15,7 @@ Survey title: [ADDITIONALINFOSURVEYTITLE] Survey alias: [ADDITIONALINFOSURVEYALIAS] - ZZZ_EXTERNAL USER EXTERNE - EXTERNE [SUBJECT] Assigned @@ -25,9 +23,8 @@ E-MAIL OTHER - 2 + 3 - INCIDENT From 30e86ac0fec875fb1ad681ce3a33674758f9853d Mon Sep 17 00:00:00 2001 From: clam Date: Fri, 1 Jul 2022 13:40:20 +0200 Subject: [PATCH 12/26] Sprint 20 --- pom.xml | 6 +- .../ec/survey/controller/BasicController.java | 11 +- .../controller/ContributionController.java | 7 +- .../survey/controller/DelphiController.java | 32 +- .../controller/EuCaptchaApiController.java | 83 + .../ec/survey/controller/HomeController.java | 24 +- .../controller/ManagementController.java | 43 +- .../ec/survey/controller/PDFController.java | 753 ++++---- .../survey/controller/RunnerController.java | 17 +- .../controller/TranslationController.java | 2 +- .../ServerEnvironmentHandlerInterceptor.java | 8 +- src/main/java/com/ec/survey/model/Form.java | 168 +- .../java/com/ec/survey/model/Statistics.java | 8 +- .../model/delphi/DelphiQuestionType.java | 5 + .../survey/model/survey/ChoiceQuestion.java | 2 +- .../survey/model/survey/ComplexTableItem.java | 10 +- .../survey/model/survey/FormulaQuestion.java | 5 + .../survey/model/survey/GalleryQuestion.java | 47 + .../com/ec/survey/model/survey/Question.java | 12 +- .../com/ec/survey/model/survey/Survey.java | 117 +- .../java/com/ec/survey/replacements/Pair.java | 42 + ...eAuthenticationSuccessHandlerExtended.java | 203 ++- .../ec/survey/service/ActivityService.java | 2 +- .../service/AnswerExplanationService.java | 1582 ++++++++--------- .../ec/survey/service/AttendeeService.java | 23 +- .../ec/survey/service/ReportingService.java | 49 +- .../survey/service/ReportingServiceProxy.java | 4 +- .../com/ec/survey/service/SessionService.java | 20 +- .../com/ec/survey/service/SurveyService.java | 52 +- .../java/com/ec/survey/tools/QuizHelper.java | 24 +- .../com/ec/survey/tools/SurveyCreator.java | 6 + .../com/ec/survey/tools/SurveyHelper.java | 5 + .../ec/survey/tools/TranslationsHelper.java | 2 +- .../survey/tools/export/DocExportCreator.java | 10 +- .../survey/tools/export/OdfExportCreator.java | 49 +- .../tools/export/StatisticsCreator.java | 152 +- .../survey/tools/export/XlsExportCreator.java | 38 +- .../webapp/WEB-INF/Content/createIncident.xml | 9 +- .../WEB-INF/classes/messages_en.properties | 11 + .../webapp/WEB-INF/spring/spring-security.xml | 1 + src/main/webapp/WEB-INF/views/captcha.jsp | 450 ++--- .../webapp/WEB-INF/views/captchainternal.jsp | 252 +-- .../WEB-INF/views/contributions/edit.jsp | 2 +- .../webapp/WEB-INF/views/generic-messages.jsp | 9 + .../webapp/WEB-INF/views/home/helpauthors.jsp | 43 + .../WEB-INF/views/home/helpauthorsde.jsp | 48 +- .../WEB-INF/views/home/helpauthorsfr.jsp | 40 + src/main/webapp/WEB-INF/views/includes.jsp | 75 +- .../webapp/WEB-INF/views/includesrunner.jsp | 53 +- .../views/management/edittemplates.jsp | 32 +- .../WEB-INF/views/management/properties.jsp | 16 +- .../views/management/propertiesHead.jsp | 64 +- .../WEB-INF/views/management/results-ajax.jsp | 6 + .../views/management/results-charts.jsp | 216 +-- .../views/management/results-content.jsp | 13 +- .../views/management/results-statistics.jsp | 36 +- .../views/management/translations-dialog.jsp | 2 +- src/main/webapp/WEB-INF/views/menu.jsp | 8 + .../WEB-INF/views/publication/publication.jsp | 7 + .../WEB-INF/views/runner/contactForm.jsp | 366 ++-- .../WEB-INF/views/runner/elementtemplates.jsp | 36 +- .../WEB-INF/views/runner/quizResultInner.jsp | 10 +- .../views/runner/runnercontentinner.jsp | 9 +- .../views/runner/runnercontentinnerpdf.jsp | 18 +- src/main/webapp/WEB-INF/views/thanks.jsp | 2 +- src/main/webapp/WEB-INF/views/thanksdraft.jsp | 807 ++++----- .../WEB-INF/views/thanksdraftrunner.jsp | 936 +++++----- src/main/webapp/WEB-INF/views/thanksinner.jsp | 60 +- src/main/webapp/resources/css/common.css | 32 + src/main/webapp/resources/css/edit.css | 18 - src/main/webapp/resources/css/runner.css | 14 +- .../webapp/resources/css/runnerresponsive.css | 8 + .../documentation/motivation_popup_1.png | Bin 0 -> 20346 bytes .../documentation/motivation_popup_1_DE.png | Bin 0 -> 7776 bytes .../documentation/motivation_popup_1_FR.png | Bin 0 -> 24991 bytes .../documentation/motivation_popup_2.png | Bin 0 -> 32317 bytes .../documentation/motivation_popup_2_DE.png | Bin 0 -> 32317 bytes .../documentation/motivation_popup_2_FR.png | Bin 0 -> 32317 bytes src/main/webapp/resources/js/edit_actions.js | 2 +- .../webapp/resources/js/edit_navigation.js | 23 +- .../webapp/resources/js/edit_properties.js | 9 +- .../resources/js/edit_properties_helper.js | 13 +- src/main/webapp/resources/js/edit_update.js | 2 +- .../webapp/resources/js/graph_data_loader.js | 41 +- src/main/webapp/resources/js/runner2.js | 6 +- 85 files changed, 4277 insertions(+), 3151 deletions(-) create mode 100644 src/main/java/com/ec/survey/controller/EuCaptchaApiController.java create mode 100644 src/main/java/com/ec/survey/replacements/Pair.java create mode 100644 src/main/webapp/resources/images/documentation/motivation_popup_1.png create mode 100644 src/main/webapp/resources/images/documentation/motivation_popup_1_DE.png create mode 100644 src/main/webapp/resources/images/documentation/motivation_popup_1_FR.png create mode 100644 src/main/webapp/resources/images/documentation/motivation_popup_2.png create mode 100644 src/main/webapp/resources/images/documentation/motivation_popup_2_DE.png create mode 100644 src/main/webapp/resources/images/documentation/motivation_popup_2_FR.png diff --git a/pom.xml b/pom.xml index 5aef4f7ae..548c37a47 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ eusurvey eusurvey war - 1.5.2.8 + 1.5.2.9 1.8 4.3.20.RELEASE @@ -150,7 +150,7 @@ org.xhtmlrenderer flying-saucer-core - 9.0.9 + 9.1.21 @@ -163,7 +163,7 @@ org.xhtmlrenderer flying-saucer-pdf - 9.0.9 + 9.1.21 bcprov-jdk14 diff --git a/src/main/java/com/ec/survey/controller/BasicController.java b/src/main/java/com/ec/survey/controller/BasicController.java index 5203421bc..264a54790 100644 --- a/src/main/java/com/ec/survey/controller/BasicController.java +++ b/src/main/java/com/ec/survey/controller/BasicController.java @@ -148,7 +148,8 @@ public class BasicController implements BeanFactoryAware { protected ECFService ecfService; public @Value("${captcha.secret}") String captchasecret; - public @Value("${captcha.serverprefix}") String captchaserverprefix; + public @Value("${captcha.serverprefix}") String captchaserverprefix; + public @Value("${captcha.serverprefixtarget}") String captchaserverprefixtarget; public @Value("${ui.enableresponsive}") String enableresponsive; private @Value("${ecaslogout}") String ecaslogout; public @Value("${showecas}") String showecas; @@ -544,6 +545,7 @@ protected boolean checkCaptcha(HttpServletRequest request) { String token = request.getParameter("captcha_token"); String id = request.getParameter("captcha_id"); String useaudio = request.getParameter("captcha_useaudio"); + String originalcookies = request.getParameter("captcha_original_cookies"); if (token == null) { String challenge = request.getParameter("recaptcha_challenge_field"); @@ -561,11 +563,16 @@ protected boolean checkCaptcha(HttpServletRequest request) { } sessionService.initializeProxy(); - URL url = new URL(captchaserverprefix + "validateCaptcha/" + id); + URL url = new URL(captchaserverprefixtarget + "validateCaptcha/" + id); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("x-jwtString", token); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); + + String[] cookies = originalcookies.split("#"); + for (String cookie : cookies) { + conn.addRequestProperty("Cookie", cookie); + } String postData = "captchaAnswer=" + str + "&useAudio=" + ("true".equalsIgnoreCase(useaudio)); byte[] postDataBytes = postData.getBytes("UTF-8"); diff --git a/src/main/java/com/ec/survey/controller/ContributionController.java b/src/main/java/com/ec/survey/controller/ContributionController.java index 9218d5e75..d5747daea 100644 --- a/src/main/java/com/ec/survey/controller/ContributionController.java +++ b/src/main/java/com/ec/survey/controller/ContributionController.java @@ -120,6 +120,7 @@ public ModelAndView quizresults(@PathVariable String code, Locale locale, HttpSe form.setSurvey(translated); form.setLanguage(surveyService.getLanguage(lang)); + form.getAnswerSets().add(answerSet); result.addObject(form); result.addObject("surveyprefix", answerSet.getSurvey().getId() + "."); @@ -470,6 +471,10 @@ public ModelAndView processSubmit(@PathVariable String code, HttpServletRequest translationService.getActiveTranslationsForSurvey(origsurvey.getId()), contextpath); form.setSurvey(origsurvey); + if(!origsurvey.getConfirmationPageLink()){ + form.getAnswerSets().add(oldAnswerSet); + } + result.addObject("form", form); result.addObject("text", origsurvey.getConfirmationPage()); @@ -480,6 +485,7 @@ public ModelAndView processSubmit(@PathVariable String code, HttpServletRequest && oldAnswerSet.getInvitationId() == null) { result.addObject("asklogout", true); } + result.addObject("surveyprefix", origsurvey.getId() + "."); return result; } else { @@ -583,7 +589,6 @@ public ModelAndView print(HttpServletRequest request, Locale locale) { AnswerSet answerSet; try { answerSet = answerService.get(code); - if (answerSet != null) { Form form = new Form(resources); String lang = answerSet.getLanguageCode(); diff --git a/src/main/java/com/ec/survey/controller/DelphiController.java b/src/main/java/com/ec/survey/controller/DelphiController.java index 135429c75..2e0806017 100644 --- a/src/main/java/com/ec/survey/controller/DelphiController.java +++ b/src/main/java/com/ec/survey/controller/DelphiController.java @@ -17,7 +17,7 @@ import com.kennycason.kumo.nlp.normalize.LowerCaseNormalizer; import com.kennycason.kumo.nlp.normalize.TrimToEmptyNormalizer; -import javafx.util.Pair; +import com.ec.survey.replacements.Pair; import org.apache.commons.io.IOUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -215,20 +215,30 @@ public ResponseEntity delphiGraph(HttpServletRequest re String languageCode = request.getParameter("languagecode"); Survey survey = surveyService.getSurvey(sid, languageCode); - + if (survey == null) { return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); } + + boolean resultsview = request.getParameter("resultsview") != null && request.getParameter("resultsview").equalsIgnoreCase("true"); + boolean allanswers = request.getSession().getAttribute("results-source-allanswers") != null && (boolean) request.getSession().getAttribute("results-source-allanswers"); + ResultFilter filter = null; User user = sessionService.getCurrentUser(request); + if (resultsview) { + if (user != null) { + filter = sessionService.getLastResultFilter(request, user.getId(), survey.getId()); + } + if (allanswers && !survey.isMissingElementsChecked()) { + surveyService.checkAndRecreateMissingElements(survey, filter); + } + } if (user != null) { Survey draft = surveyService.getSurveyByShortname(survey.getShortname(), true, user, request, false, true, true, false); sessionService.upgradePrivileges(draft, user, request); } - boolean resultsview = request.getParameter("resultsview") != null && request.getParameter("resultsview").equalsIgnoreCase("true"); - boolean privileged = resultsview && (survey.getOwner().getId().equals(user.getId()) || (user.getGlobalPrivileges().get(GlobalPrivilege.FormManagement) == 2) || (user.getLocalPrivileges().get(LocalPrivilege.AccessResults) > 0)); @@ -242,7 +252,7 @@ public ResponseEntity delphiGraph(HttpServletRequest re } String questionuid = request.getParameter("questionuid"); - Element element = survey.getQuestionMapByUniqueId().get(questionuid); + Element element = survey.getQuestionMapByUniqueId(true).get(questionuid); if (!(element instanceof Question)) { return new ResponseEntity<>(null, HttpStatus.NO_CONTENT); @@ -257,13 +267,7 @@ public ResponseEntity delphiGraph(HttpServletRequest re Statistics statistics = new Statistics(); statistics.setSurveyId(survey.getId()); - ResultFilter filter = null; - if (resultsview) { - if (user != null) { - filter = sessionService.getLastResultFilter(request, user.getId(), survey.getId()); - } - if (survey.getDedicatedResultPrivileges()) { ResultAccess resultAccess = surveyService.getResultAccess(survey.getUniqueId(), user.getId()); if (resultAccess != null && resultAccess.getResultFilter() != null) { @@ -306,7 +310,7 @@ public ResponseEntity delphiGraph(HttpServletRequest re Map numberOfAnswersMap = new HashMap<>(); Map> numberOfAnswersMapMatrix = new HashMap<>(); Map> numberOfAnswersMapRatingQuestion = new HashMap<>(); - Map> numberOfAnswersMapGallery = new HashMap<>(); + Map> numberOfAnswersMapGallery = new HashMap<>(); Map>> multipleChoiceSelectionsByAnswerset = new HashMap<>(); Map numberOfAnswersMapNumberQuestion = new HashMap<>(); Map> numberOfAnswersMapRankingQuestion = new HashMap<>(); @@ -414,7 +418,7 @@ private ResponseEntity handleDelphiGraphRankingQuestion result.setChartType(question.getDelphiChartType()); result.setQuestionType(DelphiQuestionType.Ranking); - for (RankingItem item : question.getChildElements()) { + for (RankingItem item : question.getAllChildElements()) { DelphiGraphEntry entry = new DelphiGraphEntry(); entry.setLabel(item.getStrippedTitleNoEscape()); entry.setValue(statistics.getRequestedRecordsRankingPercentScore().get(item.getId().toString())); @@ -566,7 +570,7 @@ private ResponseEntity handleDelphiFormulaQuestion(Surv DelphiGraphDataSingle result = new DelphiGraphDataSingle(); result.setChartType(question.showStatisticsForNumberQuestion() ? question.getDelphiChartType() : DelphiChartType.None); - result.setQuestionType(DelphiQuestionType.Number); + result.setQuestionType(DelphiQuestionType.Formula); result.setLabel(question.getStrippedTitle()); Map valuesMagnitude = numberQuestionStatistics.getValuesMagnitude(); diff --git a/src/main/java/com/ec/survey/controller/EuCaptchaApiController.java b/src/main/java/com/ec/survey/controller/EuCaptchaApiController.java new file mode 100644 index 000000000..6354a7751 --- /dev/null +++ b/src/main/java/com/ec/survey/controller/EuCaptchaApiController.java @@ -0,0 +1,83 @@ +package com.ec.survey.controller; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/EuCaptchaApi") +public class EuCaptchaApiController extends BasicController { + + @RequestMapping(value = "/captchaImg", produces = MediaType.APPLICATION_JSON_VALUE) + public @ResponseBody String captchaImg(String locale, String capitalized, HttpServletRequest request, HttpServletResponse response) throws IOException { + sessionService.initializeProxy(); + + CookieManager cookieManager = new CookieManager(); + CookieHandler.setDefault(cookieManager); + + URL url = new URL(captchaserverprefixtarget + "captchaImg?locale=" + locale + "&capitalized=" + capitalized); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + + String xjwtString = conn.getHeaderField("x-jwtString"); + response.setHeader("x-jwtString", xjwtString); + + List cookies = conn.getHeaderFields().get("set-cookie"); + response.addHeader("original-cookie", cookies == null ? "" : String.join("#", cookies)); + + return readData(conn); + } + + @RequestMapping(value = "/reloadCaptchaImg/{captchaId}", produces = MediaType.APPLICATION_JSON_VALUE) + public @ResponseBody String reloadCaptchaImg(@PathVariable String captchaId, HttpServletRequest request, HttpServletResponse response) throws IOException { + sessionService.initializeProxy(); + + CookieManager cookieManager = new CookieManager(); + CookieHandler.setDefault(cookieManager); + + String locale = request.getParameter("locale"); + String capitalized = request.getParameter("capitalized"); + URL url = new URL(captchaserverprefixtarget + "reloadCaptchaImg/" + captchaId + "?locale=" + locale + "&capitalized=" + capitalized); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + + String xjwtString = request.getHeader("x-jwtstring"); + conn.setRequestProperty("x-jwtString", xjwtString); + + String[] cookies = request.getHeader("original-cookie").split("#"); + for (String cookie : cookies) { + conn.addRequestProperty("Cookie", cookie); + } + + xjwtString = conn.getHeaderField("x-jwtString"); + response.setHeader("x-jwtString", xjwtString); + + return readData(conn); + } + + private String readData(HttpURLConnection conn) throws IOException { + BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + StringBuilder sb = new StringBuilder(); + for (int c; (c = in.read()) >= 0;) { + sb.append((char)c); + } + in.close(); + return sb.toString(); + } + +} diff --git a/src/main/java/com/ec/survey/controller/HomeController.java b/src/main/java/com/ec/survey/controller/HomeController.java index d0e645b2d..c37e9a875 100644 --- a/src/main/java/com/ec/survey/controller/HomeController.java +++ b/src/main/java/com/ec/survey/controller/HomeController.java @@ -48,7 +48,7 @@ @Controller("homeController") public class HomeController extends BasicController { - public @Value("${stresstests.createdata}") String createStressData; + public @Value("${stresstests.createdata}") String createStressData; private @Value("${server.prefix}") String host; private @Value("${support.recipient}") String supportEmail; @@ -339,39 +339,27 @@ private String sendSupportSmt(HttpServletRequest request, Locale locale, ModelMa try { - sessionService.initializeProxy(); HttpClient httpclient = HttpClients.createDefault(); - HttpPost httppost = new HttpPost(incidentHost.trim()); + HttpPost httppost = new HttpPost(incidentHost); + + httppost.setEntity(new StringEntity(createTemplate)); - httppost.addHeader("Content-type", "text/xml;charset=UTF-8"); - if (smtpAuth != null) { httppost.addHeader("Authorization", "Basic " + smtpAuth); } - httppost.addHeader("SOAPAction", "Create"); - - httppost.setEntity(new StringEntity(createTemplate)); - + HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); if (entity != null) { - String strResponse = EntityUtils.toString(entity, "UTF-8"); + String strResponse = EntityUtils.toString(entity); if (!strResponse.contains("message=\"success\"")) { logger.error(strResponse); throw new MessageException("Calling SMT web service failed."); } logger.info(strResponse); } - - org.apache.http.Header[] headers = response.getAllHeaders(); - for(org.apache.http.Header h:headers){ - logger.info("Response Header " + h.getName() + ": " + h.getValue()); - } - logger.info("Mime Type: " + EntityUtils.getContentMimeType(entity)); - logger.info("Char Set: " + EntityUtils.getContentCharSet(entity)); - } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); //fallback to email diff --git a/src/main/java/com/ec/survey/controller/ManagementController.java b/src/main/java/com/ec/survey/controller/ManagementController.java index 4448732e7..73b3d3ec6 100644 --- a/src/main/java/com/ec/survey/controller/ManagementController.java +++ b/src/main/java/com/ec/survey/controller/ManagementController.java @@ -1068,7 +1068,7 @@ private void ensurePropertiesDependingOnSurveyType(Survey survey, boolean creati trans2.setLanguage(trans.getLanguage()); translationsToCreate.put(trans2.getLanguage().getCode(), trans2); - String confirmation = resources.getMessage("message.confirmation", null, Survey.CONFIRMATIONTEXT, + String confirmation = resources.getMessage("message.confirmationWithTitle", null, Survey.CONFIRMATIONTEXT, new Locale(trans.getLanguage().getCode())); trans2.getTranslations().add(new Translation(Survey.CONFIRMATIONPAGE, confirmation, trans.getLanguage().getCode(), null, trans2)); @@ -1400,6 +1400,8 @@ private ModelAndView updateSurvey(Form form, HttpServletRequest request, boolean hasPendingChanges = true; if(!Tools.isEqual(survey.getMotivationText(), uploadedSurvey.getMotivationText())) hasPendingChanges = true; + if(!Tools.isEqual(survey.getMotivationPopupTitle(), uploadedSurvey.getMotivationPopupTitle())) + hasPendingChanges = true; if(!Tools.isEqual(survey.getMotivationTriggerProgress(), uploadedSurvey.getMotivationTriggerProgress())) hasPendingChanges = true; if(!Tools.isEqual(survey.getMotivationTriggerTime(), uploadedSurvey.getMotivationTriggerTime())) @@ -1575,6 +1577,7 @@ private ModelAndView updateSurvey(Form form, HttpServletRequest request, boolean survey.setMotivationPopup(uploadedSurvey.getMotivationPopup()); survey.setMotivationText(uploadedSurvey.getMotivationText()); + survey.setMotivationPopupTitle(uploadedSurvey.getMotivationPopupTitle()); survey.setMotivationTriggerProgress(uploadedSurvey.getMotivationTriggerProgress()); survey.setMotivationTriggerTime(uploadedSurvey.getMotivationTriggerTime()); survey.setMotivationType(uploadedSurvey.getMotivationType()); @@ -2870,6 +2873,10 @@ public ModelAndView testPOST(@PathVariable String shortname, HttpServletRequest sessionService.setFormStartDate(request, form, uniqueCode); form.setSurvey(survey); + if(!survey.getConfirmationPageLink()){ + form.getAnswerSets().add(answerSet); + } + result.addObject(form); if (survey.getConfirmationPageLink() != null && survey.getConfirmationPageLink() @@ -3489,7 +3496,7 @@ private ModelAndView results(Survey survey, Map parameters, Ht answer.setTitle(form.getAnswerTitle(answer)); } if (s.length() > 0) { - s.append(" - "); + s.append("; "); } s.append(answer.getTitle()); @@ -3512,7 +3519,7 @@ private ModelAndView results(Survey survey, Map parameters, Ht for (Answer answer : answerSet.getAnswers(childQuestion.getId(), childQuestion.getUniqueId())) { if (s.length() > 0) { - s.append("
"); + s.append("; "); } if (childQuestion.getCellType() == ComplexTableItem.CellType.SingleChoice || childQuestion.getCellType() == ComplexTableItem.CellType.MultipleChoice) { @@ -3526,14 +3533,24 @@ private ModelAndView results(Survey survey, Map parameters, Ht } else if (question instanceof GalleryQuestion) { GalleryQuestion gallery = (GalleryQuestion) question; StringBuilder s = new StringBuilder(); - for (int counter = 0; counter < gallery.getFiles().size(); counter++) { - for (Answer answer : answerSet.getAnswers(question.getId(), question.getUniqueId())) { - if (answer.getValue().equalsIgnoreCase(Integer.toString(counter))) { - if (s.length() > 0) { - s.append(" - "); - } - s.append(gallery.getFiles().get(counter).getName()); + + for (Answer answer : answerSet.getAnswers(question.getId(), question.getUniqueId())) { + File file = null; + if (!StringUtils.isEmpty(answer.getPossibleAnswerUniqueId())) { + file = gallery.getFileByUid(answer.getPossibleAnswerUniqueId()); + } else { + try { + file = gallery.getFiles().get(Integer.parseInt(answer.getValue())); + } catch (IndexOutOfBoundsException e) { + //ignore + } + } + + if (file != null) { + if (s.length() > 0) { + s.append("; "); } + s.append(file.getName()); } } result.add(s.toString()); @@ -3558,12 +3575,12 @@ private ModelAndView results(Survey survey, Map parameters, Ht result.add(s.toString()); } } else if (question instanceof RatingQuestion) { - for (Element childQuestion : ((RatingQuestion) question).getChildElements()) { + for (Element childQuestion : ((RatingQuestion) question).getQuestions()) { StringBuilder s = new StringBuilder(); for (Answer answer : answerSet.getAnswers(childQuestion.getId(), childQuestion.getUniqueId())) { if (s.length() > 0) { - s.append(" - "); + s.append("; "); } s.append(answer.getValue()); } @@ -3576,7 +3593,7 @@ private ModelAndView results(Survey survey, Map parameters, Ht StringBuilder s = new StringBuilder(); for (Answer answer : answerSet.getAnswers(question.getId(), question.getUniqueId())) { if (s.length() > 0) { - s.append("
"); + s.append("; "); } if (question instanceof ChoiceQuestion || question instanceof RankingQuestion) { diff --git a/src/main/java/com/ec/survey/controller/PDFController.java b/src/main/java/com/ec/survey/controller/PDFController.java index 590be788e..6feffc25c 100644 --- a/src/main/java/com/ec/survey/controller/PDFController.java +++ b/src/main/java/com/ec/survey/controller/PDFController.java @@ -1,376 +1,377 @@ -package com.ec.survey.controller; - -import com.ec.survey.exception.ForbiddenURLException; -import com.ec.survey.exception.InvalidURLException; -import com.ec.survey.model.AnswerSet; -import com.ec.survey.model.ParticipationGroup; -import com.ec.survey.model.administration.GlobalPrivilege; -import com.ec.survey.model.administration.LocalPrivilege; -import com.ec.survey.model.administration.User; -import com.ec.survey.model.attendees.Invitation; -import com.ec.survey.model.survey.Survey; -import com.ec.survey.service.*; -import com.ec.survey.tools.Constants; -import com.ec.survey.tools.ConversionTools; -import com.ec.survey.tools.NotAgreedToPsException; -import com.ec.survey.tools.NotAgreedToTosException; -import com.ec.survey.tools.SurveyExecutor; -import com.ec.survey.tools.Tools; -import com.ec.survey.tools.WeakAuthenticationException; - -import org.apache.catalina.connector.ClientAbortException; -import org.apache.commons.lang.StringUtils; -import org.apache.poi.util.IOUtils; -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.HTTPUtilities; -import org.springframework.core.task.TaskExecutor; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.servlet.ModelAndView; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Date; -import java.util.Locale; - -@Controller -@RequestMapping("/pdf") -public class PDFController extends BasicController { - - @Resource(name = "pdfService") - private PDFService pdfService; - - @Resource(name = "validCodesService") - private ValidCodesService validCodesService; - - @Resource(name = "taskExecutor") - private TaskExecutor taskExecutor; - - @RequestMapping(value = "/pubsurvey/{id}", method = { RequestMethod.GET, RequestMethod.HEAD }) - public ModelAndView pubsurvey(@PathVariable String id, HttpServletRequest request, Locale locale, - HttpServletResponse response) throws ForbiddenURLException, InvalidURLException { - try { - String language = locale.getLanguage(); - Survey survey = surveyService.getSurvey(Integer.parseInt(id), false, true); - - if (survey == null) { - throw new InvalidURLException(); - } - - if (survey.getIsDraft()) { - Survey published = surveyService.getSurvey(survey.getUniqueId(), false, false, false, false, - survey.getLanguage().getCode(), true, false); - - if (published == null) { - throw new InvalidURLException(); - } - - if (!survey.getShowPDFOnUnavailabilityPage()) { - throw new ForbiddenURLException(); - } - } - - SendFile(survey, request, response, language); - - } catch (NumberFormatException ne) { - throw new InvalidURLException(); - } catch (ForbiddenURLException | InvalidURLException fe) { - throw fe; - } catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - } - return null; - } - - @RequestMapping(value = "/survey/{id}", method = { RequestMethod.GET, RequestMethod.HEAD }) - public ModelAndView survey(@PathVariable String id, HttpServletRequest request, Locale locale, - HttpServletResponse response) throws ForbiddenURLException, InvalidURLException { - try { - - String language = locale.getLanguage(); - - Survey survey = surveyService.getSurvey(Integer.parseInt(id), false, true); - - if (survey == null) { - throw new InvalidURLException(); - } - - if (!survey.getShowPDFOnUnavailabilityPage() && !survey.getAllowQuestionnaireDownload()){ - throw new ForbiddenURLException(); - } - - if (!survey.getIsDraft()) { - // check if survey is published and user has access - Survey draft = surveyService.getSurveyByUniqueId(survey.getUniqueId(), false, true); - if (!draft.getIsPublished()) { - throw new ForbiddenURLException(); - } - - if (!draft.getSecurity().equalsIgnoreCase("open")) { - String uniquecode = request.getParameter("unique"); - if (uniquecode == null) { - throw new ForbiddenURLException(); - } - - // check password secured surveys - if (!validCodesService.checkValid(uniquecode, survey.getUniqueId())) { - // check invitation based security - Invitation invitation = attendeeService.getInvitationByUniqueId(uniquecode); - if (invitation == null - || (invitation.getDeactivated() != null && invitation.getDeactivated())) { - throw new ForbiddenURLException(); - } - - ParticipationGroup participationGroup = participationService - .get(invitation.getParticipationGroupId()); - if (participationGroup == null || !participationGroup.getActive()) { - throw new ForbiddenURLException(); - } - } - } - } else { - // check if user has access to draft - User u = sessionService.getCurrentUser(request); - - if (u == null) { - throw new ForbiddenURLException(); - } - - sessionService.upgradePrivileges(survey, u, request); - - if (!u.getId().equals(survey.getOwner().getId()) - && u.getGlobalPrivileges().get(GlobalPrivilege.FormManagement) < 2 - && u.getLocalPrivileges().get(LocalPrivilege.AccessDraft) < 1 - && u.getLocalPrivileges().get(LocalPrivilege.FormManagement) < 1) { - throw new ForbiddenURLException(); - } - } - - SendFile(survey, request, response, language); - - return null; - - } catch (NumberFormatException ne) { - throw new InvalidURLException(); - } catch (ForbiddenURLException | InvalidURLException fe) { - throw fe; - } catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - } - return null; - } - - private void SendFile(Survey survey, HttpServletRequest request, HttpServletResponse response, String language) throws IOException { - String lang = request.getParameter("lang"); - if (lang != null && lang.length() == 2 && StringUtils.isAlpha(lang)) { - language = lang; - } - String name; - String value; - name = "Content-Disposition"; - if (survey.getIsDraft()) { - value = "attachment;filename=" + survey.getShortname() + "_" - + Tools.formatDate(new Date(), ConversionTools.DateFormat) + "_" + language + "_draft.pdf"; - } else { - value = "attachment;filename=" + survey.getShortname() + "_" - + Tools.formatDate(survey.getUpdated(), ConversionTools.DateFormat) + "_" + language + ".pdf"; - } - - HTTPUtilities httpUtilities = ESAPI.httpUtilities(); - httpUtilities.setCurrentHTTP(request, response); - httpUtilities.setHeader(name, value); - - response.setContentType("application/pdf"); - - java.io.File folder = fileService.getSurveyExportsFolder(survey.getUniqueId()); - java.io.File result = new java.io.File( - String.format("%s/survey%s%s.pdf", folder.getPath(), survey.getId(), lang)); - - try { - - FileInputStream inputStream = new FileInputStream(result); - IOUtils.copy(inputStream, response.getOutputStream()); - inputStream.close(); - - response.flushBuffer(); - } catch (ClientAbortException | FileNotFoundException c) { - // ClientAbortException can happen if the user closes the browser during - // download - // FileNotFoundException can happen as we create the files asynchronously and it - // might not have been created yet - } - } - - @RequestMapping(value = "/surveyexists/{id}", method = { RequestMethod.GET, RequestMethod.HEAD }) - public @ResponseBody String surveyexists(@PathVariable String id, HttpServletRequest request, Locale locale, - HttpServletResponse response) { - try { - - String lang = request.getParameter("lang"); - - Survey survey = surveyService.getSurvey(Integer.parseInt(id), true); - java.io.File folder = fileService.getSurveyExportsFolder(survey.getUniqueId()); - - java.io.File target = new java.io.File(String.format("%s/survey%s%s.pdf", folder.getPath(), id, lang)); - if (target.exists() && target.length() > 0) { - return "exists"; - } - - SurveyExecutor export = (SurveyExecutor) context.getBean("surveyExecutor"); - export.init(surveyService.getSurvey(Integer.parseInt(id)), lang); - taskExecutor.execute(export); - - return "wait"; - - } catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - return Constants.ERROR; - } - } - - @RequestMapping(value = "/surveyready/{id}", method = { RequestMethod.GET, RequestMethod.HEAD }) - public @ResponseBody String surveyready(@PathVariable String id, HttpServletRequest request, Locale locale, - HttpServletResponse response) { - try { - String lang = request.getParameter("lang"); - - Survey survey = surveyService.getSurvey(Integer.parseInt(id), true); - java.io.File folder = fileService.getSurveyExportsFolder(survey.getUniqueId()); - - java.io.File target = new java.io.File(String.format("%s/survey%s%s.pdf", folder.getPath(), id, lang)); - if (target.exists() && target.length() > 0) { - return "exists"; - } - - return "wait"; - - } catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - return Constants.ERROR; - } - } - - @RequestMapping(value = "/answerexists/{code}", method = { RequestMethod.GET, RequestMethod.HEAD }) - public @ResponseBody String answerexists(@PathVariable String code, HttpServletRequest request, Locale locale, - HttpServletResponse response) { - try { - java.io.File target = new java.io.File(String.format("%sanswer%s.pdf", tempFileDir, code)); - if (target.exists() && target.length() > 0) { - return "exists"; - } - - pdfService.createAnswerPDF(code, null); - - return "wait"; - - } catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - return Constants.ERROR; - } - } - - @RequestMapping(value = "/answerready/{code}", method = { RequestMethod.GET, RequestMethod.HEAD }) - public @ResponseBody String answerready(@PathVariable String code, HttpServletRequest request, Locale locale, - HttpServletResponse response) { - try { - java.io.File target = new java.io.File(String.format("%sanswer%s.pdf", tempFileDir, code)); - if (target.exists() && target.length() > 0) { - return "exists"; - } - - AnswerSet answerSet = answerService.get(code); - - if (answerSet == null) - return Constants.ERROR; - - java.io.File folder = fileService.getSurveyExportsFolder(answerSet.getSurvey().getUniqueId()); - target = new java.io.File(String.format("%s/answer%s.pdf", folder.getPath(), code)); - - if (target.exists() && target.length() > 0) { - return "exists"; - } - - return "wait"; - - } catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - return Constants.ERROR; - } - } - - @RequestMapping(value = "/answer/{code}", method = { RequestMethod.GET, RequestMethod.HEAD }) - public ModelAndView answer(@PathVariable String code, HttpServletRequest request, Locale locale, - HttpServletResponse response) throws InvalidURLException, NotAgreedToTosException, ForbiddenURLException, - WeakAuthenticationException, NotAgreedToPsException { - User user = sessionService.getCurrentUser(request); - - if (user == null) { - throw new ForbiddenURLException(); - } - - if (code == null || !StringUtils.isAlphanumeric(code.replace("-", ""))) { - throw new InvalidURLException(); - } - - try { - - java.io.File result = new java.io.File(String.format("%sanswer%s.pdf", tempFileDir, code)); - - AnswerSet answerSet = answerService.get(code); - - if (!result.exists() && answerSet != null) { - java.io.File folder = fileService.getSurveyExportsFolder(answerSet.getSurvey().getUniqueId()); - result = new java.io.File(String.format("%s/answer%s.pdf", folder.getPath(), code)); - } - - if (result.exists()) { - Survey survey = surveyService.getSurveyByUniqueId(answerSet.getSurvey().getUniqueId(), false, true); - - try { - sessionService.upgradePrivileges(survey, user, request); - } catch (ForbiddenURLException fex) { - // user is no form manager - } - - if (!user.getId().equals(survey.getOwner().getId()) - && user.getGlobalPrivileges().get(GlobalPrivilege.FormManagement) < 2 - && user.getLocalPrivileges().get(LocalPrivilege.AccessResults) < 1 - && user.getLocalPrivileges().get(LocalPrivilege.AccessDraft) < 2 - && (!(answerSet.getResponderEmail() != null && user.getEmail() != null - && (answerSet.getResponderEmail().equalsIgnoreCase(user.getEmail()) || answerSet - .getResponderEmail().equalsIgnoreCase(Tools.md5hash(user.getEmail())))))) { - throw new ForbiddenURLException(); - } - - response.setHeader("Content-Disposition", - "attachment;filename=" + answerSet.getSurvey().getShortname() + "_" + code + ".pdf"); - response.setContentType("application/pdf"); - - FileInputStream inputStream = new FileInputStream(result); - IOUtils.copy(inputStream, response.getOutputStream()); - inputStream.close(); - - response.flushBuffer(); - } else { - throw new InvalidURLException(); - } - - return null; - } catch (ForbiddenURLException fe) { - throw fe; - } catch (InvalidURLException ie) { - throw ie; - } catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - } - throw new InvalidURLException(); - } - -} +package com.ec.survey.controller; + +import com.ec.survey.exception.ForbiddenURLException; +import com.ec.survey.exception.InvalidURLException; +import com.ec.survey.model.AnswerSet; +import com.ec.survey.model.ParticipationGroup; +import com.ec.survey.model.administration.GlobalPrivilege; +import com.ec.survey.model.administration.LocalPrivilege; +import com.ec.survey.model.administration.User; +import com.ec.survey.model.attendees.Invitation; +import com.ec.survey.model.survey.Survey; +import com.ec.survey.service.*; +import com.ec.survey.tools.Constants; +import com.ec.survey.tools.ConversionTools; +import com.ec.survey.tools.NotAgreedToPsException; +import com.ec.survey.tools.NotAgreedToTosException; +import com.ec.survey.tools.SurveyExecutor; +import com.ec.survey.tools.Tools; +import com.ec.survey.tools.WeakAuthenticationException; + +import org.apache.catalina.connector.ClientAbortException; +import org.apache.commons.lang.StringUtils; +import org.apache.poi.util.IOUtils; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.HTTPUtilities; +import org.springframework.core.task.TaskExecutor; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Date; +import java.util.Locale; + +@Controller +@RequestMapping("/pdf") +public class PDFController extends BasicController { + + @Resource(name = "pdfService") + private PDFService pdfService; + + @Resource(name = "validCodesService") + private ValidCodesService validCodesService; + + @Resource(name = "taskExecutor") + private TaskExecutor taskExecutor; + + @RequestMapping(value = "/pubsurvey/{id}", method = { RequestMethod.GET, RequestMethod.HEAD }) + public ModelAndView pubsurvey(@PathVariable String id, HttpServletRequest request, Locale locale, + HttpServletResponse response) throws ForbiddenURLException, InvalidURLException { + try { + String language = locale.getLanguage(); + Survey survey = surveyService.getSurvey(Integer.parseInt(id), false, true); + + if (survey == null) { + throw new InvalidURLException(); + } + + if (survey.getIsDraft()) { + Survey published = surveyService.getSurvey(survey.getUniqueId(), false, false, false, false, + survey.getLanguage().getCode(), true, false); + + if (published == null) { + throw new InvalidURLException(); + } + + if (!survey.getShowPDFOnUnavailabilityPage()) { + throw new ForbiddenURLException(); + } + } + + SendFile(survey, request, response, language); + + } catch (NumberFormatException ne) { + throw new InvalidURLException(); + } catch (ForbiddenURLException | InvalidURLException fe) { + throw fe; + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + } + return null; + } + + @RequestMapping(value = "/survey/{id}", method = { RequestMethod.GET, RequestMethod.HEAD }) + public ModelAndView survey(@PathVariable String id, HttpServletRequest request, Locale locale, + HttpServletResponse response) throws ForbiddenURLException, InvalidURLException { + try { + + String language = locale.getLanguage(); + + Survey survey = surveyService.getSurvey(Integer.parseInt(id), false, true); + + if (survey == null) { + throw new InvalidURLException(); + } + + if (!survey.getShowPDFOnUnavailabilityPage() && !survey.getAllowQuestionnaireDownload()){ + throw new ForbiddenURLException(); + } + + if (!survey.getIsDraft()) { + // check if survey is published and user has access + Survey draft = surveyService.getSurveyByUniqueId(survey.getUniqueId(), false, true); + if (!draft.getIsPublished()) { + throw new ForbiddenURLException(); + } + + if (!draft.getSecurity().equalsIgnoreCase("open")) { + String uniquecode = request.getParameter("unique"); + if (uniquecode == null) { + throw new ForbiddenURLException(); + } + + // check password secured surveys + if (!validCodesService.checkValid(uniquecode, survey.getUniqueId())) { + // check invitation based security + Invitation invitation = attendeeService.getInvitationByUniqueId(uniquecode); + if (invitation == null + || (invitation.getDeactivated() != null && invitation.getDeactivated())) { + throw new ForbiddenURLException(); + } + + ParticipationGroup participationGroup = participationService + .get(invitation.getParticipationGroupId()); + if (participationGroup == null || !participationGroup.getActive()) { + throw new ForbiddenURLException(); + } + } + } + } else { + // check if user has access to draft + User u = sessionService.getCurrentUser(request); + + if (u == null) { + throw new ForbiddenURLException(); + } + + sessionService.upgradePrivileges(survey, u, request); + + if (!u.getId().equals(survey.getOwner().getId()) + && u.getGlobalPrivileges().get(GlobalPrivilege.FormManagement) < 2 + && u.getLocalPrivileges().get(LocalPrivilege.AccessDraft) < 1 + && u.getLocalPrivileges().get(LocalPrivilege.FormManagement) < 1) { + throw new ForbiddenURLException(); + } + } + + SendFile(survey, request, response, language); + + return null; + + } catch (NumberFormatException ne) { + throw new InvalidURLException(); + } catch (ForbiddenURLException | InvalidURLException fe) { + throw fe; + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + } + return null; + } + + private void SendFile(Survey survey, HttpServletRequest request, HttpServletResponse response, String language) throws IOException { + String lang = request.getParameter("lang"); + if (lang != null && lang.length() == 2 && StringUtils.isAlpha(lang)) { + language = lang; + } + String name; + String value; + name = "Content-Disposition"; + if (survey.getIsDraft()) { + value = "attachment;filename=" + survey.getShortname() + "_" + + Tools.formatDate(new Date(), ConversionTools.DateFormat) + "_" + language + "_draft.pdf"; + } else { + value = "attachment;filename=" + survey.getShortname() + "_" + + Tools.formatDate(survey.getUpdated(), ConversionTools.DateFormat) + "_" + language + ".pdf"; + } + + HTTPUtilities httpUtilities = ESAPI.httpUtilities(); + httpUtilities.setCurrentHTTP(request, response); + httpUtilities.setHeader(name, value); + + response.setContentType("application/pdf"); + + java.io.File folder = fileService.getSurveyExportsFolder(survey.getUniqueId()); + java.io.File result = new java.io.File( + String.format("%s/survey%s%s.pdf", folder.getPath(), survey.getId(), lang)); + + try { + + FileInputStream inputStream = new FileInputStream(result); + IOUtils.copy(inputStream, response.getOutputStream()); + inputStream.close(); + + response.flushBuffer(); + } catch (ClientAbortException | FileNotFoundException c) { + // ClientAbortException can happen if the user closes the browser during + // download + // FileNotFoundException can happen as we create the files asynchronously and it + // might not have been created yet + } + } + + @RequestMapping(value = "/surveyexists/{id}", method = { RequestMethod.GET, RequestMethod.HEAD }) + public @ResponseBody String surveyexists(@PathVariable String id, HttpServletRequest request, Locale locale, + HttpServletResponse response) { + try { + + String lang = request.getParameter("lang"); + + Survey survey = surveyService.getSurvey(Integer.parseInt(id), true); + java.io.File folder = fileService.getSurveyExportsFolder(survey.getUniqueId()); + + java.io.File target = new java.io.File(String.format("%s/survey%s%s.pdf", folder.getPath(), id, lang)); + if (target.exists() && target.length() > 0) { + return "exists"; + } + + SurveyExecutor export = (SurveyExecutor) context.getBean("surveyExecutor"); + export.init(surveyService.getSurvey(Integer.parseInt(id)), lang); + taskExecutor.execute(export); + + return "wait"; + + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + return Constants.ERROR; + } + } + + @RequestMapping(value = "/surveyready/{id}", method = { RequestMethod.GET, RequestMethod.HEAD }) + public @ResponseBody String surveyready(@PathVariable String id, HttpServletRequest request, Locale locale, + HttpServletResponse response) { + try { + String lang = request.getParameter("lang"); + + Survey survey = surveyService.getSurvey(Integer.parseInt(id), true); + java.io.File folder = fileService.getSurveyExportsFolder(survey.getUniqueId()); + + java.io.File target = new java.io.File(String.format("%s/survey%s%s.pdf", folder.getPath(), id, lang)); + if (target.exists() && target.length() > 0) { + return "exists"; + } + + return "wait"; + + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + return Constants.ERROR; + } + } + + @RequestMapping(value = "/answerexists/{code}", method = { RequestMethod.GET, RequestMethod.HEAD }) + public @ResponseBody String answerexists(@PathVariable String code, HttpServletRequest request, Locale locale, + HttpServletResponse response) { + try { + java.io.File target = new java.io.File(String.format("%sanswer%s.pdf", tempFileDir, code)); + if (target.exists() && target.length() > 0) { + return "exists"; + } + + pdfService.createAnswerPDF(code, null); + + return "wait"; + + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + return Constants.ERROR; + } + } + + @RequestMapping(value = "/answerready/{code}", method = { RequestMethod.GET, RequestMethod.HEAD }) + public @ResponseBody String answerready(@PathVariable String code, HttpServletRequest request, Locale locale, + HttpServletResponse response) { + try { + java.io.File target = new java.io.File(String.format("%sanswer%s.pdf", tempFileDir, code)); + if (target.exists() && target.length() > 0) { + return "exists"; + } + + AnswerSet answerSet = answerService.get(code); + + if (answerSet == null) + return Constants.ERROR; + + java.io.File folder = fileService.getSurveyExportsFolder(answerSet.getSurvey().getUniqueId()); + target = new java.io.File(String.format("%s/answer%s.pdf", folder.getPath(), code)); + + if (target.exists() && target.length() > 0) { + return "exists"; + } + + return "wait"; + + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + return Constants.ERROR; + } + } + + @RequestMapping(value = "/answer/{code}", method = { RequestMethod.GET, RequestMethod.HEAD }) + public ModelAndView answer(@PathVariable String code, HttpServletRequest request, Locale locale, + HttpServletResponse response) throws InvalidURLException, NotAgreedToTosException, ForbiddenURLException, + WeakAuthenticationException, NotAgreedToPsException { + User user = sessionService.getCurrentUser(request); + + if (user == null) { + throw new ForbiddenURLException(); + } + + if (code == null || !StringUtils.isAlphanumeric(code.replace("-", ""))) { + throw new InvalidURLException(); + } + + try { + + java.io.File result = new java.io.File(String.format("%sanswer%s.pdf", tempFileDir, code)); + + AnswerSet answerSet = answerService.get(code); + + if (!result.exists() && answerSet != null) { + java.io.File folder = fileService.getSurveyExportsFolder(answerSet.getSurvey().getUniqueId()); + result = new java.io.File(String.format("%s/answer%s.pdf", folder.getPath(), code)); + } + + if (result.exists()) { + Survey survey = surveyService.getSurveyByUniqueId(answerSet.getSurvey().getUniqueId(), false, true); + + try { + sessionService.upgradePrivileges(survey, user, request); + } catch (ForbiddenURLException fex) { + // user is no form manager + } + + if (!user.getId().equals(survey.getOwner().getId()) + && user.getGlobalPrivileges().get(GlobalPrivilege.FormManagement) < 2 + && user.getLocalPrivileges().get(LocalPrivilege.AccessResults) < 1 + && user.getLocalPrivileges().get(LocalPrivilege.AccessDraft) < 2 + && user.getResultAccess() == null + && (!(answerSet.getResponderEmail() != null && user.getEmail() != null + && (answerSet.getResponderEmail().equalsIgnoreCase(user.getEmail()) || answerSet + .getResponderEmail().equalsIgnoreCase(Tools.md5hash(user.getEmail())))))) { + throw new ForbiddenURLException(); + } + + response.setHeader("Content-Disposition", + "attachment;filename=" + answerSet.getSurvey().getShortname() + "_" + code + ".pdf"); + response.setContentType("application/pdf"); + + FileInputStream inputStream = new FileInputStream(result); + IOUtils.copy(inputStream, response.getOutputStream()); + inputStream.close(); + + response.flushBuffer(); + } else { + throw new InvalidURLException(); + } + + return null; + } catch (ForbiddenURLException fe) { + throw fe; + } catch (InvalidURLException ie) { + throw ie; + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + } + throw new InvalidURLException(); + } + +} diff --git a/src/main/java/com/ec/survey/controller/RunnerController.java b/src/main/java/com/ec/survey/controller/RunnerController.java index 46185d13f..2c2e6c7cc 100644 --- a/src/main/java/com/ec/survey/controller/RunnerController.java +++ b/src/main/java/com/ec/survey/controller/RunnerController.java @@ -672,6 +672,8 @@ public ModelAndView invitedPOST(@PathVariable String group, @PathVariable String survey = surveyService.getSurvey(survey.getId(), lang); + + if (!survey.isAnonymous() && answerSet.getResponderEmail() != null) { result.addObject("participantsemail", answerSet.getResponderEmail()); } @@ -682,6 +684,10 @@ public ModelAndView invitedPOST(@PathVariable String group, @PathVariable String translationService.getActiveTranslationsForSurvey(survey.getId()), contextpath); form.setSurvey(survey); + if(!survey.getConfirmationPageLink()){ + form.getAnswerSets().add(answerSet); + } + result.addObject("form", form); result.addObject("text", survey.getConfirmationPage()); if (survey.getConfirmationPageLink() != null && survey.getConfirmationPageLink() @@ -801,7 +807,10 @@ public ModelAndView contactform(@PathVariable String uidorshortname, HttpServlet throw new InvalidURLException(); } - return new ModelAndView("runner/contactForm", Constants.SURVEY, survey); + ModelAndView model = new ModelAndView("runner/contactForm", Constants.SURVEY, survey); + model.addObject("USER", request.getSession().getAttribute("USER")); + return model; + } @PostMapping(value = "/contactform/{uidorshortname}") @@ -881,6 +890,7 @@ public String contactformPOST(@PathVariable String uidorshortname, HttpServletRe mailService.SendHtmlMail(owneremail, sender, sender, subject, text, attachment1, attachment2, null, true); model.put("messagesent", true); + model.put("survey", survey); return "runner/contactForm"; } @@ -2119,6 +2129,10 @@ public ModelAndView processSubmit(@PathVariable String uidorshortname, HttpServl sessionService.setFormStartDate(request, form, uniqueCode); form.setSurvey(survey); + if(!survey.getConfirmationPageLink()){ + form.getAnswerSets().add(answerSet); + } + result.addObject("form", form); result.addObject("text", survey.getConfirmationPage()); if (survey.getConfirmationPageLink() != null && survey.getConfirmationPageLink() @@ -2127,6 +2141,7 @@ public ModelAndView processSubmit(@PathVariable String uidorshortname, HttpServl } else if (survey.getEcasSecurity() && request.getParameter("passwordauthenticated") == null) { result.addObject("asklogout", true); } + result.addObject("surveyprefix", survey.getId() + "."); request.getSession().removeAttribute("ECASSURVEY"); diff --git a/src/main/java/com/ec/survey/controller/TranslationController.java b/src/main/java/com/ec/survey/controller/TranslationController.java index 99c130c61..1ff00fc13 100644 --- a/src/main/java/com/ec/survey/controller/TranslationController.java +++ b/src/main/java/com/ec/survey/controller/TranslationController.java @@ -722,7 +722,7 @@ public ModelAndView addtranslations(@PathVariable String shortname, HttpServletR } if (form.getSurvey().getConfirmationPage().equalsIgnoreCase(Survey.CONFIRMATIONTEXT)) { - String confirmation = resources.getMessage("message.confirmation", null, Survey.CONFIRMATIONTEXT, + String confirmation = resources.getMessage("message.confirmationWithTitle", null, Survey.CONFIRMATIONTEXT, new Locale(language.getCode())); newTranslation.getTranslations().add(new Translation(Survey.CONFIRMATIONPAGE, confirmation, language.getCode(), form.getSurvey().getId(), newTranslation)); diff --git a/src/main/java/com/ec/survey/handler/ServerEnvironmentHandlerInterceptor.java b/src/main/java/com/ec/survey/handler/ServerEnvironmentHandlerInterceptor.java index 64053c3dd..59015645d 100644 --- a/src/main/java/com/ec/survey/handler/ServerEnvironmentHandlerInterceptor.java +++ b/src/main/java/com/ec/survey/handler/ServerEnvironmentHandlerInterceptor.java @@ -38,7 +38,10 @@ public class ServerEnvironmentHandlerInterceptor extends HandlerInterceptorAdapt public @Value("${captcha.key}") String captchakey; public static final String APPLICATION_CAPTCHA_SERVERPREFIX = "captchaServerPrefix"; - public @Value("${captcha.serverprefix}") String captchaserverprefix; + public @Value("${captcha.serverprefix}") String captchaserverprefix; + + public static final String APPLICATION_CAPTCHA_DYNATRACE_SRC = "captchaDynatraceSrc"; + public @Value("${captcha.dynatracesrc:#{null}}") String captchaDynatraceSrc; public static final String APPLICATION_ARCHIVING = "enablearchiving"; public @Value("${ui.enablearchiving}") String enablearchiving; @@ -106,7 +109,8 @@ public void postHandle(final HttpServletRequest request, modelAndView.getModelMap().addAttribute(APPLICATION_CAPTCHA_BYPASS, isByPassCaptcha()); modelAndView.getModelMap().addAttribute(APPLICATION_CAPTCHA_KEY, captchakey); - modelAndView.getModelMap().addAttribute(APPLICATION_CAPTCHA_SERVERPREFIX, captchaserverprefix); + modelAndView.getModelMap().addAttribute(APPLICATION_CAPTCHA_SERVERPREFIX, captchaserverprefix); + modelAndView.getModelMap().addAttribute(APPLICATION_CAPTCHA_DYNATRACE_SRC, captchaDynatraceSrc); modelAndView.getModelMap().addAttribute(APPLICATION_ARCHIVING, enablearchiving != null && enablearchiving.equalsIgnoreCase("true")); modelAndView.getModelMap().addAttribute(APPLICATION_DELPHI, enabledelphi != null && enabledelphi.equalsIgnoreCase("true")); diff --git a/src/main/java/com/ec/survey/model/Form.java b/src/main/java/com/ec/survey/model/Form.java index af2e24feb..4d3b5ef85 100644 --- a/src/main/java/com/ec/survey/model/Form.java +++ b/src/main/java/com/ec/survey/model/Form.java @@ -2,9 +2,11 @@ import com.ec.survey.model.administration.User; import com.ec.survey.model.survey.*; +import com.ec.survey.model.survey.base.File; import com.ec.survey.tools.ConversionTools; import com.ec.survey.tools.Numbering; import com.ec.survey.tools.SurveyHelper; +import com.ec.survey.tools.Tools; import org.apache.commons.lang.StringEscapeUtils; import org.apache.log4j.Logger; @@ -14,6 +16,10 @@ import java.util.*; import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.persistence.Transient; @Configurable public class Form { @@ -533,7 +539,7 @@ public String getQuestionTitle(Element question) { } if (element instanceof Question && !(element instanceof Text) && !(element instanceof Image) - && !(element instanceof Confirmation)) { + && !(element instanceof Ruler) && !(element instanceof Confirmation)) { counter++; Question q = (Question) element; if (q.getId().equals(question.getId())) { @@ -766,4 +772,164 @@ public long getPassedTimeInSeconds() { long milliSeconds = this.currentDate.getTime() - this.startDate.getTime(); return milliSeconds / 1000; } + + /** + * Replaces the Hypertext Markup from the Confirmation Page text with the corresponding values at the end of a survey. + * Possible Markups: {InvitationNumber} {ContributionID} {UserName} {CreationDate} {LastUpdate} {Language} Question - {IDs} + * @return String with all Markups replaced + */ + public String replacedMarkupConfirmationPage() { + + if(getAnswerSets().size() <= 0) { + return survey.getConfirmationPage(); + } + + String confPageText = survey.getConfirmationPage(); + List> pages = getPages(); + + if(confPageText.length() <= 0) + return ""; + + Matcher m = Pattern.compile("\\{(.*?)\\}").matcher(confPageText); + int lastIndex = 0; + StringBuilder replacedMsg = new StringBuilder(); + + while (m.find()) { + String match = m.group(1); + switch(match){ + case "InvitationNumber": + replacedMsg.append(confPageText, lastIndex, m.start()).append(replaceNullByHyphen(getAnswerSets().get(0).getInvitationId())); + break; + case "ContributionID": + replacedMsg.append(confPageText, lastIndex, m.start()).append(replaceNullByHyphen(getAnswerSets().get(0).getUniqueCode())); + break; + case "UserName": + replacedMsg.append(confPageText, lastIndex, m.start()).append(replaceNullByHyphen(getAnswerSets().get(0).getResponderEmail())); + break; + case "CreationDate": + replacedMsg.append(confPageText, lastIndex, m.start()).append(Tools.formatDate(getAnswerSets().get(0).getDate(), ConversionTools.DateFormat)); + break; + case "LastUpdate": + replacedMsg.append(confPageText, lastIndex, m.start()).append(Tools.formatDate(getAnswerSets().get(0).getUpdateDate(), ConversionTools.DateFormat)); + break; + case "Language": + replacedMsg.append(confPageText, lastIndex, m.start()).append(getAnswerSets().get(0).getLanguageCode()); + break; + default: + // is ID? + for(List le : pages){ + for(Element e : le){ + switch(e.getType()){ + case "Matrix": + for(Element c : ((Matrix) e).getQuestions()){ + if(c.getShortname().equals(match)) + replacedMsg.append(confPageText, lastIndex, m.start()).append(getQuestionResult(c, true)); + } + break; + case "Table": + MatrixOrTable table = (MatrixOrTable) e; + List tableQuestions = (table.getQuestions()); + for(int i = 0; i < tableQuestions.size(); i++){ + if(tableQuestions.get(i).getShortname().equals(match)){ + AnswerSet as = getAnswerSets().get(0); + String res = ""; + for(int l = 1; l < table.getColumns(); l++){ + if(l > 1) res += "; "; + String cellAnswer = as.getTableAnswer(e,i+1, l, false); + res += (cellAnswer == "" || cellAnswer == null) ? "-" : cellAnswer; + } + replacedMsg.append(confPageText, lastIndex, m.start()).append(res); + } + } + break; + case "ComplexTable": + for(ComplexTableItem cti : ((ComplexTable) e).getChildElements()){ + if(cti.getShortname().equals(match)) + replacedMsg.append(confPageText, lastIndex, m.start()).append(getQuestionResult(cti, false)); + } + break; + case "RatingQuestion": + for(Element rq : ((RatingQuestion) e).getChildElements()){ + if(rq.getShortname().equals(match)) + replacedMsg.append(confPageText, lastIndex, m.start()).append(getQuestionResult(rq, false)); + } + break; + default: + if(e.getShortname().equals(match)) + replacedMsg.append(confPageText, lastIndex, m.start()).append(getQuestionResult(e, false)); + break; + } + } + } + break; + } + lastIndex = m.end(); + } + + // add text after last replacement + if(lastIndex <= confPageText.length()); + replacedMsg.append(confPageText, lastIndex, confPageText.length()); + + return replacedMsg.toString(); + } + + private String replaceNullByHyphen(String value) { + if (value == null) return "-"; + return value; + } + + /** + * Given an Element e, the answers from the user get returned in the correct format from the saved answerset. + * @param e the survey element we retrieve the answer from. + * @param f the form we retrieve the answers from. + * @param isMatrix Element is a Matrix? + * @return Result for a given Question Element in the correct format for the contribution page. + */ + private String getQuestionResult(Element e, Boolean isMatrix) { + + List as = getAnswerSets().get(0).getAnswers(e.getId(), e.getUniqueId()); + String result = ""; + + switch(e.getType()){ + case "SingleChoiceQuestion": + case "MultipleChoiceQuestion": + case "ComplexTableItem": + return as.size() > 0 ? ConversionTools.removeHTML(constructAnswerString(as, "; "), true) : "-"; + case "Upload": + return as.size() > 0 ? as.get(0).getFiles().get(0).getName() : "-"; + case "GalleryQuestion": + if(as.size() == 0) return "-"; + + List files = ((GalleryQuestion) e).getFiles(); + result = ""; + for(int i = 0; i < as.size(); i++){ + if(i > 0) result = result + "; "; + result = result + files.get(Integer.parseInt(getAnswerTitle(as.get(i)))).getName(); + } + return ConversionTools.removeHTML(result, true); + case "Text": + // Matrix Question + if(isMatrix) + return as.size() > 0 ? constructAnswerString(as, "; ") : "-"; + + // Ranking Question + return as.size() > 0 ? as.get(0).getValue() : "-"; + case "RankingQuestion": + if(as.size() <= 0) { + return "-"; + } + return getAnswerTitle(as.get(0)); + default: + return as.size() > 0 ? ConversionTools.removeHTML(getAnswerTitle(as.get(0)), true) : "-"; + } + } + + private String constructAnswerString(List as, String separator) { + String res = ""; + for(int i = 0; i < as.size(); i++){ + if(i>0) res = res + separator; + res = res + getAnswerTitle(as.get(i)); + } + return res; + } } diff --git a/src/main/java/com/ec/survey/model/Statistics.java b/src/main/java/com/ec/survey/model/Statistics.java index 9bfef58c3..5eab64ccc 100644 --- a/src/main/java/com/ec/survey/model/Statistics.java +++ b/src/main/java/com/ec/survey/model/Statistics.java @@ -218,17 +218,17 @@ public double getTotalsPercentForRatingQuestion(Element question, Integer answer } @Transient - public int getRequestedRecordsForGallery(Element question, int index) + public int getRequestedRecordsForGallery(Element question, String fileUid) { - String id = question.getId().toString() + "-" + index; + String id = question.getId().toString() + "-" + fileUid; Object result = requestedRecords.get(id); return (int) (result != null ? result : 0); } @Transient - public double getRequestedRecordsPercentForGallery(Element question, int index) + public double getRequestedRecordsPercentForGallery(Element question, String fileUid) { - String id = question.getId().toString() + "-" + index; + String id = question.getId().toString() + "-" + fileUid; Object result = requestedRecordsPercent.get(id); return (double) (result != null ? result : 0); } diff --git a/src/main/java/com/ec/survey/model/delphi/DelphiQuestionType.java b/src/main/java/com/ec/survey/model/delphi/DelphiQuestionType.java index 62a32307a..f6570e92b 100644 --- a/src/main/java/com/ec/survey/model/delphi/DelphiQuestionType.java +++ b/src/main/java/com/ec/survey/model/delphi/DelphiQuestionType.java @@ -9,6 +9,7 @@ public enum DelphiQuestionType { Rating, FreeText, Number, + Formula, Date, Time, RegEx, @@ -40,6 +41,10 @@ public static DelphiQuestionType from(Question question) { return Number; } + if (question instanceof FormulaQuestion) { + return Formula; + } + if (question instanceof DateQuestion) { return Date; } diff --git a/src/main/java/com/ec/survey/model/survey/ChoiceQuestion.java b/src/main/java/com/ec/survey/model/survey/ChoiceQuestion.java index 27dcb319b..9b3ec6848 100644 --- a/src/main/java/com/ec/survey/model/survey/ChoiceQuestion.java +++ b/src/main/java/com/ec/survey/model/survey/ChoiceQuestion.java @@ -42,7 +42,7 @@ public void setOrder(Integer order) { this.order = order; } - @OneToMany(targetEntity=PossibleAnswer.class, cascade = CascadeType.ALL) + @OneToMany(targetEntity=PossibleAnswer.class, cascade = CascadeType.ALL, orphanRemoval = true) @Fetch(value = FetchMode.SELECT) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @OrderBy(value = "position asc") diff --git a/src/main/java/com/ec/survey/model/survey/ComplexTableItem.java b/src/main/java/com/ec/survey/model/survey/ComplexTableItem.java index 0967af209..9601d4259 100644 --- a/src/main/java/com/ec/survey/model/survey/ComplexTableItem.java +++ b/src/main/java/com/ec/survey/model/survey/ComplexTableItem.java @@ -323,7 +323,10 @@ public String getResultTitle(ComplexTable parent) { ComplexTableItem row = parent.getChildAt(0, this.column); ComplexTableItem column = parent.getChildAt(this.row, 0); - return valueOrDash(parent.getStrippedTitle()) + " : " + valueOrDash(column.getStrippedTitle()) + " : " + valueOrDash(row.getStrippedTitle()); + String columnPart = column == null ? "-" : valueOrDash(column.getStrippedTitle()); + String rowPart = row == null ? "-" : valueOrDash(row.getStrippedTitle()); + + return valueOrDash(parent.getStrippedTitle()) + " : " + columnPart + " : " + rowPart; } private String valueOrDash(String value) { @@ -394,7 +397,10 @@ public boolean differsFrom(Element element) { if (!Tools.isEqual(minD, other.minD)) return true; if (!Tools.isEqual(unit, other.unit)) return true; if (!Tools.isEqual(formula, other.formula)) return true; - + + if (getPossibleAnswers().size() != other.getPossibleAnswers().size()) + return true; + for (int i = 0; i < getPossibleAnswers().size(); i++) { if (getPossibleAnswers().get(i).differsFrom(other.getPossibleAnswers().get(i))) { return true; diff --git a/src/main/java/com/ec/survey/model/survey/FormulaQuestion.java b/src/main/java/com/ec/survey/model/survey/FormulaQuestion.java index dd4cd586e..58b52e7a3 100644 --- a/src/main/java/com/ec/survey/model/survey/FormulaQuestion.java +++ b/src/main/java/com/ec/survey/model/survey/FormulaQuestion.java @@ -170,4 +170,9 @@ public List getAllPossibleAnswers() { return answers; } + + @Transient + public String getAnswerWithPrefix(String answer) { + return getId() + answer; + } } diff --git a/src/main/java/com/ec/survey/model/survey/GalleryQuestion.java b/src/main/java/com/ec/survey/model/survey/GalleryQuestion.java index 542d7539d..819ddb9c4 100644 --- a/src/main/java/com/ec/survey/model/survey/GalleryQuestion.java +++ b/src/main/java/com/ec/survey/model/survey/GalleryQuestion.java @@ -39,6 +39,7 @@ public GalleryQuestion(String title, String shortname, String uid) { private boolean selection; private boolean numbering; private List files = new ArrayList<>(); + private List missingFiles = new ArrayList<>(); @Column(name = "COLS") public Integer getColumns() { @@ -86,6 +87,43 @@ public void setFiles(List files) { this.files = files; } + @Transient + public List getMissingFiles() { + return missingFiles; + } + public void setMissingFiles(List missingFiles) { + this.missingFiles = missingFiles; + } + + @Transient + public List getAllFiles() { + + if (!missingFiles.isEmpty()) + { + List result = new ArrayList<>(); + for (File f : missingFiles) + { + if (!result.contains(f)) + { + result.add(f); + } + } + for (File f : files) + { + if (!result.contains(f)) + { + result.add(f); + } + } + + result.sort(Survey.newFileByPositionComparator()); + + return result; + } else { + return files; + } + } + public GalleryQuestion copy(String fileDir) throws ValidationException { GalleryQuestion copy = new GalleryQuestion(); @@ -171,5 +209,14 @@ public boolean differsFrom(Element element) { return false; } + + public File getFileByUid(String uid) { + for (File file : getAllFiles()) { + if (file.getUid().equals(uid)) { + return file; + } + } + return null; + } } diff --git a/src/main/java/com/ec/survey/model/survey/Question.java b/src/main/java/com/ec/survey/model/survey/Question.java index 7c6ca194a..7b7f83b23 100644 --- a/src/main/java/com/ec/survey/model/survey/Question.java +++ b/src/main/java/com/ec/survey/model/survey/Question.java @@ -136,7 +136,11 @@ public DelphiChartType getDelphiChartType() { } if (!delphiQuestion && (delphiChartType == null || delphiChartType == DelphiChartType.None)) { - if (this instanceof ChoiceQuestion || this instanceof Matrix || this instanceof NumberQuestion || this instanceof RatingQuestion || this instanceof FormulaQuestion) { + if (this instanceof ChoiceQuestion || + this instanceof Matrix || + this instanceof NumberQuestion || + this instanceof RatingQuestion || + this instanceof FormulaQuestion) { return DelphiChartType.Pie; } @@ -149,7 +153,11 @@ public DelphiChartType getDelphiChartType() { } if (this instanceof ComplexTableItem) { - if (((ComplexTableItem)this).getCellType() == ComplexTableItem.CellType.FreeText) { + ComplexTableItem item = (ComplexTableItem) this; + if (item.isChoice() || item.getCellType() == ComplexTableItem.CellType.Formula || item.getCellType() == ComplexTableItem.CellType.Number){ + return DelphiChartType.Pie; + } + else if (item.getCellType() == ComplexTableItem.CellType.FreeText) { return DelphiChartType.WordCloud; } } diff --git a/src/main/java/com/ec/survey/model/survey/Survey.java b/src/main/java/com/ec/survey/model/survey/Survey.java index 18c47383e..69cd71eca 100644 --- a/src/main/java/com/ec/survey/model/survey/Survey.java +++ b/src/main/java/com/ec/survey/model/survey/Survey.java @@ -14,6 +14,8 @@ import java.util.stream.Collectors; import java.util.SortedMap; import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.persistence.CascadeType; import javax.persistence.Column; @@ -32,10 +34,7 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; -import com.ec.survey.model.ECFProfile; -import com.ec.survey.model.Language; -import com.ec.survey.model.Publication; -import com.ec.survey.model.Skin; +import com.ec.survey.model.*; import com.ec.survey.model.administration.User; import com.ec.survey.model.survey.base.File; import com.ec.survey.service.SurveyService; @@ -75,7 +74,7 @@ final public class Survey implements java.io.Serializable { public static final String QUIZWELCOMEMESSAGE = "QUIZWELCOMEMESSAGE"; public static final String QUIZRESULTSMESSAGE = "QUIZRESULTSMESSAGE"; - public static final String CONFIRMATIONTEXT = "Thank you for your contribution"; + public static final String CONFIRMATIONTEXT = " Contribution successfully submitted

Thank you for your contribution!"; public static final String ESCAPETEXT = "This survey has not yet been published or has already been unpublished in the meantime."; public static final String RESULTSMESSAGETEXT = "Thank you for your contribution"; @@ -189,7 +188,7 @@ final public class Survey implements java.io.Serializable { private Boolean isDelphiShowAnswersAndStatisticsInstantly = false; private Boolean isDelphiShowAnswers = false; private Integer minNumberDelphiStatistics = 1; - private String logoText; + private String logoText = ""; private Boolean isShowCountdown = true; private String timeLimit; private boolean preventGoingBack = false; @@ -200,6 +199,7 @@ final public class Survey implements java.io.Serializable { private Boolean motivationPopup = false; private Boolean motivationType = false; private String motivationText = MOTIVATIONPOPUPTEXT; + private String motivationPopupTitle = ""; private Integer motivationTriggerProgress = MOTIVATIONPOPUPPROGRESS; private Integer motivationTriggerTime = MOTIVATIONPOPUPTIME; private String codaLink; @@ -846,6 +846,22 @@ public void setMotivationText(String motivationText) { : MOTIVATIONPOPUPTEXT; } + @Column(name = "MOTIVATIONTITLE") + public String getMotivationPopupTitle() { + if (this.motivationPopupTitle == null){ + return ""; + } + return this.motivationPopupTitle; + } + + public void setMotivationPopupTitle(String motivationTitle) { + if (motivationTitle == null){ + motivationTitle = ""; + } + this.motivationPopupTitle = motivationTitle; + } + + @Column(name = "MOTIVATIONTRIGGERPROGRESS") public Integer getMotivationTriggerProgress() { return this.motivationTriggerProgress != null && this.motivationTriggerProgress > 0 @@ -1148,10 +1164,19 @@ public Map getQuestionMap() { return result; } - @Transient - public Map getQuestionMapByUniqueId() { - Map result = new HashMap<>(); - for (Element element : elements) { + @Transient + public Map getQuestionMapByUniqueId() { + return getQuestionMapByUniqueId(false); + } + + @Transient + public Map getQuestionMapByUniqueId(boolean includeMissing) { + Map result = new HashMap<>(); + List elementsList = new ArrayList<>(elements); + if (includeMissing) { + elementsList.addAll(missingElements); + } + for (Element element : elementsList) { if (element instanceof Question) { result.put(element.getUniqueId(), element); } @@ -1274,7 +1299,60 @@ public List getQuestions() { return result; } + @Transient + public List getValidMarkupIDs() { + List questions = getQuestions(); + List validIDs = new ArrayList<>(); + + for(Question q : questions){ + switch(q.getType()){ + case "Table": + case "Matrix": + for(Element e : ((MatrixOrTable) q).getQuestions()){ + validIDs.add(e.getShortname()); + } + break; + case "ComplexTable": + for(Element e : ((ComplexTable) q).getChildElements()) { + validIDs.add(e.getShortname()); + } + break; + case "RatingQuestion": + for(Element e : ((RatingQuestion) q).getChildElements()){ + validIDs.add(e.getShortname()); + } + break; + case "Download": + case "Confirmation": + case "": + break; + default: + validIDs.add(q.getShortname()); + break; + } + } + + return validIDs; + } + protected static Comparator newElementByPositionComparator() { + return (first, second) -> { + + int result = 0; + if (first.getPosition() != null && second.getPosition() != null) { + result = first.getPosition().compareTo(second.getPosition()); + } + + // if both elements have the same position, the older one should be first + if (result == 0) { + result = first.getId().compareTo(second.getId()); + } + + return result; + }; + } + + protected static Comparator newFileByPositionComparator() { return (first, second) -> { int result = 0; @@ -1653,6 +1731,9 @@ public String serialize(boolean elementOrderOnly) { result.append(" confirmationLink: ").append(this.getConfirmationLink()).append(";"); result.append(" confirmationPageLink: ").append(confirmationPageLink).append(";"); result.append(" motivationText: ").append(this.getMotivationText()).append(";"); + if (motivationPopupTitle != null){ + result.append(" motivationPopupTitle: ").append(motivationPopupTitle).append(";"); + } result.append(" motivationTriggerProgress: ").append(motivationTriggerProgress).append(";"); result.append(" motivationTriggerTime: ").append(motivationTriggerTime).append(";"); result.append(" motivationPopup: ").append(motivationPopup).append(";"); @@ -1759,6 +1840,7 @@ public Survey copy(SurveyService surveyService, User powner, String fileDir, boo copy.motivationText = Tools.filterHTML(motivationText); copy.motivationTriggerProgress = motivationTriggerProgress; copy.motivationTriggerTime = motivationTriggerTime; + copy.motivationPopupTitle = motivationPopupTitle; copy.progressDisplay = progressDisplay; copy.validatedPerPage = validatedPerPage; copy.setWcagCompliance(wcagCompliance); @@ -1904,6 +1986,15 @@ public Map copyElements(Survey surveyCopy, SurveyService surve for (Element child : t.getChildElements()) { elementsBySourceId.put(child.getSourceId(), child); } + } else if (newElement instanceof ComplexTable) { + ComplexTable table = (ComplexTable) newElement; + for (ComplexTableItem item : table.getChildElements()){ + if (item.isChoice()){ + for (PossibleAnswer answer : item.getPossibleAnswers()) { + elementsBySourceId.put(answer.getSourceId(), answer); + } + } + } } } @@ -2042,10 +2133,16 @@ public void setPublication(Publication publication) { @Column(name = "LOGOTEXT") public String getLogoText() { + if (logoText == null){ + return ""; + } return logoText; } public void setLogoText(String logoText) { + if (logoText == null){ + logoText = ""; + } this.logoText = logoText; } diff --git a/src/main/java/com/ec/survey/replacements/Pair.java b/src/main/java/com/ec/survey/replacements/Pair.java new file mode 100644 index 000000000..1c2a74f08 --- /dev/null +++ b/src/main/java/com/ec/survey/replacements/Pair.java @@ -0,0 +1,42 @@ +package com.ec.survey.replacements; + +import java.io.Serializable; +import java.util.Objects; + +//Replacement for javafx.util.Pair; +public class Pair implements Serializable { + private final K key; + private final V value; + + public Pair(K key, V value){ + this.key = key; + this.value = value; + } + + public K getKey(){ + return key; + } + + public V getValue(){ + return value; + } + + @Override + public String toString() { + return key + "=" + value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Pair pair = (Pair) o; + return Objects.equals(key, pair.key) && + Objects.equals(value, pair.value); + } + + @Override + public int hashCode() { + return Objects.hash(key, value); + } +} diff --git a/src/main/java/com/ec/survey/security/SavedRequestAwareAuthenticationSuccessHandlerExtended.java b/src/main/java/com/ec/survey/security/SavedRequestAwareAuthenticationSuccessHandlerExtended.java index 94b02af5d..8e860517a 100644 --- a/src/main/java/com/ec/survey/security/SavedRequestAwareAuthenticationSuccessHandlerExtended.java +++ b/src/main/java/com/ec/survey/security/SavedRequestAwareAuthenticationSuccessHandlerExtended.java @@ -1,104 +1,99 @@ -package com.ec.survey.security; - -import com.ec.survey.controller.ManagementController; -import com.ec.survey.model.administration.User; -import com.ec.survey.tools.Constants; - -import org.apache.log4j.Logger; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; -import org.springframework.security.web.savedrequest.HttpSessionRequestCache; -import org.springframework.security.web.savedrequest.RequestCache; -import org.springframework.security.web.savedrequest.SavedRequest; -import org.springframework.util.StringUtils; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.List; - -public class SavedRequestAwareAuthenticationSuccessHandlerExtended extends SimpleUrlAuthenticationSuccessHandler { - protected static final Logger logger = Logger.getLogger(ManagementController.class); - - private RequestCache requestCache = new HttpSessionRequestCache(); - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws ServletException, IOException { - SavedRequest savedRequest = requestCache.getRequest(request, response); - - String survey = request.getParameter(Constants.SURVEY); - - if (survey != null) { - getRedirectStrategy().sendRedirect(request, response, "/runner/" + survey); - return; - } - - User user = (User) request.getSession().getAttribute("USER"); - - if (!user.isAgreedToPS()) { - getRedirectStrategy().sendRedirect(request, response, "/auth/ps"); - return; - } - - if (!user.isAgreedToToS()) { - getRedirectStrategy().sendRedirect(request, response, "/auth/tos"); - return; - } - - if (user.isDeleted()) { - getRedirectStrategy().sendRedirect(request, response, "/auth/deleted"); - return; - } - - if (savedRequest == null) { - super.onAuthenticationSuccess(request, response, authentication); - - return; - } - String targetUrlParameter = getTargetUrlParameter(); - if (isAlwaysUseDefaultTargetUrl() - || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) { - requestCache.removeRequest(request, response); - super.onAuthenticationSuccess(request, response, authentication); - - return; - } - - clearAuthenticationAttributes(request); - - // Use the DefaultSavedRequest URL - String targetUrl = savedRequest.getRedirectUrl(); - - if (!savedRequest.getMethod().equalsIgnoreCase("GET") || isAjax(targetUrl, savedRequest)) { - targetUrl = "/dashboard"; - } - - // check if url is valid - if (targetUrl.endsWith("addUser")) { - targetUrl = "/dashboard"; - } - - getRedirectStrategy().sendRedirect(request, response, targetUrl); - - } - - private boolean isAjax(String url, SavedRequest request) { - if (url != null && (url.contains("checkNew") || url.contains("/messages") || url.contains("/dashboard/") - || url.toLowerCase().contains("json") || url.toLowerCase().contains("ajax"))) { - return true; - } - - List requestedWithHeader = request.getHeaderValues("X-Requested-With"); - return requestedWithHeader != null && requestedWithHeader.contains("XMLHttpRequest"); - } - - public void setRequestCache(RequestCache requestCache) { - this.requestCache = requestCache; - } - - public RequestCache getRequestCache() { - return requestCache; - } -} +package com.ec.survey.security; + +import com.ec.survey.controller.ManagementController; +import com.ec.survey.model.administration.User; +import com.ec.survey.tools.Constants; + +import org.apache.log4j.Logger; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.security.web.savedrequest.HttpSessionRequestCache; +import org.springframework.security.web.savedrequest.RequestCache; +import org.springframework.security.web.savedrequest.SavedRequest; +import org.springframework.util.StringUtils; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; + +public class SavedRequestAwareAuthenticationSuccessHandlerExtended extends SimpleUrlAuthenticationSuccessHandler { + protected static final Logger logger = Logger.getLogger(ManagementController.class); + + private RequestCache requestCache = new HttpSessionRequestCache(); + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws ServletException, IOException { + SavedRequest savedRequest = requestCache.getRequest(request, response); + + String survey = request.getParameter(Constants.SURVEY); + + if (survey != null) { + getRedirectStrategy().sendRedirect(request, response, "/runner/" + survey); + return; + } + + User user = (User) request.getSession().getAttribute("USER"); + + if (!user.isAgreedToPS()) { + getRedirectStrategy().sendRedirect(request, response, "/auth/ps"); + return; + } + + if (!user.isAgreedToToS()) { + getRedirectStrategy().sendRedirect(request, response, "/auth/tos"); + return; + } + + if (user.isDeleted()) { + getRedirectStrategy().sendRedirect(request, response, "/auth/deleted"); + return; + } + + if (savedRequest == null) { + super.onAuthenticationSuccess(request, response, authentication); + + return; + } + String targetUrlParameter = getTargetUrlParameter(); + if (isAlwaysUseDefaultTargetUrl() + || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) { + requestCache.removeRequest(request, response); + super.onAuthenticationSuccess(request, response, authentication); + + return; + } + + clearAuthenticationAttributes(request); + + // Use the DefaultSavedRequest URL + String targetUrl = savedRequest.getRedirectUrl(); + + if (!savedRequest.getMethod().equalsIgnoreCase("GET") || isAjax(targetUrl, savedRequest) || targetUrl.equals("@null") || targetUrl.endsWith("addUser")) { + targetUrl = "/dashboard"; + } + + getRedirectStrategy().sendRedirect(request, response, targetUrl); + + } + + private boolean isAjax(String url, SavedRequest request) { + if (url != null && (url.contains("checkNew") || url.contains("/messages") || url.contains("/dashboard/") + || url.toLowerCase().contains("json") || url.toLowerCase().contains("ajax"))) { + return true; + } + + List requestedWithHeader = request.getHeaderValues("X-Requested-With"); + return requestedWithHeader != null && requestedWithHeader.contains("XMLHttpRequest"); + } + + public void setRequestCache(RequestCache requestCache) { + this.requestCache = requestCache; + } + + public RequestCache getRequestCache() { + return requestCache; + } +} diff --git a/src/main/java/com/ec/survey/service/ActivityService.java b/src/main/java/com/ec/survey/service/ActivityService.java index 8e966b8ef..67bf61be0 100644 --- a/src/main/java/com/ec/survey/service/ActivityService.java +++ b/src/main/java/com/ec/survey/service/ActivityService.java @@ -353,7 +353,7 @@ private String getHQL(ActivityFilter filter, Map params) case "Token/Contacts/Department": hql += " AND logID > 500 AND logID < 506"; break; case "Translation": - hql += " AND (logID > 220 AND logID < 225) OR logID = 227 OR logID = 228"; break; + hql += " AND ((logID > 220 AND logID < 225) OR logID = 227 OR logID = 228)"; break; case "UsefulLink": hql += " AND (logID = 203 OR logID = 204)"; break; case "DeleteColumn": diff --git a/src/main/java/com/ec/survey/service/AnswerExplanationService.java b/src/main/java/com/ec/survey/service/AnswerExplanationService.java index dfb6f4459..c9f32429e 100644 --- a/src/main/java/com/ec/survey/service/AnswerExplanationService.java +++ b/src/main/java/com/ec/survey/service/AnswerExplanationService.java @@ -1,791 +1,791 @@ -package com.ec.survey.service; - -import com.ec.survey.model.*; -import com.ec.survey.model.delphi.DelphiContribution; -import com.ec.survey.model.delphi.DelphiContributions; -import com.ec.survey.model.delphi.DelphiMedian; -import com.ec.survey.model.delphi.DelphiTableOrderBy; -import com.ec.survey.model.survey.*; -import com.ec.survey.model.survey.base.File; -import com.ec.survey.tools.Constants; -import com.ec.survey.tools.ConversionTools; -import org.hibernate.Query; -import org.hibernate.SQLQuery; -import org.hibernate.Session; -import org.hibernate.transform.Transformers; -import org.owasp.esapi.ESAPI; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.math.BigInteger; -import java.util.*; -import java.util.stream.Collectors; - -@Service("answerExplanationService") -public class AnswerExplanationService extends BasicService { - - public static final String DELETED_DELPHI_COMMENT_WITH_REPLIES_TEXT = "[DELETED]"; - - @Transactional - public void deleteExplanationByAnswerSet(AnswerSet answerSet) { - - final Integer answerSetId = answerSet.getId(); - final Session session = sessionFactory.getCurrentSession(); - final Query query = session.createQuery("DELETE FROM AnswerExplanation WHERE answerSetId = :answerId") - .setInteger("answerId", answerSetId); - query.executeUpdate(); - } - - @Transactional(readOnly = true) - public AnswerExplanation getExplanation(int answerSetId, String questionUid) { - - final Session session = sessionFactory.getCurrentSession(); - final Query query = session.createQuery( - "SELECT ex FROM AnswerExplanation ex WHERE answerSetId = :answerSetId AND questionUid = :questionUid") - .setInteger("answerSetId", answerSetId).setString("questionUid", questionUid); - AnswerExplanation explanation = (AnswerExplanation) query.uniqueResult(); - if (explanation == null) { - throw new NoSuchElementException(); - } - return explanation; - } - - @Transactional(readOnly = true) - public Map> getAllExplanations(Survey survey) { - final Session session = sessionFactory.getCurrentSession(); - final Query query = session.createSQLQuery( - "SELECT ex.ANSWER_SET_ID, ex.QUESTION_UID, ex.TEXT FROM ANSWERS_EXPLANATIONS ex JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ex.ANSWER_SET_ID JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft") - .setBoolean("draft", survey.getIsDraft()).setString("surveyUid", survey.getUniqueId()); - - Map> result = new HashMap<>(); - - @SuppressWarnings("rawtypes") - List res = query.list(); - - for (Object o : res) { - Object[] a = (Object[]) o; - - int answerSetId = ConversionTools.getValue(a[0]); - String questionUid = (String) a[1]; - String explanation = (String) a[2]; - - if (!result.containsKey(answerSetId)) { - result.put(answerSetId, new HashMap()); - } - - result.get(answerSetId).put(questionUid, explanation); - } - - return result; - } - - @Transactional(readOnly = true) - public List getExplanationsOfSurvey(final String surveyUid, final boolean draft) { - - final Session session = sessionFactory.getCurrentSession(); - final Query query = session.createSQLQuery( - "SELECT f.FILE_ID, f.FILE_NAME, f.FILE_UID, ex.ANSWER_EXPLANATION_ID, ex.ANSWER_SET_ID, ex.QUESTION_UID, ex.TEXT FROM ANSWERS_EXPLANATIONS ex " - + "LEFT JOIN ANSWERS_EXPLANATIONS_FILES aef ON ex.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " - + "LEFT JOIN FILES f ON aef.files_FILE_ID = f.FILE_ID " - + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ex.ANSWER_SET_ID " - + "JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID " - + "WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft") - .setString("surveyUid", surveyUid).setBoolean("draft", draft); - - @SuppressWarnings("rawtypes") - final List queryResult = query.list(); - - Map explanations = new HashMap<>(); - for (Object row : queryResult) { - final Object[] element = (Object[]) row; - - final int fileId = ConversionTools.getValue(element[0]); - - final int answerExplanationId = ConversionTools.getValue(element[3]); - final int answerExplanationAnswerSetId = ConversionTools.getValue(element[4]); - final String answerExplanationQuestionUid = (String) element[5]; - final String answerExplanationText = (String) element[6]; - - if (!explanations.containsKey(answerExplanationId)) { - final AnswerExplanation explanation = new AnswerExplanation(); - explanation.setId(answerExplanationId); - explanation.setAnswerSetId(answerExplanationAnswerSetId); - explanation.setQuestionUid(answerExplanationQuestionUid); - explanation.setText(answerExplanationText); - explanations.put(answerExplanationId, explanation); - } - - if (fileId > 0) { - final String fileName = (String) element[1]; - final String fileUid = (String) element[2]; - - final File file = new File(); - file.setId(fileId); - file.setName(fileName); - file.setUid(fileUid); - explanations.get(answerExplanationId).addFile(file); - } - } - - return new ArrayList<>(explanations.values()); - } - - @Transactional(readOnly = true) - public List getCommentsOfSurvey(final String surveyUid, final boolean draft) { - - final Session session = sessionFactory.getCurrentSession(); - final Query query = session - .createSQLQuery("SELECT ac.ANSWER_COMMENT_ID " + "FROM ANSWERS_COMMENTS ac " - + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ac.ANSWER_SET_ID " - + "JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID " - + "WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft") - .setString("surveyUid", surveyUid).setBoolean("draft", draft); - - @SuppressWarnings("rawtypes") - List res = query.list(); - - final List comments = new ArrayList<>(); - - for (Object o : res) { - int commentId = ConversionTools.getValue(o); - AnswerComment comment = getComment(commentId); - comments.add(comment); - } - - return comments; - } - - @Transactional(readOnly = true) - public List getExplanations(int answerSetId) { - - final Session session = sessionFactory.getCurrentSession(); - final Query query = session.createQuery("SELECT ex FROM AnswerExplanation ex WHERE answerSetId = :answerSetId") - .setInteger("answerSetId", answerSetId); - @SuppressWarnings("unchecked") - List explanations = (List) query.list(); - return explanations; - } - - @Transactional(readOnly = true) - public DelphiContributions getDelphiContributions(ChoiceQuestion question, DelphiTableOrderBy orderBy, int limit, - int offset) { - return getDelphiContributions(Collections.singletonList(question.getUniqueId()), question.getUniqueId(), - question.getSurvey().getIsDraft(), orderBy, limit, offset); - } - - @Transactional(readOnly = true) - public DelphiContributions getDelphiContributions(Matrix question, DelphiTableOrderBy orderBy, int limit, - int offset) { - List uids = question.getQuestions().stream().map(Element::getUniqueId).collect(Collectors.toList()); - return getDelphiContributions(uids, question.getUniqueId(), question.getSurvey().getIsDraft(), orderBy, limit, - offset); - } - - @Transactional(readOnly = true) - public DelphiContributions getDelphiContributions(RatingQuestion question, DelphiTableOrderBy orderBy, int limit, - int offset) { - List uids = question.getQuestions().stream().map(Element::getUniqueId).collect(Collectors.toList()); - return getDelphiContributions(uids, question.getUniqueId(), question.getSurvey().getIsDraft(), orderBy, limit, - offset); - } - - @Transactional(readOnly = true) - public DelphiContributions getDelphiContributions(Table question, DelphiTableOrderBy orderBy, int limit, - int offset) { - return getDelphiContributions(Collections.singletonList(question.getUniqueId()), question.getUniqueId(), - question.getSurvey().getIsDraft(), orderBy, limit, offset); - } - - @Transactional(readOnly = true) - public DelphiContributions getDelphiContributions(Collection questionUids, String mainQuestionUid, - boolean isDraft, DelphiTableOrderBy orderBy, int limit, int offset) { - String orderByClauseInner; - String orderByClauseOuter; - - switch (orderBy) { - case UpdateAsc: - orderByClauseInner = "aset.ANSWER_SET_UPDATE ASC"; - orderByClauseOuter = "`update` ASC, answerSetId"; - break; - - case UpdateDesc: - orderByClauseInner = "aset.ANSWER_SET_UPDATE DESC"; - orderByClauseOuter = "`update` DESC, answerSetId"; - break; - - default: - throw new IllegalStateException("Unexpected value: " + orderBy); - } - - String contributionsQueryText = "" + "SELECT\n" + " aset.ANSWER_SET_ID answerSetId,\n" - + " aset.UNIQUECODE answerSetUniqueCode,\n" + " aset.ANSWER_SET_UPDATE `update`,\n" - + " a.VALUE value,\n" + " a.PA_UID answerUid,\n" + " a.QUESTION_UID questionUid,\n" - + " a.ANSWER_COL `column`,\n" + " a.ANSWER_ROW row,\n" - + " COALESCE(ex.TEXT, main_explanation.TEXT) explanation\n" + "FROM ANSWERS a\n" + "JOIN (\n" + - // select all answers sets that are relevant for this query - " SELECT\n" + " aset.ANSWER_SET_ID,\n" + " aset.UNIQUECODE,\n" - + " aset.ANSWER_SET_UPDATE\n" + " FROM ANSWERS a\n" - + " JOIN ANSWERS_SET aset ON a.AS_ID = aset.ANSWER_SET_ID\n" - + " JOIN SURVEYS s ON aset.SURVEY_ID = s.SURVEY_ID\n" + " WHERE a.QUESTION_UID IN :questionUids\n" - + " AND s.ISDRAFT = :isDraft\n" + " GROUP BY aset.ANSWER_SET_ID, aset.ANSWER_SET_UPDATE\n" - + " ORDER BY " + orderByClauseInner + "\n" + - // pagination - " LIMIT :limit OFFSET :offset\n" + ") AS aset ON a.AS_ID = aset.ANSWER_SET_ID\n" + - // add explanations - "LEFT JOIN ANSWERS_EXPLANATIONS ex ON a.QUESTION_UID = ex.QUESTION_UID AND ex.ANSWER_SET_ID = a.AS_ID\n" - + - // add explanation of main question (i.e. for ratings) - "LEFT JOIN (\n" + " SELECT TEXT, ANSWER_SET_ID\n" + " FROM ANSWERS_EXPLANATIONS\n" - + " WHERE QUESTION_UID = :mainQuestionUid\n" - + ") AS main_explanation ON a.AS_ID = main_explanation.ANSWER_SET_ID\n" + - // filter by question - "WHERE a.QUESTION_UID IN :questionUids\n" + - // sort data as required - "ORDER BY " + orderByClauseOuter + ", row, `column`"; - - Session session = sessionFactory.getCurrentSession(); - SQLQuery contributionsQuery = session.createSQLQuery(contributionsQueryText); - contributionsQuery.setResultTransformer(Transformers.aliasToBean(DelphiContribution.class)); - contributionsQuery.setParameterList("questionUids", questionUids); - contributionsQuery.setBoolean("isDraft", isDraft); - contributionsQuery.setString("mainQuestionUid", mainQuestionUid); - contributionsQuery.setInteger("limit", limit); - contributionsQuery.setInteger("offset", offset); - - @SuppressWarnings("unchecked") - List contributions = contributionsQuery.list(); - - int totalCount = getTotalDelphiContributions(questionUids, isDraft); - return new DelphiContributions(totalCount, contributions); - } - - - @Transactional(readOnly = true) - public List getDelphiDependentAnswers(String dependentElementUid, int answerSetId) { - Session session = sessionFactory.getCurrentSession(); - String sql = "SELECT VALUE FROM ANSWERS a WHERE a.QUESTION_UID = :questionUid AND a.AS_ID = :answerSetId"; - SQLQuery query = session.createSQLQuery(sql); - query.setString("questionUid", dependentElementUid); - query.setInteger("answerSetId", answerSetId); - - @SuppressWarnings("unchecked") - List result = query.list(); - - return result; - } - - @Transactional(readOnly = true) - public int getTotalDelphiContributions(Collection questionUids, boolean isDraft) { - String totalCountQueryText = "SELECT COUNT(DISTINCT aset.ANSWER_SET_ID) FROM ANSWERS a\n" - + "JOIN ANSWERS_SET aset ON a.AS_ID = aset.ANSWER_SET_ID\n" - + "JOIN SURVEYS s ON aset.SURVEY_ID = s.SURVEY_ID\n" - + "WHERE a.QUESTION_UID IN :questionUids AND s.ISDRAFT = :isDraft"; - - Session session = sessionFactory.getCurrentSession(); - SQLQuery totalCountQuery = session.createSQLQuery(totalCountQueryText); - totalCountQuery.setParameterList("questionUids", questionUids); - totalCountQuery.setBoolean("isDraft", isDraft); - return ((BigInteger) totalCountQuery.uniqueResult()).intValue(); - } - - @Transactional - public void createUpdateOrDeleteExplanations(AnswerSet answerSet) throws Exception { - final Session session = sessionFactory.getCurrentSession(); - - final Survey survey = answerSet.getSurvey(); - final String surveyUid = survey.getUniqueId(); - for (Question question : survey.getQuestions()) { - final String questionUid = question.getUniqueId(); - if (question.getIsDelphiQuestion()) { - AnswerSet.ExplanationData explanationData = answerSet.getExplanations().get(questionUid); - if (explanationData == null) { - continue; - } - - List answers; - if (question instanceof Matrix) { - Matrix matrix = (Matrix) question; - answers = answerSet.getMatrixAnswers(matrix); - } else if (question instanceof RatingQuestion) { - RatingQuestion rating = (RatingQuestion) question; - answers = answerSet.getRatingAnswers(rating); - } else { - answers = answerSet.getAnswers(question.getId(), questionUid); - } - - AnswerExplanation explanation; - boolean hasNoLinkedAnswers = answers.isEmpty(); - try { - explanation = getExplanation(answerSet.getId(), questionUid); - - if (hasNoLinkedAnswers) { - fileService.deleteUploadedExplanationFiles(surveyUid, answerSet.getUniqueCode(), questionUid); - fileService.deleteFilesFromDiskAndDatabase(surveyUid, explanation.getFiles()); - session.delete(explanation); - continue; - } - } catch (NoSuchElementException ex) { - if (hasNoLinkedAnswers) { - fileService.deleteUploadedExplanationFiles(surveyUid, answerSet.getUniqueCode(), questionUid); - explanationData.files.clear(); - } - if (explanationData.text.length() == 0 && explanationData.files.isEmpty()) { - continue; - } - - explanation = new AnswerExplanation(answerSet.getId(), questionUid); - } - - // special case: if the participant adds an explanation why she is outside the - // median - DelphiMedian median = null; - if (explanationData.text != null && explanationData.text.length() > 0 - && !explanationData.text.equalsIgnoreCase(explanation.getText()) && !answers.isEmpty()) { - if (question instanceof SingleChoiceQuestion) { - SingleChoiceQuestion choiceQuestion = (SingleChoiceQuestion) question; - if (choiceQuestion.getUseLikert() && choiceQuestion.getMaxDistance() > -1) { - median = answerService.getMedian(survey, choiceQuestion, answers.get(0), null); - } - } else if (question instanceof NumberQuestion) { - NumberQuestion numberQuestion = (NumberQuestion) question; - if (numberQuestion.isSlider() && numberQuestion.getMaxDistance() > -1) { - median = answerService.getMedian(survey, numberQuestion, answers.get(0), null); - } - } - if (median != null && median.isMaxDistanceExceeded()) { - - if (answerSet.getMedianWarningVisible()) - { - explanation.setChangedForMedian(true); - answerSet.setChangedForMedian(true); - } else if (explanation.getChangedForMedian() == null || !explanation.getChangedForMedian()) { - answerSet.setChangeExplanationText(true); - } - } - } - - explanation.setText(explanationData.text); - explanation.setFiles(explanationData.files); - - session.saveOrUpdate(explanation); - session.flush(); - } - } - } - - @Transactional - public void createComments(AnswerSet answerSet) { - if (answerSet.getComments() != null && !answerSet.getComments().isEmpty()) { - // this should only happen during a import survey operation for delphi surveys - - for (String surveyUid : answerSet.getComments().keySet()) { - for (AnswerComment comment : answerSet.getComments().get(surveyUid)) { - comment.setAnswerSetId(answerSet.getId()); - answerExplanationService.saveOrUpdateComment(comment); - } - } - } - } - - @Transactional - public void saveOrUpdateComment(AnswerComment comment) { - final Session session = sessionFactory.getCurrentSession(); - session.saveOrUpdate(comment); - } - - @Transactional - public void deleteComment(AnswerComment comment) { - final Session session = sessionFactory.getCurrentSession(); - session.delete(comment); - } - - @Transactional(readOnly = true) - public List loadComments(int answerSetId, String questionUid) { - final Session session = sessionFactory.getCurrentSession(); - - Query query = session.createQuery( - "FROM AnswerComment WHERE answerSetId = :answerSetId and questionUid = :questionUid ORDER BY id"); - query.setInteger("answerSetId", answerSetId).setString("questionUid", questionUid); - - @SuppressWarnings("unchecked") - List list = query.list(); - - return list; - } - - @Transactional - public void deleteCommentsForDeletedAnswers(final AnswerSet answerSet) { - - final Session session = sessionFactory.getCurrentSession(); - for (Question question : answerSet.getSurvey().getQuestions()) { - - if (question.getIsDelphiQuestion()) { - - if (question instanceof Matrix) { - Matrix matrix = (Matrix) question; - if (!answerSet.getMatrixAnswers(matrix).isEmpty()) { - continue; - } - } else if (question instanceof RatingQuestion) { - RatingQuestion rating = (RatingQuestion) question; - if (!answerSet.getRatingAnswers(rating).isEmpty()) { - continue; - } - } else { - if (!answerSet.getAnswers(question.getId(), question.getUniqueId()).isEmpty()) { - continue; - } - } - - final Query replyDeletionQuery = session.createQuery("DELETE FROM AnswerComment " - + "WHERE answerSetId = :answerSetId AND questionUid = :questionUid AND parent IS NOT NULL") - .setInteger("answerSetId", answerSet.getId()).setString("questionUid", question.getUniqueId()); - replyDeletionQuery.executeUpdate(); - - final Query commentDeletionQuery = session - .createQuery("DELETE FROM AnswerComment " - + "WHERE answerSetId = :answerSetId AND questionUid = :questionUid AND parent IS NULL") - .setInteger("answerSetId", answerSet.getId()).setString("questionUid", question.getUniqueId()); - commentDeletionQuery.executeUpdate(); - } - } - } - - @Transactional - public void deleteCommentsOfSurvey(final int surveyId) { - - final Session session = sessionFactory.getCurrentSession(); - - final Query replyDeletionQuery = session.createSQLQuery("DELETE ac.* " + "FROM ANSWERS_COMMENTS ac " - + "JOIN ANSWERS_SET an ON an.ANSWER_SET_ID = ac.ANSWER_SET_ID " - + "WHERE ac.PARENT IS NOT NULL AND an.SURVEY_ID = :id").setInteger("id", surveyId); - replyDeletionQuery.executeUpdate(); - - final Query commentDeletionQuery = session.createSQLQuery("DELETE ac.* " + "FROM ANSWERS_COMMENTS ac " - + "JOIN ANSWERS_SET an ON an.ANSWER_SET_ID = ac.ANSWER_SET_ID " - + "WHERE ac.PARENT IS NULL AND an.SURVEY_ID = :id").setInteger("id", surveyId); - commentDeletionQuery.executeUpdate(); - } - - @Transactional - public void deleteExplanationFilesOfSurvey(final int surveyId) { - - final Session session = sessionFactory.getCurrentSession(); - - final Query explanationFilesRetrievalQuery = session.createSQLQuery("SELECT f.FILE_UID " + "FROM FILES f " - + "JOIN ANSWERS_EXPLANATIONS_FILES aef ON aef.files_FILE_ID = f.FILE_ID " - + "JOIN ANSWERS_EXPLANATIONS ae ON ae.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " - + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ae.ANSWER_SET_ID " + "WHERE ans.SURVEY_ID = :id") - .setInteger("id", surveyId); - final List fileUids = explanationFilesRetrievalQuery.list(); - - final Query explanationFilesDeletionQuery12 = session.createSQLQuery("DELETE aef.* " - + "FROM ANSWERS_EXPLANATIONS_FILES aef " - + "JOIN ANSWERS_EXPLANATIONS ae ON ae.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " - + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ae.ANSWER_SET_ID " + "WHERE ans.SURVEY_ID = :id") - .setInteger("id", surveyId); - explanationFilesDeletionQuery12.executeUpdate(); - - if (fileUids.isEmpty()) - return; - final Query explanationFilesDeletionQuery2 = session - .createSQLQuery("DELETE f.* " + "FROM FILES f " + "WHERE f.FILE_UID IN :ids") - .setParameterList("ids", fileUids); - explanationFilesDeletionQuery2.executeUpdate(); - } - - @Transactional - public void deleteExplanationsOfSurvey(final int surveyId) { - - final Session session = sessionFactory.getCurrentSession(); - - final Query query = session - .createSQLQuery("DELETE ae.* " + "FROM ANSWERS_EXPLANATIONS ae " - + "JOIN ANSWERS_SET an ON ae.ANSWER_SET_ID = an.ANSWER_SET_ID " + "WHERE an.SURVEY_ID = :id") - .setInteger("id", surveyId); - query.executeUpdate(); - } - - @Transactional(readOnly = true) - public Map getUserAliases(String surveyUid) { - final Session session = sessionFactory.getCurrentSession(); - - Query query = session.createSQLQuery( - "SELECT DISTINCT ac.ANSWER_SET_CODE FROM ANSWERS_COMMENTS ac JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ac.ANSWER_SET_ID JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID WHERE s.SURVEY_UID = :surveyuid ORDER BY ac.COMMENT_DATE DESC"); - query.setString("surveyuid", surveyUid); - - @SuppressWarnings("unchecked") - List list = query.list(); - - Map result = new HashMap<>(); - - for (String code : list) { - result.put(code, "User " + (result.size() + 1)); - } - - return result; - } - - @Transactional(readOnly = true) - public String getDiscussion(int answerSetId, String questionUid, boolean useHtml, Map usersByUid) { - List comments = loadComments(answerSetId, questionUid); - StringBuilder s = new StringBuilder(); - - Map> commentsByParent = new HashMap<>(); - - for (AnswerComment comment : comments) { - if (comment.getParent() == null) { - commentsByParent.put(comment.getId(), new ArrayList<>()); - commentsByParent.get(comment.getId()).add(comment); - } else { - commentsByParent.get(comment.getParent().getId()).add(comment); - } - } - - for (List list : commentsByParent.values()) { - boolean first = true; - for (AnswerComment comment : list) { - String userPrefix = ""; - - if (!comment.getText().equalsIgnoreCase(DELETED_DELPHI_COMMENT_WITH_REPLIES_TEXT)) { - userPrefix = usersByUid.get(comment.getUniqueCode()) + ": "; - } - - if (useHtml) { - s.append("
").append("").append(userPrefix) - .append(ESAPI.encoder().encodeForHTML(comment.getText())).append("").append("
"); - } else { - if (first) { - s.append(userPrefix).append(comment.getText()); - first = false; - } else { - s.append("\n ").append(userPrefix).append(comment.getText()); - } - } - } - } - - return s.toString(); - } - - @Transactional(readOnly = true) - public String getFormattedExplanationWithFiles(final int answerSetId, final String questionUid, - final String surveyUid, final boolean useHtml) { - - final AnswerExplanation explanation = answerExplanationService.getExplanation(answerSetId, questionUid); - final StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(explanation.getText()); - final List files = explanation.getFiles(); - if (!files.isEmpty() && stringBuilder.length() > 0) { - if (useHtml) { - stringBuilder.append("
"); - } else { - stringBuilder.append("\n"); - } - } - for (int i = 0; i < files.size(); i++) { - final File file = files.get(i); - if (useHtml) { - stringBuilder.append(""); - } else { - stringBuilder.append(file.getUid()).append("|"); - } - stringBuilder.append(file.getNameForExport()); - if (useHtml) { - stringBuilder.append(""); - } - if (i != files.size() - 1) { - stringBuilder.append(";"); - } - } - return stringBuilder.toString(); - } - - @Transactional(readOnly = true) - public Map> getAllDiscussions(Survey survey) { - final Session session = sessionFactory.getCurrentSession(); - final Query query = session.createSQLQuery( - "SELECT ac.ANSWER_SET_ID, ac.QUESTION_UID, ac.TEXT, ac.ANSWER_SET_CODE, ac.PARENT FROM ANSWERS_COMMENTS ac JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ac.ANSWER_SET_ID JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft ORDER BY ac.COMMENT_DATE") - .setBoolean("draft", survey.getIsDraft()).setString("surveyUid", survey.getUniqueId()); - - Map> result = new HashMap<>(); - - @SuppressWarnings("rawtypes") - List res = query.list(); - - Map usersByUid = new HashMap(); - - for (Object o : res) { - Object[] a = (Object[]) o; - - int answerSetId = ConversionTools.getValue(a[0]); - String questionUid = (String) a[1]; - String explanation = (String) a[2]; - String code = (String) a[3]; - int parent = ConversionTools.getValue(a[4]); - - if (!usersByUid.containsKey(code)) { - usersByUid.put(code, "User " + (usersByUid.size() + 1)); - } - - if (!result.containsKey(answerSetId)) { - result.put(answerSetId, new HashMap()); - } - - String text = ""; - if (!explanation.equals(DELETED_DELPHI_COMMENT_WITH_REPLIES_TEXT)) { - // Only put the user when the comment with replies has not been deleted. - text += usersByUid.get(code) + ": "; - } - text += explanation; - - if (!result.get(answerSetId).containsKey(questionUid)) { - result.get(answerSetId).put(questionUid, text); - } else { - String old = result.get(answerSetId).get(questionUid); - if (parent == 0) { - result.get(answerSetId).put(questionUid, old + "\n" + text); - } else { - result.get(answerSetId).put(questionUid, old + "\n " + text); - } - } - } - - return result; - } - - @Transactional(readOnly = true) - public AnswerComment getComment(int id) { - final Session session = sessionFactory.getCurrentSession(); - return (AnswerComment) session.get(AnswerComment.class, id); - } - - @Transactional(readOnly = true) - public boolean hasCommentChildren(int id) { - final Session session = sessionFactory.getCurrentSession(); - final Query query = session.createSQLQuery("SELECT COUNT(*) FROM ANSWERS_COMMENTS WHERE PARENT = :parent") - .setInteger("parent", id); - return ((BigInteger) query.uniqueResult()).intValue() > 0; - } - - /** - * Checks whether a user (answer set) needs to be notified about unread comments. - * @param uniqueCode ID of answer set (user) - * @param questionUid Question UID - */ - @Transactional(readOnly = true) - public boolean hasUnreadComments(String uniqueCode, String questionUid) { - Session session = sessionFactory.getCurrentSession(); - Query query = session.createSQLQuery("" - + "SELECT EXISTS " - + "( " - + " SELECT * " - + " FROM ANSWERS_COMMENTS ac " - + " LEFT JOIN ANSWERS_COMMENTS parents ON ac.PARENT = parents.ANSWER_COMMENT_ID " - + " JOIN ANSWERS_SET asets ON asets.ANSWER_SET_ID = ac.ANSWER_SET_ID " - + " WHERE ac.QUESTION_UID = :questionUid " - + " AND (" - + " (parents.ANSWER_SET_CODE = :uniqueCode AND (ac.READ_BY_PARENT = FALSE OR ac.READ_BY_PARENT IS NULL)) " - + " OR (asets.UNIQUECODE = :uniqueCode AND (ac.READ_BY_PARTICIPANT = FALSE OR ac.READ_BY_PARTICIPANT IS NULL)) " - + " )" - + ")"); - - query.setString("uniqueCode", uniqueCode); - query.setString("questionUid", questionUid); - - return ((BigInteger) query.uniqueResult()).intValue() > 0; - } - - @Transactional(readOnly = true) - public FilesByTypes getExplanationFilesByAnswerSetIdAndQuestionUid(final Survey survey) { - - final FilesByTypes result = new FilesByTypes<>(); - - final Session session = sessionFactory.getCurrentSession(); - final Query query = session - .createSQLQuery("SELECT f.FILE_ID, f.FILE_NAME, f.FILE_UID, ans.ANSWER_SET_ID, ae.QUESTION_UID " - + "FROM FILES f " + "JOIN ANSWERS_EXPLANATIONS_FILES aef ON aef.files_FILE_ID = f.FILE_ID " - + "JOIN ANSWERS_EXPLANATIONS ae ON ae.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " - + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ae.ANSWER_SET_ID " - + "JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID " - + "WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft " + "ORDER BY f.FILE_NAME") - .setBoolean("draft", survey.getIsDraft()).setString("surveyUid", survey.getUniqueId()); - - @SuppressWarnings("rawtypes") - final List queryResult = query.list(); - - for (Object row : queryResult) { - final Object[] element = (Object[]) row; - - final int fileId = ConversionTools.getValue(element[0]); - final String fileName = (String) element[1]; - final String fileUid = (String) element[2]; - final int answerSetId = ConversionTools.getValue(element[3]); - final String questionUid = (String) element[4]; - - final File file = new File(); - file.setId(fileId); - file.setName(fileName); - file.setUid(fileUid); - result.addFile(answerSetId, questionUid, file); - } - - return result; - } - - @Transactional(readOnly = true) - public File getExplanationFile(final String surveyUid, final String uniqueCode, final String questiondUid, - final String fileName) { - - final Session session = sessionFactory.getCurrentSession(); - final Query query = session - .createSQLQuery("SELECT f.FILE_ID, f.FILE_UID " - + "FROM FILES f " - + "JOIN ANSWERS_EXPLANATIONS_FILES aef ON aef.files_FILE_ID = f.FILE_ID " - + "JOIN ANSWERS_EXPLANATIONS ae ON ae.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " - + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ae.ANSWER_SET_ID " - + "JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID " - + "WHERE s.SURVEY_UID = :surveyUid AND ans.UNIQUECODE = :uniqueCode " - + "AND ae.QUESTION_UID = :questionUid AND f.FILE_NAME = :fileName") - .setString("surveyUid", surveyUid) - .setString("uniqueCode", uniqueCode) - .setString("questionUid", questiondUid) - .setString("fileName", fileName); - - @SuppressWarnings("rawtypes") - final Object result = query.uniqueResult(); - if (result == null) - return null; - final Object[] element = (Object[]) result; - - final int fileId = ConversionTools.getValue(element[0]); - final String fileUid = (String) element[1]; - - final File file = new File(); - file.setId(fileId); - file.setName(fileName); - file.setUid(fileUid); - return file; - } - - @Transactional - public void removeFileFromExplanation(final int answerSetId, final String questionUid, final String fileUid) { - - final Session session = sessionFactory.getCurrentSession(); - final AnswerExplanation explanation = getExplanation(answerSetId, questionUid); - explanation.getFiles().removeIf(f -> f.getUid().equals(fileUid)); - session.saveOrUpdate(explanation); - session.flush(); - } - -} +package com.ec.survey.service; + +import com.ec.survey.model.*; +import com.ec.survey.model.delphi.DelphiContribution; +import com.ec.survey.model.delphi.DelphiContributions; +import com.ec.survey.model.delphi.DelphiMedian; +import com.ec.survey.model.delphi.DelphiTableOrderBy; +import com.ec.survey.model.survey.*; +import com.ec.survey.model.survey.base.File; +import com.ec.survey.tools.Constants; +import com.ec.survey.tools.ConversionTools; +import org.hibernate.Query; +import org.hibernate.SQLQuery; +import org.hibernate.Session; +import org.hibernate.transform.Transformers; +import org.owasp.esapi.ESAPI; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigInteger; +import java.util.*; +import java.util.stream.Collectors; + +@Service("answerExplanationService") +public class AnswerExplanationService extends BasicService { + + public static final String DELETED_DELPHI_COMMENT_WITH_REPLIES_TEXT = "[DELETED]"; + + @Transactional + public void deleteExplanationByAnswerSet(AnswerSet answerSet) { + + final Integer answerSetId = answerSet.getId(); + final Session session = sessionFactory.getCurrentSession(); + final Query query = session.createQuery("DELETE FROM AnswerExplanation WHERE answerSetId = :answerId") + .setInteger("answerId", answerSetId); + query.executeUpdate(); + } + + @Transactional(readOnly = true) + public AnswerExplanation getExplanation(int answerSetId, String questionUid) { + + final Session session = sessionFactory.getCurrentSession(); + final Query query = session.createQuery( + "SELECT ex FROM AnswerExplanation ex WHERE answerSetId = :answerSetId AND questionUid = :questionUid") + .setInteger("answerSetId", answerSetId).setString("questionUid", questionUid); + AnswerExplanation explanation = (AnswerExplanation) query.uniqueResult(); + if (explanation == null) { + throw new NoSuchElementException(); + } + return explanation; + } + + @Transactional(readOnly = true) + public Map> getAllExplanations(Survey survey) { + final Session session = sessionFactory.getCurrentSession(); + final Query query = session.createSQLQuery( + "SELECT ex.ANSWER_SET_ID, ex.QUESTION_UID, ex.TEXT FROM ANSWERS_EXPLANATIONS ex JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ex.ANSWER_SET_ID JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft") + .setBoolean("draft", survey.getIsDraft()).setString("surveyUid", survey.getUniqueId()); + + Map> result = new HashMap<>(); + + @SuppressWarnings("rawtypes") + List res = query.list(); + + for (Object o : res) { + Object[] a = (Object[]) o; + + int answerSetId = ConversionTools.getValue(a[0]); + String questionUid = (String) a[1]; + String explanation = (String) a[2]; + + if (!result.containsKey(answerSetId)) { + result.put(answerSetId, new HashMap()); + } + + result.get(answerSetId).put(questionUid, explanation); + } + + return result; + } + + @Transactional(readOnly = true) + public List getExplanationsOfSurvey(final String surveyUid, final boolean draft) { + + final Session session = sessionFactory.getCurrentSession(); + final Query query = session.createSQLQuery( + "SELECT f.FILE_ID, f.FILE_NAME, f.FILE_UID, ex.ANSWER_EXPLANATION_ID, ex.ANSWER_SET_ID, ex.QUESTION_UID, ex.TEXT FROM ANSWERS_EXPLANATIONS ex " + + "LEFT JOIN ANSWERS_EXPLANATIONS_FILES aef ON ex.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " + + "LEFT JOIN FILES f ON aef.files_FILE_ID = f.FILE_ID " + + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ex.ANSWER_SET_ID " + + "JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID " + + "WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft") + .setString("surveyUid", surveyUid).setBoolean("draft", draft); + + @SuppressWarnings("rawtypes") + final List queryResult = query.list(); + + Map explanations = new HashMap<>(); + for (Object row : queryResult) { + final Object[] element = (Object[]) row; + + final int fileId = ConversionTools.getValue(element[0]); + + final int answerExplanationId = ConversionTools.getValue(element[3]); + final int answerExplanationAnswerSetId = ConversionTools.getValue(element[4]); + final String answerExplanationQuestionUid = (String) element[5]; + final String answerExplanationText = (String) element[6]; + + if (!explanations.containsKey(answerExplanationId)) { + final AnswerExplanation explanation = new AnswerExplanation(); + explanation.setId(answerExplanationId); + explanation.setAnswerSetId(answerExplanationAnswerSetId); + explanation.setQuestionUid(answerExplanationQuestionUid); + explanation.setText(answerExplanationText); + explanations.put(answerExplanationId, explanation); + } + + if (fileId > 0) { + final String fileName = (String) element[1]; + final String fileUid = (String) element[2]; + + final File file = new File(); + file.setId(fileId); + file.setName(fileName); + file.setUid(fileUid); + explanations.get(answerExplanationId).addFile(file); + } + } + + return new ArrayList<>(explanations.values()); + } + + @Transactional(readOnly = true) + public List getCommentsOfSurvey(final String surveyUid, final boolean draft) { + + final Session session = sessionFactory.getCurrentSession(); + final Query query = session + .createSQLQuery("SELECT ac.ANSWER_COMMENT_ID " + "FROM ANSWERS_COMMENTS ac " + + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ac.ANSWER_SET_ID " + + "JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID " + + "WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft") + .setString("surveyUid", surveyUid).setBoolean("draft", draft); + + @SuppressWarnings("rawtypes") + List res = query.list(); + + final List comments = new ArrayList<>(); + + for (Object o : res) { + int commentId = ConversionTools.getValue(o); + AnswerComment comment = getComment(commentId); + comments.add(comment); + } + + return comments; + } + + @Transactional(readOnly = true) + public List getExplanations(int answerSetId) { + + final Session session = sessionFactory.getCurrentSession(); + final Query query = session.createQuery("SELECT ex FROM AnswerExplanation ex WHERE answerSetId = :answerSetId") + .setInteger("answerSetId", answerSetId); + @SuppressWarnings("unchecked") + List explanations = (List) query.list(); + return explanations; + } + + @Transactional(readOnly = true) + public DelphiContributions getDelphiContributions(ChoiceQuestion question, DelphiTableOrderBy orderBy, int limit, + int offset) { + return getDelphiContributions(Collections.singletonList(question.getUniqueId()), question.getUniqueId(), + question.getSurvey().getIsDraft(), orderBy, limit, offset); + } + + @Transactional(readOnly = true) + public DelphiContributions getDelphiContributions(Matrix question, DelphiTableOrderBy orderBy, int limit, + int offset) { + List uids = question.getQuestions().stream().map(Element::getUniqueId).collect(Collectors.toList()); + return getDelphiContributions(uids, question.getUniqueId(), question.getSurvey().getIsDraft(), orderBy, limit, + offset); + } + + @Transactional(readOnly = true) + public DelphiContributions getDelphiContributions(RatingQuestion question, DelphiTableOrderBy orderBy, int limit, + int offset) { + List uids = question.getQuestions().stream().map(Element::getUniqueId).collect(Collectors.toList()); + return getDelphiContributions(uids, question.getUniqueId(), question.getSurvey().getIsDraft(), orderBy, limit, + offset); + } + + @Transactional(readOnly = true) + public DelphiContributions getDelphiContributions(Table question, DelphiTableOrderBy orderBy, int limit, + int offset) { + return getDelphiContributions(Collections.singletonList(question.getUniqueId()), question.getUniqueId(), + question.getSurvey().getIsDraft(), orderBy, limit, offset); + } + + @Transactional(readOnly = true) + public DelphiContributions getDelphiContributions(Collection questionUids, String mainQuestionUid, + boolean isDraft, DelphiTableOrderBy orderBy, int limit, int offset) { + String orderByClauseInner; + String orderByClauseOuter; + + switch (orderBy) { + case UpdateAsc: + orderByClauseInner = "aset.ANSWER_SET_UPDATE ASC"; + orderByClauseOuter = "`update` ASC, answerSetId"; + break; + + case UpdateDesc: + orderByClauseInner = "aset.ANSWER_SET_UPDATE DESC"; + orderByClauseOuter = "`update` DESC, answerSetId"; + break; + + default: + throw new IllegalStateException("Unexpected value: " + orderBy); + } + + String contributionsQueryText = "" + "SELECT\n" + " aset.ANSWER_SET_ID answerSetId,\n" + + " aset.UNIQUECODE answerSetUniqueCode,\n" + " aset.ANSWER_SET_UPDATE `update`,\n" + + " a.VALUE value,\n" + " a.PA_UID answerUid,\n" + " a.QUESTION_UID questionUid,\n" + + " a.ANSWER_COL `column`,\n" + " a.ANSWER_ROW row,\n" + + " COALESCE(ex.TEXT, main_explanation.TEXT) explanation\n" + "FROM ANSWERS a\n" + "JOIN (\n" + + // select all answers sets that are relevant for this query + " SELECT\n" + " aset.ANSWER_SET_ID,\n" + " aset.UNIQUECODE,\n" + + " aset.ANSWER_SET_UPDATE\n" + " FROM ANSWERS a\n" + + " JOIN ANSWERS_SET aset ON a.AS_ID = aset.ANSWER_SET_ID\n" + + " JOIN SURVEYS s ON aset.SURVEY_ID = s.SURVEY_ID\n" + " WHERE a.QUESTION_UID IN :questionUids\n" + + " AND s.ISDRAFT = :isDraft\n" + " GROUP BY aset.ANSWER_SET_ID, aset.ANSWER_SET_UPDATE\n" + + " ORDER BY " + orderByClauseInner + "\n" + + // pagination + " LIMIT :limit OFFSET :offset\n" + ") AS aset ON a.AS_ID = aset.ANSWER_SET_ID\n" + + // add explanations + "LEFT JOIN ANSWERS_EXPLANATIONS ex ON a.QUESTION_UID = ex.QUESTION_UID AND ex.ANSWER_SET_ID = a.AS_ID\n" + + + // add explanation of main question (i.e. for ratings) + "LEFT JOIN (\n" + " SELECT TEXT, ANSWER_SET_ID\n" + " FROM ANSWERS_EXPLANATIONS\n" + + " WHERE QUESTION_UID = :mainQuestionUid\n" + + ") AS main_explanation ON a.AS_ID = main_explanation.ANSWER_SET_ID\n" + + // filter by question + "WHERE a.QUESTION_UID IN :questionUids\n" + + // sort data as required + "ORDER BY " + orderByClauseOuter + ", row, `column`"; + + Session session = sessionFactory.getCurrentSession(); + SQLQuery contributionsQuery = session.createSQLQuery(contributionsQueryText); + contributionsQuery.setResultTransformer(Transformers.aliasToBean(DelphiContribution.class)); + contributionsQuery.setParameterList("questionUids", questionUids); + contributionsQuery.setBoolean("isDraft", isDraft); + contributionsQuery.setString("mainQuestionUid", mainQuestionUid); + contributionsQuery.setInteger("limit", limit); + contributionsQuery.setInteger("offset", offset); + + @SuppressWarnings("unchecked") + List contributions = contributionsQuery.list(); + + int totalCount = getTotalDelphiContributions(questionUids, isDraft); + return new DelphiContributions(totalCount, contributions); + } + + + @Transactional(readOnly = true) + public List getDelphiDependentAnswers(String dependentElementUid, int answerSetId) { + Session session = sessionFactory.getCurrentSession(); + String sql = "SELECT VALUE FROM ANSWERS a WHERE a.QUESTION_UID = :questionUid AND a.AS_ID = :answerSetId"; + SQLQuery query = session.createSQLQuery(sql); + query.setString("questionUid", dependentElementUid); + query.setInteger("answerSetId", answerSetId); + + @SuppressWarnings("unchecked") + List result = query.list(); + + return result; + } + + @Transactional(readOnly = true) + public int getTotalDelphiContributions(Collection questionUids, boolean isDraft) { + String totalCountQueryText = "SELECT COUNT(DISTINCT aset.ANSWER_SET_ID) FROM ANSWERS a\n" + + "JOIN ANSWERS_SET aset ON a.AS_ID = aset.ANSWER_SET_ID\n" + + "JOIN SURVEYS s ON aset.SURVEY_ID = s.SURVEY_ID\n" + + "WHERE a.QUESTION_UID IN :questionUids AND s.ISDRAFT = :isDraft"; + + Session session = sessionFactory.getCurrentSession(); + SQLQuery totalCountQuery = session.createSQLQuery(totalCountQueryText); + totalCountQuery.setParameterList("questionUids", questionUids); + totalCountQuery.setBoolean("isDraft", isDraft); + return ((BigInteger) totalCountQuery.uniqueResult()).intValue(); + } + + @Transactional + public void createUpdateOrDeleteExplanations(AnswerSet answerSet) throws Exception { + final Session session = sessionFactory.getCurrentSession(); + + final Survey survey = answerSet.getSurvey(); + final String surveyUid = survey.getUniqueId(); + for (Question question : survey.getQuestions()) { + final String questionUid = question.getUniqueId(); + if (question.getIsDelphiQuestion()) { + AnswerSet.ExplanationData explanationData = answerSet.getExplanations().get(questionUid); + if (explanationData == null) { + continue; + } + + List answers; + if (question instanceof Matrix) { + Matrix matrix = (Matrix) question; + answers = answerSet.getMatrixAnswers(matrix); + } else if (question instanceof RatingQuestion) { + RatingQuestion rating = (RatingQuestion) question; + answers = answerSet.getRatingAnswers(rating); + } else { + answers = answerSet.getAnswers(question.getId(), questionUid); + } + + AnswerExplanation explanation; + boolean hasNoLinkedAnswers = answers.isEmpty(); + try { + explanation = getExplanation(answerSet.getId(), questionUid); + + if (hasNoLinkedAnswers) { + fileService.deleteUploadedExplanationFiles(surveyUid, answerSet.getUniqueCode(), questionUid); + fileService.deleteFilesFromDiskAndDatabase(surveyUid, explanation.getFiles()); + session.delete(explanation); + continue; + } + } catch (NoSuchElementException ex) { + if (hasNoLinkedAnswers) { + fileService.deleteUploadedExplanationFiles(surveyUid, answerSet.getUniqueCode(), questionUid); + explanationData.files.clear(); + } + if (explanationData.text.length() == 0 && explanationData.files.isEmpty()) { + continue; + } + + explanation = new AnswerExplanation(answerSet.getId(), questionUid); + } + + // special case: if the participant adds an explanation why she is outside the + // median + DelphiMedian median = null; + if (explanationData.text != null && explanationData.text.length() > 0 + && !explanationData.text.equalsIgnoreCase(explanation.getText()) && !answers.isEmpty()) { + if (question instanceof SingleChoiceQuestion) { + SingleChoiceQuestion choiceQuestion = (SingleChoiceQuestion) question; + if (choiceQuestion.getUseLikert() && choiceQuestion.getMaxDistance() > -1) { + median = answerService.getMedian(survey, choiceQuestion, answers.get(0), null); + } + } else if (question instanceof NumberQuestion) { + NumberQuestion numberQuestion = (NumberQuestion) question; + if (numberQuestion.isSlider() && numberQuestion.getMaxDistance() > -1) { + median = answerService.getMedian(survey, numberQuestion, answers.get(0), null); + } + } + if (median != null && median.isMaxDistanceExceeded()) { + + if (answerSet.getMedianWarningVisible()) + { + explanation.setChangedForMedian(true); + answerSet.setChangedForMedian(true); + } else if (explanation.getChangedForMedian() == null || !explanation.getChangedForMedian()) { + answerSet.setChangeExplanationText(true); + } + } + } + + explanation.setText(explanationData.text); + explanation.setFiles(explanationData.files); + + session.saveOrUpdate(explanation); + session.flush(); + } + } + } + + @Transactional + public void createComments(AnswerSet answerSet) { + if (answerSet.getComments() != null && !answerSet.getComments().isEmpty()) { + // this should only happen during a import survey operation for delphi surveys + + for (String surveyUid : answerSet.getComments().keySet()) { + for (AnswerComment comment : answerSet.getComments().get(surveyUid)) { + comment.setAnswerSetId(answerSet.getId()); + answerExplanationService.saveOrUpdateComment(comment); + } + } + } + } + + @Transactional + public void saveOrUpdateComment(AnswerComment comment) { + final Session session = sessionFactory.getCurrentSession(); + session.saveOrUpdate(comment); + } + + @Transactional + public void deleteComment(AnswerComment comment) { + final Session session = sessionFactory.getCurrentSession(); + session.delete(comment); + } + + @Transactional(readOnly = true) + public List loadComments(int answerSetId, String questionUid) { + final Session session = sessionFactory.getCurrentSession(); + + Query query = session.createQuery( + "FROM AnswerComment WHERE answerSetId = :answerSetId and questionUid = :questionUid ORDER BY id"); + query.setInteger("answerSetId", answerSetId).setString("questionUid", questionUid); + + @SuppressWarnings("unchecked") + List list = query.list(); + + return list; + } + + @Transactional + public void deleteCommentsForDeletedAnswers(final AnswerSet answerSet) { + + final Session session = sessionFactory.getCurrentSession(); + for (Question question : answerSet.getSurvey().getQuestions()) { + + if (question.getIsDelphiQuestion()) { + + if (question instanceof Matrix) { + Matrix matrix = (Matrix) question; + if (!answerSet.getMatrixAnswers(matrix).isEmpty()) { + continue; + } + } else if (question instanceof RatingQuestion) { + RatingQuestion rating = (RatingQuestion) question; + if (!answerSet.getRatingAnswers(rating).isEmpty()) { + continue; + } + } else { + if (!answerSet.getAnswers(question.getId(), question.getUniqueId()).isEmpty()) { + continue; + } + } + + final Query replyDeletionQuery = session.createQuery("DELETE FROM AnswerComment " + + "WHERE answerSetId = :answerSetId AND questionUid = :questionUid AND parent IS NOT NULL") + .setInteger("answerSetId", answerSet.getId()).setString("questionUid", question.getUniqueId()); + replyDeletionQuery.executeUpdate(); + + final Query commentDeletionQuery = session + .createQuery("DELETE FROM AnswerComment " + + "WHERE answerSetId = :answerSetId AND questionUid = :questionUid AND parent IS NULL") + .setInteger("answerSetId", answerSet.getId()).setString("questionUid", question.getUniqueId()); + commentDeletionQuery.executeUpdate(); + } + } + } + + @Transactional + public void deleteCommentsOfSurvey(final int surveyId) { + + final Session session = sessionFactory.getCurrentSession(); + + final Query replyDeletionQuery = session.createSQLQuery("DELETE ac.* " + "FROM ANSWERS_COMMENTS ac " + + "JOIN ANSWERS_SET an ON an.ANSWER_SET_ID = ac.ANSWER_SET_ID " + + "WHERE ac.PARENT IS NOT NULL AND an.SURVEY_ID = :id").setInteger("id", surveyId); + replyDeletionQuery.executeUpdate(); + + final Query commentDeletionQuery = session.createSQLQuery("DELETE ac.* " + "FROM ANSWERS_COMMENTS ac " + + "JOIN ANSWERS_SET an ON an.ANSWER_SET_ID = ac.ANSWER_SET_ID " + + "WHERE ac.PARENT IS NULL AND an.SURVEY_ID = :id").setInteger("id", surveyId); + commentDeletionQuery.executeUpdate(); + } + + @Transactional + public void deleteExplanationFilesOfSurvey(final int surveyId) { + + final Session session = sessionFactory.getCurrentSession(); + + final Query explanationFilesRetrievalQuery = session.createSQLQuery("SELECT f.FILE_UID " + "FROM FILES f " + + "JOIN ANSWERS_EXPLANATIONS_FILES aef ON aef.files_FILE_ID = f.FILE_ID " + + "JOIN ANSWERS_EXPLANATIONS ae ON ae.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " + + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ae.ANSWER_SET_ID " + "WHERE ans.SURVEY_ID = :id") + .setInteger("id", surveyId); + final List fileUids = explanationFilesRetrievalQuery.list(); + + final Query explanationFilesDeletionQuery12 = session.createSQLQuery("DELETE aef.* " + + "FROM ANSWERS_EXPLANATIONS_FILES aef " + + "JOIN ANSWERS_EXPLANATIONS ae ON ae.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " + + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ae.ANSWER_SET_ID " + "WHERE ans.SURVEY_ID = :id") + .setInteger("id", surveyId); + explanationFilesDeletionQuery12.executeUpdate(); + + if (fileUids.isEmpty()) + return; + final Query explanationFilesDeletionQuery2 = session + .createSQLQuery("DELETE f.* " + "FROM FILES f " + "WHERE f.FILE_UID IN :ids") + .setParameterList("ids", fileUids); + explanationFilesDeletionQuery2.executeUpdate(); + } + + @Transactional + public void deleteExplanationsOfSurvey(final int surveyId) { + + final Session session = sessionFactory.getCurrentSession(); + + final Query query = session + .createSQLQuery("DELETE ae.* " + "FROM ANSWERS_EXPLANATIONS ae " + + "JOIN ANSWERS_SET an ON ae.ANSWER_SET_ID = an.ANSWER_SET_ID " + "WHERE an.SURVEY_ID = :id") + .setInteger("id", surveyId); + query.executeUpdate(); + } + + @Transactional(readOnly = true) + public Map getUserAliases(String surveyUid) { + final Session session = sessionFactory.getCurrentSession(); + + Query query = session.createSQLQuery( + "SELECT DISTINCT ac.ANSWER_SET_CODE FROM ANSWERS_COMMENTS ac JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ac.ANSWER_SET_ID JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID WHERE s.SURVEY_UID = :surveyuid ORDER BY ac.COMMENT_DATE DESC"); + query.setString("surveyuid", surveyUid); + + @SuppressWarnings("unchecked") + List list = query.list(); + + Map result = new HashMap<>(); + + for (String code : list) { + result.put(code, "User " + (result.size() + 1)); + } + + return result; + } + + @Transactional(readOnly = true) + public String getDiscussion(int answerSetId, String questionUid, boolean useHtml, Map usersByUid) { + List comments = loadComments(answerSetId, questionUid); + StringBuilder s = new StringBuilder(); + + Map> commentsByParent = new HashMap<>(); + + for (AnswerComment comment : comments) { + if (comment.getParent() == null) { + commentsByParent.put(comment.getId(), new ArrayList<>()); + commentsByParent.get(comment.getId()).add(comment); + } else { + commentsByParent.get(comment.getParent().getId()).add(comment); + } + } + + for (List list : commentsByParent.values()) { + boolean first = true; + for (AnswerComment comment : list) { + String userPrefix = ""; + + if (!comment.getText().equalsIgnoreCase(DELETED_DELPHI_COMMENT_WITH_REPLIES_TEXT)) { + userPrefix = usersByUid.get(comment.getUniqueCode()) + ": "; + } + + if (useHtml) { + s.append("
").append("").append(userPrefix) + .append(ESAPI.encoder().encodeForHTML(comment.getText())).append("").append("
"); + } else { + if (first) { + s.append(userPrefix).append(comment.getText()); + first = false; + } else { + s.append("\n ").append(userPrefix).append(comment.getText()); + } + } + } + } + + return s.toString(); + } + + @Transactional(readOnly = true) + public String getFormattedExplanationWithFiles(final int answerSetId, final String questionUid, + final String surveyUid, final boolean useHtml) { + + final AnswerExplanation explanation = answerExplanationService.getExplanation(answerSetId, questionUid); + final StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(explanation.getText()); + final List files = explanation.getFiles(); + if (!files.isEmpty() && stringBuilder.length() > 0) { + if (useHtml) { + stringBuilder.append("
"); + } else { + stringBuilder.append("\n"); + } + } + for (int i = 0; i < files.size(); i++) { + final File file = files.get(i); + if (useHtml) { + stringBuilder.append(""); + } else { + stringBuilder.append(file.getUid()).append("|"); + } + stringBuilder.append(file.getNameForExport()); + if (useHtml) { + stringBuilder.append(""); + } + if (i != files.size() - 1) { + stringBuilder.append(";"); + } + } + return stringBuilder.toString(); + } + + @Transactional(readOnly = true) + public Map> getAllDiscussions(Survey survey) { + final Session session = sessionFactory.getCurrentSession(); + final Query query = session.createSQLQuery( + "SELECT ac.ANSWER_SET_ID, ac.QUESTION_UID, ac.TEXT, ac.ANSWER_SET_CODE, ac.PARENT FROM ANSWERS_COMMENTS ac JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ac.ANSWER_SET_ID JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft ORDER BY ac.COMMENT_DATE") + .setBoolean("draft", survey.getIsDraft()).setString("surveyUid", survey.getUniqueId()); + + Map> result = new HashMap<>(); + + @SuppressWarnings("rawtypes") + List res = query.list(); + + Map usersByUid = new HashMap(); + + for (Object o : res) { + Object[] a = (Object[]) o; + + int answerSetId = ConversionTools.getValue(a[0]); + String questionUid = (String) a[1]; + String explanation = (String) a[2]; + String code = (String) a[3]; + int parent = ConversionTools.getValue(a[4]); + + if (!usersByUid.containsKey(code)) { + usersByUid.put(code, "User " + (usersByUid.size() + 1)); + } + + if (!result.containsKey(answerSetId)) { + result.put(answerSetId, new HashMap()); + } + + String text = ""; + if (!explanation.equals(DELETED_DELPHI_COMMENT_WITH_REPLIES_TEXT)) { + // Only put the user when the comment with replies has not been deleted. + text += usersByUid.get(code) + ": "; + } + text += explanation; + + if (!result.get(answerSetId).containsKey(questionUid)) { + result.get(answerSetId).put(questionUid, text); + } else { + String old = result.get(answerSetId).get(questionUid); + if (parent == 0) { + result.get(answerSetId).put(questionUid, old + "\n" + text); + } else { + result.get(answerSetId).put(questionUid, old + "\n " + text); + } + } + } + + return result; + } + + @Transactional(readOnly = true) + public AnswerComment getComment(int id) { + final Session session = sessionFactory.getCurrentSession(); + return (AnswerComment) session.get(AnswerComment.class, id); + } + + @Transactional(readOnly = true) + public boolean hasCommentChildren(int id) { + final Session session = sessionFactory.getCurrentSession(); + final Query query = session.createSQLQuery("SELECT COUNT(*) FROM ANSWERS_COMMENTS WHERE PARENT = :parent") + .setInteger("parent", id); + return ((BigInteger) query.uniqueResult()).intValue() > 0; + } + + /** + * Checks whether a user (answer set) needs to be notified about unread comments. + * @param uniqueCode ID of answer set (user) + * @param questionUid Question UID + */ + @Transactional(readOnly = true) + public boolean hasUnreadComments(String uniqueCode, String questionUid) { + Session session = sessionFactory.getCurrentSession(); + Query query = session.createSQLQuery("" + + "SELECT EXISTS " + + "( " + + " SELECT * " + + " FROM ANSWERS_COMMENTS ac " + + " LEFT JOIN ANSWERS_COMMENTS parents ON ac.PARENT = parents.ANSWER_COMMENT_ID " + + " JOIN ANSWERS_SET asets ON asets.ANSWER_SET_ID = ac.ANSWER_SET_ID " + + " WHERE ac.QUESTION_UID = :questionUid " + + " AND (" + + " (parents.ANSWER_SET_CODE = :uniqueCode AND (ac.READ_BY_PARENT = FALSE OR ac.READ_BY_PARENT IS NULL)) " + + " OR (asets.UNIQUECODE = :uniqueCode AND (ac.READ_BY_PARTICIPANT = FALSE OR ac.READ_BY_PARTICIPANT IS NULL)) " + + " )" + + ")"); + + query.setString("uniqueCode", uniqueCode); + query.setString("questionUid", questionUid); + + return ((BigInteger) query.uniqueResult()).intValue() > 0; + } + + @Transactional(readOnly = true) + public FilesByTypes getExplanationFilesByAnswerSetIdAndQuestionUid(final Survey survey) { + + final FilesByTypes result = new FilesByTypes<>(); + + final Session session = sessionFactory.getCurrentSession(); + final Query query = session + .createSQLQuery("SELECT f.FILE_ID, f.FILE_NAME, f.FILE_UID, ans.ANSWER_SET_ID, ae.QUESTION_UID " + + "FROM FILES f " + "JOIN ANSWERS_EXPLANATIONS_FILES aef ON aef.files_FILE_ID = f.FILE_ID " + + "JOIN ANSWERS_EXPLANATIONS ae ON ae.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " + + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ae.ANSWER_SET_ID " + + "JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID " + + "WHERE s.SURVEY_UID = :surveyUid AND s.ISDRAFT = :draft " + "ORDER BY f.FILE_NAME") + .setBoolean("draft", survey.getIsDraft()).setString("surveyUid", survey.getUniqueId()); + + @SuppressWarnings("rawtypes") + final List queryResult = query.list(); + + for (Object row : queryResult) { + final Object[] element = (Object[]) row; + + final int fileId = ConversionTools.getValue(element[0]); + final String fileName = (String) element[1]; + final String fileUid = (String) element[2]; + final int answerSetId = ConversionTools.getValue(element[3]); + final String questionUid = (String) element[4]; + + final File file = new File(); + file.setId(fileId); + file.setName(fileName); + file.setUid(fileUid); + result.addFile(answerSetId, questionUid, file); + } + + return result; + } + + @Transactional(readOnly = true) + public File getExplanationFile(final String surveyUid, final String uniqueCode, final String questiondUid, + final String fileName) { + + final Session session = sessionFactory.getCurrentSession(); + final Query query = session + .createSQLQuery("SELECT f.FILE_ID, f.FILE_UID " + + "FROM FILES f " + + "JOIN ANSWERS_EXPLANATIONS_FILES aef ON aef.files_FILE_ID = f.FILE_ID " + + "JOIN ANSWERS_EXPLANATIONS ae ON ae.ANSWER_EXPLANATION_ID = aef.ANSWERS_EXPLANATIONS_ANSWER_EXPLANATION_ID " + + "JOIN ANSWERS_SET ans ON ans.ANSWER_SET_ID = ae.ANSWER_SET_ID " + + "JOIN SURVEYS s ON s.SURVEY_ID = ans.SURVEY_ID " + + "WHERE s.SURVEY_UID = :surveyUid AND ans.UNIQUECODE = :uniqueCode " + + "AND ae.QUESTION_UID = :questionUid AND f.FILE_NAME = :fileName") + .setString("surveyUid", surveyUid) + .setString("uniqueCode", uniqueCode) + .setString("questionUid", questiondUid) + .setString("fileName", fileName); + + @SuppressWarnings("rawtypes") + final Object result = query.uniqueResult(); + if (result == null) + return null; + final Object[] element = (Object[]) result; + + final int fileId = ConversionTools.getValue(element[0]); + final String fileUid = (String) element[1]; + + final File file = new File(); + file.setId(fileId); + file.setName(fileName); + file.setUid(fileUid); + return file; + } + + @Transactional + public void removeFileFromExplanation(final int answerSetId, final String questionUid, final String fileUid) { + + final Session session = sessionFactory.getCurrentSession(); + final AnswerExplanation explanation = getExplanation(answerSetId, questionUid); + explanation.getFiles().removeIf(f -> f.getUid().equals(fileUid)); + session.saveOrUpdate(explanation); + session.flush(); + } + +} diff --git a/src/main/java/com/ec/survey/service/AttendeeService.java b/src/main/java/com/ec/survey/service/AttendeeService.java index f97f2bcb1..e89a862e9 100644 --- a/src/main/java/com/ec/survey/service/AttendeeService.java +++ b/src/main/java/com/ec/survey/service/AttendeeService.java @@ -198,8 +198,15 @@ private String getSql(Integer ownerId, Map attributeFilter, && !entry.getKey().equalsIgnoreCase("owner") && !entry.getKey().equalsIgnoreCase("_csrf") && !entry.getKey().startsWith("visibleAttendee") && entry.getValue() != null && entry.getValue().trim().length() > 0) { - sql.append(" LEFT OUTER JOIN ATTRIBUTE at ON at.ATTE_ID = a.ATTENDEE_ID "); - break; + + if (org.apache.commons.lang3.StringUtils.isNumeric(entry.getKey())) { + //LEFT OUTER JOIN ATTRIBUTE at ON at.ATTE_ID = a.ATTENDEE_ID + sql.append(" LEFT OUTER JOIN ATTRIBUTE at") + .append(entry.getKey()) + .append(" ON at") + .append(entry.getKey()) + .append(".ATTE_ID = a.ATTENDEE_ID "); + } } } if (attributeFilter.containsKey("owner") && attributeFilter.get("owner") != null @@ -233,9 +240,9 @@ private String getSql(Integer ownerId, Map attributeFilter, String value = entry.getValue().trim(); if (value.length() > 0) { - sql.append( - " AND at.attributeName_AN_ID IN (SELECT AN_ID FROM ATTRIBUTENAME WHERE AN_NAME IN (SELECT AN_NAME FROM ATTRIBUTENAME WHERE AN_ID = :aid") - .append(counter).append(")) AND at.ATTRIBUTE_VALUE like :av").append(counter); + sql.append(" AND at").append(intKey) + .append(".attributeName_AN_ID IN (SELECT AN_ID FROM ATTRIBUTENAME WHERE AN_NAME IN (SELECT AN_NAME FROM ATTRIBUTENAME WHERE AN_ID = :aid") + .append(counter).append(")) AND at").append(intKey).append(".ATTRIBUTE_VALUE like :av").append(counter); oQueryParameters.put("aid" + counter, intKey); oQueryParameters.put("av" + counter++, "%" + value + "%"); } @@ -325,6 +332,12 @@ public void add(Attendee attendee) { attribute.setAttendeeId(attendee.getId()); session.update(attribute); } + + if (attendee.getOriginalId() != null) { + Query query = session.createSQLQuery("UPDATE SHARES_ATTENDEE SET attendees_ATTENDEE_ID = :id WHERE attendees_ATTENDEE_ID = :origId") + .setParameter("id", attendee.getId()).setParameter("origId", attendee.getOriginalId()); + query.executeUpdate(); + } } @Transactional(readOnly = false) diff --git a/src/main/java/com/ec/survey/service/ReportingService.java b/src/main/java/com/ec/survey/service/ReportingService.java index 843150610..385c8033d 100644 --- a/src/main/java/com/ec/survey/service/ReportingService.java +++ b/src/main/java/com/ec/survey/service/ReportingService.java @@ -619,7 +619,7 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt String v = ""; for (String answerid : answerids) { - if (v.length() > 0) v += ";"; + if (v.length() > 0) v += "; "; Element answer = choicequestion.getPossibleAnswerByUniqueId(answerid); if (answer != null) { @@ -631,7 +631,7 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt } if (showShortnames) { - v += " (" +answer.getShortname() + ")"; + v += "(" +answer.getShortname() + ")"; } } } @@ -642,7 +642,7 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt String v = ""; for (String answerid : answerids) { - if (v.length() > 0) v += ";"; + if (v.length() > 0) v += "; "; Element answer = matrix.getChildByUniqueId(answerid); if (answer != null) { @@ -654,7 +654,7 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt } if (showShortnames) { - v += " (" +answer.getShortname() + ")"; + v += "(" +answer.getShortname() + ")"; } } } @@ -665,13 +665,21 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt String v = ""; for (String index : indexes) { - if (v.length() > 0) v += ";"; - int i = Integer.parseInt(index); - File file = gallery.getFiles().get(i); + File file = null; + if (index.contains("-")) { + file = gallery.getFileByUid(index); + } else { + int i = Integer.parseInt(index); + if (gallery.getFiles().size() > i) { + file = gallery.getFiles().get(i); + } + } + if (file != null) { + if (v.length() > 0) v += "; "; v += file.getName(); - } + } } row.add(v.length() > 0 ? v : null); } else if (question instanceof Upload) { @@ -680,8 +688,6 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt String v = ""; for (String fileuid : fileuids) { - if (v.length() > 0) v += ";"; - File file = fileService.get(fileuid); if (file != null) { @@ -689,6 +695,7 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt { v += "" + file.getNameForExport() + "
"; } else if (forexport) { + if (v.length() > 0) v += " "; v += file.getUid() + "|" + file.getNameForExport() + ";"; } else { v += file.getNameForExport() + "
"; @@ -703,7 +710,7 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt String v = ""; for (String uniqueId : answerids) { - if (v.length() > 0) v += ";"; + if (v.length() > 0) v += "; "; RankingItem child = children.get(uniqueId); if (child != null) { @@ -715,7 +722,7 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt } if (showShortnames) { - v += " (" +child.getShortname() + ")"; + v += "(" +child.getShortname() + ")"; } } } @@ -734,7 +741,7 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt String v = ""; for (String answerid : answerids) { - if (v.length() > 0) v += ";"; + if (v.length() > 0) v += "; "; Element answer = child.getPossibleAnswerByUniqueId(answerid); if (answer != null) { @@ -746,7 +753,7 @@ public List> getAnswerSetsInternal(Survey survey, ResultFilter filt } if (showShortnames) { - v += " (" +answer.getShortname() + ")"; + v += "(" +answer.getShortname() + ")"; } } } @@ -1029,7 +1036,7 @@ private Map getColumnNamesAndTypes(Survey survey) throws Message } else if (question instanceof RatingQuestion) { RatingQuestion rating = (RatingQuestion) question; - for (Element child : rating.getChildElements()) { + for (Element child : rating.getQuestions()) { putColumnNameAndType(columnNamesToType, child.getUniqueId(), "TEXT"); } } else if (question instanceof RankingQuestion) { @@ -1533,7 +1540,11 @@ private void parseAnswerSetForReportingTable(AnswerSet answerSet, boolean create v = "'"; for (Answer answer : answers) { - v += answer.getValue() + ";"; + if (!StringUtils.isEmpty(answer.getPossibleAnswerUniqueId())) { + v += answer.getPossibleAnswerUniqueId() + ";"; + } else { + v += answer.getValue() + ";"; + } } v += "'"; } @@ -1805,7 +1816,7 @@ public int getCountInternal(Survey survey, String where, Map val } @Transactional(readOnly = true, transactionManager = "transactionManagerReporting") - public int getCountInternal(Survey survey, String quid, String auid, boolean noPrefixSearch, boolean noPostfixSearch, String where, Map values) { + public int getCountInternal(Survey survey, String quid, String auid, boolean noPrefixSearch, boolean noPostfixSearch, boolean noUUIDs, String where, Map values) { Session sessionReporting = sessionFactoryReporting.getCurrentSession(); String sql = "SELECT COUNT(*) FROM " + getOLAPTableName(survey) + " WHERE Q" + quid.replace("-", ""); @@ -1819,6 +1830,10 @@ public int getCountInternal(Survey survey, String quid, String auid, boolean noP sql += " LIKE '%" + auid + "%'"; } + if (noUUIDs) { + sql += " AND Q" + quid.replace("-", "") + " NOT LIKE '%-%'"; + } + if (where != null) { sql += " AND QANSWERSETID IN (SELECT QANSWERSETID FROM " + getOLAPTableName(survey) + " " + where + ")"; diff --git a/src/main/java/com/ec/survey/service/ReportingServiceProxy.java b/src/main/java/com/ec/survey/service/ReportingServiceProxy.java index 880b8d426..084de3b82 100644 --- a/src/main/java/com/ec/survey/service/ReportingServiceProxy.java +++ b/src/main/java/com/ec/survey/service/ReportingServiceProxy.java @@ -81,10 +81,10 @@ public int getCount(Survey survey, String where, Map values) { return reportingService.getCountInternal(survey, where, values); } - public int getCount(Survey survey, String quid, String auid, boolean noPrefixSearch, boolean noPostfixSearch, String where, Map values) + public int getCount(Survey survey, String quid, String auid, boolean noPrefixSearch, boolean noPostfixSearch, boolean noUUIDs, String where, Map values) { if (!isReportingDatabaseEnabled()) return -1; - return reportingService.getCountInternal(survey, quid, auid, noPrefixSearch, noPostfixSearch, where, values); + return reportingService.getCountInternal(survey, quid, auid, noPrefixSearch, noPostfixSearch, noUUIDs, where, values); } public int getAnswerSetsByQuestionUID(Survey survey, String quid, Map> answersByAnswerSetID) diff --git a/src/main/java/com/ec/survey/service/SessionService.java b/src/main/java/com/ec/survey/service/SessionService.java index 5d6358efd..997e84363 100644 --- a/src/main/java/com/ec/survey/service/SessionService.java +++ b/src/main/java/com/ec/survey/service/SessionService.java @@ -474,7 +474,7 @@ public ResultFilter getLastResultFilter(HttpServletRequest request, int userid, ResultFilter filter = null; if (userid > 0 && surveyid > 0) { - filter = getResultFilter(userid, surveyid); + filter = getResultFilter(userid, surveyid, true); } else { filter = (ResultFilter) request.getSession().getAttribute("ResultFilter"); } @@ -532,7 +532,8 @@ public ResultFilter getLastResultFilter(HttpServletRequest request, int userid, return filter; } - public ResultFilter getResultFilter(int userid, int surveyid) { + @Transactional + public ResultFilter getResultFilter(int userid, int surveyid, boolean evict) { Session session = sessionFactory.getCurrentSession(); Query query = session .createQuery( @@ -542,7 +543,14 @@ public ResultFilter getResultFilter(int userid, int surveyid) { List result = query.list(); if (!result.isEmpty()) { - return answerService.initialize(result.get(0)); + ResultFilter filter = result.get(0); + filter = answerService.initialize(filter); + + if (evict) { + session.evict(filter); + } + + return filter; } return null; @@ -610,7 +618,7 @@ public void internalSetLastResultFilter(ResultFilter filter, int user, int surve filter.setUserId(user); filter.setSurveyId(survey); - ResultFilter existing = getResultFilter(user, survey); + ResultFilter existing = getResultFilter(user, survey, false); if (existing == null) { existing = new ResultFilter(); } @@ -711,12 +719,12 @@ public void initializeProxy() { System.getProperties().setProperty("https.proxyUser", proxyUser); System.getProperties().setProperty("https.proxyPassword", proxyPassword); - System.setProperty("nonProxyHosts", "localhost"); - if (StringUtils.isEmpty(proxyNonProxyHosts)) { proxyNonProxyHosts = "localhost"; } + System.setProperty("nonProxyHosts", proxyNonProxyHosts); + logger.info("SessionService set Non proxy Host " + proxyNonProxyHosts); System.getProperties().setProperty("http.nonProxyHosts", proxyNonProxyHosts); diff --git a/src/main/java/com/ec/survey/service/SurveyService.java b/src/main/java/com/ec/survey/service/SurveyService.java index 36154ca3a..cdc479a25 100644 --- a/src/main/java/com/ec/survey/service/SurveyService.java +++ b/src/main/java/com/ec/survey/service/SurveyService.java @@ -32,6 +32,8 @@ import javax.naming.NamingException; import javax.servlet.http.HttpServletRequest; import javax.xml.parsers.DocumentBuilder; + +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; @@ -1010,6 +1012,9 @@ public void initializeSurvey(Survey survey) { } else if (element instanceof RankingQuestion) { RankingQuestion ranking = (RankingQuestion) element; Hibernate.initialize(ranking.getChildElements()); + } else if (element instanceof ComplexTable){ + ComplexTable table = (ComplexTable) element; + Hibernate.initialize(table.getChildElements()); } } } @@ -1158,7 +1163,7 @@ public Survey getSurvey(String uidorshortname, boolean isDraft, boolean checkAct survey.setIsPublished(true); session.update(survey); } - + Survey draft = null; if (isDraft) { draft = survey; @@ -1171,8 +1176,8 @@ public Survey getSurvey(String uidorshortname, boolean isDraft, boolean checkAct draft = (Survey) session.get(Survey.class, id); } - - if (checkActive && !isDraft) { + + if (checkActive && !isDraft) { if (!draft.getIsActive()) return null; } @@ -1235,6 +1240,16 @@ private void UpdatePossibleAnswers(Survey survey) { pa.setQuestionId(element.getId()); } } + if (element instanceof ComplexTable){ + ComplexTable table = (ComplexTable) element; + for (ComplexTableItem item : table.getChildElements()){ + if (item.isChoice()){ + for (PossibleAnswer pa : item.getPossibleAnswers()) { + pa.setQuestionId(table.getId()); + } + } + } + } } } @@ -3004,6 +3019,8 @@ private String translateKey(String key, Map elementsBySourceId if (oldToNewUniqueIds.containsKey(uid)) { return oldToNewUniqueIds.get(uid) + GalleryQuestion.TITLE; + } else if (convertedFileUIDs.containsKey(uid)){ + return convertedFileUIDs.get(uid) + GalleryQuestion.TITLE; } retVal = Integer.parseInt(uid); if (elementsBySourceId.containsKey(retVal)) @@ -3012,6 +3029,8 @@ private String translateKey(String key, Map elementsBySourceId uid = key.substring(0, key.indexOf(GalleryQuestion.TEXT)); if (oldToNewUniqueIds.containsKey(uid)) { return oldToNewUniqueIds.get(uid) + GalleryQuestion.TEXT; + } else if (convertedFileUIDs.containsKey(uid)){ + return convertedFileUIDs.get(uid) + GalleryQuestion.TEXT; } retVal = Integer.parseInt(uid); if (elementsBySourceId.containsKey(retVal)) @@ -3347,7 +3366,7 @@ public List getSurveysToNotify() { public Map getPendingChanges(Survey draftSurvey) { Map result = new HashMap<>(); - Survey publishedSurvey = getSurvey(draftSurvey.getShortname(), false, false, false, false, null, + Survey publishedSurvey = getSurvey(draftSurvey.getShortname(), false, false, false, false, draftSurvey.getLanguage().getCode(), true, true); // Compare elements @@ -3397,6 +3416,8 @@ public Map getPendingChanges(Survey draftSurvey) { hasPendingChanges = true; if(!Tools.isEqual(draftSurvey.getMotivationText(), publishedSurvey.getMotivationText())) hasPendingChanges = true; + if(!Tools.isEqual(draftSurvey.getMotivationPopupTitle(), publishedSurvey.getMotivationPopupTitle())) + hasPendingChanges = true; if (draftSurvey.getValidatedPerPage() != publishedSurvey.getValidatedPerPage()) hasPendingChanges = true; if (draftSurvey.getPreventGoingBack() != publishedSurvey.getPreventGoingBack()) @@ -3419,6 +3440,7 @@ public Map getPendingChanges(Survey draftSurvey) { hasPendingChanges = true; if (!Tools.isEqual(draftSurvey.getConfirmationPageLink(), publishedSurvey.getConfirmationPageLink())) hasPendingChanges = true; + if (!Tools.isEqual(draftSurvey.getConfirmationLink(), publishedSurvey.getConfirmationLink())) hasPendingChanges = true; @@ -3785,6 +3807,7 @@ public void checkAndRecreateMissingElements(Survey survey, ResultFilter filter) List surveyelements = survey.getElementsRecursive(true); Map surveyelementsbyuid = new HashMap<>(); Map missingelementuids = new HashMap<>(); + Map missingfileuids = new HashMap<>(); Set rankingQuestionUids = new HashSet<>(); for (Element element : surveyelements) { surveyelementsbyuid.put(element.getUniqueId(), element); @@ -3963,6 +3986,7 @@ public void checkAndRecreateMissingElements(Survey survey, ResultFilter filter) if (possibleAnswerUID != null && possibleAnswerUID.length() > 0 && !possibleAnswerUID.contains("#") && !surveyelementsbyuid.containsKey(possibleAnswerUID) && !missingelementuids.containsKey(possibleAnswerUID)) { + Element pa = getNewestElementByUid(possibleAnswerUID); if (pa != null) { @@ -4006,6 +4030,26 @@ public void checkAndRecreateMissingElements(Survey survey, ResultFilter filter) } } } + } else { + //try gallery file + Element parent = null; + if (surveyelementsbyuid.containsKey(questionUID)) { + parent = surveyelementsbyuid.get(questionUID); + } else if (missingelementuids.containsKey(questionUID)) { + parent = missingelementuids.get(questionUID); + } + if (parent != null && parent instanceof GalleryQuestion) { + GalleryQuestion gallery = (GalleryQuestion) parent; + try { + if (gallery.getFileByUid(possibleAnswerUID) == null && !missingfileuids.containsKey(possibleAnswerUID)) { + File file = fileService.get(possibleAnswerUID); + ((GalleryQuestion) parent).getMissingFiles().add(file); + missingfileuids.put(possibleAnswerUID, file); + } + } catch (FileNotFoundException e) { + // ignore; + } + } } } } diff --git a/src/main/java/com/ec/survey/tools/QuizHelper.java b/src/main/java/com/ec/survey/tools/QuizHelper.java index 9addb75a7..e7f315a84 100644 --- a/src/main/java/com/ec/survey/tools/QuizHelper.java +++ b/src/main/java/com/ec/survey/tools/QuizHelper.java @@ -151,11 +151,11 @@ public static QuizResult getQuizResult(AnswerSet answerSet, Survey survey, final } else if (wrong) { result.getQuestionScores().put(question.getUniqueId(), 0); - } else if (defaultItem != null) + } else if (defaultItem != null && defaultItem.isCorrect()) { - score += defaultItem.getPoints(); - currentSectionScore += defaultItem.getPoints(); - result.getQuestionScores().put(question.getUniqueId(), defaultItem.getPoints()); + score += question.getQuizPoints(); + currentSectionScore += question.getQuizPoints(); + result.getQuestionScores().put(question.getUniqueId(), question.getQuizPoints()); result.getQuestionScoringItems().put(element.getUniqueId(), defaultItem); } } else if (question instanceof DateQuestion) @@ -191,11 +191,11 @@ public static QuizResult getQuizResult(AnswerSet answerSet, Survey survey, final } else if (wrong) { result.getQuestionScores().put(question.getUniqueId(), 0); - } else if (defaultItem != null) + } else if (defaultItem != null && defaultItem.isCorrect()) { - score += defaultItem.getPoints(); - currentSectionScore += defaultItem.getPoints(); - result.getQuestionScores().put(question.getUniqueId(), defaultItem.getPoints()); + score += question.getQuizPoints(); + currentSectionScore += question.getQuizPoints(); + result.getQuestionScores().put(question.getUniqueId(), question.getQuizPoints()); result.getQuestionScoringItems().put(element.getUniqueId(), defaultItem); } } else if (question instanceof FreeTextQuestion) @@ -231,7 +231,7 @@ public static QuizResult getQuizResult(AnswerSet answerSet, Survey survey, final } else if (wrong) { result.getQuestionScores().put(question.getUniqueId(), 0); - } else if (defaultItem != null) + } else if (defaultItem != null && defaultItem.isCorrect()) { score += question.getQuizPoints(); currentSectionScore += question.getQuizPoints(); @@ -341,7 +341,7 @@ public static QuizResult getQuizResult(AnswerSet answerSet, Survey survey, final result.getQuestionScores().put(question.getUniqueId(), bestMatch.getPoints()); result.getQuestionScoringItems().put(element.getUniqueId(), bestMatch); } else { - if (defaultItem != null) + if (defaultItem != null && defaultItem.isCorrect()) { score += defaultItem.getPoints(); currentSectionScore += defaultItem.getPoints(); @@ -383,7 +383,7 @@ public static QuizResult getQuizResult(AnswerSet answerSet, Survey survey, final result.getQuestionScores().put(question.getUniqueId(), bestMatch.getPoints()); result.getQuestionScoringItems().put(element.getUniqueId(), bestMatch); } else { - if (defaultItem != null) + if (defaultItem != null && defaultItem.isCorrect()) { score += defaultItem.getPoints(); currentSectionScore += defaultItem.getPoints(); @@ -425,7 +425,7 @@ public static QuizResult getQuizResult(AnswerSet answerSet, Survey survey, final result.getQuestionScores().put(question.getUniqueId(), bestMatch.getPoints()); result.getQuestionScoringItems().put(element.getUniqueId(), bestMatch); } else { - if (defaultItem != null) + if (defaultItem != null && defaultItem.isCorrect()) { score += defaultItem.getPoints(); currentSectionScore += defaultItem.getPoints(); diff --git a/src/main/java/com/ec/survey/tools/SurveyCreator.java b/src/main/java/com/ec/survey/tools/SurveyCreator.java index fc17115ac..766853a74 100644 --- a/src/main/java/com/ec/survey/tools/SurveyCreator.java +++ b/src/main/java/com/ec/survey/tools/SurveyCreator.java @@ -228,9 +228,14 @@ public static Survey createDemoSkinSurvey(User owner, Language la) { Matrix matrix = new Matrix("This is a matrix", "matrix1", UUID.randomUUID().toString()); matrix.setPosition(position); + matrix.setOptional(true); matrix.setColumns(4); matrix.setRows(3); matrix.setIsSingleChoice(true); + matrix.setMinRows(1); + matrix.setMaxRows(2); + matrix.setIsInterdependent(false); + matrix.setHelp("This is the help message"); EmptyElement dummy = new EmptyElement("empty", "empty"); dummy.setPosition(0); matrix.getChildElements().add(dummy); @@ -238,6 +243,7 @@ public static Survey createDemoSkinSurvey(User owner, Language la) { { Text text = new Text("Text" + i, UUID.randomUUID().toString()); text.setPosition(i); + text.setOptional(true); matrix.getChildElements().add(text); } survey.getElements().add(matrix); diff --git a/src/main/java/com/ec/survey/tools/SurveyHelper.java b/src/main/java/com/ec/survey/tools/SurveyHelper.java index 08c52a91c..1e9a26b8e 100644 --- a/src/main/java/com/ec/survey/tools/SurveyHelper.java +++ b/src/main/java/com/ec/survey/tools/SurveyHelper.java @@ -175,6 +175,11 @@ public static AnswerSet parseAnswerSet(HttpServletRequest request, Survey survey answer.setPossibleAnswerId(paid); answer.setPossibleAnswerUniqueId(item.getPossibleAnswer(paid).getUniqueId()); } + } else if (question instanceof GalleryQuestion) { + GalleryQuestion gallery = (GalleryQuestion)question; + int index = Integer.parseInt(value); + File file = gallery.getFiles().get(index); + answer.setPossibleAnswerUniqueId(file.getUid()); } answerSet.addAnswer(answer); diff --git a/src/main/java/com/ec/survey/tools/TranslationsHelper.java b/src/main/java/com/ec/survey/tools/TranslationsHelper.java index 8824adb64..7dbaa5846 100644 --- a/src/main/java/com/ec/survey/tools/TranslationsHelper.java +++ b/src/main/java/com/ec/survey/tools/TranslationsHelper.java @@ -647,7 +647,7 @@ public static List getLongDescriptions(Survey survey, MessageSource re Table table = (Table) element; result.add(new KeyValue(element.getUniqueId() + MatrixOrTable.FIRSTCELL, - resources.getMessage("label.MatrixText", null, "Matrix Text", locale))); + resources.getMessage("label.TableText", null, "Table Text", locale))); for (Element child : table.getChildElements()) { if (child instanceof Text) { diff --git a/src/main/java/com/ec/survey/tools/export/DocExportCreator.java b/src/main/java/com/ec/survey/tools/export/DocExportCreator.java index 51d476d17..b2d92d694 100644 --- a/src/main/java/com/ec/survey/tools/export/DocExportCreator.java +++ b/src/main/java/com/ec/survey/tools/export/DocExportCreator.java @@ -146,22 +146,22 @@ void exportStatistics() throws IOException { XWPFTable table = createTableForAnswer(cellValue); GalleryQuestion galleryQuestion = (GalleryQuestion)question; - for (int i = 0; i < galleryQuestion.getFiles().size(); i++) { + for (com.ec.survey.model.survey.base.File file : galleryQuestion.getAllFiles()) { XWPFTableRow row = table.createRow(); - cellValue = ConversionTools.removeHTMLNoEscape(galleryQuestion.getFiles().get(i).getName()); + cellValue = ConversionTools.removeHTMLNoEscape(file.getName()); row.getCell(0).setText(cellValue); - Double percent = statistics.getRequestedRecordsPercent().get(galleryQuestion.getId().toString() + "-" + i); + Double percent = statistics.getRequestedRecordsPercent().get(galleryQuestion.getId().toString() + "-" + file.getUid()); if (percent > 0) { drawChart(percent, row); } - row.getCell(2).setText(statistics.getRequestedRecords().get(galleryQuestion.getId().toString() + "-" + i).toString()); - row.getCell(3).setText(df.format(statistics.getRequestedRecordsPercent().get(galleryQuestion.getId().toString() + "-" + i)) + "%"); + row.getCell(2).setText(statistics.getRequestedRecords().get(galleryQuestion.getId().toString() + "-" + file.getUid()).toString()); + row.getCell(3).setText(df.format(statistics.getRequestedRecordsPercent().get(galleryQuestion.getId().toString() + "-" + file.getUid())) + "%"); } //noanswers diff --git a/src/main/java/com/ec/survey/tools/export/OdfExportCreator.java b/src/main/java/com/ec/survey/tools/export/OdfExportCreator.java index 8a691b835..15e10c5f7 100644 --- a/src/main/java/com/ec/survey/tools/export/OdfExportCreator.java +++ b/src/main/java/com/ec/survey/tools/export/OdfExportCreator.java @@ -14,6 +14,8 @@ import com.ec.survey.service.SqlQueryService; import com.ec.survey.tools.Constants; import com.ec.survey.tools.ConversionTools; +import com.mysql.jdbc.StringUtils; + import org.apache.commons.compress.archivers.ArchiveOutputStream; import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; @@ -756,21 +758,28 @@ private void parseAnswerSet(AnswerSet answerSet, List answerrow, Publica cell.setStringValue(ConversionTools.removeHTMLNoEscape(v)); cell.setDisplayText(ConversionTools.removeHTMLNoEscape(v)); } - } else { List answers = answerSet.getAnswers(question.getId(), question.getUniqueId()); StringBuilder cellValue = new StringBuilder(); boolean first = true; + GalleryQuestion gallery = (GalleryQuestion) question; for (Answer answer : answers) { - - if (answer.getValue() != null && answer.getValue().length() > 0) { - int index = Integer.parseInt(answer.getValue()); - if (!first) - cellValue.append(", "); - cellValue.append(((GalleryQuestion) question).getFiles().get(index).getName()) - .append(" "); + + File file = null; + if (!StringUtils.isNullOrEmpty(answer.getPossibleAnswerUniqueId())) { + file = gallery.getFileByUid(answer.getPossibleAnswerUniqueId()); + } else if (!StringUtils.isNullOrEmpty(answer.getValue())) { + file = gallery.getAllFiles().get(Integer.parseInt(answer.getValue())); + } + + if (!first) + cellValue.append(", "); + try { + cellValue.append(file.getName()); first = false; + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); } } @@ -1221,10 +1230,10 @@ void ExportStatisticsODS() throws Exception { CreateTableForAnswerStat(cellValue); GalleryQuestion galleryQuestion = (GalleryQuestion) question; - for (int i = 0; i < galleryQuestion.getFiles().size(); i++) { + for (File file : galleryQuestion.getAllFiles()) { rowIndex++; - cellValue = ConversionTools.removeHTMLNoEscape(galleryQuestion.getFiles().get(i).getName()); + cellValue = ConversionTools.removeHTMLNoEscape(file.getName()); cell = sheet.getCellByPosition(0, rowIndex); cell.setStringValue(cellValue); @@ -1232,7 +1241,7 @@ void ExportStatisticsODS() throws Exception { cell.setValueType(Constants.STRING); Double percent = statistics.getRequestedRecordsPercent() - .get(galleryQuestion.getId().toString() + "-" + i); + .get(galleryQuestion.getId().toString() + "-" + file.getUid()); cell = sheet.getCellByPosition(1, rowIndex); cell.removeContent(); @@ -1243,15 +1252,15 @@ void ExportStatisticsODS() throws Exception { cell = sheet.getCellByPosition(2, rowIndex); cell.setDoubleValue((double) statistics.getRequestedRecords() - .get(galleryQuestion.getId().toString() + "-" + i)); + .get(galleryQuestion.getId().toString() + "-" + file.getUid())); cell.setDisplayText(statistics.getRequestedRecords() - .get(galleryQuestion.getId().toString() + "-" + i).toString()); + .get(galleryQuestion.getId().toString() + "-" + file.getUid()).toString()); cell = sheet.getCellByPosition(3, rowIndex); cell.setPercentageValue(statistics.getRequestedRecordsPercent() - .get(galleryQuestion.getId().toString() + "-" + i)); + .get(galleryQuestion.getId().toString() + "-" + file.getUid())); cell.setDisplayText(df.format(statistics.getRequestedRecordsPercent() - .get(galleryQuestion.getId().toString() + "-" + i)) + "%"); + .get(galleryQuestion.getId().toString() + "-" + file.getUid())) + "%"); } rowIndex++; @@ -1810,24 +1819,24 @@ void exportStatistics() throws Exception { org.odftoolkit.simple.table.Table table = CreateTableForAnswer(document, cellValue); GalleryQuestion galleryQuestion = (GalleryQuestion) question; - for (int i = 0; i < galleryQuestion.getFiles().size(); i++) { + for (File file : galleryQuestion.getAllFiles()) { Row row = table.appendRow(); - cellValue = ConversionTools.removeHTMLNoEscape(galleryQuestion.getFiles().get(i).getName()); + cellValue = ConversionTools.removeHTMLNoEscape(file.getName()); row.getCellByIndex(0).setStringValue(cellValue); Double percent = statistics.getRequestedRecordsPercent() - .get(galleryQuestion.getId().toString() + "-" + i); + .get(galleryQuestion.getId().toString() + "-" + file.getUid()); if (percent > 0) { drawChart(percent, row); } row.getCellByIndex(2).setDoubleValue((double) statistics.getRequestedRecords() - .get(galleryQuestion.getId().toString() + "-" + i)); + .get(galleryQuestion.getId().toString() + "-" + file.getUid())); row.getCellByIndex(2).setStringValue(Integer.toString( - statistics.getRequestedRecords().get(galleryQuestion.getId().toString() + "-" + i))); + statistics.getRequestedRecords().get(galleryQuestion.getId().toString() + "-" + file.getUid()))); row.getCellByIndex(3).setStringValue(df.format(percent) + "%"); diff --git a/src/main/java/com/ec/survey/tools/export/StatisticsCreator.java b/src/main/java/com/ec/survey/tools/export/StatisticsCreator.java index f386f05d3..18673095c 100644 --- a/src/main/java/com/ec/survey/tools/export/StatisticsCreator.java +++ b/src/main/java/com/ec/survey/tools/export/StatisticsCreator.java @@ -16,7 +16,6 @@ import com.ec.survey.tools.QuizHelper; import org.apache.log4j.Logger; import org.hibernate.*; -import org.hibernate.mapping.Formula; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -91,7 +90,7 @@ public Statistics runSync() throws Exception { Map numberOfAnswersMap = new HashMap<>(); Map> numberOfAnswersMapMatrix = new HashMap<>(); Map> numberOfAnswersMapRatingQuestion = new HashMap<>(); - Map> numberOfAnswersMapGallery = new HashMap<>(); + Map> numberOfAnswersMapGallery = new HashMap<>(); Map>> multipleChoiceSelectionsByAnswerset = new HashMap<>(); Map numberOfNumberAnswersMap = new HashMap<>(); Map> numberOfAnswersMapRankingQuestion = new HashMap<>(); @@ -149,7 +148,7 @@ public Statistics runSync() throws Exception { ComplexTable table = (ComplexTable) element; for (ComplexTableItem child : table.getQuestionChildElements()) { - if (child.getCellType() == ComplexTableItem.CellType.SingleChoice || child.getCellType() == ComplexTableItem.CellType.MultipleChoice) { + if (child.isChoice()) { addChoiceStatistics(survey, child, statistics, numberOfAnswersMap, multipleChoiceSelectionsByAnswerset); } else if ((child.getCellType() == ComplexTableItem.CellType.Number || child.getCellType() == ComplexTableItem.CellType.Formula) && child.showStatisticsForNumberQuestion()) { @@ -352,16 +351,16 @@ public Statistics runSync() throws Exception { } private void addReportingAnswers4Statistics(Question q, Map map, - Map> mapMatrix, Map> mapGallery, + Map> mapMatrix, Map> mapGallery, Map> mapRatingQuestion, Map values, String where, Map mapNumberQuestion, Map>> multipleChoiceSelectionsByAnswerset, Map> mapRankingQuestion) { if (q instanceof ChoiceQuestion) { ChoiceQuestion choice = (ChoiceQuestion) q; for (PossibleAnswer a : choice.getAllPossibleAnswers()) { - int count = reportingService.getCount(survey, choice.getUniqueId(), a.getUniqueId(), false, false, where, + int count = reportingService.getCount(survey, choice.getUniqueId(), a.getUniqueId(), false, false, false, where, values); map.put(a.getId(), count); } - int count = reportingService.getCount(survey, choice.getUniqueId(), null, false, false, where, values); + int count = reportingService.getCount(survey, choice.getUniqueId(), null, false, false, false, where, values); map.put(q.getId(), count); if (q instanceof MultipleChoiceQuestion) { Set paUIDs = new HashSet<>(); @@ -385,12 +384,25 @@ private void addReportingAnswers4Statistics(Question q, Map ma if (!mapGallery.containsKey(g.getId())) { mapGallery.put(g.getId(), new HashMap<>()); } + for (com.ec.survey.model.survey.base.File file : g.getAllFiles()) { + int countbyuid = reportingService.getCount(survey, g.getUniqueId(), file.getUid(), false, false, false, where, + values); + mapGallery.get(g.getId()).put(file.getUid(), countbyuid); + } + + //also add "old" answers (without file uid in answers) for (int i = 0; i < g.getFiles().size(); i++) { - int count = reportingService.getCount(survey, g.getUniqueId(), Integer.toString(i), false, false, where, + com.ec.survey.model.survey.base.File file = g.getFiles().get(i); + int count = reportingService.getCount(survey, g.getUniqueId(), Integer.toString(i), false, false, true, where, values); - mapGallery.get(g.getId()).put(i, count); - } - int count = reportingService.getCount(survey, g.getUniqueId(), null, false, false, where, values); + + if (count > 0) { + int oldcount = mapGallery.get(g.getId()).get(file.getUid()); + mapGallery.get(g.getId()).put(file.getUid(), count + oldcount); + } + } + + int count = reportingService.getCount(survey, g.getUniqueId(), null, false, false, false, where, values); map.put(q.getId(), count); } else if (q instanceof Matrix) { Matrix matrix = (Matrix) q; @@ -399,10 +411,10 @@ private void addReportingAnswers4Statistics(Question q, Map ma if (!mapMatrix.containsKey(matrixQuestion.getId())) mapMatrix.put(matrixQuestion.getId(), new HashMap<>()); int count = reportingService.getCount(survey, matrixQuestion.getUniqueId(), - matrixAnswer.getUniqueId(), false, false, where, values); + matrixAnswer.getUniqueId(), false, false, false, where, values); mapMatrix.get(matrixQuestion.getId()).put(matrixAnswer.getId(), count); } - int count = reportingService.getCount(survey, matrixQuestion.getUniqueId(), null, false, false, where, values); + int count = reportingService.getCount(survey, matrixQuestion.getUniqueId(), null, false, false, false, where, values); map.put(matrixQuestion.getId(), count); } } else if (q instanceof RatingQuestion) { @@ -412,19 +424,19 @@ private void addReportingAnswers4Statistics(Question q, Map ma if (!mapRatingQuestion.containsKey(childQuestion.getId())) mapRatingQuestion.put(childQuestion.getId(), new HashMap<>()); int count = reportingService.getCount(survey, childQuestion.getUniqueId(), - Integer.toString(i) + Constants.PATH_DELIMITER, true, false, where, values); + Integer.toString(i) + Constants.PATH_DELIMITER, true, false, false, where, values); mapRatingQuestion.get(childQuestion.getId()).put(i, count); } - int count = reportingService.getCount(survey, childQuestion.getUniqueId(), null, false, false, where, values); + int count = reportingService.getCount(survey, childQuestion.getUniqueId(), null, false, false, false, where, values); map.put(childQuestion.getId(), count); } } else if (q instanceof RankingQuestion) { RankingQuestion ranking = (RankingQuestion) q; - int size = ranking.getChildElements().size(); + int size = ranking.getAllChildElements().size(); List answers = reportingService.getAnswersByQuestionUID(survey, ranking.getUniqueId(), where, values); - for (Element childQuestion : ranking.getChildElements()) { + for (Element childQuestion : ranking.getAllChildElements()) { if (!mapRankingQuestion.containsKey(childQuestion.getUniqueId())) { HashMap childMap = new HashMap<>(); for (int i = 0; i < size; i++) { @@ -449,40 +461,40 @@ private void addReportingAnswers4Statistics(Question q, Map ma NumberQuestion number = (NumberQuestion) q; if (number.showStatisticsForNumberQuestion()) { for (String answer : number.getAllPossibleAnswers()) { - int count = reportingService.getCount(survey, number.getUniqueId(), answer, true, true, where, values); + int count = reportingService.getCount(survey, number.getUniqueId(), answer, true, true, false, where, values); mapNumberQuestion.put(number.getUniqueId() + answer, count); } } - int count = reportingService.getCount(survey, number.getUniqueId(), null, false, false, where, values); + int count = reportingService.getCount(survey, number.getUniqueId(), null, false, false, false, where, values); map.put(q.getId(), count); } else if (q instanceof FormulaQuestion) { FormulaQuestion formula = (FormulaQuestion) q; if (formula.showStatisticsForNumberQuestion()) { for (String answer : formula.getAllPossibleAnswers()) { - int count = reportingService.getCount(survey, formula.getUniqueId(), answer, true, true, where, values); + int count = reportingService.getCount(survey, formula.getUniqueId(), answer, true, true, false, where, values); mapNumberQuestion.put(formula.getUniqueId() + answer, count); } } - int count = reportingService.getCount(survey, formula.getUniqueId(), null, false, false, where, values); + int count = reportingService.getCount(survey, formula.getUniqueId(), null, false, false, false, where, values); map.put(q.getId(), count); } else if (q instanceof ComplexTable) { ComplexTable table = (ComplexTable) q; for (ComplexTableItem child : table.getQuestionChildElements()) { if (child.getCellType() == ComplexTableItem.CellType.SingleChoice || child.getCellType() == ComplexTableItem.CellType.MultipleChoice) { for (PossibleAnswer a : child.getPossibleAnswers()) { - int count = reportingService.getCount(survey, child.getUniqueId(), a.getUniqueId(), false, false, where, + int count = reportingService.getCount(survey, child.getUniqueId(), a.getUniqueId(), false, false, false, where, values); map.put(a.getId(), count); } - int count = reportingService.getCount(survey, child.getUniqueId(), null, false, false, where, values); + int count = reportingService.getCount(survey, child.getUniqueId(), null, false, false, false, where, values); map.put(child.getId(), count); } else if (child.getCellType() == ComplexTableItem.CellType.Number || child.getCellType() == ComplexTableItem.CellType.Formula) { if (child.showStatisticsForNumberQuestion()) { for (String answer : child.getPossibleNumberAnswers()) { - int count = reportingService.getCount(survey, child.getUniqueId(), answer, true, true, where, values); + int count = reportingService.getCount(survey, child.getUniqueId(), answer, true, true, false, where, values); mapNumberQuestion.put(child.getUniqueId() + answer, count); } - int count = reportingService.getCount(survey, child.getUniqueId(), null, false, false, where, values); + int count = reportingService.getCount(survey, child.getUniqueId(), null, false, false, false, where, values); map.put(child.getId(), count); } } @@ -492,19 +504,19 @@ private void addReportingAnswers4Statistics(Question q, Map ma ComplexTableItem child = (ComplexTableItem) q; if (child.getCellType() == ComplexTableItem.CellType.SingleChoice || child.getCellType() == ComplexTableItem.CellType.MultipleChoice) { for (PossibleAnswer a : child.getPossibleAnswers()) { - int count = reportingService.getCount(survey, child.getUniqueId(), a.getUniqueId(), false, false, where, + int count = reportingService.getCount(survey, child.getUniqueId(), a.getUniqueId(), false, false, false, where, values); map.put(a.getId(), count); } - int count = reportingService.getCount(survey, child.getUniqueId(), null, false, false, where, values); + int count = reportingService.getCount(survey, child.getUniqueId(), null, false, false, false, where, values); map.put(child.getId(), count); } else if (child.getCellType() == ComplexTableItem.CellType.Number || child.getCellType() == ComplexTableItem.CellType.Formula) { if (child.showStatisticsForNumberQuestion()) { for (String answer : child.getPossibleNumberAnswers()) { - int count = reportingService.getCount(survey, child.getUniqueId(), answer, true, true, where, values); + int count = reportingService.getCount(survey, child.getUniqueId(), answer, true, true, false, where, values); mapNumberQuestion.put(child.getUniqueId() + answer, count); } - int count = reportingService.getCount(survey, child.getUniqueId(), null, false, false, where, values); + int count = reportingService.getCount(survey, child.getUniqueId(), null, false, false, false, where, values); map.put(child.getId(), count); } } @@ -512,7 +524,7 @@ private void addReportingAnswers4Statistics(Question q, Map ma } private void addMainAnswers4Statistics(Question q, Map map, - Map> mapMatrix, Map> mapGallery, + Map> mapMatrix, Map> mapGallery, Map> mapRatingQuestion, Map counts, Map countsUID, Map gallerycounts, Map> answerSetQuestion, Map matrixcounts, @@ -534,8 +546,17 @@ private void addMainAnswers4Statistics(Question q, Map map, if (!mapGallery.containsKey(g.getId())) { mapGallery.put(g.getId(), new HashMap<>()); } + for (com.ec.survey.model.survey.base.File file : g.getAllFiles()) { + mapGallery.get(g.getId()).put(file.getUid(), countsUID.getOrDefault(file.getUid() + "#" + g.getUniqueId(), 0)); + } + //also add "old" answers (without file uid in answers) for (int i = 0; i < g.getFiles().size(); i++) { - mapGallery.get(g.getId()).put(i, gallerycounts.getOrDefault(g.getUniqueId() + "-" + i, 0)); + com.ec.survey.model.survey.base.File file = g.getFiles().get(i); + int count = gallerycounts.getOrDefault(g.getUniqueId() + "-" + i, 0); + if (count > 0) { + int oldcount = mapGallery.get(g.getId()).get(file.getUid()); + mapGallery.get(g.getId()).put(file.getUid(), count + oldcount); + } } map.put(q.getId(), answerSetQuestion.get(q.getUniqueId()) != null ? answerSetQuestion.get(q.getUniqueId()).size() : 0); @@ -580,8 +601,8 @@ private void addMainAnswers4Statistics(Question q, Map map, } } else if (q instanceof RankingQuestion) { RankingQuestion ranking = (RankingQuestion) q; - int size = ranking.getChildElements().size(); - for (Element childQuestion : ranking.getChildElements()) { + int size = ranking.getAllChildElements().size(); + for (Element childQuestion : ranking.getAllChildElements()) { if (!mapRankingQuestion.containsKey(childQuestion.getUniqueId())) { HashMap childMap = new HashMap<>(); for (int i = 0; i < size; i++) { @@ -613,8 +634,8 @@ private void addMainAnswers4Statistics(Question q, Map map, if (child.getCellType() == ComplexTableItem.CellType.SingleChoice || child.getCellType() == ComplexTableItem.CellType.MultipleChoice) { for (PossibleAnswer a : child.getPossibleAnswers()) { - if (countsUID.containsKey(a.getUniqueId() + "#" + q.getUniqueId())) { - map.put(a.getId(), countsUID.get(a.getUniqueId() + "#" + q.getUniqueId())); + if (countsUID.containsKey(a.getUniqueId() + "#" + child.getUniqueId())) { + map.put(a.getId(), countsUID.get(a.getUniqueId() + "#" + child.getUniqueId())); } else { map.put(a.getId(), counts.getOrDefault(a.getId().toString(), 0)); } @@ -714,7 +735,9 @@ private void parseAnswers4Statistics(ScrollableResults results, Set res counts.put(key, 1); } } else { - if (value != null && org.apache.commons.lang3.StringUtils.isNumeric(value)) { + if (pauid == null && value != null && org.apache.commons.lang3.StringUtils.isNumeric(value)) { + //gallery + //"old" style String galleryKey = qid.toString() + "-" + value; if (gallerycounts.containsKey(galleryKey)) { gallerycounts.put(galleryKey, gallerycounts.get(galleryKey) + 1); @@ -748,6 +771,7 @@ private void parseAnswers4Statistics(ScrollableResults results, Set res } } else { if (value != null && org.apache.commons.lang3.StringUtils.isNumeric(value) && quid != null) { + //gallery String galleryKey = quid + "-" + value; if (gallerycounts.containsKey(galleryKey)) { gallerycounts.put(galleryKey, gallerycounts.get(galleryKey) + 1); @@ -860,7 +884,7 @@ public List getAnswers4FreeTextStatistics(Survey survey, Question questi @Transactional public int getAnswers4Statistics(Survey survey, Question question, Map map, - Map> mapMatrix, Map> mapGallery, + Map> mapMatrix, Map> mapGallery, Map>> multipleChoiceSelectionsByAnswerset, Map> mapRatingQuestion, Map mapNumberQuestion, Map> mapRankingQuestion) throws TooManyFiltersException { @@ -981,7 +1005,7 @@ public int getAnswers4Statistics(Survey survey, Question question, Map map, - Map> mapMatrix, Map> mapGallery, + Map> mapMatrix, Map> mapGallery, Map>> multipleChoiceSelectionsByAnswerset, Map> mapRatingQuestion, Map mapNumberQuestion, Map> mapRankingQuestion, Map> rankingQuestionAnswers) throws TooManyFiltersException { @@ -1122,39 +1146,41 @@ public int addStatistics4RatingQuestion(Survey survey, Integer answer, Element q } public void addStatistics4RankingQuestion(Survey survey, RankingQuestion ranking, Statistics statistics, Map> numberOfAnswersMapRankingQuestion) { - int size = ranking.getChildElements().size(); + int size = ranking.getAllChildElements().size(); int total = survey.getNumberOfAnswerSets(); - int answered = 0; - boolean firstChild = true; - for (Element child : ranking.getChildElements()) { + int maxAnswered = 0; + List rankingItems = ranking.getAllChildElements(); + for (int j = 0; j < size; j++) { + Element child = rankingItems.get(j); int score = 0; + int answered = 0; for (int i = 0; i < size; i++) { int value = numberOfAnswersMapRankingQuestion.get(child.getUniqueId()).get(i); statistics.getRequestedRecordsRankingScore().put(child.getId() + "-" + i, value); score += (size - i) * value; - if (firstChild) { - answered += value; - } + answered += value; } - + + maxAnswered = Math.max(answered, maxAnswered); + statistics.getRequestedRecordsRankingScore().put(child.getId().toString(), score); - firstChild = false; } - - for (Element child : ranking.getChildElements()) { + + for (int j = 0; j < size; j++) { + Element child = rankingItems.get(j); for (int i = 0; i < size; i++) { int value = statistics.getRequestedRecordsRankingScore().get(child.getId() + "-" + i); - statistics.getRequestedRecordsRankingPercentScore().put(child.getId() + "-" + i, divideToPercent(value, answered)); + statistics.getRequestedRecordsRankingPercentScore().put(child.getId() + "-" + i, divideToPercent(value, maxAnswered)); } int score = statistics.getRequestedRecordsRankingScore().get(child.getId().toString()); - statistics.getRequestedRecordsRankingPercentScore().put(child.getId().toString(), divide(score, answered)); - } - - statistics.getRequestedRecordsRankingScore().put(ranking.getId().toString(), answered); - - statistics.getRequestedRecords().put(ranking.getId().toString(), total - answered); - double percent = total == 0 ? 0 : (double) (total - answered) / (double) total * 100; + statistics.getRequestedRecordsRankingPercentScore().put(child.getId().toString(), divide(score, maxAnswered)); + } + + statistics.getRequestedRecordsRankingScore().put(ranking.getId().toString(), maxAnswered); + + statistics.getRequestedRecords().put(ranking.getId().toString(), total - maxAnswered); + double percent = total == 0 ? 0 : (double) (total - maxAnswered) / (double) total * 100; statistics.getRequestedRecordsPercent().put(ranking.getId().toString(), percent); } @@ -1252,21 +1278,21 @@ private void addStatistics4Quiz(Survey survey, Question question, Statistics sta } private void addStatistics4Gallery(Survey survey, GalleryQuestion question, Statistics statistics, - Map> numberOfAnswersMap, Map numberOfAnswersMap2) { + Map> numberOfAnswersMap, Map numberOfAnswersMap2) { int total = survey.getNumberOfAnswerSets(); - for (int i = 0; i < question.getFiles().size(); i++) { + for (com.ec.survey.model.survey.base.File file: question.getAllFiles()) { int numberOfAnswers = 0; if (numberOfAnswersMap.containsKey(question.getId()) - && numberOfAnswersMap.get(question.getId()).containsKey(i)) { - numberOfAnswers = numberOfAnswersMap.get(question.getId()).get(i); + && numberOfAnswersMap.get(question.getId()).containsKey(file.getUid())) { + numberOfAnswers = numberOfAnswersMap.get(question.getId()).get(file.getUid()); } double percent = total == 0 ? 0 : (double) numberOfAnswers / (double) total * 100; - statistics.getRequestedRecords().put(question.getId() + "-" + i, numberOfAnswers); - statistics.getRequestedRecordsPercent().put(question.getId() + "-" + i, percent); - statistics.getTotalsPercent().put(question.getId() + "-" + i, percent); + statistics.getRequestedRecords().put(question.getId() + "-" + file.getUid(), numberOfAnswers); + statistics.getRequestedRecordsPercent().put(question.getId() + "-" + file.getUid(), percent); + statistics.getTotalsPercent().put(question.getId() + "-" + file.getUid(), percent); } int answered = numberOfAnswersMap2.get(question.getId()); diff --git a/src/main/java/com/ec/survey/tools/export/XlsExportCreator.java b/src/main/java/com/ec/survey/tools/export/XlsExportCreator.java index b7719bc5a..4e8b92e4e 100644 --- a/src/main/java/com/ec/survey/tools/export/XlsExportCreator.java +++ b/src/main/java/com/ec/survey/tools/export/XlsExportCreator.java @@ -19,6 +19,8 @@ import com.ec.survey.service.ExportService; import com.ec.survey.tools.Constants; import com.ec.survey.tools.ConversionTools; +import com.mysql.jdbc.StringUtils; + import org.apache.commons.compress.archivers.ArchiveOutputStream; import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; @@ -810,6 +812,7 @@ private void parseAnswerSet(AnswerSet answerSet, List answerrow, Publica } else if (question instanceof GalleryQuestion) { Cell cell = checkColumnsParseAnswerSet(); + GalleryQuestion gallery = (GalleryQuestion) question; if (answerSet == null) { String v = answerrow.get(answerrowcounter++); @@ -823,17 +826,20 @@ private void parseAnswerSet(AnswerSet answerSet, List answerrow, Publica StringBuilder cellValue = new StringBuilder(); boolean first = true; for (Answer answer : answers) { - - if (answer.getValue() != null && answer.getValue().length() > 0) { - int index = Integer.parseInt(answer.getValue()); - if (!first) - cellValue.append(", "); - try { - cellValue.append(((GalleryQuestion) question).getFiles().get(index).getName()); - first = false; - } catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - } + File file = null; + if (!StringUtils.isNullOrEmpty(answer.getPossibleAnswerUniqueId())) { + file = gallery.getFileByUid(answer.getPossibleAnswerUniqueId()); + } else if (!StringUtils.isNullOrEmpty(answer.getValue())) { + file = gallery.getAllFiles().get(Integer.parseInt(answer.getValue())); + } + + if (!first) + cellValue.append(", "); + try { + cellValue.append(file.getName()); + first = false; + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); } } cell.setCellValue(cellValue.toString()); @@ -1273,26 +1279,26 @@ void exportStatistics() throws Exception { CreateTableForAnswer(cellValue, boldstyle); GalleryQuestion galleryQuestion = (GalleryQuestion) question; - for (int i = 0; i < galleryQuestion.getFiles().size(); i++) { + for (File file : galleryQuestion.getAllFiles()) { row = sheet.createRow(rowIndex++); - cellValue = ConversionTools.removeHTMLNoEscape(galleryQuestion.getFiles().get(i).getName()); + cellValue = ConversionTools.removeHTMLNoEscape(file.getName()); row.createCell(0).setCellValue(cellValue); Double percent = statistics.getRequestedRecordsPercent() - .get(galleryQuestion.getId().toString() + "-" + i); + .get(galleryQuestion.getId().toString() + "-" + file.getUid()); if (percent > 0) { drawChart(percent, helper, drawing); } row.createCell(2).setCellValue( - statistics.getRequestedRecords().get(galleryQuestion.getId().toString() + "-" + i)); + statistics.getRequestedRecords().get(galleryQuestion.getId().toString() + "-" + file.getUid())); Cell pcell = row.createCell(3); pcell.setCellValue(statistics.getRequestedRecordsPercent() - .get(galleryQuestion.getId().toString() + "-" + i) / 100); + .get(galleryQuestion.getId().toString() + "-" + file.getUid()) / 100); pcell.setCellStyle(percentStyle); } diff --git a/src/main/webapp/WEB-INF/Content/createIncident.xml b/src/main/webapp/WEB-INF/Content/createIncident.xml index 6eb31659e..7f861845b 100644 --- a/src/main/webapp/WEB-INF/Content/createIncident.xml +++ b/src/main/webapp/WEB-INF/Content/createIncident.xml @@ -4,9 +4,9 @@ - 3 + 2 DIGIT EUSURVEY SUPPORT - SURVEYS + SMT [MESSAGE] User name: [ADDITIONALINFOUSERNAME] @@ -15,15 +15,16 @@ Survey title: [ADDITIONALINFOSURVEYTITLE] Survey alias: [ADDITIONALINFOSURVEYALIAS] + ZZZ_EXTERNAL USER EXTERNE - EXTERNE + [SUBJECT] Assigned [REASON] E-MAIL OTHER - 3 + 2 diff --git a/src/main/webapp/WEB-INF/classes/messages_en.properties b/src/main/webapp/WEB-INF/classes/messages_en.properties index 4e09286c8..9d3661446 100644 --- a/src/main/webapp/WEB-INF/classes/messages_en.properties +++ b/src/main/webapp/WEB-INF/classes/messages_en.properties @@ -368,9 +368,11 @@ info.ColumnSpan = It is possible for a cell to span across multiple columns. Ple info.ConfirmExplanationDeletion = By removing an answer, the corresponding explanation text, possibly uploaded files and discussion will be deleted. info.ConfirmationFileInfo = Additional confirmation document info.ConfirmationPage = This page is displayed when a contribution has been successfully submitted. +info.ConfirmationMarkUpPage = This page is displayed when a contribution has been submitted. You can customize a feedback to your survey participants by using the available metadata and data collected.

Metadata: You can include the following: {InvitationNumber}, {ContributionID}, {UserName}, {CreationDate}, {LastUpdate}, {Language}. If the value exists, it will be replaced in the Confirmation page. If not, nothing will be displayed.

Data collected: You can include collected or calculated data in your survey by using its ID this way: {ID10} (ID10 being the Identifier). This will be replaced by the value of the data collected or calculated if it exists. If not, nothing will be displayed. info.ConfirmationTextInfo = Display additional confirmation text in a new dialog info.ContactList = Invite users from your Address Book info.ContactForm = Contact the survey owner here +info.ContactFormClose = You may now close this tab. info.contributionId = In case of token invitations, the Contribution ID is the last part of your invitation link info.Contributions = Specify a set of contributions by selected answers. Only contributions of participants who have submitted some of the selected answers will be published info.ContributionsNew = You can publish all the answers or only some of them. For the latter, only contributions with one or more of the selected answers will be published. @@ -502,6 +504,7 @@ info.Readonly = The participant will not be able to enter an answer. Please do o info.ReadonlyFormula = Survey participant will not be able to enter an answer. info.RecreateStarted2 = The recreation process has been started. You will receive an e-mail when it has finished. info.redirect = You are being redirected to an external page, please wait. +info.ContributionSubmitted = Contribution successfully submitted info.RegEx = A regular expression that is used to validate the input of the text box info.Reminder = Send a reminder to all Form Managers.
Form Managers are privileged users (see the Privileges section) who have received access rights for the Form Management role. info.ReportAbuse = Please refer to our Terms of Service and let us know why you think this survey may violate this agreement. Please note that reporting this survey is strictly confidential. @@ -799,11 +802,14 @@ label.ContinueEditing = Continue Editing label.Contribution = Contribution label.ContributionDate = Contribution Date label.ContributionId = Contribution ID +label.ContributionSavingHint = Please take a moment to save your Contribution ID +label.ContributionSavingExplanation = You may need it in the future (e.g. to edit your contribution). label.ContributionManagement = Contribution Management label.Contributions = Contributions label.ContributionsIncludingDeletedQuestions = Contributions (including deleted questions) label.ContributionsPerUser = Contributions per user label.Copy = Copy +label.CopyContributionID = Copy Contribution ID label.copyElement = copy element label.CopySurvey = Copy Survey label.CopyToClipboard = Copy to Clipboard @@ -972,6 +978,7 @@ label.DisplayProgress = Display progress as a percentage, a ratio or both label.MotivationPopupTrigger = Trigger to display the popup label.MotivationPopupThreshold = Threshold value (expressed as a percentage or minutes) label.MotivationPopupText = Motivation text: +label.MotivationPopupTitle = Motivation popup title: label.DisplayTimeSec = Display time (sec) label.Discussion = Discussion label.Document = Document @@ -1179,6 +1186,7 @@ label.InitialOrder = Initial order is as follows label.closePane = close pane label.hideDateOption = Hide date options label.HideResults = Hide results +label.HideUnusedAnswers = Hide unused answers label.Home = Home label.hours = hours label.Iagree = Yes I agree @@ -2564,6 +2572,7 @@ validation.invalidTableChildren1 = This element cannot be deleted as tables requ validation.invalidTime = Invalid time format. Enter a time in the format 'HH:mm:ss'. validation.invalidURL = This is not a valid URL validation.required = This field is required. +validation.confirmationMarkupError = The metadata or ID '{0}' is not valid. validation.MatrixAnswers = Please specify at least one answer. validation.MatrixQuestions = Please specify at least one question. validation.max255Characters = The text (including HTML code) must have less than 256 characters @@ -2588,6 +2597,7 @@ validation.TableQuestions = Please specify at least one question. validation.TableQuestions1 = Please specify at least two questions. validation.textNotLongEnough = This text is not long enough validation.textTooLong = This text is too long +validation.numberBracketsNotMatching = The number of '{' and '}' in your text is not equal validation.textTooLong5000 = This question type does not allow more than 5000 characters validation.tooManyAnswers = Too many answers selected validation.valueTooBig = This value is too big @@ -2912,6 +2922,7 @@ quiz.equalTo = equal to quiz.other = all other values message.ColumnDeleted = Question answers successfully removed. message.confirmation = Thank you for your contribution. +message.confirmationWithTitle = Contribution successfully submitted

Thank you for your contribution! message.escape = This survey has not yet been published or has already been unpublished in the meantime. message.validateDelete = Your account is about to be deleted. You will receive a confirmation mail with a link. Click on the link to confirm the deletion of your account. label.un.NorthMacedonia = North Macedonia diff --git a/src/main/webapp/WEB-INF/spring/spring-security.xml b/src/main/webapp/WEB-INF/spring/spring-security.xml index 2fbbc0836..f82c721d1 100644 --- a/src/main/webapp/WEB-INF/spring/spring-security.xml +++ b/src/main/webapp/WEB-INF/spring/spring-security.xml @@ -31,6 +31,7 @@ + diff --git a/src/main/webapp/WEB-INF/views/captcha.jsp b/src/main/webapp/WEB-INF/views/captcha.jsp index ff7523acf..3e7a02be2 100644 --- a/src/main/webapp/WEB-INF/views/captcha.jsp +++ b/src/main/webapp/WEB-INF/views/captcha.jsp @@ -1,223 +1,227 @@ -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> -<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> - -<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> - -<%@ include file="captchainternal.jsp" %> - - - - - - +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> +<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> + +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> + +<%@ include file="captchainternal.jsp" %> + + + + + + diff --git a/src/main/webapp/WEB-INF/views/captchainternal.jsp b/src/main/webapp/WEB-INF/views/captchainternal.jsp index b4706b2f2..821aaa53b 100644 --- a/src/main/webapp/WEB-INF/views/captchainternal.jsp +++ b/src/main/webapp/WEB-INF/views/captchainternal.jsp @@ -1,125 +1,127 @@ -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> -<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> - -<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> - - - - - - -
- - - ${form.getMessage("info.verifyhuman")} - - - - - -
- - - -
-
- Captcha Loading - -
- - - - - - - - - - - - - - " style="width: 260px"> - - - - - - - - -
-
- -
- "/>
- - - - ${form.getMessage("info.entertext")} - - - - - - -
- - -
-
- - - -
data-size="compact" data-callback="hidecaptchaerror" data-sitekey="">
- - - -
-
- - - -
${form.getMessage("validation.required")}
-
- -
-
-
- - -
${captchaerror}
-
- - - -
${form.getMessage("message.captchawrongnew")}
-
- -
-
-
- - - - - -
-
-
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> +<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> + +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> + + + + + + +
+ + + ${form.getMessage("info.verifyhuman")} + + + + + +
+ + + +
+
+ Captcha Loading + +
+ + + + + + + + + + + + + + + " style="width: 260px"> + + + + + + + + + +
+
+ +
+ "/>
+ + + + ${form.getMessage("info.entertext")} + + + + + + +
+ + +
+
+ + + +
data-size="compact" data-callback="hidecaptchaerror" data-sitekey="">
+ + + +
+
+ + + +
${form.getMessage("validation.required")}
+
+ +
+
+
+ + +
${captchaerror}
+
+ + + +
${form.getMessage("message.captchawrongnew")}
+
+ +
+
+
+ + + + + +
+
+
diff --git a/src/main/webapp/WEB-INF/views/contributions/edit.jsp b/src/main/webapp/WEB-INF/views/contributions/edit.jsp index ed9f715ee..7d45da262 100644 --- a/src/main/webapp/WEB-INF/views/contributions/edit.jsp +++ b/src/main/webapp/WEB-INF/views/contributions/edit.jsp @@ -365,7 +365,7 @@ - " aria-label="" href="${contextpath}/runner/contactform/${form.survey.shortname}"> + " aria-label="" href="${serverprefix}runner/contactform/${form.survey.shortname}"> diff --git a/src/main/webapp/WEB-INF/views/generic-messages.jsp b/src/main/webapp/WEB-INF/views/generic-messages.jsp index 3dc86a7cd..d78bebb9e 100644 --- a/src/main/webapp/WEB-INF/views/generic-messages.jsp +++ b/src/main/webapp/WEB-INF/views/generic-messages.jsp @@ -377,10 +377,19 @@ function showExportDialogAndFocusEmail(caller) { + $('#ask-export-dialog').find(".foremail").hide(); + $('#ask-export-dialog').find(".forexport").show(); showModalDialog($('#ask-export-dialog'), caller); setTimeout(function() { $('#email').focus(); }, 1000); } + function showAskEmailDialog(caller) + { + $('#ask-export-dialog').find(".foremail").show(); + $('#ask-export-dialog').find(".forexport").hide(); + showModalDialog($('#ask-export-dialog'), caller); + } + var sessiontimeout = ${uisessiontimeout * 60}; var timeoutTime = new Date(); refreshTimeout(); diff --git a/src/main/webapp/WEB-INF/views/home/helpauthors.jsp b/src/main/webapp/WEB-INF/views/home/helpauthors.jsp index 4e555f744..5b406f7c6 100644 --- a/src/main/webapp/WEB-INF/views/home/helpauthors.jsp +++ b/src/main/webapp/WEB-INF/views/home/helpauthors.jsp @@ -58,6 +58,13 @@ #ulContainer { margin-bottom: 50px; } + + figcaption { + font-style: italic; + padding: 2px; + text-align: center; + } + @@ -568,6 +575,42 @@ .

+

+ What is the 'Motivation popup' and how to use it? +

+

+ The 'Motivation popup' is a dialog window which opens to the survey participant during the completion + of the form. It displays a message to motivate the participant to continue his progress. This message + is customisable as well as the time at which the popup is displayed. +

+

+ 'Motivation popup' is available in the survey's 'Properties' under the 'Appearance' tab. +

+
+ +
Motivation popup in the survey's 'Properties'
+
+

+ Once the switch has been activated, the options appear. +

+

+ The 'Trigger' can be based on the progress or a timer. +

+

+ 'Progress' is expressed as a percentage. For instance, 50%. This means that the popup will be displayed + as soon as the survey participant answered 50% of the questions. +

+

+ In case the 'timer' option has been selected, the popup will be displayed after X minutes. X being the + number of minutes specified in the 'Threshold value' field. +

+

+ Finally, the text is customizable using the 'Motivation text' field. +

+
+ +
Motivation popup configuration fields
+

Editing a survey

diff --git a/src/main/webapp/WEB-INF/views/home/helpauthorsde.jsp b/src/main/webapp/WEB-INF/views/home/helpauthorsde.jsp index aff146242..92adc8ea3 100644 --- a/src/main/webapp/WEB-INF/views/home/helpauthorsde.jsp +++ b/src/main/webapp/WEB-INF/views/home/helpauthorsde.jsp @@ -59,6 +59,13 @@ #ulContainer { margin-bottom: 50px; } + + figcaption { + font-style: italic; + padding: 2px; + text-align: center; + } + @@ -722,7 +729,46 @@ .

-

+

+ Was ist das „Motivations-Popup“ und wie wird es verwendet? +

+

+ Das „Motivations-Popup“ ist ein Dialogfenster, welches dem Umfrageteilnehmer während des Ausfüllens + des Formulars angezeigt wird. Es zeigt eine Nachricht an, um den Teilnehmer zu motivieren, seine Arbeit + fortzusetzen. Diese Nachricht ist individuell gestaltbar, ebenso wie der Zeitpunkt, zu dem das + Popup-Fenster angezeigt wird. +

+

+ Das „Motivations-Popup“/ „Motivation popup“ ist in den „Eigenschaften“ der Umfrage in dem Reiter + „Aussehen“ verfügbar. +

+
+ +
Motivations-Popup in den „Eigenschaften“ der Umfrage
+
+

+ Sobald der Schalter aktiviert wurde, erscheinen die Optionen. +

+

+ Der „Trigger”, zur Anzeige des Popups, kann auf dem Fortschritt/ „progress” oder einem Zeitschalter/ + „timer” basieren. +

+

+ Der Fortschritt wird in Prozent angegeben, z.B. 50%. Das bedeutet, dass das „Motivations-Popup“ + angezeigt wird, sobald der Umfrageteilnehmer 50% der Fragen beantwortet hat. +

+

+ Falls die Option „timer” ausgewählt wurde, wird das „Motivations-Popup“ nach X Minuten angezeigt. X ist + die Anzahl der Minuten, die im Feld Schwellenwert/ „Threshold value“ angegeben wurde. +

+

+ Schließlich kann der Text über das Feld „Motivationstext“/ „Motivation text“ angepasst werden. +

+
+ +
Optionen des Motivations-Popups
+
+

Umfrage bearbeiten

diff --git a/src/main/webapp/WEB-INF/views/home/helpauthorsfr.jsp b/src/main/webapp/WEB-INF/views/home/helpauthorsfr.jsp index 120ee8990..fbefc5283 100644 --- a/src/main/webapp/WEB-INF/views/home/helpauthorsfr.jsp +++ b/src/main/webapp/WEB-INF/views/home/helpauthorsfr.jsp @@ -59,6 +59,13 @@ #ulContainer { margin-bottom: 50px; } + + figcaption { + font-style: italic; + padding: 2px; + text-align: center; + } + @@ -753,6 +760,39 @@ .

+

+ Qu'est-ce que la «popup de motivation» et comment l'utiliser ? +

+

+ La «popup de motivation» est une fenêtre pop-up qui s'ouvre au participant à l'enquête pendant qu'il + remplit le formulaire. Elle affiche un message pour motiver le participant à continuer à remplir le + formulaire. Ce message est personnalisable et l'heure à laquelle la popup s'affiche peut également + être configurée. +

+

+ La «popup de motivation» est disponible dans les propriétés de l'enquête sous l'onglet Apparence. +

+
+ +
Motivation popup dans les propriétés de l’enquête
+
+

+ Une fois l'interrupteur activé, les options apparaissent : +

+

+ Le déclencheur peut être basé sur la progression ou sur une minuterie. La progression est exprimée en + pourcentage. Par exemple, 50%. Cela signifie que le popup s'affichera dès que le participant à + l'enquête aura répondu à 50% des questions. Si l'option Minuterie a été sélectionnée, la fenêtre + contextuelle s'affichera après X minutes. X étant le nombre de minutes spécifié dans le champ de + valeur Seuil. +

+

+ Enfin, le texte est personnalisable à l'aide du dernier champ. +

+
+ +
Configuration de la «popup de motivation»
+

Modifier une enquête

diff --git a/src/main/webapp/WEB-INF/views/includes.jsp b/src/main/webapp/WEB-INF/views/includes.jsp index 7c1310b08..68aafb6de 100644 --- a/src/main/webapp/WEB-INF/views/includes.jsp +++ b/src/main/webapp/WEB-INF/views/includes.jsp @@ -313,6 +313,75 @@ }; + var myConfigSettingFull = { + + // Location of TinyMCE script + script_url : '${contextpath}/resources/js/tinymce/tinymce.min.js', + theme : "modern", + entity_encoding : "raw", + element_format : "xhtml", + menubar : false, + statusbar: true, + toolbar : ["bold italic underline strikethrough | undo redo | bullist numlist | link code | fontsizeselect forecolor fontselect fullscreen"], + plugins : "paste link image code textcolor fullscreen", + font_formats: + "Sans Serif=FreeSans, Arial, Helvetica, Tahoma, Verdana, sans-serif;"+ + "Serif=FreeSerif,Times,serif;"+ + "Mono=FreeMono,Courier, mono;", + language : globalLanguage, + image_advtab: true, + width: "510", + entities: "", + content_css : "${contextpath}/resources/css/tinymce.css", + popup_css_add : "${contextpath}/resources/css/tinymcepopup.css", + forced_root_block : '', + resize: true, + browser_spellcheck: true, + + paste_postprocess : function(pl, o) { + o.node.innerHTML = replaceBRs(strip_tags( o.node.innerHTML,'


' )); + }, + + init_instance_callback: function (editor) { + editor.on('Change', function (e) { + try { + $('#savetextbutton').removeAttr('disabled'); + unsavedChanges = true; + } catch (e) {} + }); + }, + + setup : function(ed) { + ed.on('FullscreenStateChanged', function(e) { + if (e.state) + { + $('#tinymceconfpage').css("z-index", "10000"); + $("#editorheader").hide(); + if ($(".btn-primary1").length == 0) + { + $(".mce-i-fullscreen").closest("div").after("") + }; + } else { + $('#tinymceconfpage').css("z-index", ""); + $("#editorheader").show(); + $(".btn-primary1").remove(); + $(".btn-default1").remove(); + } + }); + }, + + relative_urls : false, + remove_script_host : false, + document_base_url : serverPrefix, + default_link_target: "_blank", + anchor_top: false, + anchor_bottom: false, + branding: false, + valid_classes: 'y', + invalid_elements : 'html,head,body' + + }; + var myConfigSetting2Editor = { // Location of TinyMCE script @@ -393,7 +462,11 @@ $(this).tinymce(myConfigSetting2); }); - $('textarea.tinymcealign').each(function(){ + $('textarea.tinymcefullscreen').each(function(){ + $(this).tinymce(myConfigSettingFull); + }); + + $('textarea.tinymcealign').each(function(){ $(this).tinymce(myConfigSetting); }); diff --git a/src/main/webapp/WEB-INF/views/includesrunner.jsp b/src/main/webapp/WEB-INF/views/includesrunner.jsp index 108d46319..311cbaab5 100644 --- a/src/main/webapp/WEB-INF/views/includesrunner.jsp +++ b/src/main/webapp/WEB-INF/views/includesrunner.jsp @@ -100,12 +100,14 @@ var unsavedChangesText = "${form.getMessage("message.UnsavedChanges")}"; var requiredText = "${form.getMessage("validation.required")}"; + var confirmationMarkupError = "${form.getMessage("validation.confirmationMarkupError")}"; var nomatchText = "${form.getMessage("validation.nomatch")}"; var shortnameText = "${form.getMessage("validation.name2")}"; var shortnameText2 = "${form.getMessage("validation.shortname2")}"; var shortnameText3 = "${form.getMessage("validation.shortname3")}"; var textnotlongenoughText = "${form.getMessage("validation.textNotLongEnough")}"; var texttoolongText = "${form.getMessage("validation.textTooLong")}"; + var bracketCountNotMatching = "${form.getMessage("validation.numberBracketsNotMatching")}"; var texttoolong5000Text = "${form.getMessage("validation.textTooLong5000")}"; var invalidnumberText = "${form.getMessage("validation.invalidNumber")}"; var invalidCharacter = "${form.getMessage("validation.invalidCharacter")}"; @@ -186,12 +188,14 @@ var unsavedChangesText = ""; var requiredText = ""; + var confirmationMarkupError = ""; var nomatchText = ""; var shortnameText = ""; var shortnameText2 = ""; var shortnameText3 = ""; var textnotlongenoughText = ""; var texttoolongText = ""; + var bracketCountNotMatching = ""; var texttoolong5000Text = ""; var invalidnumberText = ""; var valuetoosmall = ""; @@ -372,46 +376,43 @@ var attr = $(input).attr('class'); - if (typeof attr !== typeof undefined && attr !== false) { - var classes =attr.split(" "); - var min = 0; - var max = 0; - - for ( var i = 0, l = classes.length; i{ + if (cla.startsWith("min")){ + min = parseInt(cla.substring(3)); + } else if (cla.startsWith("max")){ + max = parseInt(cla.substring(3)); + } + }) + + el.find(".charactercounter").text(cs); if (max > 0 && max - cs < 5) { - $(input).closest(".survey-element").find(".glyphicon-alert").show(); + el.find(".glyphicon-alert").show(); } else { - $(input).closest(".survey-element").find(".glyphicon-alert").hide(); + el.find(".glyphicon-alert").hide(); } if (max > 0 && max - cs < 0) { - $(input).closest(".survey-element").find(".charactercounterdiv").css("color", "#f00"); + el.find(".charactercounterdiv").css("color", "#f00"); } else { - $(input).closest(".survey-element").find(".charactercounterdiv").css("color", "#777"); + el.find(".charactercounterdiv").css("color", "#777"); } if(max > 0 && max - cs <= 0) { - $(input).closest(".survey-element").find(".glyphicon-alert").hide(); - $(input).closest(".survey-element").find(".characterlimitreached").show(); - $(input).closest(".survey-element").find(".charactersused").hide(); + el.find(".glyphicon-alert").hide(); + el.find(".characterlimitreached").show(); + el.find(".charactersused").hide(); } else { - $(input).closest(".survey-element").find(".characterlimitreached").hide(); - $(input).closest(".survey-element").find(".charactersused").show(); + el.find(".characterlimitreached").hide(); + el.find(".charactersused").show(); } } } diff --git a/src/main/webapp/WEB-INF/views/management/edittemplates.jsp b/src/main/webapp/WEB-INF/views/management/edittemplates.jsp index 829980543..05ddc0ba5 100644 --- a/src/main/webapp/WEB-INF/views/management/edittemplates.jsp +++ b/src/main/webapp/WEB-INF/views/management/edittemplates.jsp @@ -72,7 +72,7 @@

- +
@@ -83,11 +83,11 @@ - + - +
 
 
@@ -101,11 +101,11 @@ - + - +
 
 
@@ -134,11 +134,11 @@ - + - +
 
 
@@ -469,7 +469,7 @@ - + @@ -710,7 +710,7 @@ - + @@ -745,11 +745,11 @@ - + - +
 
 
@@ -831,11 +831,11 @@ - + - +
 
 
@@ -850,7 +850,7 @@
- +
@@ -899,11 +899,11 @@ - + - +
 
 
diff --git a/src/main/webapp/WEB-INF/views/management/properties.jsp b/src/main/webapp/WEB-INF/views/management/properties.jsp index eb4e33c29..44eecca29 100644 --- a/src/main/webapp/WEB-INF/views/management/properties.jsp +++ b/src/main/webapp/WEB-INF/views/management/properties.jsp @@ -924,6 +924,16 @@ + + +
+ +
+
+ +
+ +
@@ -1190,7 +1200,7 @@
* -
+
  @@ -1198,8 +1208,8 @@
${form.survey.confirmationPage}
-