branch develop updated (18949e2b -> 0de236cf)
This is an automated email from the git hooks/post-receive script. New change to branch develop in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git from 18949e2b UI des listes de votants pour les sondages restraints new 0de236cf feedback utilisateur (ref #45) The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit 0de236cff0e556d3ecfb8efeace810925c5ca25d Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Mon Jul 24 17:43:13 2017 +0200 feedback utilisateur (ref #45) Summary of changes: .../pollen/rest/api/PollenRestApiApplication.java | 2 + .../rest/api/PollenRestApiRequestFilter.java | 2 + .../pollen/rest/api/beans/Resource64Bean.java | 33 ++-- .../org/chorem/pollen/rest/api/v1/ApiUtils.java | 30 ++++ .../org/chorem/pollen/rest/api/v1/FeedbackApi.java | 29 ++++ .../pollen/rest/api/v1/PollenResourceApi.java | 15 ++ pollen-services/src/main/config/PollenServices.ini | 13 +- .../chorem/pollen/services/bean/FeedbackBean.java | 133 ++++++++++++++++ .../services/config/PollenServicesConfig.java | 9 ++ .../pollen/services/service/FeedbackService.java | 38 +++++ .../pollen/services/service/mail/EmailService.java | 17 +++ .../services/service/mail/FeedbackEmail.java | 72 +++++++++ .../main/resources/email/FeedbackEmail.mustache | 33 ++++ .../main/resources/email/FeedbackEmail_fr.mustache | 32 ++++ .../i18n/pollen-services_en_GB.properties | 3 + .../i18n/pollen-services_fr_FR.properties | 3 + pollen-ui-riot-js/package.json | 2 + pollen-ui-riot-js/src/main/web/i18n.json | 26 +++- pollen-ui-riot-js/src/main/web/index.js | 1 + .../main/web/js/{Choice.js => FeedbackService.js} | 16 +- pollen-ui-riot-js/src/main/web/js/Logger.js | 4 + .../src/main/web/js/ResourceService.js | 5 + .../src/main/web/tag/PollenHeader.tag.html | 17 +++ .../src/main/web/tag/popup/FeedbackModal.tag.html | 169 +++++++++++++++++++++ 24 files changed, 678 insertions(+), 26 deletions(-) copy pollen-services/src/main/java/org/chorem/pollen/services/bean/export/ExportBean.java => pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/beans/Resource64Bean.java (61%) create mode 100644 pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/FeedbackApi.java create mode 100644 pollen-services/src/main/java/org/chorem/pollen/services/bean/FeedbackBean.java create mode 100644 pollen-services/src/main/java/org/chorem/pollen/services/service/FeedbackService.java create mode 100644 pollen-services/src/main/java/org/chorem/pollen/services/service/mail/FeedbackEmail.java create mode 100644 pollen-services/src/main/resources/email/FeedbackEmail.mustache create mode 100644 pollen-services/src/main/resources/email/FeedbackEmail_fr.mustache copy pollen-ui-riot-js/src/main/web/js/{Choice.js => FeedbackService.js} (73%) create mode 100644 pollen-ui-riot-js/src/main/web/tag/popup/FeedbackModal.tag.html -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit 0de236cff0e556d3ecfb8efeace810925c5ca25d Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Mon Jul 24 17:43:13 2017 +0200 feedback utilisateur (ref #45) --- .../pollen/rest/api/PollenRestApiApplication.java | 2 + .../rest/api/PollenRestApiRequestFilter.java | 2 + .../pollen/rest/api/beans/Resource64Bean.java | 37 +++++ .../org/chorem/pollen/rest/api/v1/ApiUtils.java | 30 ++++ .../org/chorem/pollen/rest/api/v1/FeedbackApi.java | 29 ++++ .../pollen/rest/api/v1/PollenResourceApi.java | 15 ++ pollen-services/src/main/config/PollenServices.ini | 13 +- .../chorem/pollen/services/bean/FeedbackBean.java | 133 ++++++++++++++++ .../services/config/PollenServicesConfig.java | 9 ++ .../pollen/services/service/FeedbackService.java | 38 +++++ .../pollen/services/service/mail/EmailService.java | 17 +++ .../services/service/mail/FeedbackEmail.java | 72 +++++++++ .../main/resources/email/FeedbackEmail.mustache | 33 ++++ .../main/resources/email/FeedbackEmail_fr.mustache | 32 ++++ .../i18n/pollen-services_en_GB.properties | 3 + .../i18n/pollen-services_fr_FR.properties | 3 + pollen-ui-riot-js/package.json | 2 + pollen-ui-riot-js/src/main/web/i18n.json | 26 +++- pollen-ui-riot-js/src/main/web/index.js | 1 + .../src/main/web/js/FeedbackService.js | 33 ++++ pollen-ui-riot-js/src/main/web/js/Logger.js | 4 + .../src/main/web/js/ResourceService.js | 5 + .../src/main/web/tag/PollenHeader.tag.html | 17 +++ .../src/main/web/tag/popup/FeedbackModal.tag.html | 169 +++++++++++++++++++++ 24 files changed, 722 insertions(+), 3 deletions(-) diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiApplication.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiApplication.java index d7fa862c..9f1711ae 100644 --- a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiApplication.java +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiApplication.java @@ -18,6 +18,7 @@ import org.chorem.pollen.rest.api.v1.ChoiceApi; import org.chorem.pollen.rest.api.v1.CommentApi; import org.chorem.pollen.rest.api.v1.DocApi; import org.chorem.pollen.rest.api.v1.FavoriteListApi; +import org.chorem.pollen.rest.api.v1.FeedbackApi; import org.chorem.pollen.rest.api.v1.PollApi; import org.chorem.pollen.rest.api.v1.PollenResourceApi; import org.chorem.pollen.rest.api.v1.PollenUserApi; @@ -52,6 +53,7 @@ public class PollenRestApiApplication extends Application { new VoteCountingApi(), new VoteCountingTypeApi(), new VoterListApi(), + new FeedbackApi(), new PollenAuthenticationExceptionMapper(), new PollenInvalidSessionTokenExceptionMapper(), new PollenUnauthorizedExceptionMapper(), diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiRequestFilter.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiRequestFilter.java index 815ccd62..5ee83aa9 100644 --- a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiRequestFilter.java +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiRequestFilter.java @@ -36,6 +36,7 @@ import org.chorem.pollen.services.service.ChoiceService; import org.chorem.pollen.services.service.CommentService; import org.chorem.pollen.services.service.FavoriteListService; import org.chorem.pollen.services.service.FeedService; +import org.chorem.pollen.services.service.FeedbackService; import org.chorem.pollen.services.service.NotificationService; import org.chorem.pollen.services.service.PollService; import org.chorem.pollen.services.service.PollenResourceService; @@ -174,6 +175,7 @@ public class PollenRestApiRequestFilter implements ContainerRequestFilter, Conta ResteasyProviderFactory.pushContext(VoteCountingTypeService.class, serviceContext.newService(VoteCountingTypeService.class)); ResteasyProviderFactory.pushContext(VoteService.class, serviceContext.newService(VoteService.class)); ResteasyProviderFactory.pushContext(PollenUserService.class, serviceContext.newService(PollenUserService.class)); + ResteasyProviderFactory.pushContext(FeedbackService.class, serviceContext.newService(FeedbackService.class)); } private PollenUIContext extractUIContext(ContainerRequestContext context) { diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/beans/Resource64Bean.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/beans/Resource64Bean.java new file mode 100644 index 00000000..ec236f98 --- /dev/null +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/beans/Resource64Bean.java @@ -0,0 +1,37 @@ +package org.chorem.pollen.rest.api.beans; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class Resource64Bean { + + protected String data; + + protected String name; + + protected String contentType; + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } +} diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/ApiUtils.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/ApiUtils.java index 43ced8eb..0c8cd409 100644 --- a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/ApiUtils.java +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/ApiUtils.java @@ -2,6 +2,7 @@ package org.chorem.pollen.rest.api.v1; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; +import org.chorem.pollen.rest.api.beans.Resource64Bean; import org.chorem.pollen.services.PollenTechnicalException; import org.chorem.pollen.services.bean.ResourceFileBean; import org.chorem.pollen.services.bean.export.ExportBean; @@ -9,10 +10,12 @@ import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; import javax.ws.rs.core.Response; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.util.Base64; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -68,4 +71,31 @@ public class ApiUtils { throw new PollenTechnicalException(e); } } + + public static ResourceFileBean resource64ToResourceBean(Resource64Bean resource64Bean) { + try { + + Base64.Decoder decoder = Base64.getDecoder(); + byte[] data = decoder.decode(resource64Bean.getData()); + + java.nio.file.Path tempPath = Files.createTempDirectory("pollen"); + File uploadFile = new File(tempPath.toFile(), resource64Bean.getName()); + InputStream in = new ByteArrayInputStream(data); + Files.copy(in, uploadFile.toPath()); + + ResourceFileBean resourceBean = new ResourceFileBean(); + resourceBean.setFile(uploadFile); + resourceBean.setName(resource64Bean.getName()); + + resourceBean.setContentType(resource64Bean.getContentType()); + resourceBean.setSize(uploadFile.length()); + + + return resourceBean; + + } catch (IOException e) { + throw new PollenTechnicalException(e); + } + + } } diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/FeedbackApi.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/FeedbackApi.java new file mode 100644 index 00000000..bfa724f9 --- /dev/null +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/FeedbackApi.java @@ -0,0 +1,29 @@ +package org.chorem.pollen.rest.api.v1; + +import org.chorem.pollen.services.bean.FeedbackBean; +import org.chorem.pollen.services.service.FeedbackService; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ + +@Path("") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class FeedbackApi { + + @Path("/feedback") + @POST + public boolean addFeedBack(@Context FeedbackService feedbackService, + FeedbackBean feedbackBean) { + return feedbackService.addFeedback(feedbackBean); + } + +} diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenResourceApi.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenResourceApi.java index 8345db5a..4b3dee65 100644 --- a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenResourceApi.java +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenResourceApi.java @@ -22,6 +22,7 @@ package org.chorem.pollen.rest.api.v1; */ import org.chorem.pollen.persistence.entity.PollenResource; +import org.chorem.pollen.rest.api.beans.Resource64Bean; import org.chorem.pollen.services.bean.PollenEntityId; import org.chorem.pollen.services.bean.PollenEntityRef; import org.chorem.pollen.services.bean.ResourceFileBean; @@ -146,6 +147,20 @@ public class PollenResourceApi { return createRef; } + @Path("/resources64") + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public PollenEntityRef<PollenResource> createResource64(@Context PollenResourceService pollenResourceService, + Resource64Bean input) throws InvalidFormException { + + ResourceFileBean resourceBean = ApiUtils.resource64ToResourceBean(input); + + PollenEntityRef<PollenResource> createRef = pollenResourceService.createResource(resourceBean); + + return createRef; + } + @Path("/resources/{resourceId}") @POST @Consumes(MediaType.MULTIPART_FORM_DATA) diff --git a/pollen-services/src/main/config/PollenServices.ini b/pollen-services/src/main/config/PollenServices.ini index 147756c5..93c92d2c 100644 --- a/pollen-services/src/main/config/PollenServices.ini +++ b/pollen-services/src/main/config/PollenServices.ini @@ -170,4 +170,15 @@ defaultValue = "0 0/5 * * * ?" description = pollen.configuration.report.maxScore key = pollen.report.maxScore type = long -defaultValue = 100 \ No newline at end of file +defaultValue = 100 + +[option mailsFeedback] +description = pollen.configuration.feedback.mails +key = pollen.feedback.mails +type = String + +[option localeFeedback] +description = pollen.configuration.feedback.locale +key = pollen.feedback.locale +type = String +defaultValue = en \ No newline at end of file diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/FeedbackBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/FeedbackBean.java new file mode 100644 index 00000000..d76d1c1b --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/FeedbackBean.java @@ -0,0 +1,133 @@ +package org.chorem.pollen.services.bean; + +import org.chorem.pollen.persistence.entity.PollenResource; + +import java.awt.Dimension; +import java.util.Locale; + + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class FeedbackBean { + + protected String description; + + protected String category; + + protected String browser; + + protected String operatingSystem; + + protected String platform; + + protected Dimension screenResolution; + + protected Dimension windowDimension; + + protected Locale locale; + + protected String location; + + protected String locationTitle; + + protected PollenEntityId<PollenResource> screenShotId; + + protected String consoleHistory; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getBrowser() { + return browser; + } + + public void setBrowser(String browser) { + this.browser = browser; + } + + public String getOperatingSystem() { + return operatingSystem; + } + + public void setOperatingSystem(String operatingSystem) { + this.operatingSystem = operatingSystem; + } + + public String getPlatform() { + return platform; + } + + public void setPlatform(String platform) { + this.platform = platform; + } + + public Dimension getScreenResolution() { + return screenResolution; + } + + public void setScreenResolution(Dimension screenResolution) { + this.screenResolution = screenResolution; + } + + public Dimension getWindowDimension() { + return windowDimension; + } + + public void setWindowDimension(Dimension windowDimension) { + this.windowDimension = windowDimension; + } + + public Locale getLocale() { + return locale; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getLocationTitle() { + return locationTitle; + } + + public void setLocationTitle(String locationTitle) { + this.locationTitle = locationTitle; + } + + public PollenEntityId<PollenResource> getScreenShotId() { + return screenShotId; + } + + public void setScreenShotId(PollenEntityId<PollenResource> screenShotId) { + this.screenShotId = screenShotId; + } + + public String getConsoleHistory() { + return consoleHistory; + } + + public void setConsoleHistory(String consoleHistory) { + this.consoleHistory = consoleHistory; + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java b/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java index c1c4461f..bca8228d 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java @@ -37,6 +37,7 @@ import org.nuiton.config.ApplicationConfig; import org.nuiton.config.ArgumentsParserException; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Properties; @@ -138,4 +139,12 @@ public class PollenServicesConfig extends GeneratedPollenServicesConfig { public Boolean getDefaultVoteNotification() { return Boolean.valueOf(get().getOption(PollenServicesConfigOption.DEFAULT_VOTE_NOTIFICATION.getKey())); } + + public List<String> getMailsFeedbackList() { + return Lists.newArrayList(get().getOption(PollenServicesConfigOption.MAILS_FEEDBACK.getKey()).split(",")); + } + + public Locale getFeedbackLocale() { + return Locale.forLanguageTag(get().getOption(PollenServicesConfigOption.LOCALE_FEEDBACK.getKey())); + } } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/FeedbackService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/FeedbackService.java new file mode 100644 index 00000000..cb769280 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/FeedbackService.java @@ -0,0 +1,38 @@ +package org.chorem.pollen.services.service; + +import org.chorem.pollen.persistence.entity.PollenUser; +import org.chorem.pollen.services.bean.FeedbackBean; +import org.chorem.pollen.services.bean.PollenUserBean; +import org.chorem.pollen.services.service.mail.EmailService; +import org.chorem.pollen.services.service.mail.FeedbackEmail; + +import java.util.List; +import java.util.Locale; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class FeedbackService extends PollenServiceSupport { + + public boolean addFeedback(FeedbackBean feedbackBean) { + List<String> mailsFeedbackList = getPollenServiceConfig().getMailsFeedbackList(); + Locale locale = getPollenServiceConfig().getFeedbackLocale(); + + PollenUser user = getConnectedUser(); + PollenUserBean userBean = null; + if (user != null) { + userBean = new PollenUserBean(); + userBean.fromEntity(user); + } + + + EmailService emailService = getEmailService(); + FeedbackEmail feedbackEMail = emailService.newFeedbackMail(feedbackBean, userBean, locale); + + feedbackEMail.getTos().addAll(mailsFeedbackList); + emailService.send(feedbackEMail); + + return true; + } + +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/EmailService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/EmailService.java index dfd31ced..f1dbe453 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/EmailService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/EmailService.java @@ -45,7 +45,9 @@ import org.chorem.pollen.persistence.entity.PollenResource; import org.chorem.pollen.persistence.entity.PollenUser; import org.chorem.pollen.persistence.entity.Report; import org.chorem.pollen.persistence.entity.Vote; +import org.chorem.pollen.services.bean.FeedbackBean; import org.chorem.pollen.services.bean.PollenEntityId; +import org.chorem.pollen.services.bean.PollenUserBean; import org.chorem.pollen.services.config.PollenServicesConfig; import org.chorem.pollen.services.service.PollenServiceSupport; import org.nuiton.topia.persistence.BeanTopiaConfiguration; @@ -558,4 +560,19 @@ public class EmailService extends PollenServiceSupport { } return value; } + + public FeedbackEmail newFeedbackMail(FeedbackBean feedbackBean, PollenUserBean user, Locale locale) { + FeedbackEmail email = new FeedbackEmail(locale); + email.setUser(user); + email.setFeedback(feedbackBean); + email.setFeedbackDate(getNow()); + if (feedbackBean.getScreenShotId() != null) { + String screenShotUrl = getPollenUIUrlRenderService().getResourceUrl( + getUIContext().getResourceUrl(), + feedbackBean.getScreenShotId().getReducedId()); + email.setScreenShotUrl(screenShotUrl); + } + email.setApplicationVersion(getPollenServiceConfig().getVersion()); + return email; + } } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/FeedbackEmail.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/FeedbackEmail.java new file mode 100644 index 00000000..9e341651 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/FeedbackEmail.java @@ -0,0 +1,72 @@ +package org.chorem.pollen.services.service.mail; + +import org.chorem.pollen.services.bean.FeedbackBean; +import org.chorem.pollen.services.bean.PollenUserBean; +import org.nuiton.i18n.I18n; + +import java.util.Date; +import java.util.Locale; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class FeedbackEmail extends PollenMail { + + protected PollenUserBean user; + + protected FeedbackBean feedback; + + protected Date feedbackDate; + + protected String applicationVersion; + private String screenShotUrl; + + public FeedbackEmail(Locale locale) { + super(locale); + } + + @Override + public String getSubject() { + return I18n.l(locale, "pollen.service.mail.feedbackEmail.subject", feedback.getCategory()); + } + + public PollenUserBean getUser() { + return user; + } + + public void setUser(PollenUserBean user) { + this.user = user; + } + + public FeedbackBean getFeedback() { + return feedback; + } + + public void setFeedback(FeedbackBean feedback) { + this.feedback = feedback; + } + + public void setFeedbackDate(Date feedbackDate) { + this.feedbackDate = feedbackDate; + } + + public String getFeedBackDate() { + return formatDate(feedbackDate); + } + + public String getApplicationVersion() { + return applicationVersion; + } + + public void setApplicationVersion(String applicationVersion) { + this.applicationVersion = applicationVersion; + } + + public void setScreenShotUrl(String screenShotUrl) { + this.screenShotUrl = screenShotUrl; + } + + public String getScreenShotUrl() { + return screenShotUrl; + } +} diff --git a/pollen-services/src/main/resources/email/FeedbackEmail.mustache b/pollen-services/src/main/resources/email/FeedbackEmail.mustache new file mode 100644 index 00000000..8aa5d042 --- /dev/null +++ b/pollen-services/src/main/resources/email/FeedbackEmail.mustache @@ -0,0 +1,33 @@ +On {{feedbackDate}} + +Description +----------- +{{feedback.description}} + + +Location +-------- +Page title : {{feedback.locationTitle}} +URL : {{feedback.location}} + +Environment +----------- +Browser : {{feedback.browser}} +Operating System : {{feedback.operatingSystem}} +Platform : {{feedback.platform}} +Screen resolution : {{feedback.screenResolution.width}} x {{feedback.screenResolution.height}} (L x H) +window resolution : {{feedback.windowDimension.width}} x {{feedback.windowDimension.height}} (L x H) +User : {{#user}}{{user.name}} ({{user.email}}){{/user}}{{^user}}Anonymous{{/user}} +Locale : {{feedback.locale}} + +Services version: {{applicationVersion}} + +{{#screenShotUrl}} +Screen shot +----------- +{{screenShotUrl}} +{{/screenShotUrl}} + +Console history +--------------- +{{{feedback.consoleHistory}}} diff --git a/pollen-services/src/main/resources/email/FeedbackEmail_fr.mustache b/pollen-services/src/main/resources/email/FeedbackEmail_fr.mustache new file mode 100644 index 00000000..ae33ed8a --- /dev/null +++ b/pollen-services/src/main/resources/email/FeedbackEmail_fr.mustache @@ -0,0 +1,32 @@ +Le {{feedbackDate}} + +Description +----------- +{{feedback.description}} + + +Emplacement +----------- +Titre de la page : {{feedback.locationTitle}} +URL : {{feedback.location}} + +Environnement +------------- +Navigateur : {{feedback.browser}} +Système d'exploitation : {{feedback.operatingSystem}} +Plateforme : {{feedback.platform}} +Résolution : {{feedback.screenResolution.width}} x {{feedback.screenResolution.height}} (L x H) +Taille d'affichage : {{feedback.windowDimension.width}} x {{feedback.windowDimension.height}} (L x H) +Langue : {{feedback.locale}} +Utilisateur : {{#user}}{{user.name}} ({{user.email}}){{/user}}{{^user}}Anonyme{{/user}} +Version services : {{applicationVersion}} +{{#screenShotUrl}} + + Capture d'écran +--------------- +{{screenShotUrl}} +{{/screenShotUrl}} + +Historique de la console +------------------------ +{{{feedback.consoleHistory}}} \ No newline at end of file diff --git a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties index 0b78d8bd..1ba0fd60 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties @@ -19,6 +19,8 @@ pollen.configuration.defaultVoteCountingType=Default vote counting type used whe pollen.configuration.defaultVoteNotification=Default notification type for the votes of a poll pollen.configuration.defaultVoteVisibility=Default vote visiblity pollen.configuration.devMode=Dev mode +pollen.configuration.feedback.locale=locale to send feedback +pollen.configuration.feedback.mails=mails to send feedback pollen.configuration.logConfigurationFile=Path to log configuration file pollen.configuration.registration.emailAddressPattern=Regular expression that the user email address must match for registration pollen.configuration.report.maxScore=Maximum score for reporting before administrators are notified @@ -156,3 +158,4 @@ pollen.service.mail.VoteAddedEmail.subject=[Pollen] A vote was added in poll %s pollen.service.mail.VoteDeletedEmail.subject=[Pollen] A vote was deleted in poll %s pollen.service.mail.VoteEditedEmail.subject=[Pollen] A vote was edited in poll %s pollen.service.mail.VoteSummaryEmail.subject=[Pollen] Summary of the votes since %s for the poll %s +pollen.service.mail.feedbackEmail.subject=[Pollen] feedback - %s diff --git a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties index 2675ce7c..497bc4ab 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties @@ -19,6 +19,8 @@ pollen.configuration.defaultVoteCountingType=Type de dépouillement par défaut pollen.configuration.defaultVoteNotification=Type de notification par défaut pour les votes pollen.configuration.defaultVoteVisibility=Visibilité des votes par défaut pollen.configuration.devMode=Mode développement +pollen.configuration.feedback.locale=La locale pour envoyer les retours utlisateur +pollen.configuration.feedback.mails=Courriel destinataires des retours utilisateur pollen.configuration.logConfigurationFile=Chemin vers le fichier de configuration des logs pollen.configuration.registration.emailAddressPattern=Expression régulière que doivent vérifier les adresses email des utilisateurs lors de l'inscription pollen.configuration.report.maxScore=Score maximum pour un signalement avant que les administrateurs soient avertis @@ -154,3 +156,4 @@ pollen.service.mail.UserAccountPasswordChangedEmail.subject=[Pollen] Confirmatio pollen.service.mail.VoteAddedEmail.subject=[Pollen] Un nouveau vote a été ajouté au sondage %s pollen.service.mail.VoteDeletedEmail.subject=[Pollen] Un vote a été supprimé sur le sondage %s pollen.service.mail.VoteEditedEmail.subject=[Pollen] Un vote a été modifié du sondage %s +pollen.service.mail.feedbackEmail.subject=[Pollen] retour utilisateur - %s diff --git a/pollen-ui-riot-js/package.json b/pollen-ui-riot-js/package.json index 82231d23..27c1099c 100644 --- a/pollen-ui-riot-js/package.json +++ b/pollen-ui-riot-js/package.json @@ -42,7 +42,9 @@ "webpack-dev-server": "^2.5.1" }, "dependencies": { + "console.history": "^1.5.0", "font-awesome": "4.7.0", + "html2canvas": "^0.5.0-beta4", "moment": "^2.17.1", "nprogress": "^0.2.0", "object.values": "^1.0.4", diff --git a/pollen-ui-riot-js/src/main/web/i18n.json b/pollen-ui-riot-js/src/main/web/i18n.json index 8c56ba3d..e75d1078 100644 --- a/pollen-ui-riot-js/src/main/web/i18n.json +++ b/pollen-ui-riot-js/src/main/web/i18n.json @@ -224,6 +224,7 @@ "header_myFavoriteLists": "Mes listes de favoris", "header_createOtherPoll": "Nouveau sondage standard", "header_createDatePoll": "Nouveau sondage de dates", + "header_feedback": "Un bug, une remarque ?", "poll_cancel": "Annuler", "poll_description_next": "Suivant", "poll_description_info": "Informations", @@ -521,7 +522,17 @@ "report_reports_title": "Signalements", "report_toIgnore": "Ignorer", "report_toEnable": "Ré-activer", - "report_ignores": "Ignorés" + "report_ignores": "Ignorés", + "feedback_title": "Envoyer un bug, une remarque", + "feedback_detail": "Si vous voyez un bug ou si vous avez simplement une remarque à faire, remplissez ce formulaire pour nous faire parvenir votre avis. N'hésitez pas à en abuser !", + "feedback_category": "Catégorie", + "feedback_category_bug": "Bug", + "feedback_category_ergonomics": "Ergonomie", + "feedback_category_other": "Autres", + "feedback_description": "Merci de détailler ci-dessous votre rapport de bug ou votre remarque", + "feedback_descriptionNotBlank": "La description ne doit pas être blanc", + "feedback_uploadScreenShot": "Copie d'écran", + "feedback_create_success": "La remarque est envoyée." }, "en": { "main_pollen_title": "Pollen - ", @@ -739,6 +750,7 @@ "header_myPolls": "My polls", "header_myInvitedPolls": "Invited polls", "header_myParticipatedPolls": "Participated polls", + "header_feedback": "Bug, observation ?", "header_polls": "Polls", "header_users": "Users", "header_myFavoriteLists": "My favorite lists", @@ -1024,6 +1036,16 @@ "report_reports_title": "Reports", "report_toIgnore": "Ignore", "report_toEnable": "Enable", - "report_ignores": "Ignores" + "report_ignores": "Ignores", + "feedback_title": "Send a bug, an observation", + "feedback_detail": "If you see a bug or if you have an obvervation to do, set this form for send your opinon. Do not hesitate to abuse it !", + "feedback_category": "Category", + "feedback_category_bug": "Bug", + "feedback_category_ergonomics": "Ergonomics", + "feedback_category_other": "Others", + "feedback_description": "Thank to details below your bug report ou your observation", + "feedback_descriptionNotBlank": "Observation must be not blank", + "feedback_uploadScreenShot": "Screenshot", + "feedback_create_success": "Observation has benn send." } } diff --git a/pollen-ui-riot-js/src/main/web/index.js b/pollen-ui-riot-js/src/main/web/index.js index 1a76a90c..30945fa3 100644 --- a/pollen-ui-riot-js/src/main/web/index.js +++ b/pollen-ui-riot-js/src/main/web/index.js @@ -18,6 +18,7 @@ // * along with this program. If not, see <http://www.gnu.org/licenses/>. // * #L% // */ +require("console.history"); riot.mixin(require("./js/I18nHelper")); riot.mixin(require("./js/UIHelper")); riot.mixin({logger: require("./js/Logger")}); diff --git a/pollen-ui-riot-js/src/main/web/js/FeedbackService.js b/pollen-ui-riot-js/src/main/web/js/FeedbackService.js new file mode 100644 index 00000000..9678daf3 --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/js/FeedbackService.js @@ -0,0 +1,33 @@ +/*- + * #%L + * Pollen :: UI (Riot Js) + * %% + * Copyright (C) 2009 - 2017 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +let singleton = require("./Singleton"); +let FetchService = require("./FetchService"); + +class FeedbackService extends FetchService { + + createfeedback(form) { + let url = "/v1/feedback"; + return this.post(url, form); + } + +} + +module.exports = singleton(FeedbackService); diff --git a/pollen-ui-riot-js/src/main/web/js/Logger.js b/pollen-ui-riot-js/src/main/web/js/Logger.js index 3ce2f687..acb186e2 100644 --- a/pollen-ui-riot-js/src/main/web/js/Logger.js +++ b/pollen-ui-riot-js/src/main/web/js/Logger.js @@ -14,5 +14,9 @@ module.exports = { error(message) { console.error(message); + }, + + getHistory() { + return console.history; } }; diff --git a/pollen-ui-riot-js/src/main/web/js/ResourceService.js b/pollen-ui-riot-js/src/main/web/js/ResourceService.js index 42521b84..8799faf4 100644 --- a/pollen-ui-riot-js/src/main/web/js/ResourceService.js +++ b/pollen-ui-riot-js/src/main/web/js/ResourceService.js @@ -37,6 +37,11 @@ class ResourceService extends FetchService { return this.form(url, {resource: resource}, true); } + create64(resource64) { + let url = "/v1/resources64"; + return this.post(url, resource64); + } + getResource(resourceId) { let url = this._getUrlPrefix(resourceId); return this.get(url); diff --git a/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html b/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html index 7a434820..14e3003e 100644 --- a/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html @@ -19,6 +19,7 @@ * #L% */ require("./HeaderI18n.tag.html"); +require("./popup/FeedbackModal.tag.html"); <PollenHeader> <a class="header-home instance-title" href="#home" target="_top"></a> @@ -58,6 +59,13 @@ require("./HeaderI18n.tag.html"); <HeaderI18n/> + <a class="header-link" onclick="{openFeedback}" title={__.feedback}> + <i class="fa fa-exclamation" aria-hidden="true"></i> + <i class="fa fa-question" aria-hidden="true"></i> + </a> + + <feedbackModal ref="feedbackModal"> + </div> <script type="es6"> @@ -90,6 +98,15 @@ require("./HeaderI18n.tag.html"); this.listen("user", this.onUserChange); + this.openFeedback = () => { + this.refs.feedbackModal.open().then(() => { + this.update(); + }, () => { + this.update(); + }); + this.update(); + }; + </script> <style> diff --git a/pollen-ui-riot-js/src/main/web/tag/popup/FeedbackModal.tag.html b/pollen-ui-riot-js/src/main/web/tag/popup/FeedbackModal.tag.html new file mode 100644 index 00000000..370ea216 --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/popup/FeedbackModal.tag.html @@ -0,0 +1,169 @@ +require("./Modal.tag.html"); + +<FeedbackModal> + + <modal header={__.title} + onsubmit={sendFeedback} + ref="modal"> + + {parent.__.detail} + + <div class="o-fieldset categories"> + <legend class="o-fieldset__legend">{parent.__.category}</legend> + <label class="c-field c-field--choice"> + <input type="radio" + name="category" + ref="category" + required + value="BUG"> + {parent.__.category_bug} + </label> + <label class="c-field c-field--choice"> + <input type="radio" + name="category" + ref="category" + value="ERGONOMICS"> + {parent.__.category_ergonomics} + </label> + <label class="c-field c-field--choice"> + <input type="radio" + name="category" + ref="category" + value="OTHER"> + {parent.__.category_other} + </label> + </div> + <div class="o-form-element"> + <label class="c-label" for="description">{parent.__.description}</label> + <textarea class="c-field" + name="description" + ref="description" + pattern=".*[^\s]+.*" + title={parent.__.descriptionNotBlank} + required> + </textarea> + </div> + + <div class="o-form-element"> + <label class="c-toggle c-toggle--info"> + {parent.__.uploadScreenShot} + <input type="checkbox" + name="uploadScreenShot" + ref="uploadScreenShot"> + <div class="c-toggle__track"> + <div class="c-toggle__handle"></div> + </div> + </label> + </div> + + </modal> + + <script type="es6"> + let session = require("../../js/Session"); + let Message = require("../../js/Message"); + this.installBundle(session, "feedback"); + let resourceService = require("../../js/ResourceService"); + let feedbackService = require("../../js/FeedbackService"); + let html2canvas = require("html2canvas"); + + this.open = () => { + this.refs.modal.refs.category.forEach(input => {input.checked = false;}); + this.refs.modal.refs.description.value = ""; + this.refs.modal.refs.uploadScreenShot.checked = true; + return this.refs.modal.open(); + }; + + + this.getBrowserNameAndVersion = () => { + let ua = navigator.userAgent, tem, + match = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*([\d\.]+)/i) || []; + if (/trident/i.test(match[1])) { + tem = /\brv[ :]+(\d+(\.\d+)?)/g.exec(ua) || []; + return "IE " + (tem[1] || ""); + } + match = match[2] ? [match[1], match[2]] : [navigator.appName, navigator.appVersion, "-?"]; + if ((tem = ua.match(/version\/([\.\d]+)/i)) !== null) { + match[2] = tem[1]; + } + return match.join(" "); + }; + + this.getOperatingSystemNameAndVersion = () => { + var OSName; + if (navigator.userAgent.indexOf("Windows NT 6.3") !== -1) {OSName = "Windows 8.1";} + if (navigator.userAgent.indexOf("Windows NT 6.2") !== -1) {OSName = "Windows 8";} + if (navigator.userAgent.indexOf("Windows NT 6.1") !== -1) {OSName = "Windows 7";} + if (navigator.userAgent.indexOf("Windows NT 6.0") !== -1) {OSName = "Windows Vista";} + if (navigator.userAgent.indexOf("Windows NT 5.1") !== -1) {OSName = "Windows XP";} + if (navigator.userAgent.indexOf("Windows NT 5.0") !== -1) {OSName = "Windows 2000";} + if (navigator.userAgent.indexOf("Mac") !== -1) {OSName = "Mac/iOS";} + if (navigator.userAgent.indexOf("X11") !== -1) {OSName = "UNIX";} + if (navigator.userAgent.indexOf("Linux") !== -1) {OSName = "Linux";} + return OSName; + }; + + this.sendFeedback = () => { + let promiseScreenshot; + if (this.refs.modal.refs.uploadScreenShot.checked) { + // take screenshot + this.refs.modal.openModal = false; + this.update(); + promiseScreenshot = html2canvas(document.body).then(canvas => { + let type = "image/jpeg"; + let screenshot = canvas.toDataURL(type); + let data = screenshot.replace("data:" + type + ";base64,", ""); + let resource64 = { + data: data, + name: "screenShot.jpeg", + contentType: type + }; + return resourceService.create64(resource64); + }); + } else { + promiseScreenshot = Promise.resolve(); + } + return promiseScreenshot.then(screenshotId => { + let feedback = { + description: this.refs.modal.refs.description.value, + category: this.refs.modal.refs.category.find(radio => radio.checked).value, + browser: this.getBrowserNameAndVersion(), + operatingSystem: this.getOperatingSystemNameAndVersion(), + platform: navigator.platform, + screenResolution: {width: screen.width, height: screen.height}, + windowDimension: {width: window.innerWidth, height: window.innerHeight}, + locale: navigator.language, + location: window.location.href, + locationTitle: window.document.title, + screenShotId: screenshotId.id, + consoleHistory: JSON.stringify(this.logger.getHistory(), null, 2) + }; + return feedbackService.createfeedback(feedback).then(() => { + this.bus.trigger("message", this.__.create_success, "success"); + }, (error) => { + this.refs.modal.openModal = true; + this.update(); + return Promise.reject(error); + }); + }); + }; + + </script> + + <style> + .categories { + display: flex; + flex-wrap: nowrap; + flex-direction: row; + } + + .categories > * { + width: auto; + } + + .categories .o-fieldset__legend { + padding: 0.5em; + } + + </style> + +</FeedbackModal> -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
participants (1)
-
chorem.org scm