package application import ( "context" "encoding/json" "fmt" "io" "net/http" "sort" "strconv" "strings" "time" formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/utils/form-validators" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" "git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting" filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" agenda "git.coopgo.io/coopgo-platform/agenda/grpcapi" agendastorage "git.coopgo.io/coopgo-platform/agenda/storage" groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi" "git.coopgo.io/coopgo-platform/groups-management/storage" mobilityaccounts "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi" "github.com/google/uuid" "github.com/gorilla/mux" "github.com/rs/zerolog/log" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" ) type EventsForm struct { Name string `json:"name" validate:"required"` Type string `json:"type" validate:"required"` Description string `json:"description"` Address any `json:"address,omitempty"` Allday bool `json:"allday"` Startdate *time.Time `json:"startdate"` Enddate *time.Time `json:"enddate"` Starttime string `json:"starttime"` Endtime string `json:"endtime"` MaxSubscribers int `json:"max_subscribers"` } func (h *ApplicationHandler) AgendaHome(w http.ResponseWriter, r *http.Request) { resp, err := h.services.GRPC.Agenda.GetEvents(context.TODO(), &agenda.GetEventsRequest{ Namespaces: []string{"parcoursmob_dispositifs"}, Mindate: timestamppb.New(time.Now().Add(-24 * time.Hour)), }) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } responses := []agendastorage.Event{} groupids := []string{} for _, e := range resp.Events { groupids = append(groupids, e.Owners...) responses = append(responses, e.ToStorageType()) } sort.Sort(sorting.EventsByStartdate(responses)) groupsresp, err := h.services.GRPC.GroupsManagement.GetGroupsBatch(context.TODO(), &groupsmanagement.GetGroupsBatchRequest{ Groupids: groupids, }) groups := map[string]any{} if err == nil { for _, g := range groupsresp.Groups { groups[g.Id] = g.ToStorageType() } } h.Renderer.AgendaHome(w, r, responses, groups) } func (h *ApplicationHandler) AgendaHistory(w http.ResponseWriter, r *http.Request) { resp, err := h.services.GRPC.Agenda.GetEvents(context.TODO(), &agenda.GetEventsRequest{ Namespaces: []string{"parcoursmob_dispositifs"}, //Maxdate: timestamppb.New(time.Now().Add(24 * time.Hour)), }) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } responses := []agendastorage.Event{} groupids := []string{} for _, e := range resp.Events { groupids = append(groupids, e.Owners...) responses = append(responses, e.ToStorageType()) } sort.Sort(sorting.EventsByStartdate(responses)) groupsresp, err := h.services.GRPC.GroupsManagement.GetGroupsBatch(context.TODO(), &groupsmanagement.GetGroupsBatchRequest{ Groupids: groupids, }) groups := map[string]any{} if err == nil { for _, g := range groupsresp.Groups { groups[g.Id] = g.ToStorageType() } } h.Renderer.AgendaHistory(w, r, responses, groups) } func (h *ApplicationHandler) AgendaCreateEvent(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { // Get current group g := r.Context().Value(identification.GroupKey) if g == nil { w.WriteHeader(http.StatusBadRequest) return } group := g.(storage.Group) eventForm, err := parseEventsForm(r) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusBadRequest) return } log.Debug().Any("eventFrom", eventForm).Msg("Form data submitted to create event") data, _ := structpb.NewStruct(map[string]any{ "address": eventForm.Address, }) request := &agenda.CreateEventRequest{ Event: &agenda.Event{ Namespace: "parcoursmob_dispositifs", Owners: []string{group.ID}, Type: eventForm.Type, Name: eventForm.Name, Description: eventForm.Description, Startdate: timestamppb.New(*eventForm.Startdate), Enddate: timestamppb.New(*eventForm.Enddate), Starttime: eventForm.Starttime, Endtime: eventForm.Endtime, Allday: eventForm.Allday, MaxSubscribers: int64(eventForm.MaxSubscribers), Data: data, Deleted: false, }, } resp, err := h.services.GRPC.Agenda.CreateEvent(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } contentType := r.Header.Get("Content-Type") if strings.HasPrefix(contentType, "multipart/form-data") { err = r.ParseMultipartForm(100 * 1024 * 1024) // 100 MB limit if err != nil { log.Error().Err(err).Msg("Error parsing multipart form") w.WriteHeader(http.StatusBadRequest) return } file, header, err := r.FormFile("file-upload") if err == nil { defer file.Close() document_type := r.FormValue("file_type") document_name := r.FormValue("file_name") fileid := uuid.NewString() metadata := map[string]string{ "file_type": document_type, "file_name": document_name, } if err := h.filestorage.Put(file, filestorage.PREFIX_AGENDA, fmt.Sprintf("%s/%s_%s", resp.Event.Id, fileid, header.Filename), header.Size, metadata); err != nil { log.Error().Err(err).Msg("Error uploading file") w.WriteHeader(http.StatusInternalServerError) return } } else if err != http.ErrMissingFile { log.Error().Err(err).Msg("Error retrieving file") w.WriteHeader(http.StatusInternalServerError) return } } http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", resp.Event.Id), http.StatusFound) return } h.Renderer.AgendaCreateEvent(w, r, h.config.GetStringSlice("modules.agenda.documents_types"), h.config.GetStringMapString("storage.files.file_types"), nil) } func (h *ApplicationHandler) AgendaDisplayEvent(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) eventid := vars["eventid"] request := &agenda.GetEventRequest{ Id: eventid, } resp, err := h.services.GRPC.Agenda.GetEvent(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } grouprequest := &groupsmanagement.GetGroupRequest{ Id: resp.Event.Owners[0], } groupresp, err := h.services.GRPC.GroupsManagement.GetGroup(context.TODO(), grouprequest) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } subscribers := map[string]any{} accids := []string{} for _, v := range resp.Event.Subscriptions { accids = append(accids, v.Subscriber) } subscriberresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch( context.TODO(), &mobilityaccounts.GetAccountsBatchRequest{ Accountids: accids, }, ) if err == nil { for _, sub := range subscriberresp.Accounts { subscribers[sub.Id] = sub.ToStorageType() } } g := r.Context().Value(identification.GroupKey) if g == nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } group := g.(storage.Group) accountids := []string{} for _, m := range group.Members { if !contains(resp.Event.Subscriptions, m) { accountids = append(accountids, m) } } accountresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch( context.TODO(), &mobilityaccounts.GetAccountsBatchRequest{ Accountids: accountids, }, ) accounts := []any{} if err == nil { for _, acc := range accountresp.Accounts { accounts = append(accounts, acc) } } documents := h.filestorage.List(filestorage.PREFIX_AGENDA + "/" + eventid) events_file_types := h.config.GetStringSlice("modules.agenda.documents_types") file_types_map := h.config.GetStringMapString("storage.files.file_types") h.Renderer.AgendaDisplayEvent(w, r, resp.Event.ToStorageType(), groupresp.Group.ToStorageType(), events_file_types, file_types_map, documents, subscribers, accounts) } func (h *ApplicationHandler) AgendaSubscribeEvent(w http.ResponseWriter, r *http.Request) { current_group, err := h.currentGroup(r) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } current_user_token, current_user_claims, err := h.currentUser(r) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } vars := mux.Vars(r) eventid := vars["eventid"] if err := r.ParseForm(); err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusBadRequest) return } subscriber := r.FormValue("subscriber") data := map[string]any{ "subscribed_by": map[string]any{ "user": map[string]any{ "id": current_user_token.Subject, "display_name": current_user_claims["first_name"].(string) + " " + current_user_claims["last_name"].(string), "email": current_user_claims["email"].(string), }, "group": map[string]any{ "id": current_group.ID, "name": current_group.Data["name"], }, }, } datapb, err := structpb.NewStruct(data) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } request := &agenda.SubscribeEventRequest{ Eventid: eventid, Subscriber: subscriber, Data: datapb, } _, err = h.services.GRPC.Agenda.SubscribeEvent(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventid), http.StatusFound) } func parseEventsForm(r *http.Request) (*EventsForm, error) { if err := r.ParseForm(); err != nil { return nil, err } var startdate *time.Time var enddate *time.Time if r.PostFormValue("startdate") != "" { d, err := time.Parse("2006-01-02", r.PostFormValue("startdate")) if err != nil { return nil, err } startdate = &d } if r.PostFormValue("enddate") != "" { d, err := time.Parse("2006-01-02", r.PostFormValue("enddate")) if err != nil { return nil, err } enddate = &d } max_subscribers, err := strconv.Atoi(r.PostFormValue("max_subscribers")) if err != nil { return nil, err } formData := &EventsForm{ Name: r.PostFormValue("name"), Type: r.PostFormValue("type"), Description: r.PostFormValue("description"), Startdate: startdate, Enddate: enddate, Starttime: r.PostFormValue("starttime"), Endtime: r.PostFormValue("endtime"), MaxSubscribers: max_subscribers, } if r.PostFormValue("allday") == "true" { formData.Allday = true } if r.PostFormValue("address") != "" { var a any json.Unmarshal([]byte(r.PostFormValue("address")), &a) formData.Address = a } validate := formvalidators.New() if err := validate.Struct(formData); err != nil { return nil, err } return formData, nil } func contains(s []*agenda.Subscription, e string) bool { for _, a := range s { if a.Subscriber == e { return true } } return false } // /////////////////////////////Update Event///////////////////////////////////////// func (h *ApplicationHandler) AgendaUpdateEvent(w http.ResponseWriter, r *http.Request) { adm := strings.Split(r.URL.Path, "/") eventID := adm[3] request := &agenda.GetEventRequest{ Id: eventID, } resp, err := h.services.GRPC.Agenda.GetEvent(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } if r.Method == "POST" { g := r.Context().Value(identification.GroupKey) if g == nil { w.WriteHeader(http.StatusBadRequest) return } group := g.(storage.Group) eventForm, err := parseEventsForm(r) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusBadRequest) return } data, _ := structpb.NewStruct(map[string]any{ "address": eventForm.Address, }) request := &agenda.UpdateEventRequest{ Event: &agenda.Event{ Namespace: "parcoursmob_dispositifs", Id: eventID, Owners: []string{group.ID}, Type: eventForm.Type, Name: eventForm.Name, Description: eventForm.Description, Startdate: timestamppb.New(*eventForm.Startdate), Enddate: timestamppb.New(*eventForm.Enddate), Starttime: eventForm.Starttime, Endtime: eventForm.Endtime, Allday: eventForm.Allday, MaxSubscribers: int64(eventForm.MaxSubscribers), Data: data, Subscriptions: resp.Event.Subscriptions, }, } resp, err := h.services.GRPC.Agenda.UpdateEvent(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", resp.Event.Id), http.StatusFound) return } h.Renderer.AgendaUpdateEvent(w, r, resp.Event.ToStorageType()) } func (h *ApplicationHandler) AgendaDeleteEvent(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) eventID := vars["eventid"] request := &agenda.GetEventRequest{ Id: eventID, } resp, err := h.services.GRPC.Agenda.GetEvent(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } if r.Method == "POST" { request := &agenda.UpdateEventRequest{ Event: &agenda.Event{ Namespace: resp.Event.Namespace, Id: resp.Event.Id, Owners: resp.Event.Owners, Type: resp.Event.Type, Name: resp.Event.Name, Description: resp.Event.Description, Startdate: resp.Event.Startdate, Enddate: resp.Event.Enddate, Starttime: resp.Event.Starttime, Endtime: resp.Event.Endtime, Allday: resp.Event.Allday, MaxSubscribers: int64(resp.Event.MaxSubscribers), Data: resp.Event.Data, Subscriptions: resp.Event.Subscriptions, Deleted: true, }, } _, err := h.services.GRPC.Agenda.UpdateEvent(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, "/app/agenda/", http.StatusFound) return } h.Renderer.AgendaDeleteEvent(w, r, resp.Event.ToStorageType()) } // /////////////////////////Delete subscriber/////////////////////////////// func (h *ApplicationHandler) AgendaDeleteSubscribeEvent(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) eventId := vars["eventid"] subscribeid := vars["subscribeid"] s_b_id := "" s_b_name := "" s_b_email := "" s_b_group_id := "" s_b_group_name := "" request := &agenda.GetEventRequest{ Id: eventId, } resp, err := h.services.GRPC.Agenda.GetEvent(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } for i := range resp.Event.Subscriptions { if resp.Event.Subscriptions[i].Subscriber == subscribeid { subscribed_by_id := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["user"].GetStructValue().Fields["id"].GetStringValue() subscribed_by_name := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["user"].GetStructValue().Fields["display_name"].GetStringValue() subscribed_by_email := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["user"].GetStructValue().Fields["email"].GetStringValue() subscribed_by_group_id := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["group"].GetStructValue().Fields["id"].GetStringValue() subscribed_by_group_name := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["group"].GetStructValue().Fields["name"].GetStringValue() s_b_id = subscribed_by_id s_b_name = subscribed_by_name s_b_email = subscribed_by_email s_b_group_id = subscribed_by_group_id s_b_group_name = subscribed_by_group_name } } current_group, err := h.currentGroup(r) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } current_user_token, current_user_claims, err := h.currentUser(r) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } data := map[string]any{ "subscribed_by": map[string]any{ "user": map[string]any{ "id": s_b_id, "display_name": s_b_name, "email": s_b_email, }, "group": map[string]any{ "id": s_b_group_id, "name": s_b_group_name, }, }, "unsubscribed_by": map[string]any{ "user": map[string]any{ "id": current_user_token.Subject, "display_name": current_user_claims["first_name"].(string) + " " + current_user_claims["last_name"].(string), "email": current_user_claims["email"], }, "group": map[string]any{ "id": current_group.ID, "name": current_group.Data["name"], }, }, "motif": r.FormValue("motif"), } datapb, err := structpb.NewStruct(data) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } if r.Method == "POST" { request := &agenda.DeleteSubscriptionRequest{ Subscriber: subscribeid, Eventid: eventId, Data: datapb, } data := map[string]any{ "motif": r.FormValue("motif"), "user": current_user_claims["first_name"].(string) + " " + current_user_claims["last_name"].(string), "subscriber": fmt.Sprintf("http://localhost:9000/app/beneficiaries/%s", subscribeid), "link": fmt.Sprintf("http://localhost:9000/app/agenda/%s", eventId), } // récupérer l'adresse mail de l'utilisateur qui a créé l'événement mail := s_b_email log.Debug().Str("mail", mail).Msg("Email content") _, err := h.services.GRPC.Agenda.DeleteSubscription(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } if err := h.emailing.Send("delete_subscriber.request", mail, data); err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventId), http.StatusFound) return } h.Renderer.AgendaDeleteSubscribeEvent(w, r, eventId) } // ////////////////////////////////////////////////////// // /////////////////////History Event//////////////////////// func (h *ApplicationHandler) AgendaHistoryEvent(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) eventId := vars["eventid"] request := &agenda.GetEventRequest{ Id: eventId, } resp, err := h.services.GRPC.Agenda.GetEvent(context.TODO(), request) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } grouprequest := &groupsmanagement.GetGroupRequest{ Id: resp.Event.Owners[0], } groupresp, err := h.services.GRPC.GroupsManagement.GetGroup(context.TODO(), grouprequest) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } subscribers := map[string]any{} accids := []string{} for _, v := range resp.Event.DeletedSubscription { accids = append(accids, v.Subscriber) } subscriberresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch( context.TODO(), &mobilityaccounts.GetAccountsBatchRequest{ Accountids: accids, }, ) if err == nil { for _, sub := range subscriberresp.Accounts { subscribers[sub.Id] = sub.ToStorageType() } } g := r.Context().Value(identification.GroupKey) if g == nil { w.WriteHeader(http.StatusInternalServerError) return } group := g.(storage.Group) accountids := []string{} for _, m := range group.Members { if !contains(resp.Event.DeletedSubscription, m) { accountids = append(accountids, m) } } accountresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch( context.TODO(), &mobilityaccounts.GetAccountsBatchRequest{ Accountids: accountids, }, ) accounts := []any{} if err == nil { for _, acc := range accountresp.Accounts { accounts = append(accounts, acc) } } h.Renderer.AgendaHistoryEvent(w, r, resp.Event.ToStorageType(), groupresp.Group.ToStorageType(), subscribers, accounts) } // //// ADD DOCUMENTS ////// func (h *ApplicationHandler) EventDocuments(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) eventID := vars["eventid"] //r.ParseForm() r.ParseMultipartForm(100 * 1024 * 1024) document_type := r.FormValue("type") document_name := r.FormValue("name") file, header, err := r.FormFile("file-upload") if err != nil { log.Error().Err(err).Msg("") return } defer file.Close() fileid := uuid.NewString() metadata := map[string]string{ "type": document_type, "name": document_name, } if err := h.filestorage.Put(file, filestorage.PREFIX_AGENDA, fmt.Sprintf("%s/%s_%s", eventID, fileid, header.Filename), header.Size, metadata); err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventID), http.StatusFound) } func (h *ApplicationHandler) EventDocumentDownload(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) eventID := vars["eventid"] document := vars["document"] file, info, err := h.filestorage.Get(filestorage.PREFIX_AGENDA, fmt.Sprintf("%s/%s", eventID, document)) if err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } w.Header().Set("Content-Type", info.ContentType) if _, err = io.Copy(w, file); err != nil { log.Error().Err(err).Msg("") w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventID), http.StatusFound) }