deploiement sur VM

This commit is contained in:
Alexandre Chevalier
2026-02-13 23:37:08 +01:00
parent e37f99144b
commit 2291a85b79
142 changed files with 322045 additions and 208 deletions

View File

@@ -0,0 +1,97 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
# Before building the container image run:
#
# ./mvnw package
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/tlcdemoApp-jvm .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/tlcdemoApp-jvm
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
# when running the container
#
# Then run the container using :
#
# docker run -i --rm -p 8080:8080 quarkus/tlcdemoApp-jvm
#
# This image uses the `run-java.sh` script to run the application.
# This scripts computes the command line to execute your Java application, and
# includes memory/GC tuning.
# You can configure the behavior using the following environment properties:
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
# in JAVA_OPTS (example: "-Dsome.property=foo")
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
# used to calculate a default maximal heap memory based on a containers restriction.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
# of the container available memory as set here. The default is `50` which means 50%
# of the available memory is used as an upper boundary. You can skip this mechanism by
# setting this value to `0` in which case no `-Xmx` option is added.
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
# is used to calculate a default initial heap memory based on the maximum heap memory.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
# is used as the initial heap size. You can skip this mechanism by setting this value
# to `0` in which case no `-Xms` option is added (example: "25")
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
# This is used to calculate the maximum value of the initial heap memory. If used in
# a container without any memory constraints for the container then this option has
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
# here. The default is 4096MB which means the calculated value of `-Xms` never will
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
# when things are happening. This option, if set to true, will set
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
# true").
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
# (example: "20")
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
# (example: "40")
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
# (example: "4")
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
# previous GC times. (example: "90")
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
# contain the necessary JRE command-line options to specify the required GC, which
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
# accessed directly. (example: "foo.example.com,bar.example.com")
#
###
FROM registry.access.redhat.com/ubi8/openjdk-17:1.16
ENV LANGUAGE='en_US:en'
# We make four distinct layers so if there are application changes the library layers can be re-used
COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
COPY --chown=185 target/quarkus-app/*.jar /deployments/
COPY --chown=185 target/quarkus-app/app/ /deployments/app/
COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
EXPOSE 8080
USER 185
ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]

View File

@@ -0,0 +1,44 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode
#
# Before building the container image run:
#
# mvn package -Pnative -Dquarkus.native.container-build=true
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.native -t barais/doodleback-with-quarkus .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 barais/doodleback-with-quarkus
#
###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.8
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
&& chown 1001:root /work
COPY --chown=1001:root target/*-runner /work/application
EXPOSE 8080
USER 1001
# ENV quarkus_datasource_jdbc_url "jdbc:mysql://db:3306/tlc?useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&createDatabaseIfNotExist=true&serverTimezone=Europe/Paris"
# ENV quarkus_datasource_username tlc
# ENV quarkus_datasource_password tlc
# ENV quarkus_hibernate_orm_database_generation update
# ENV quarkus_mailer_from olivier.barais@gmail.com
# ENV quarkus_mailer_host localhost
# ENV quarkus_mailer_port 2525
# ENV quarkus_mailer_ssl false
# ENV quarkus_mailer_username ""
# ENV quarkus_mailer_password ""
# ENV quarkus_mailer_mock true
# ENV doodle_usepad false
# ENV doodle_padUrl http://etherpad:9001/
# ENV doodle_padApiKey "changeit"
# ENV doodle_organizermail "olivier.barais@gmail.com"
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

View File

@@ -0,0 +1,12 @@
package fr.istic.tlc.dao;
import fr.istic.tlc.domain.Choice;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ChoiceRepository implements PanacheRepository<Choice> {
}

View File

@@ -0,0 +1,10 @@
package fr.istic.tlc.dao;
import jakarta.enterprise.context.ApplicationScoped;
import fr.istic.tlc.domain.Comment;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
@ApplicationScoped
public class CommentRepository implements PanacheRepository<Comment> {
}

View File

@@ -0,0 +1,10 @@
package fr.istic.tlc.dao;
import jakarta.enterprise.context.ApplicationScoped;
import fr.istic.tlc.domain.MealPreference;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
@ApplicationScoped
public class MealPreferenceRepository implements PanacheRepository<MealPreference> {
}

View File

@@ -0,0 +1,23 @@
package fr.istic.tlc.dao;
import java.util.List;
import jakarta.enterprise.context.ApplicationScoped;
import fr.istic.tlc.domain.Poll;
import fr.istic.tlc.domain.User;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
@ApplicationScoped
public class PollRepository implements PanacheRepository<Poll> {
public Poll findBySlug(String slug){
return find("slug", slug).firstResult();
}
public Poll findByAdminSlug(String slug){
return find("slugAdmin", slug).firstResult();
}
public List<User> findAllUser4Poll(long id){
return this.getEntityManager().createQuery("select distinct c.users from Poll p join p.pollChoices as c where p.id = ?1").setParameter(1, id).getResultList();
}
}

View File

@@ -0,0 +1,10 @@
package fr.istic.tlc.dao;
import jakarta.enterprise.context.ApplicationScoped;
import fr.istic.tlc.domain.User;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
@ApplicationScoped
public class UserRepository implements PanacheRepository<User> {
}

View File

@@ -0,0 +1,120 @@
package fr.istic.tlc.domain;
import java.util.Date;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
@Entity
public class Choice {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
//private String name;
@Temporal(TemporalType.TIMESTAMP)
private Date startDate;
@Temporal(TemporalType.TIMESTAMP)
private Date endDate;
@ManyToMany
@JoinTable(
name = "choice_user",
joinColumns = @JoinColumn(name = "choice_id"),
inverseJoinColumns = @JoinColumn(name = "user_id"))
private List<User> users;
public Choice(){}
public Choice(Date startDate, Date endDate, List<User> users) {
this.startDate = startDate;
this.endDate = endDate;
this.users = users;
}
public void addUser(User user){
users.add(user);
}
public void removeUser(User user){
users.remove(user);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getstartDate() {
return startDate;
}
public void setstartDate(Date startDate) {
this.startDate = startDate;
}
public Date getendDate() {
return endDate;
}
public void setendDate(Date endDate) {
this.endDate = endDate;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
@Override
public String toString() {
return "Choice{" +
"id=" + id +
", startDate=" + startDate +
", endDate=" + endDate +
'}';
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Choice other = (Choice) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}

View File

@@ -0,0 +1,60 @@
package fr.istic.tlc.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String content;
private String auteur;
public Comment(){}
public Comment(String content){
this.content=content;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAuteur() {
return auteur;
}
public void setAuteur(String auteur) {
this.auteur = auteur;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", content='" + content + '\'' +
'}';
}
}

View File

@@ -0,0 +1,59 @@
package fr.istic.tlc.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
@Entity
public class MealPreference {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String content;
@ManyToOne
User user;
public MealPreference(){}
public MealPreference(String content){
this.content=content;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "MealPreference{" +
"id=" + id +
", content='" + content + '\'' +
", user=" + user +
'}';
}
}

View File

@@ -0,0 +1,235 @@
package fr.istic.tlc.domain;
import fr.istic.tlc.services.Utils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.OrderBy;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
@Entity
public class Poll {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String location;
private String description;
private boolean has_meal;
private String slug = Utils.getInstance().generateSlug(24);
private String slugAdmin = Utils.getInstance().generateSlug(24);
private String tlkURL = "https://tlk.io/"+Utils.getInstance().generateSlug(12);
public boolean clos = false;
@CreationTimestamp
private Date createdAt;
@UpdateTimestamp
private Date updatedAt;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "pollID")
@OrderBy("startDate ASC")
List<Choice> pollChoices;
@OneToOne(cascade = {CascadeType.PERSIST,CascadeType.REMOVE,CascadeType.REFRESH})
Choice selectedChoice;
@OneToMany(cascade = {CascadeType.PERSIST,CascadeType.REMOVE,CascadeType.REFRESH})
@JoinColumn(name = "pollID")
List<Comment> pollComments = new ArrayList<>();
@OneToMany(cascade = {CascadeType.PERSIST,CascadeType.REMOVE,CascadeType.REFRESH})
@JoinColumn(name = "pollID")
List<MealPreference> pollMealPreferences = new ArrayList<>();
private String padURL;
public Poll(){}
public Poll(String title, String location, String description, boolean has_meal, List<Choice> pollChoices) {
this.title = title;
this.location = location;
this.description = description;
this.has_meal = has_meal;
this.pollChoices = pollChoices;
}
public void addChoice(Choice choice){
this.pollChoices.add(choice);
}
public void removeChoice(Choice choice){
this.pollChoices.remove(choice);
}
public void addComment(Comment comment){ this.pollComments.add(comment);}
public void removeComment(Comment comment){ this.pollComments.remove(comment);}
public void addMealPreference(MealPreference mealPreference){ this.pollMealPreferences.add(mealPreference);}
public void removeComment(MealPreference mealPreference){ this.pollMealPreferences.remove(mealPreference);}
public Long getId() {
return id;
}
public String getTlkURL() {
return tlkURL;
}
public void setTlkURL(String tlkURL) {
this.tlkURL = tlkURL;
}
public List<Comment> getPollComments() {
return pollComments;
}
public List<MealPreference> getPollMealPreferences() {
return pollMealPreferences;
}
public void setPollMealPreferences(List<MealPreference> pollMealPreferences) {
this.pollMealPreferences = pollMealPreferences;
}
public void setPollComments(List<Comment> pollComments) {
this.pollComments = pollComments;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isHas_meal() {
return has_meal;
}
public void setHas_meal(boolean has_meal) {
this.has_meal = has_meal;
}
public String getSlug() {
return slug;
}
public void setSlug(String slug) {
this.slug = slug;
}
public String getSlugAdmin() {
return slugAdmin;
}
public void setSlugAdmin(String slugAdmin) {
this.slugAdmin = slugAdmin;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public List<Choice> getPollChoices() {
return pollChoices;
}
public void setPollChoices(List<Choice> pollChoices) {
this.pollChoices = pollChoices;
}
public Choice getSelectedChoice() {
return selectedChoice;
}
public void setSelectedChoice(Choice selectedChoice) {
this.selectedChoice = selectedChoice;
}
public String getPadURL() {
return this.padURL;
}
public void setPadURL(String padURL) {
this.padURL=padURL;
}
public boolean isClos() {
return clos;
}
public void setClos(boolean clos) {
this.clos = clos;
}
@Override
public String toString() {
return "Poll{" +
"id=" + id +
", title='" + title + '\'' +
", location='" + location + '\'' +
", description='" + description + '\'' +
", has_meal=" + has_meal +
", createdAt=" + createdAt +
'}';
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}

View File

@@ -0,0 +1,111 @@
package fr.istic.tlc.domain;
import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.OneToMany;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
private String mail;
private String icsurl;
@JsonIgnore
@ManyToMany(mappedBy = "users")
List<Choice> userChoices = new ArrayList<>();
@JsonIgnore
@OneToMany(mappedBy="user", cascade = CascadeType.ALL)
List<MealPreference> userMealPreferences = new ArrayList<>();
public User(){}
public User(String username) {
this.username = username;
}
public void addChoice(Choice choice){
this.userChoices.add(choice);
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public String getIcsurl() {
return icsurl;
}
public void setIcsurl(String icsurl) {
this.icsurl = icsurl;
}
public void removeChoice(Choice choice){
this.userChoices.remove(choice);
}
public void addMealPreference (MealPreference mealPreference) {this.userMealPreferences.add(mealPreference);}
public void removeMealPreference (MealPreference mealPreference) {this.userMealPreferences.remove(mealPreference);}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<Choice> getUserChoices() {
return userChoices;
}
public List<MealPreference> getUserMealPreferences() {
return userMealPreferences;
}
public void setUserMealPreferences(List<MealPreference> userMealPreferences) {
this.userMealPreferences = userMealPreferences;
}
public void setUserChoices(List<Choice> userChoices) {
this.userChoices = userChoices;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}

View File

@@ -0,0 +1,51 @@
package fr.istic.tlc.dto;
import java.util.List;
public class ChoiceUser {
private List<Long> choices;
private String mail;
private String pref;
private String ics;
private String username;
public List<Long> getChoices() {
return choices;
}
public void setChoices(List<Long> value) {
this.choices = value;
}
public String getMail() {
return mail;
}
public void setMail(String value) {
this.mail = value;
}
public String getPref() {
return pref;
}
public void setPref(String value) {
this.pref = value;
}
public String getUsername() {
return username;
}
public void setUsername(String value) {
this.username = value;
}
public String getIcs() {
return ics;
}
public void setIcs(String ics) {
this.ics = ics;
}
}

View File

@@ -0,0 +1,40 @@
package fr.istic.tlc.dto;
import java.util.Date;
public class EventDTO {
private Date startDate;
private Date endDate;
private String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
}

View File

@@ -0,0 +1,22 @@
package fr.istic.tlc.dto;
import java.util.List;
public class EventDTOAndSelectedChoice {
List<EventDTO> eventdtos;
List<Long> selectedChoices;
public List<EventDTO> getEventdtos() {
return eventdtos;
}
public void setEventdtos(List<EventDTO> eventdtos) {
this.eventdtos = eventdtos;
}
public List<Long> getSelectedChoices() {
return selectedChoices;
}
public void setSelectedChoices(List<Long> selectedChoices) {
this.selectedChoices = selectedChoices;
}
}

View File

@@ -0,0 +1,244 @@
package fr.istic.tlc.resources;
import java.util.HashMap;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import fr.istic.tlc.dao.ChoiceRepository;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.dao.UserRepository;
import fr.istic.tlc.domain.Choice;
import fr.istic.tlc.domain.Poll;
import fr.istic.tlc.domain.User;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/api")
public class ChoiceResourceEx {
@Autowired
ChoiceRepository choiceRepository;
@Autowired
PollRepository pollRepository;
@Autowired
UserRepository userRepository;
@GetMapping("/polls/{slug}/choices")
public ResponseEntity<List<Choice>> retrieveAllChoicesFromPoll(@PathVariable String slug) {
// On vérifie que le choix existe
Poll poll = pollRepository.findBySlug(slug);
if (poll == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
List<Choice> choices = poll.getPollChoices();
return new ResponseEntity<>(choices, HttpStatus.OK);
}
@GetMapping("/users/{idUser}/choices")
public ResponseEntity<List<Choice>> retrieveAllChoicesFromUser(@PathVariable long idUser) {
// On vérifie que l'utilisateur existe
User user = userRepository.findById(idUser);
if (user == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(user.getUserChoices(), HttpStatus.OK);
}
@GetMapping("/polls/{slug}/choices/{idChoice}")
public ResponseEntity<Choice> retrieveChoiceFromPoll(@PathVariable String slug, @PathVariable long idChoice) {
// On vérifie que le choix et le poll existent
Poll poll = pollRepository.findBySlug(slug);
Choice choice = choiceRepository.findById(idChoice);
if (poll == null || choice== null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le choix appartienne bien au poll
if(!poll.getPollChoices().contains(choice)){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(choice, HttpStatus.OK);
}
@GetMapping("/users/{idUser}/choices/{idChoice}")
public ResponseEntity<Choice> retrieveChoiceFromUser(@PathVariable long idUser, @PathVariable long idChoice) {
// On vérifie que le choix et l'utilisateur existent
User user = userRepository.findById(idUser);
Choice choice = choiceRepository.findById(idChoice);
if (user == null || choice == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le choix appartienne bien à l'utilisateur
if(!user.getUserChoices().contains(choice)){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(choice, HttpStatus.OK);
}
@DeleteMapping("/polls/{slug}/choices")
public ResponseEntity<?> deleteChoiceFromPoll(@RequestBody HashMap<String, List<Long>> choices, @PathVariable String slug, @RequestParam String token) {
// On vérifie que le poll existe
List<Long> idchoices = choices.get("choices");
Poll poll = pollRepository.findBySlug(slug);
if (poll == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le token soit bon
if(!poll.getSlugAdmin().equals(token)){
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
// On enlève les choix du poll
for (Long id: idchoices) {
// On vérifie que le choice existe
Choice choice = choiceRepository.findById(id);
if (choice!= null) {
// On remove le choice du poll
poll.removeChoice(choice);
pollRepository.getEntityManager().merge(poll);
// On remove le choices des utilisateurs
for (User user:userRepository.findAll().list()) {
if(user.getUserChoices().contains(choice)){
user.getUserChoices().remove(choice);
userRepository.getEntityManager().merge(user);
}
}
// On supprime le choice
choiceRepository.deleteById(id);
}
}
return new ResponseEntity<>(HttpStatus.OK);
}
@PostMapping("/polls/{slug}/choices")
public ResponseEntity<List<Choice>> createChoices(@RequestBody List<Choice> choices, @PathVariable String slug, @RequestParam String token) {
// On vérifie que le poll existe
Poll poll = pollRepository.findBySlug(slug);
if (poll == null){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
// On vérifie que le token soit bon
if(!poll.getSlugAdmin().equals(token)){
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
// On ajoute chaque choix au poll et vice versa
for (Choice choice:choices) {
this.choiceRepository.persist(choice);
poll.addChoice(choice);
pollRepository.getEntityManager().merge(poll);
}
return new ResponseEntity<>(choices, HttpStatus.CREATED);
}
@PutMapping("/polls/{slug}/choices/{idChoice}")
public ResponseEntity<Choice> updateChoice(@Valid @RequestBody Choice choice1, @PathVariable String slug, @PathVariable long idChoice, @RequestParam String token) {
// On vérifie que le poll et le choix existent
Poll poll = pollRepository.findBySlug(slug);
Choice choice = choiceRepository.findById(idChoice);
if (poll == null || choice == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le choix appartienne bien au poll
if(!poll.getPollChoices().contains(choice)){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le token soit bon
if(!poll.getSlugAdmin().equals(token)){
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
// On met à jour l'ancien choix
Choice ancientChoice = choice;
if (choice1.getstartDate()!=null){
ancientChoice.setstartDate(choice1.getstartDate());
}
if (choice1.getendDate()!=null){
ancientChoice.setendDate(choice1.getendDate());
}
// On update la bdd
Choice updatedChoice = choiceRepository.getEntityManager().merge(ancientChoice);
return new ResponseEntity<>(updatedChoice, HttpStatus.OK);
}
@PostMapping("/polls/{slug}/vote/{idUser}")
public ResponseEntity<Object> vote(@RequestBody HashMap<String, List<Long>> choices, @PathVariable String slug, @PathVariable long idUser) {
// On vérifie que le poll et l'utilisateur existent
List<Long> idchoices = choices.get("choices");
Poll poll = pollRepository.findBySlug(slug);
User user = userRepository.findById(idUser);
if (poll == null || user == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
for (Long choice : idchoices) {
// On vérifie que le choice existe
Choice optchoice = choiceRepository.findById(choice);
if (optchoice == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le choix appartienne bien au poll
if(!poll.getPollChoices().contains(optchoice)){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le user n'ai pas déjà voté pour ce choix
if(user.getUserChoices().contains(optchoice)){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
// On ajoute le choix à la liste de l'utilisateur et vice versa
optchoice.addUser(user);
choiceRepository.getEntityManager().merge(optchoice);
}
return new ResponseEntity<>(HttpStatus.OK);
}
@PostMapping("/polls/{slug}/choices/{idChoice}/removevote/{idUser}")
public ResponseEntity<Object> removeVote(@PathVariable String slug, @PathVariable long idChoice, @PathVariable long idUser) {
// On vérifie que le poll, le choix et l'utilisateur existent
Poll poll = pollRepository.findBySlug(slug);
Choice choice = choiceRepository.findById(idChoice);
User user = userRepository.findById(idUser);
if (poll == null || choice == null || user == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le choix appartienne bien au poll
if(!poll.getPollChoices().contains(choice)){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le user ait bien voté pour ce choix
if(!user.getUserChoices().contains(choice)){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
// On retire le choix à la liste de l'utilisateur et vice versa
choice.removeUser(user);
choiceRepository.getEntityManager().merge(choice);
user.removeChoice(choice);
userRepository.getEntityManager().merge(user);
return new ResponseEntity<>(HttpStatus.OK);
}
@GetMapping("/polls/{slug}/choices/{idChoice}/count")
public ResponseEntity<Object> numberOfVoteForChoice(@PathVariable String slug, @PathVariable long idChoice){
// On vérifie que le poll et choix existent
Poll poll = pollRepository.findBySlug(slug);
Choice choice = choiceRepository.findById(idChoice);
if (poll == null || choice == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le choix appartienne bien au poll
if(!poll.getPollChoices().contains(choice)){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On compte le nombre de vote pour le choix
return new ResponseEntity<>(choice.getUsers().size(),HttpStatus.OK);
}
}

View File

@@ -0,0 +1,79 @@
package fr.istic.tlc.resources;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import fr.istic.tlc.dao.CommentRepository;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.domain.Comment;
import fr.istic.tlc.domain.Poll;
@RestController
@RequestMapping("/api")
public class CommentResourceEx {
@Autowired
PollRepository pollRepository;
@Autowired
CommentRepository commentRepository;
/* @GetMapping("users/{idUser}/comments")
public ResponseEntity<List<Comment>> getAllCommentsFromUser(@PathVariable long idUser) {
// On vérifie que l'utilisateur existe
User optUser = userRepository.findById(idUser);
if(optUser== null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(optUser.getUserComments(), HttpStatus.OK);
}*/
@GetMapping("polls/{slug}/comments")
public ResponseEntity<Object> getAllCommentsFromPoll(@PathVariable String slug) {
// On vérifie que le poll existe
Poll optPoll = pollRepository.findBySlug(slug);
if(optPoll== null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(optPoll.getPollComments(),HttpStatus.OK);
}
@GetMapping("polls/{slug}/comments/{idComment}")
public ResponseEntity<Object> getCommentFromPoll(@PathVariable String slug, @PathVariable long idComment){
// On vérifie que le poll et le comment existe
Poll optPoll = pollRepository.findBySlug(slug);
Comment optComment = commentRepository.findById(idComment);
if(optPoll== null || optComment== null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le comment appartienne bien au poll
if (!optPoll.getPollComments().contains(optComment)){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(optComment,HttpStatus.OK);
}
/* @PostMapping("polls/{slug}/comments/{idUser}")
public ResponseEntity<Object> createComment(@Valid @RequestBody Comment comment, @PathVariable String slug, @PathVariable long idUser){
// On vérifie que le poll et le User existe
Poll poll = pollRepository.findBySlug(slug);
User user = userRepository.findById(idUser);
if (poll== null || user== null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On set le user dans comment
comment.setUser(user);
// On ajoute le commentaire dans le poll
poll.addComment(comment);
pollRepository.save(poll);
// On save le commentaire
Comment savedComment = commentRepository.save(comment);
return new ResponseEntity<>(savedComment, HttpStatus.CREATED);
}*/
}

View File

@@ -0,0 +1,154 @@
package fr.istic.tlc.resources;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.mail.MessagingException;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import fr.istic.tlc.dao.ChoiceRepository;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.dao.UserRepository;
import fr.istic.tlc.domain.Choice;
import fr.istic.tlc.domain.Poll;
import fr.istic.tlc.dto.EventDTO;
import fr.istic.tlc.dto.EventDTOAndSelectedChoice;
import fr.istic.tlc.services.Utils;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.ComponentList;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Period;
import net.fortuna.ical4j.model.PeriodList;
import net.fortuna.ical4j.model.component.CalendarComponent;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.util.MapTimeZoneCache;
@Path("/api/ics")
public class ICSResources {
@Inject
PollRepository pollRep;
@Inject
UserRepository userRep;
@Inject
ChoiceRepository choiceRep;
@GET
@Path("polls/{slug}/{ics}")
@Produces(MediaType.APPLICATION_JSON)
public EventDTOAndSelectedChoice parseCalendartoAppointments(@PathParam("slug") String slug,
@PathParam("ics") String ics)
throws IOException, ParserException, InterruptedException, ExecutionException, MessagingException {
EventDTOAndSelectedChoice result = new EventDTOAndSelectedChoice();
List<EventDTO> appointments = new ArrayList<>();
List<Long> selectedChoices = new ArrayList<>();
result.setEventdtos(appointments);
result.setSelectedChoices(selectedChoices);
// Get Poll
Poll p = this.pollRep.findBySlug(slug);
Date minDate = new Date();
if (p != null) {
// Get minimal date for Poll to filter ics
if (p.getPollChoices().size() > 0 && minDate.after(p.getPollChoices().get(0).getstartDate()))
minDate = p.getPollChoices().get(0).getstartDate();
}
// Get user to get its ICS
// User u = this.userRep.find("mail", usermail).firstResult();
byte[] decodedBytes = Base64.getDecoder().decode(ics);
String decodedString = new String(decodedBytes);
if (decodedString != null && !"".equals(decodedString)) {
// String s =
// "http://zimbra.inria.fr/home/olivier.barais@irisa.fr/Calendar.ics";
// Query the ics url
System.setProperty("net.fortuna.ical4j.timezone.cache.impl", MapTimeZoneCache.class.getName());
CloseableHttpAsyncClient client = HttpAsyncClients.createDefault();
client.start();
HttpGet request = new HttpGet(decodedString);
Future<HttpResponse> future = client.execute(request, null);
HttpResponse response = future.get();
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
String responseString = out.toString();
out.close();
client.close();
// Parse result
StringReader sin = new StringReader(responseString);
CalendarBuilder builder = new CalendarBuilder();
Calendar calendar = builder.build(sin);
ComponentList<CalendarComponent> events = calendar.getComponents(Component.VEVENT);
List<Choice> choices = new ArrayList<Choice>();
if (p!= null)
choices = p.getPollChoices();
// Create Event to draw
java.util.Calendar calEnd = java.util.Calendar.getInstance();
calEnd.setTime(new Date());
calEnd.add(java.util.Calendar.YEAR, 1);
DateTime start = new DateTime(minDate);
DateTime end = new DateTime(calEnd.getTime());
for (CalendarComponent event : events) {
Period period = new Period(start, end);
PeriodList list = event.calculateRecurrenceSet(period);
for (Period p1 : list) {
if (minDate.before(p1.getStart())) {
EventDTO a = new EventDTO();
a.setStartDate(p1.getStart());
a.setEndDate(p1.getEnd());
if (((VEvent) event).getSummary() != null)
a.setDescription(((VEvent) event).getSummary().getValue());
// Si intersection ajoute l'ID du choice comme ID selected
// https://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
for (Choice choice : choices) {
if (Utils.getInstance().intersect(choice.getstartDate(), choice.getendDate(), p1.getStart(),
p1.getEnd())) {
if (!selectedChoices.contains(choice.getId())) {
selectedChoices.add(choice.getId());
}
}
}
appointments.add(a);
}
}
}
}
return result;
}
}

View File

@@ -0,0 +1,80 @@
package fr.istic.tlc.resources;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import fr.istic.tlc.dao.ChoiceRepository;
import fr.istic.tlc.dao.MealPreferenceRepository;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.dao.UserRepository;
import fr.istic.tlc.domain.MealPreference;
import fr.istic.tlc.domain.Poll;
import fr.istic.tlc.domain.User;
@RestController
@RequestMapping("/api")
public class MealPreferenceResource {
@Autowired
ChoiceRepository choiceRepository;
@Autowired
PollRepository pollRepository;
@Autowired
UserRepository userRepository;
@Autowired
MealPreferenceRepository mealPreferenceRepository;
@GetMapping("polls/{slug}/mealpreferences")
public ResponseEntity<Object> getAllMealPreferencesFromPoll(@PathVariable String slug) {
// On vérifie que le poll existe
Poll optPoll = pollRepository.findBySlug(slug);
if(optPoll == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(optPoll.getPollMealPreferences(),HttpStatus.OK);
}
@GetMapping("polls/{slug}/mealpreference/{idMealPreference}")
public ResponseEntity<Object> getMealPreferenceFromPoll(@PathVariable String slug, @PathVariable long idMealPreference){
// On vérifie que le poll et la meal preference existe
Poll optPoll = pollRepository.findBySlug(slug);
MealPreference optMealPreference = mealPreferenceRepository.findById(idMealPreference);
if(optPoll == null || optMealPreference == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que la meal preference appartienne bien au poll
if (!optPoll.getPollMealPreferences().contains(optMealPreference)){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(optMealPreference,HttpStatus.OK);
}
@PostMapping("polls/{slug}/mealpreference/{idUser}")
public ResponseEntity<Object> createMealPreference(@Valid @RequestBody MealPreference mealPreference, @PathVariable String slug, @PathVariable long idUser){
// On vérifie que le poll et le User existe
Poll poll = pollRepository.findBySlug(slug);
User user = userRepository.findById(idUser);
if (poll == null || user == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On set le user dans la meal preference
mealPreference.setUser(user);
// On ajoute la meal preference dans le poll
poll.addMealPreference(mealPreference);
// On save la meal preference
mealPreferenceRepository.persist(mealPreference);
pollRepository.getEntityManager().merge(poll);
return new ResponseEntity<>(mealPreference, HttpStatus.CREATED);
}
}

View File

@@ -0,0 +1,187 @@
package fr.istic.tlc.resources;
import java.util.ArrayList;
import java.util.List;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import fr.istic.tlc.dao.ChoiceRepository;
import fr.istic.tlc.dao.CommentRepository;
import fr.istic.tlc.dao.MealPreferenceRepository;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.dao.UserRepository;
import fr.istic.tlc.domain.Choice;
import fr.istic.tlc.domain.Comment;
import fr.istic.tlc.domain.MealPreference;
import fr.istic.tlc.domain.Poll;
import fr.istic.tlc.domain.User;
import fr.istic.tlc.dto.ChoiceUser;
import fr.istic.tlc.services.SendEmail;
@Path("/api/poll")
public class NewPollResourceEx {
@Inject
PollRepository pollRep;
@Inject
UserRepository userRep;
@Inject
ChoiceRepository choiceRep;
@Inject
MealPreferenceRepository mealprefRep;
@Inject
CommentRepository commentRep;
@Inject
SendEmail sendmail;
@Path("/slug/{slug}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Poll getPollBySlug(@PathParam("slug") String slug) {
Poll p = pollRep.findBySlug(slug);
if (p != null)
p.getPollComments().clear();
p.setSlugAdmin("");
return p;
}
@Path("/aslug/{aslug}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Poll getPollByASlug(@PathParam("aslug") String aslug) {
return pollRep.findByAdminSlug(aslug);
}
@Path("/comment/{slug}")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
@Produces(MediaType.APPLICATION_JSON)
public Comment createComment4Poll(@PathParam("slug") String slug, Comment c) {
this.commentRep.persist(c);
Poll p = pollRep.findBySlug(slug);
p.addComment(c);
this.pollRep.persistAndFlush(p);
return c;
}
@PUT
@Path("/update1")
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
@Produces(MediaType.APPLICATION_JSON)
public Poll updatePoll(Poll p) {
System.err.println( "p " + p);
Poll p1 = pollRep.findById(p.getId());
List<Choice> choicesToRemove = new ArrayList<Choice>();
for (Choice c : p1.getPollChoices()) {
if (!p.getPollChoices().contains(c)) {
choicesToRemove.add(c);
System.err.println("toremove " + c.getId());
}
}
for (Choice c : p.getPollChoices()) {
if (c.getId() != null) {
this.choiceRep.getEntityManager().merge(c);
} else {
this.choiceRep.getEntityManager().persist(c);
}
}
for (Choice c : choicesToRemove) {
if (c.equals(p1.getSelectedChoice())) {
p.setSelectedChoice(null);
p1.setSelectedChoice(null);
p.setClos(false);
}
for (User u : c.getUsers()) {
u.getUserChoices().remove(c);
}
c.getUsers().clear();
this.choiceRep.delete(c);
}
for (Choice c : p.getPollChoices()) {
System.err.println("tomerge " + c.getId());
}
Poll p2 = this.pollRep.getEntityManager().merge(p);
return p2;
}
@Path("/choiceuser")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
public User addChoiceUser(ChoiceUser userChoice) {
User u = this.userRep.find("mail", userChoice.getMail()).firstResult();
if (u == null) {
u = new User();
u.setUsername(userChoice.getUsername());
u.setIcsurl(userChoice.getIcs());
u.setMail(userChoice.getMail());
this.userRep.persist(u);
}
if (userChoice.getPref() != null && !"".equals(userChoice.getPref())) {
MealPreference mp = new MealPreference();
mp.setContent(userChoice.getPref());
mp.setUser(u);
this.mealprefRep.persist(mp);
}
for (Long choiceId : userChoice.getChoices()) {
Choice c = this.choiceRep.findById(choiceId);
c.addUser(u);
this.choiceRep.persistAndFlush(c);
}
return u;
}
@Path("/selectedchoice/{choiceid}")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
public void closePoll(@PathParam("choiceid") String choiceid) {
Choice c = choiceRep.findById(Long.parseLong(choiceid));
Poll p = this.pollRep.find("select p from Poll as p join p.pollChoices as c where c.id= ?1", c.getId())
.firstResult();
p.setClos(true);
p.setSelectedChoice(c);
this.pollRep.persist(p);
this.sendmail.sendASimpleEmail(p);
// TODO Send Email
}
@GET()
@Path("polls/{slug}/comments")
@Produces(MediaType.APPLICATION_JSON)
public List<Comment> getAllCommentsFromPoll(@PathParam("slug") String slug) {
Poll p = this.pollRep.findBySlug(slug);
if (p!= null)
return p.getPollComments();
return null;
}
}

View File

@@ -0,0 +1,181 @@
package fr.istic.tlc.resources;
import fr.istic.tlc.services.Utils;
import java.util.List;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.domain.Poll;
import io.quarkus.panache.common.Sort;
import net.gjerull.etherpad.client.EPLiteClient;
@RestController
@RequestMapping("/api")
public class PollResourceEx {
@Autowired
PollRepository pollRepository;
@ConfigProperty(name = "doodle.usepad")
Boolean usePad = true;
@ConfigProperty(name = "doodle.internalPadUrl", defaultValue="http://etherpad:9001/")
String padUrl = "";
@ConfigProperty(name = "doodle.externalPadUrl", defaultValue="http://etherpad.diverse-team.fr/")
String externalPadUrl = "";
@ConfigProperty(name = "doodle.padApiKey")
String apikey = "";
EPLiteClient client;
@GetMapping("/polls")
public ResponseEntity<List<Poll>> retrieveAllpolls() {
// On récupère la liste de tous les poll qu'on trie ensuite par titre
List<Poll> polls = pollRepository.findAll(Sort.by("title", Sort.Direction.Ascending)).list();
return new ResponseEntity<>(polls, HttpStatus.OK);
}
@GetMapping("/polls/{slug}")
public ResponseEntity<Poll> retrievePoll(@PathVariable String slug, @RequestParam(required = false) String token) {
// On vérifie que le poll existe
Poll poll = pollRepository.findBySlug(slug);
if (poll == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// Si un token est donné, on vérifie qu'il soit bon
if (token != null && !poll.getSlugAdmin().equals(token)) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
poll.setSlugAdmin("");
return new ResponseEntity<>(poll, HttpStatus.OK);
}
@GetMapping("/polls/{slug}/pad")
public ResponseEntity<String> retrievePadURL(@PathVariable("slug") String slug) {
// On vérifie que le poll existe
Poll poll = pollRepository.findBySlug(slug);
if (poll == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(poll.getPadURL(), HttpStatus.OK);
}
@DeleteMapping("/polls/{slug}")
@Transactional
public ResponseEntity<Poll> deletePoll(@PathVariable("slug") String slug, @RequestParam String token) {
// On vérifie que le poll existe
Poll poll = pollRepository.findBySlug(slug);
if (poll == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On vérifie que le token soit bon
if (!poll.getSlugAdmin().equals(token)) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
// On supprime tous les choix du poll
// Fait automatiquement par le cascade type ALL
// On supprime tous les commentaires du poll
// Fait automatiquement par le cascade type ALL
// On supprime le pad
if (client == null) {
client = new EPLiteClient(padUrl, apikey);
}
client.deletePad(getPadId(poll));
// On supprime le poll de la bdd
pollRepository.deleteById(poll.getId());
return new ResponseEntity<>(poll, HttpStatus.OK);
}
@PostMapping("/polls")
@Transactional
public ResponseEntity<Poll> createPoll(@Valid @RequestBody Poll poll) {
// On enregistre le poll dans la bdd
String padId = Utils.getInstance().generateSlug(15);
if (this.usePad) {
if (client == null) {
client = new EPLiteClient(padUrl, apikey);
}
client.createPad(padId);
initPad(poll.getTitle(), poll.getLocation(), poll.getDescription(), client, padId);
poll.setPadURL(externalPadUrl + "p/" + padId);
}
pollRepository.persist(poll);
return new ResponseEntity<>(poll, HttpStatus.CREATED);
}
@PutMapping("/polls/{slug}")
@Transactional
public ResponseEntity<Object> updatePoll(@Valid @RequestBody Poll poll, @PathVariable String slug,
@RequestParam String token) {
// On vérifie que le poll existe
Poll optionalPoll = pollRepository.findBySlug(slug);
if (optionalPoll == null)
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
// On vérifie que le token soit bon
if (!optionalPoll.getSlugAdmin().equals(token)) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
// On met au poll le bon id et les bons slugs
Poll ancientPoll = optionalPoll;
// On se connecte au pad
String padId = getPadId(ancientPoll);
// On sauvegarde les anciennes données pour mettre à jour le pad
String title = ancientPoll.getTitle();
String location = ancientPoll.getLocation();
String description = ancientPoll.getDescription();
// On met à jour l'ancien poll
if (poll.getTitle() != null) {
ancientPoll.setTitle(poll.getTitle());
}
if (poll.getLocation() != null) {
ancientPoll.setLocation(poll.getLocation());
}
if (poll.getDescription() != null) {
ancientPoll.setDescription(poll.getDescription());
}
ancientPoll.setHas_meal(poll.isHas_meal());
// On update le pad
String ancientPad = (String) client.getText(padId).get("text");
ancientPad = ancientPad.replaceFirst(title, ancientPoll.getTitle());
ancientPad = ancientPad.replaceFirst(location, ancientPoll.getLocation());
ancientPad = ancientPad.replaceFirst(description, ancientPoll.getDescription());
client.setText(padId, ancientPad);
// On enregistre le poll dans la bdd
Poll updatedPoll = pollRepository.getEntityManager().merge(ancientPoll);
return new ResponseEntity<>(updatedPoll, HttpStatus.OK);
}
private static void initPad(String pollTitle, String pollLocation, String pollDescription, EPLiteClient client,
String padId) {
final String str = pollTitle + '\n' + "Localisation : " + pollLocation + '\n' + "Description : "
+ pollDescription + '\n';
client.setText(padId, str);
}
private static String getPadId(Poll poll) {
//return poll.getPadURL().substring(poll.getPadURL().length() - 6);
return poll.getPadURL().substring(poll.getPadURL().lastIndexOf('/') + 1);
}
}

View File

@@ -0,0 +1,123 @@
package fr.istic.tlc.resources;
import java.util.ArrayList;
import java.util.List;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import fr.istic.tlc.dao.ChoiceRepository;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.dao.UserRepository;
import fr.istic.tlc.domain.Choice;
import fr.istic.tlc.domain.Poll;
import fr.istic.tlc.domain.User;
import io.quarkus.panache.common.Sort;
@RestController
@RequestMapping("/api")
public class UserResource {
@Autowired
ChoiceRepository choiceRepository;
@Autowired
PollRepository pollRepository;
@Autowired
UserRepository userRepository;
@GetMapping("/users")
public ResponseEntity<List<User>> retrieveAllUsers() {
// On récupère tous les utilisateurs qu'on trie ensuite par username
List<User> users = userRepository.findAll(Sort.by( "username", Sort.Direction.Ascending)).list();
return new ResponseEntity<>(users, HttpStatus.OK);
}
@GetMapping("/users/{idUser}")
public ResponseEntity<User> retrieveUser(@PathVariable long idUser) {
// On vérifie que l'utilisateur existe
User user = userRepository.findById(idUser);
if (user== null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(user, HttpStatus.OK);
}
@GetMapping("/polls/{slug}/users")
public ResponseEntity<List<User>> getAllUserFromPoll(@PathVariable String slug) {
List<User> users = new ArrayList<>();
// On vérifie que le poll existe
Poll poll = pollRepository.findBySlug(slug);
if (poll== null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On parcours les choix du poll pour récupérer les users ayant voté
if (!poll.getPollChoices().isEmpty()) {
for (Choice choice : poll.getPollChoices()) {
if (!choice.getUsers().isEmpty()) {
for (User user : choice.getUsers()) {
// On vérifie que le user ne soit pas déjà dans la liste
if (!users.contains(user)) {
users.add(user);
}
}
}
}
}
return new ResponseEntity<>(users, HttpStatus.OK);
}
@DeleteMapping("/users/{idUser}")
public ResponseEntity<User> deleteUser(@PathVariable long idUser) {
// On vérifie que l'utilisateur existe
User user = userRepository.findById(idUser);
if (user== null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On supprime l'utilisateur de la liste d'utilisateur de chaque choix
for (Choice choice : user.getUserChoices()) {
choice.removeUser(user);
choiceRepository.getEntityManager().merge(choice);
}
// On supprime les commentaires de l'utilisateurs
// Fait automatiquement par le cascade type ALL
// On supprime l'utilisateur de la bdd
userRepository.deleteById(idUser);
return new ResponseEntity<>(user, HttpStatus.OK);
}
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
// On sauvegarde l'utilisateur dans la bdd
userRepository.persist(user);
return new ResponseEntity<>(user, HttpStatus.CREATED);
}
@PutMapping("/users/{idUser}")
public ResponseEntity<User> updateUser(@PathVariable long idUser, @Valid @RequestBody User user) {
// On vérifie que l'utilisateur existe
User optionalUser = userRepository.findById(idUser);
if (optionalUser== null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// On met le bon id sur l'utilisateur
User ancientUser = optionalUser;
if (user.getUsername() != null) {
ancientUser.setUsername(user.getUsername());
}
// On update l'utilisateur dans la bdd
User updatedUser = userRepository.getEntityManager().merge(ancientUser);
return new ResponseEntity<>(updatedUser, HttpStatus.OK);
}
}

View File

@@ -0,0 +1,332 @@
package fr.istic.tlc.resources.features;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import fr.istic.tlc.dao.ChoiceRepository;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.dao.UserRepository;
import fr.istic.tlc.domain.Choice;
import fr.istic.tlc.domain.Poll;
import fr.istic.tlc.domain.User;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jxl.Workbook;
import jxl.format.Alignment;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.Colour;
import jxl.format.VerticalAlignment;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
@Path("/api")
public class ExportResource {
@Inject
ChoiceRepository choiceRepository;
@Inject
PollRepository pollRepository;
@Inject
UserRepository userRepository;
@ConfigProperty(name = "doodle.tmpfolder")
String EXCEL_FILE_LOCATION = "/tmp/excelFiles";
@GET
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Path("/polls/{slug}/results")
public Response downloadResultsExcel(@PathParam("slug") String slug) throws IOException {
Poll poll = pollRepository.findBySlug(slug);
if (poll == null) {
return null;
}
String filePath = createExcelFile(poll, slug);
return getHttpEntityToDownload(filePath, "vnd.ms-excel");
}
/*
* @RequestMapping(value = "/polls/{slug}/print", method = RequestMethod.GET,
* produces = APPLICATION_PDF) public @ResponseBody HttpEntity<byte[]>
* downloadResultsPdf(@PathVariable String slug) throws IOException { Poll poll
* = pollRepository.findBySlug(slug); if (poll == null) { return new
* ResponseEntity<>(HttpStatus.NOT_FOUND); } String filePath = "./Test.xls";
* //Utils.excel2pdf(); //convertToPdf(filePath); return
* getHttpEntityToDownload(filePath,"pdf"); }
*/
/*
* private String convertToPdf(String filePath){ return "";
*
* }
*/
static int beginningColumnCell = 0;
static int beginningRowCell = 3;
static int fontSize = 9;
static Colour borderColour = Colour.WHITE;
private String createExcelFile(Poll poll, String slug) throws IOException {
DateFormat dateFormat = new SimpleDateFormat("dd.MM.yy-HH.mm.ss");
Date date = new Date();
String fileName = EXCEL_FILE_LOCATION + File.separator + slug + "-" + dateFormat.format(date) + ".xls";
File folder = new File(EXCEL_FILE_LOCATION);
if (!folder.exists()) {
folder.mkdir();
}
// Create an Excel file
WritableWorkbook Wbook = null;
try {
System.out.println("Création du fichier");
// Create an Excel file in the file location
File file = new File(fileName);
if (!file.createNewFile()) {
System.out.println("Erreur lors de la création du fichier");
}
Wbook = Workbook.createWorkbook(file);
// Create an Excel sheet
WritableSheet mainSheet = Wbook.createSheet("SONDAGE", 0);
Wbook.setColourRGB(Colour.BLUE, 53, 37, 230);
// Format objects
WritableCellFormat formatTitle = new WritableCellFormat();
WritableFont fontTitle = new WritableFont(WritableFont.TAHOMA, 16, WritableFont.BOLD);
fontTitle.setColour(Colour.BLUE);
formatTitle.setFont(fontTitle);
Label label;
label = new Label(0, 0, "Sondage \"" + poll.getTitle() + "\"", formatTitle);
mainSheet.addCell(label);
label = new Label(0, 1, "http://localhost:3000/polls/" + poll.getSlug());
mainSheet.addCell(label);
// On récupere les users qui ont voté dans ce sondage
List<User> users = retrieveUsers(poll);
// On ecrit les users sur la première colonne
writeUsers(poll, Wbook, users);
// On ecrit les choix avec les votes de chaque users
writeChoices(poll, Wbook, users);
System.out.println("Enregistrement du fichier");
// On ecrit les donnée du workbook dans un format excel
Wbook.write();
} catch (Exception e) {
System.out.println("Erreur lors de la création du fichier :( " + e.toString());
e.printStackTrace();
} finally {
if (Wbook != null) {
try {
Wbook.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return fileName;
}
private List<User> retrieveUsers(Poll poll) {
List<User> users = new ArrayList<>();
// On parcours les choix du poll pour récupérer les users ayant voté
if (!poll.getPollChoices().isEmpty()) {
for (Choice choice : poll.getPollChoices()) {
if (!choice.getUsers().isEmpty()) {
for (User user : choice.getUsers()) {
// On vérifie que le user ne soit pas déjà dans la liste
if (!users.contains(user)) {
users.add(user);
}
}
}
}
}
return users;
}
private void writeChoices(Poll poll, WritableWorkbook Wbook, List<User> users) throws jxl.write.WriteException {
Label label;
Number number;
WritableSheet mainSheet = Wbook.getSheet(0);
List<fr.istic.tlc.domain.Choice> choices = poll.getPollChoices();
// List<Choice> choices =
// choiceRepository.findAll(Sort.by(Sort.Direction.ASC,"startDate"));
// Format objects
WritableCellFormat formatVoteYes = new WritableCellFormat();
formatVoteYes.setAlignment(Alignment.CENTRE);
formatVoteYes.setVerticalAlignment(VerticalAlignment.CENTRE);
formatVoteYes.setBorder(Border.ALL, BorderLineStyle.THIN, borderColour);
formatVoteYes.setBackground(Colour.LIGHT_GREEN);
WritableFont fontVoteYes = new WritableFont(WritableFont.TAHOMA, fontSize, WritableFont.NO_BOLD);
fontVoteYes.setColour(Colour.BLACK);
formatVoteYes.setFont(fontVoteYes);
// Format objects
Wbook.setColourRGB(Colour.LIGHT_ORANGE, 255, 195, 195);
WritableCellFormat formatVoteNo = new WritableCellFormat();
formatVoteNo.setAlignment(Alignment.CENTRE);
formatVoteNo.setVerticalAlignment(VerticalAlignment.CENTRE);
formatVoteNo.setBorder(Border.ALL, BorderLineStyle.THIN, borderColour);
formatVoteNo.setBackground(Colour.LIGHT_ORANGE);
WritableFont fontVoteNo = new WritableFont(WritableFont.TAHOMA, fontSize, WritableFont.NO_BOLD);
fontVoteNo.setColour(Colour.BLACK);
formatVoteNo.setFont(fontVoteNo);
// On ecrit les colonnes des choix
for (int i = 0; i < choices.size(); i++) {
mainSheet.setColumnView(1 + beginningColumnCell + i, 14);
// On ecrit la date
writeChoiceDate(Wbook, choices, i);
// On ecrit les votes
List<User> listUsersVotes = choices.get(i).getUsers();
for (int x = 0; x < users.size(); x++) {
if (listUsersVotes.contains(users.get(x))) {
label = new Label(1 + beginningColumnCell + i, 3 + beginningRowCell + x, "OK", formatVoteYes);
} else {
label = new Label(1 + beginningColumnCell + i, 3 + beginningRowCell + x, "-", formatVoteNo);
}
mainSheet.addCell(label);
}
// on ecrit le nombre total de vote pour le choix
number = new Number(1 + beginningColumnCell + i, 3 + beginningRowCell + users.size(),
listUsersVotes.size());
mainSheet.addCell(number);
}
}
private void writeChoiceDate(WritableWorkbook Wbook, List<Choice> choices, int i) throws jxl.write.WriteException {
Label label;
WritableSheet mainSheet = Wbook.getSheet(0);
String month[] = { "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Aout", "Septembre",
"Novembre", "Décembre" };
String dayOfWeek[] = { "Lun.", "Mar.", "Mer.", "Jeu.", "Ven.", "Sam.", "Dim." };
Wbook.setColourRGB(Colour.BLUE, 53, 37, 230);
// Format objects
WritableCellFormat formatDate = new WritableCellFormat();
formatDate.setAlignment(Alignment.CENTRE);
formatDate.setVerticalAlignment(VerticalAlignment.CENTRE);
formatDate.setBorder(Border.ALL, BorderLineStyle.THIN, borderColour);
formatDate.setBackground(Colour.BLUE);
WritableFont fontDate = new WritableFont(WritableFont.TAHOMA, fontSize, WritableFont.NO_BOLD);
fontDate.setColour(Colour.WHITE);
formatDate.setFont(fontDate);
// On recupère la date de début
Choice choice = choices.get(i);
Date startDate = choice.getstartDate();
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
int startYear = calendar.get(Calendar.YEAR);
String startMonth = month[calendar.get(Calendar.MONTH)];
int startDayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
String startDayOfWeek = dayOfWeek[calendar.get(Calendar.DAY_OF_WEEK) - 1];
int startHourInt = calendar.get(Calendar.HOUR_OF_DAY);
String startHour = (startHourInt < 10 ? "0" : "") + startHourInt;
int startMinuteInt = calendar.get(Calendar.MINUTE);
String startMinute = (startMinuteInt < 10 ? "0" : "") + startMinuteInt;
// On recupère la date de fin
Date endDate = choice.getendDate();
calendar.setTime(endDate);
int endHourInt = calendar.get(Calendar.HOUR_OF_DAY);
String endHour = (endHourInt < 10 ? "0" : "") + endHourInt;
int endMinuteInt = calendar.get(Calendar.MINUTE);
String endMinute = (endMinuteInt < 10 ? "0" : "") + endMinuteInt;
label = new Label(1 + beginningColumnCell + i, beginningRowCell, startMonth + " " + startYear, formatDate);
mainSheet.addCell(label);
label = new Label(1 + beginningColumnCell + i, 1 + beginningRowCell, startDayOfWeek + " " + startDayOfMonth,
formatDate);
mainSheet.addCell(label);
label = new Label(1 + beginningColumnCell + i, 2 + beginningRowCell,
startHour + ":" + startMinute + " - " + endHour + ":" + endMinute, formatDate);
mainSheet.addCell(label);
}
private void writeUsers(Poll poll, WritableWorkbook Wbook, List<User> users) throws jxl.write.WriteException {
Label label;
WritableSheet mainSheet = Wbook.getSheet(0);
mainSheet.setColumnView(beginningColumnCell, 25);
// Format objects
WritableCellFormat formatUser = new WritableCellFormat();
formatUser.setAlignment(Alignment.RIGHT);
formatUser.setVerticalAlignment(VerticalAlignment.CENTRE);
formatUser.setBorder(Border.ALL, BorderLineStyle.THIN, borderColour);
formatUser.setBackground(Colour.GRAY_25);
WritableFont fontUser = new WritableFont(WritableFont.TAHOMA, fontSize, WritableFont.NO_BOLD);
fontUser.setColour(Colour.BLACK);
formatUser.setFont(fontUser);
// On ecrit la premier colonne avec users et label "Nombre"
for (int i = 0; i < users.size(); i++) {
label = new Label(beginningColumnCell, 3 + beginningRowCell + i, users.get(i).getUsername(), formatUser);
mainSheet.addCell(label);
}
label = new Label(beginningColumnCell, 3 + beginningRowCell + users.size(), "Nombre");
mainSheet.addCell(label);
}
private Response getHttpEntityToDownload(String filePath, String fileType) throws IOException {
File file = getFile(filePath);
// header.set("Content-Disposition", "inline; filename=" + file.getName());
return Response.ok(((Object) file), MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"").build();
}
private File getFile(String filePath) throws FileNotFoundException {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException("file with path: " + filePath + " was not found.");
}
return file;
}
/*
* @ControllerAdvice public class GlobalExceptionHandler {
*
* @ExceptionHandler(value = FileNotFoundException.class) public void
* handle(FileNotFoundException ex, HttpServletResponse response) throws
* IOException { System.out.println("handling file not found exception");
* response.sendError(404, ex.getMessage()); }
*
* @ExceptionHandler(value = IOException.class) public void handle(IOException
* ex, HttpServletResponse response) throws IOException {
* System.out.println("handling io exception"); response.sendError(500,
* ex.getMessage()); } }
*/
}

View File

@@ -0,0 +1,11 @@
package fr.istic.tlc.resources.itf;
import fr.istic.tlc.dao.ChoiceRepository;
import fr.istic.tlc.domain.Choice;
import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource;
import io.quarkus.rest.data.panache.ResourceProperties;
@ResourceProperties(path = "/api/choice")
public interface ChoiceResource extends PanacheRepositoryResource<ChoiceRepository, Choice,Long> {
}

View File

@@ -0,0 +1,10 @@
package fr.istic.tlc.resources.itf;
import fr.istic.tlc.dao.CommentRepository;
import fr.istic.tlc.domain.Comment;
import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource;
import io.quarkus.rest.data.panache.ResourceProperties;
@ResourceProperties(path = "/api/comment")
public interface CommentResource extends PanacheRepositoryResource<CommentRepository, Comment,Long> {
}

View File

@@ -0,0 +1,10 @@
package fr.istic.tlc.resources.itf;
import fr.istic.tlc.dao.MealPreferenceRepository;
import fr.istic.tlc.domain.MealPreference;
import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource;
import io.quarkus.rest.data.panache.ResourceProperties;
@ResourceProperties(path = "/api/mealpreference")
public interface MealPreferenceResource extends PanacheRepositoryResource<MealPreferenceRepository,MealPreference,Long> {
}

View File

@@ -0,0 +1,10 @@
package fr.istic.tlc.resources.itf;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.domain.Poll;
import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource;
import io.quarkus.rest.data.panache.ResourceProperties;
@ResourceProperties(path = "/api/poll")
public interface PollResource extends PanacheRepositoryResource<PollRepository, Poll,Long> {
}

View File

@@ -0,0 +1,10 @@
package fr.istic.tlc.resources.itf;
import fr.istic.tlc.dao.UserRepository;
import fr.istic.tlc.domain.User;
import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource;
import io.quarkus.rest.data.panache.ResourceProperties;
@ResourceProperties(path = "/api/user")
public interface UserResource extends PanacheRepositoryResource<UserRepository, User,Long> {
}

View File

@@ -0,0 +1,124 @@
package fr.istic.tlc.services;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import fr.istic.tlc.dao.PollRepository;
import fr.istic.tlc.domain.Poll;
import fr.istic.tlc.domain.User;
import io.quarkus.mailer.Mail;
import io.quarkus.mailer.Mailer;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.TimeZoneRegistry;
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.component.VTimeZone;
import net.fortuna.ical4j.model.parameter.Role;
import net.fortuna.ical4j.model.property.Attendee;
import net.fortuna.ical4j.model.property.CalScale;
import net.fortuna.ical4j.model.property.Method;
import net.fortuna.ical4j.model.property.Organizer;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.Uid;
import net.fortuna.ical4j.model.property.Version;
import net.fortuna.ical4j.util.MapTimeZoneCache;
import net.fortuna.ical4j.util.RandomUidGenerator;
import net.fortuna.ical4j.util.UidGenerator;
@ApplicationScoped
public class SendEmail {
@Inject
Mailer mailer;
@Inject
PollRepository pollRep;
@ConfigProperty(name = "doodle.organizermail")
String organizermail= "test@test.fr";
public void sendASimpleEmail(Poll p ) {
// Create a default MimeMessage object.
System.setProperty("net.fortuna.ical4j.timezone.cache.impl", MapTimeZoneCache.class.getName());
List<User> u = this.pollRep.findAllUser4Poll(p.getId());
List<String> attendees = new ArrayList<String>();
for (User u1 : u) {
attendees.add(u1.getMail());
}
String ics = this.getICS1(p.getSelectedChoice().getstartDate(), p.getSelectedChoice().getendDate(), p.getTitle(), attendees, organizermail);
Mail m = new Mail();
m.addAttachment("meeting.ics", ics.getBytes(), "text/calendar");
m.setFrom(organizermail);
m.setTo(attendees);
m.setCc(Arrays.asList(organizermail));
m.setFrom(organizermail);
m.setSubject("Réunion c" + p.getTitle() + " [créneau confirmé] ");
m.setHtml("La date définitive pour la réunion : \""+ p.getTitle() + "\" a été validée par l\'organisateur. <BR>" +
"Un salon a été créé de discussion pour cette réunion est accessible à cette adresse <a [href]=\" " +p.getTlkURL() + "\" target=\"_blank\">" + p.getTlkURL() + "</a>.<BR>\n" +
"Un pad a été créé pour cette réunion <a [href]=\""+ p.getPadURL() + "\" target=\"_blank\">\""+ p.getPadURL() + "\"</a>.</span><BR>\n");
mailer.send(m);
}
public String getICS1(Date start, Date end, String libelle, List<String> attendees, String organizer) {
// Create a TimeZone
TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
TimeZone timezone = registry.getTimeZone("Europe/Paris");
VTimeZone tz = timezone.getVTimeZone();
// Create the event
DateTime startd = new DateTime(start);
DateTime endd = new DateTime(end);
VEvent meeting = new VEvent(startd, endd, libelle);
// add timezone info..
meeting.getProperties().add(tz.getTimeZoneId());
// generate unique identifier..
UidGenerator ug = new RandomUidGenerator();
Uid uid = ug.generateUid();
meeting.getProperties().add(uid);
// add attendees..
for (String attendee : attendees) {
Attendee p1 = new Attendee(URI.create("mailto:"+attendee));
p1.getParameters().add(Role.REQ_PARTICIPANT);
// dev1.getParameters().add(new Cn("Developer 1"));
meeting.getProperties().add(p1);
}
Organizer p1 = new Organizer(URI.create("mailto:"+organizer));
meeting.getProperties().add(p1);
// Create a calendar
net.fortuna.ical4j.model.Calendar icsCalendar = new net.fortuna.ical4j.model.Calendar();
icsCalendar.getProperties().add(Version.VERSION_2_0);
icsCalendar.getProperties().add(new ProdId("Zimbra-Calendar-Provider"));
icsCalendar.getProperties().add(CalScale.GREGORIAN);
icsCalendar.getProperties().add(Method.REQUEST);
icsCalendar.getComponents().add(tz);
// Add the event and print
icsCalendar.getComponents().add(meeting);
return icsCalendar.toString();
}
}

View File

@@ -0,0 +1,41 @@
package fr.istic.tlc.services;
import java.util.Date;
import java.util.Random;
public class Utils {
private Random random = null;// = new Random();
private static final String CHARS = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ234567890";
private static Utils instance = null;
private Utils(){
this.random = new Random();
}
public static Utils getInstance(){
if (instance == null)
instance = new Utils();
return instance;
}
public String generateSlug(int length) {
if (random == null){
random = new Random();
}
StringBuilder slug = new StringBuilder(length);
for (int i = 0; i < length; i++) {
slug.append(CHARS.charAt(random.nextInt(CHARS.length())));
}
return slug.toString();
}
public boolean intersect(Date start1, Date end1, Date start2, Date end2) {
if (start1 == null || start2 == null ||end1 == null||end2 == null)
return false;
return end1.after(start2) && start1.before(end2);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,34 @@
doodle:
usepad: true
internalPadUrl: "http://localhost:9001/"
externalPadUrl: "http://localhost:9001/"
padApiKey: "19d89ca52bc0fa4f19d6325464d9d7a032649b9fa68c111514627081e2784b4a"
organizermail: "olivier.barais@gmail.com"
tmpfolder: "/tmp/excelFiles"
quarkus:
datasource:
db-kind: mysql
username: tlc
password: tlc
jdbc:
url: jdbc:mysql://localhost:3306/tlc?useUnicode=true&serverTimezone=Europe/Paris
driver: com.mysql.cj.jdbc.Driver
hibernate-orm:
validate-in-dev-mode: false
# flyway:
# migrate-at-start: true
# baseline-on-migrate: true
database:
generation: update
globally-quoted-identifiers: true
mailer:
from: test@quarkus.io
# host: smtp.sendgrid.net
host: localhost
port: 2525
# port: 465
# ssl: true
# username: ""
# password: ""
mock: false

View File

@@ -0,0 +1,294 @@
-- phpMyAdmin SQL Dump
-- version 5.1.1deb5ubuntu1
-- https://www.phpmyadmin.net/
--
-- Hôte : localhost:3306
-- Généré le : mar. 22 août 2023 à 10:08
-- Version du serveur : 8.0.33-0ubuntu0.22.04.4
-- Version de PHP : 8.1.2-1ubuntu2.13
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Base de données : `tlc`
--
CREATE DATABASE IF NOT EXISTS `tlc` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
USE `tlc`;
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Base de données : `tlc`
--
-- --------------------------------------------------------
--
-- Structure de la table `Choice`
--
CREATE TABLE `Choice` (
`id` bigint NOT NULL,
`endDate` datetime(6) DEFAULT NULL,
`startDate` datetime(6) DEFAULT NULL,
`pollID` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Structure de la table `Choice_SEQ`
--
CREATE TABLE `Choice_SEQ` (
`next_val` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Déchargement des données de la table `Choice_SEQ`
--
INSERT INTO `Choice_SEQ` (`next_val`) VALUES
(1);
-- --------------------------------------------------------
--
-- Structure de la table `choice_user`
--
CREATE TABLE `choice_user` (
`choice_id` bigint NOT NULL,
`user_id` bigint NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Structure de la table `Comment`
--
CREATE TABLE `Comment` (
`id` bigint NOT NULL,
`auteur` varchar(255) DEFAULT NULL,
`content` varchar(255) DEFAULT NULL,
`pollID` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Structure de la table `Comment_SEQ`
--
CREATE TABLE `Comment_SEQ` (
`next_val` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Déchargement des données de la table `Comment_SEQ`
--
INSERT INTO `Comment_SEQ` (`next_val`) VALUES
(1);
-- --------------------------------------------------------
--
-- Structure de la table `MealPreference`
--
CREATE TABLE `MealPreference` (
`id` bigint NOT NULL,
`content` varchar(255) DEFAULT NULL,
`user_id` bigint DEFAULT NULL,
`pollID` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Structure de la table `MealPreference_SEQ`
--
CREATE TABLE `MealPreference_SEQ` (
`next_val` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Déchargement des données de la table `MealPreference_SEQ`
--
INSERT INTO `MealPreference_SEQ` (`next_val`) VALUES
(1);
-- --------------------------------------------------------
--
-- Structure de la table `Poll`
--
CREATE TABLE `Poll` (
`id` bigint NOT NULL,
`clos` bit(1) NOT NULL,
`createdAt` datetime(6) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`has_meal` bit(1) NOT NULL,
`location` varchar(255) DEFAULT NULL,
`padURL` varchar(255) DEFAULT NULL,
`slug` varchar(255) DEFAULT NULL,
`slugAdmin` varchar(255) DEFAULT NULL,
`title` varchar(255) DEFAULT NULL,
`tlkURL` varchar(255) DEFAULT NULL,
`updatedAt` datetime(6) DEFAULT NULL,
`selectedChoice_id` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Structure de la table `Poll_SEQ`
--
CREATE TABLE `Poll_SEQ` (
`next_val` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Déchargement des données de la table `Poll_SEQ`
--
INSERT INTO `Poll_SEQ` (`next_val`) VALUES
(1);
-- --------------------------------------------------------
--
-- Structure de la table `User`
--
CREATE TABLE `User` (
`id` bigint NOT NULL,
`icsurl` varchar(255) DEFAULT NULL,
`mail` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Structure de la table `User_SEQ`
--
CREATE TABLE `User_SEQ` (
`next_val` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Déchargement des données de la table `User_SEQ`
--
INSERT INTO `User_SEQ` (`next_val`) VALUES
(1);
--
-- Index pour les tables déchargées
--
--
-- Index pour la table `Choice`
--
ALTER TABLE `Choice`
ADD PRIMARY KEY (`id`),
ADD KEY `FKpptbydus718x0n5w5s1hmtvnp` (`pollID`);
--
-- Index pour la table `choice_user`
--
ALTER TABLE `choice_user`
ADD KEY `FK74lqrm3h9f56d6ijnvjobl0wb` (`user_id`),
ADD KEY `FKljka9n83yo9s4qpol3wplp1lw` (`choice_id`);
--
-- Index pour la table `Comment`
--
ALTER TABLE `Comment`
ADD PRIMARY KEY (`id`),
ADD KEY `FKgw1unu5kgu9s7sdkqaoy0kyyh` (`pollID`);
--
-- Index pour la table `MealPreference`
--
ALTER TABLE `MealPreference`
ADD PRIMARY KEY (`id`),
ADD KEY `FK61nykkil19yk0on84o44ykk3p` (`user_id`),
ADD KEY `FK9pk3lx8mh8478nxj8lvxvaox0` (`pollID`);
--
-- Index pour la table `Poll`
--
ALTER TABLE `Poll`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `UK_n779urxmh62kwbspgd6gp8564` (`selectedChoice_id`);
--
-- Index pour la table `User`
--
ALTER TABLE `User`
ADD PRIMARY KEY (`id`);
--
-- Contraintes pour les tables déchargées
--
--
-- Contraintes pour la table `Choice`
--
ALTER TABLE `Choice`
ADD CONSTRAINT `FKpptbydus718x0n5w5s1hmtvnp` FOREIGN KEY (`pollID`) REFERENCES `Poll` (`id`);
--
-- Contraintes pour la table `choice_user`
--
ALTER TABLE `choice_user`
ADD CONSTRAINT `FK74lqrm3h9f56d6ijnvjobl0wb` FOREIGN KEY (`user_id`) REFERENCES `User` (`id`),
ADD CONSTRAINT `FKljka9n83yo9s4qpol3wplp1lw` FOREIGN KEY (`choice_id`) REFERENCES `Choice` (`id`);
--
-- Contraintes pour la table `Comment`
--
ALTER TABLE `Comment`
ADD CONSTRAINT `FKgw1unu5kgu9s7sdkqaoy0kyyh` FOREIGN KEY (`pollID`) REFERENCES `Poll` (`id`);
--
-- Contraintes pour la table `MealPreference`
--
ALTER TABLE `MealPreference`
ADD CONSTRAINT `FK61nykkil19yk0on84o44ykk3p` FOREIGN KEY (`user_id`) REFERENCES `User` (`id`),
ADD CONSTRAINT `FK9pk3lx8mh8478nxj8lvxvaox0` FOREIGN KEY (`pollID`) REFERENCES `Poll` (`id`);
--
-- Contraintes pour la table `Poll`
--
ALTER TABLE `Poll`
ADD CONSTRAINT `FKfdictafwo8dwab5rjrjkmmxri` FOREIGN KEY (`selectedChoice_id`) REFERENCES `Choice` (`id`);
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

View File

@@ -0,0 +1,39 @@
#
# Licensed to Apereo under one or more contributor license
# agreements. See the NOTICE file distributed with this work
# for additional information regarding copyright ownership.
# Apereo licenses this file to you under the Apache License,
# Version 2.0 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a
# copy of the License at the following location:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Known optional properties for ical4j:
#net.fortuna.ical4j.parser=net.fortuna.ical4j.data.HCalendarParserFactory
#net.fortuna.ical4j.timezone.registry=net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory
#net.fortuna.ical4j.timezone.update.enabled={true|false}
#net.fortuna.ical4j.timezone.date.floating={true|false}
#net.fortuna.ical4j.factory.decoder=net.fortuna.ical4j.util.DefaultDecoderFactory
#net.fortuna.ical4j.factory.encoder=net.fortuna.ical4j.util.DefaultEncoderFactory
#net.fortuna.ical4j.recur.maxincrementcount=1000
#ical4j.unfolding.relaxed={true|false}
#ical4j.parsing.relaxed={true|false}
#ical4j.validation.relaxed={true|false}
#ical4j.compatibility.outlook={true|false}
#ical4j.compatibility.notes={true|false}
# Values...
ical4j.unfolding.relaxed=true
ical4j.compatibility.outlook=true
ical4j.compatibility.notes=true
ical4j.parsing.relaxed=true
ical4j.validation.relaxed=true

View File

@@ -0,0 +1,21 @@
[
{
"name" : "net.fortuna.ical4j.util.JCacheTimeZoneCache",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"allDeclaredFields" : true,
"allPublicFields" : true
},
{
"name" : "net.fortuna.ical4j.util.MapTimeZoneCache",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"allDeclaredFields" : true,
"allPublicFields" : true
}
]