This is an automated email from the git hooks/post-receive script. New commit to branch feature/14_add_rss_feed in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit a8face4b03d26cb2d2b2a6edc69bd805f5e93533 Author: Kevin Morin <morin@codelutin.com> Date: Fri May 12 09:26:40 2017 +0200 refs #14 création du flux atom à la création du sondage + ajout des entrées dans le flux pour l'ajout, la suppression et la modification d'un vote + ajout du bouton pour lire le flux à partir du sondage --- pollen-persistence/src/main/xmi/pollen.properties | 1 + pollen-persistence/src/main/xmi/pollen.zargo | Bin 24937 -> 25019 bytes .../org/chorem/pollen/rest/api/v1/PollApi.java | 6 ++ pollen-rest-api/src/main/resources/mapping | 2 + pollen-services/pom.xml | 6 +- .../pollen/services/service/FeedService.java | 116 +++++++++++++++++++++ .../pollen/services/service/PollService.java | 1 + .../services/service/PollenServiceSupport.java | 28 +++++ .../pollen/services/service/VoteService.java | 3 + .../pollen/services/service/mail/EmailService.java | 35 +------ .../i18n/pollen-services_en_GB.properties | 5 + .../i18n/pollen-services_fr_FR.properties | 5 + .../src/main/web/tag/poll/Poll.tag.html | 3 +- pom.xml | 8 +- 14 files changed, 181 insertions(+), 38 deletions(-) diff --git a/pollen-persistence/src/main/xmi/pollen.properties b/pollen-persistence/src/main/xmi/pollen.properties index e074670..3dccb21 100644 --- a/pollen-persistence/src/main/xmi/pollen.properties +++ b/pollen-persistence/src/main/xmi/pollen.properties @@ -27,6 +27,7 @@ model.tagValue.hibernateAttributeType.String=string package.org.chorem.pollen.persistence.entity.stereotype=entity # Text org.chorem.pollen.persistence.entity.Poll.attribute.description.tagValue.hibernateAttributeType=text +org.chorem.pollen.persistence.entity.Poll.attribute.feedContent.tagValue.hibernateAttributeType=text org.chorem.pollen.persistence.entity.Choice.attribute.description.tagValue.hibernateAttributeType=text org.chorem.pollen.persistence.entity.Comment.attribute.text.tagValue.hibernateAttributeType=text # Blob diff --git a/pollen-persistence/src/main/xmi/pollen.zargo b/pollen-persistence/src/main/xmi/pollen.zargo index 6f6fdb3..aa99064 100644 Binary files a/pollen-persistence/src/main/xmi/pollen.zargo and b/pollen-persistence/src/main/xmi/pollen.zargo differ diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollApi.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollApi.java index 732839b..0c25748 100644 --- a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollApi.java +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollApi.java @@ -32,9 +32,11 @@ import org.chorem.pollen.services.bean.PollenEntityId; import org.chorem.pollen.services.bean.PollenEntityRef; import org.chorem.pollen.services.bean.VoterListBean; import org.chorem.pollen.services.bean.VoterListMemberBean; +import org.chorem.pollen.services.service.FeedService; import org.chorem.pollen.services.service.InvalidFormException; import org.chorem.pollen.services.service.PollService; import org.debux.webmotion.server.WebMotionController; +import org.debux.webmotion.server.render.Render; import java.io.File; import java.util.List; @@ -133,4 +135,8 @@ public class PollApi extends WebMotionController { public PollBean assignPoll(PollService pollService, PollenEntityId<Poll> pollId) { return pollService.assignPollToConnectedUser(pollId.getEntityId()); } + + public Render getFeedForPoll(FeedService feedService, PollenEntityId<Poll> pollId) { + return renderAtom(feedService.getFeedForPoll(pollId.getEntityId())); + } } diff --git a/pollen-rest-api/src/main/resources/mapping b/pollen-rest-api/src/main/resources/mapping index c1a63e2..b434a60 100644 --- a/pollen-rest-api/src/main/resources/mapping +++ b/pollen-rest-api/src/main/resources/mapping @@ -123,6 +123,8 @@ PUT /v1/polls/{pollId}/close PollApi.closePoll PUT /v1/polls/{pollId}/reopen PollApi.reopenPoll PUT /v1/polls/{pollId}/assign PollApi.assignPoll +GET /v1/polls/{pollId}/feed PollApi.getFeedForPoll + # PollenResourceApi GET /v1/resources/{resourceId} PollenResourceApi.getResource diff --git a/pollen-services/pom.xml b/pollen-services/pom.xml index 7fa0d31..2a5151d 100644 --- a/pollen-services/pom.xml +++ b/pollen-services/pom.xml @@ -126,10 +126,10 @@ <artifactId>guava</artifactId> </dependency> - <!--dependency> - <groupId>rome</groupId> + <dependency> + <groupId>com.rometools</groupId> <artifactId>rome</artifactId> - </dependency--> + </dependency> <dependency> <groupId>junit</groupId> diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/FeedService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/FeedService.java new file mode 100644 index 0000000..9623986 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/FeedService.java @@ -0,0 +1,116 @@ +package org.chorem.pollen.services.service; + +import com.rometools.rome.feed.synd.SyndContent; +import com.rometools.rome.feed.synd.SyndContentImpl; +import com.rometools.rome.feed.synd.SyndEntry; +import com.rometools.rome.feed.synd.SyndEntryImpl; +import com.rometools.rome.feed.synd.SyndFeed; +import com.rometools.rome.feed.synd.SyndFeedImpl; +import com.rometools.rome.io.FeedException; +import com.rometools.rome.io.SyndFeedInput; +import com.rometools.rome.io.SyndFeedOutput; +import org.chorem.pollen.persistence.entity.Poll; +import org.chorem.pollen.persistence.entity.Vote; +import org.chorem.pollen.services.PollenTechnicalException; +import org.chorem.pollen.services.service.security.PermissionVerb; + +import java.io.StringReader; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.nuiton.i18n.I18n.n; +import static org.nuiton.i18n.I18n.t; + +/** + * @author Kevin Morin (Code Lutin) + * @since 3.0 + */ +public class FeedService extends PollenServiceSupport { + + public static final String FEED_TYPE = "atom_1.0"; + public static final String DESCRIPTION_TYPE = "text/plain"; + + public void onPollCreated(Poll poll) { + SyndFeed feed = new SyndFeedImpl(); + feed.setFeedType(FEED_TYPE); + + feed.setTitle(poll.getTitle()); + String voteUrl = getPollVoteUrl(poll); + feed.setLink(voteUrl); + feed.setDescription(poll.getDescription()); + + SyndEntry entry = new SyndEntryImpl(); + entry.setTitle(t("pollen.service.feed.pollCreated.title", poll.getCreator().getName(), poll.getTitle())); + entry.setLink(voteUrl); + entry.setPublishedDate(poll.getTopiaCreateDate()); + + SyndContent description = new SyndContentImpl(); + description.setType(DESCRIPTION_TYPE); + description.setValue(t("pollen.service.feed.pollCreated.description", + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(poll.getTopiaCreateDate()), + poll.getCreator().getName(), + poll.getTitle())); + entry.setDescription(description); + + List<SyndEntry> feedEntries = Collections.singletonList(entry); + feed.setEntries(feedEntries); + + saveFeed(poll, feed); + } + + public void onVoteAdded(Poll poll, Vote vote) { + addVoteFeedEntry(poll, vote, n("pollen.service.feed.voteAdded.title")); + } + + public void onVoteEdited(Poll poll, Vote vote) { + addVoteFeedEntry(poll, vote, n("pollen.service.feed.voteEdited.title")); + } + + public void onVoteDeleted(Poll poll, Vote vote) { + addVoteFeedEntry(poll, vote, n("pollen.service.feed.voteDeleted.title")); + } + + public SyndFeed getFeedForPoll(String pollId) { + checkNotNull(pollId); + checkPermission(PermissionVerb.readPoll, pollId); + + Poll poll = getPollService().getPoll0(pollId); + + try { + SyndFeedInput feedInput = new SyndFeedInput(true, getLocale()); + return feedInput.build(new StringReader(poll.getFeedContent())); + + } catch (FeedException e) { + throw new PollenTechnicalException("Error while reading the feed", e); + } + } + + private void addVoteFeedEntry(Poll poll, Vote vote, String messageKey) { + SyndFeed feed = getFeedForPoll(poll.getTopiaId()); + List<SyndEntry> entries = new ArrayList<>(feed.getEntries()); + + SyndEntry entry = new SyndEntryImpl(); + entry.setTitle(t(messageKey, vote.getVoter().getName())); + entry.setLink(getPollVoteUrl(poll)); + entry.setPublishedDate(vote.getTopiaCreateDate()); + + entries.add(entry); + feed.setEntries(entries); + + saveFeed(poll, feed); + } + + private void saveFeed(Poll poll, SyndFeed feed) { + try { + SyndFeedOutput output = new SyndFeedOutput(); + String feedContent = output.outputString(feed); + poll.setFeedContent(feedContent); + commit(); + + } catch (FeedException e) { + throw new PollenTechnicalException("Error while creating the feed", e); + } + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollService.java index 1569927..0618b9a 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollService.java @@ -225,6 +225,7 @@ public class PollService extends PollenServiceSupport { commit(); getNotificationService().onPollCreated(savedPoll); + getFeedService().onPollCreated(savedPoll); return PollenEntityRef.of(savedPoll); diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenServiceSupport.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenServiceSupport.java index f6076a6..62704bd 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenServiceSupport.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenServiceSupport.java @@ -33,6 +33,7 @@ import org.chorem.pollen.persistence.entity.ChoiceTopiaDao; import org.chorem.pollen.persistence.entity.CommentTopiaDao; import org.chorem.pollen.persistence.entity.FavoriteListMemberTopiaDao; import org.chorem.pollen.persistence.entity.FavoriteListTopiaDao; +import org.chorem.pollen.persistence.entity.Poll; import org.chorem.pollen.persistence.entity.PollTopiaDao; import org.chorem.pollen.persistence.entity.PollenPrincipalTopiaDao; import org.chorem.pollen.persistence.entity.PollenResourceTopiaDao; @@ -49,6 +50,7 @@ import org.chorem.pollen.services.PollenUIContext; import org.chorem.pollen.services.bean.PaginationResultBean; import org.chorem.pollen.services.bean.PollenBean; import org.chorem.pollen.services.bean.PollenBeans; +import org.chorem.pollen.services.bean.PollenEntityId; import org.chorem.pollen.services.config.PollenServicesConfig; import org.chorem.pollen.services.service.mail.EmailService; import org.chorem.pollen.services.service.security.PermissionVerb; @@ -128,6 +130,10 @@ public abstract class PollenServiceSupport implements PollenService { return newService(NotificationService.class); } + protected FeedService getFeedService() { + return newService(FeedService.class); + } + protected PollService getPollService() { return newService(PollService.class); } @@ -447,4 +453,26 @@ public abstract class PollenServiceSupport implements PollenService { } + protected <E extends TopiaEntity> PollenEntityId<E> getPollenEntityId(E entity) { + PollenEntityId<E> pollenEntityId = PollenEntityId.newId((Class<E>) entity.getClass()); + pollenEntityId.setEntityId(entity.getTopiaId()); + pollenEntityId.encode(serviceContext.getTopiaApplicationContext().getTopiaIdFactory()); + return pollenEntityId; + } + + protected String getPollVoteUrl(Poll poll) { + return getPollVoteUrl(getPollenEntityId(poll), null); + } + + protected String getPollVoteUrl(Poll poll, String token) { + return getPollVoteUrl(getPollenEntityId(poll), token); + } + + protected String getPollVoteUrl(PollenEntityId<Poll> pollId, String token) { + return getPollenUIUrlRenderService().getPollVoteUrl + (getUIContext().getPollVoteUrl(), + pollId.getReducedId(), + token); + } + } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteService.java index 5d8a368..32c7e5e 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/VoteService.java @@ -145,6 +145,7 @@ public class VoteService extends PollenServiceSupport { commit(); getNotificationService().onVoteAdded(poll, result); + getFeedService().onVoteAdded(poll, result); return PollenEntityRef.of(result); @@ -166,6 +167,7 @@ public class VoteService extends PollenServiceSupport { commit(); getNotificationService().onVoteEdited(poll, result); + getFeedService().onVoteEdited(poll, result); return toBean(VoteBean.class, result); @@ -185,6 +187,7 @@ public class VoteService extends PollenServiceSupport { commit(); getNotificationService().onVoteDeleted(poll, vote); + getFeedService().onVoteDeleted(poll, vote); } 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 a104a06..6555fda 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 @@ -146,10 +146,7 @@ public class EmailService extends PollenServiceSupport { email.setPollenUrl(getUIContext().getUiEndPoint()); - PollenEntityId<Poll> pollId = PollenEntityId.newId(Poll.class); - pollId.setEntityId(poll.getTopiaId()); - pollId.encode(serviceContext.getTopiaApplicationContext().getTopiaIdFactory()); - + PollenEntityId<Poll> pollId = getPollenEntityId(poll); email.setUrl(getPollenUIUrlRenderService().getPollEditUrl(getUIContext().getPollEditUrl(), pollId.getReducedId(), poll.getCreator().getPermission().getToken())); @@ -196,15 +193,7 @@ public class EmailService extends PollenServiceSupport { public PollInvitationEmail newPollInvitationEmail(Poll poll) { PollInvitationEmail email = new PollInvitationEmail(getLocale()); email.setPoll(poll); - - PollenEntityId<Poll> pollId = PollenEntityId.newId(Poll.class); - pollId.setEntityId(poll.getTopiaId()); - pollId.encode(serviceContext.getTopiaApplicationContext().getTopiaIdFactory()); - - email.setVoteUrl(getPollenUIUrlRenderService().getPollVoteUrl - (getUIContext().getPollVoteUrl(), - pollId.getReducedId(), - null)); + email.setVoteUrl(getPollVoteUrl(poll)); return email; } @@ -213,15 +202,7 @@ public class EmailService extends PollenServiceSupport { RestrictedPollInvitationEmail email = new RestrictedPollInvitationEmail(getLocale()); email.setPoll(poll); email.setPrincipal(principal); - - PollenEntityId<Poll> pollId = PollenEntityId.newId(Poll.class); - pollId.setEntityId(poll.getTopiaId()); - pollId.encode(serviceContext.getTopiaApplicationContext().getTopiaIdFactory()); - - email.setVoteUrl(getPollenUIUrlRenderService().getPollVoteUrl - (getUIContext().getPollVoteUrl(), - pollId.getReducedId(), - principal.getPermission().getToken())); + email.setVoteUrl(getPollVoteUrl(poll, principal.getPermission().getToken())); email.setToken(principal.getPermission().getToken()); @@ -234,10 +215,7 @@ public class EmailService extends PollenServiceSupport { email.setPollenUrl(pollenUIContext.getUiEndPoint()); - PollenEntityId<PollenUser> userId = PollenEntityId.newId(PollenUser.class); - userId.setEntityId(user.getTopiaId()); - userId.encode(serviceContext.getTopiaApplicationContext().getTopiaIdFactory()); - + PollenEntityId<PollenUser> userId = getPollenEntityId(user); email.setValidateUrl(getPollenUIUrlRenderService().getUserValidateUrl(pollenUIContext.getUserValidateUrl(), userId.getReducedId(), user.getEmailActivationToken().getToken())); @@ -250,10 +228,7 @@ public class EmailService extends PollenServiceSupport { email.setUser(user); email.setPollenUrl(pollenUIContext.getUiEndPoint()); - PollenEntityId<PollenUser> userId = PollenEntityId.newId(PollenUser.class); - userId.setEntityId(user.getTopiaId()); - userId.encode(serviceContext.getTopiaApplicationContext().getTopiaIdFactory()); - + PollenEntityId<PollenUser> userId = getPollenEntityId(user); email.setValidateUrl(getPollenUIUrlRenderService().getUserValidateUrl(pollenUIContext.getUserValidateUrl(), userId.getReducedId(), user.getEmailActivationToken().getToken())); 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 9e6e6b1..a854b1e 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 @@ -100,6 +100,11 @@ pollen.error.voterList.member.weight.greaterThan0=member weight must be greater pollen.error.voterList.name.alreadyUsed=voterList name already used pollen.error.voterList.name.mandatory=voterList name can not be empty pollen.error.voterList.weight.greaterThan0=voterList weight must be greater than 0 +pollen.service.feed.pollCreated.description=On the %s, %s created the poll "%s". +pollen.service.feed.pollCreated.title=%s created the poll "%s" +pollen.service.feed.voteAdded.title=%s voted +pollen.service.feed.voteDeleted.title=%s deleted his vote +pollen.service.feed.voteEdited.title=%s edited his vote pollen.service.mail.ChoiceAddedEmail.subject=[Pollen] A choice was added in poll %s pollen.service.mail.ChoiceDeletedEmail.subject=[Pollen] A choice was deleted in poll %s pollen.service.mail.ChoiceEditedEmail.subject=[Pollen] A choice was edited in poll %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 bcf9cd2..dc26311 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 @@ -99,6 +99,11 @@ pollen.error.voterList.member.weight.greaterThan0=Le membre doit avoir un poid s pollen.error.voterList.name.alreadyUsed=Le nom de la liste de votant existe déjà pollen.error.voterList.name.mandatory=Nom de la liste de votant est obligatoire pollen.error.voterList.weight.greaterThan0=Poid de la liste de votant doit être supérieur à 0 +pollen.service.feed.pollCreated.description=Le %s, %s a créé le sondage %s. +pollen.service.feed.pollCreated.title=%s a créé le sondage "%s" +pollen.service.feed.voteAdded.title=%s a voté +pollen.service.feed.voteDeleted.title=%s a supprimé son vote +pollen.service.feed.voteEdited.title=%s a modifié son vote pollen.service.mail.ChoiceAddedEmail.subject=[Pollen] Un choix a été ajouté au sondage %s pollen.service.mail.ChoiceDeletedEmail.subject=[Pollen] Un choix a été supprimé du sondage %s pollen.service.mail.ChoiceEditedEmail.subject=[Pollen] Un choix a été modifié sur le sondage %s diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag.html index eae9ce3..6f57e39 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag.html @@ -120,7 +120,8 @@ require("../popup/QrCodeButton.tag.html"); <h1> <QrCodeButton if={poll.pollType === "FREE"} value="{window.location.origin}{window.location.pathname}#poll/{poll.id}/vote"/> {poll.title} - <h1> + <a href="{session.configuration.endPoint}/v1/polls/{poll.id}/feed" class="fa fa-rss-square" aria-hidden="true"></a> + </h1> <h3>{poll.description}</h3> diff --git a/pom.xml b/pom.xml index 371aa49..2af2b0b 100644 --- a/pom.xml +++ b/pom.xml @@ -464,11 +464,11 @@ <!-- Rome (rss) --> - <!--dependency> - <groupId>rome</groupId> + <dependency> + <groupId>com.rometools</groupId> <artifactId>rome</artifactId> - <version>1.0</version> - </dependency--> + <version>1.7.2</version> + </dependency> <!-- Logging --> -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.