eintopf.info@v0.13.16/service/event/event.go (about) 1 // Copyright (C) 2022 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package event 17 18 import ( 19 "context" 20 "fmt" 21 "strings" 22 "time" 23 24 "eintopf.info/internal/crud" 25 "eintopf.info/service/auth" 26 ) 27 28 // NewEvent defines the data for a new event. 29 type NewEvent struct { 30 Published bool `json:"published"` 31 Parent string `json:"parent"` 32 ParentListed bool `json:"parentListed"` 33 Name string `json:"name"` 34 Organizers []string `json:"organizers"` 35 Involved []Involved `json:"involved"` 36 Location string `json:"location"` 37 Location2 string `json:"location2"` 38 Description string `json:"description"` 39 Start time.Time `json:"start"` 40 End *time.Time `json:"end"` 41 Tags []string `json:"tags"` 42 Image string `json:"image"` 43 OwnedBy []string `json:"ownedBy"` 44 } 45 46 // Involved defines an entity, that is involved with an event. 47 type Involved struct { 48 Name string `json:"name" db:"name"` 49 Description string `json:"description" db:"description"` 50 } 51 52 // IsOwned returns true if the id is in the OwnedBy field. 53 func (e *NewEvent) IsOwned(id string) bool { 54 owned := false 55 for _, owner := range e.OwnedBy { 56 if owner == id { 57 owned = true 58 } 59 } 60 return owned 61 } 62 63 // Event defines a calendar event. 64 // It implements indexo.Coppyable. 65 type Event struct { 66 ID string `json:"id" db:"id"` 67 Deactivated bool `json:"deactivated" db:"deactivated"` 68 Published bool `json:"published" db:"published"` 69 Canceled bool `json:"canceled" db:"canceled"` 70 Parent string `json:"parent" db:"parent"` 71 ParentListed bool `json:"parentListed" db:"parentListed"` 72 Name string `json:"name" db:"name"` 73 Organizers []string `json:"organizers" db:"organizers"` 74 Involved []Involved `json:"involved" db:"involved"` 75 Location string `json:"location" db:"location"` 76 Location2 string `json:"location2" db:"location2"` 77 Description string `json:"description" db:"description"` 78 Start time.Time `json:"start" db:"start"` 79 End *time.Time `json:"end" db:"end"` 80 Tags []string `json:"tags" db:"tags"` 81 Image string `json:"image" db:"image"` 82 OwnedBy []string `json:"ownedBy" db:"ownedBy"` 83 } 84 85 func (e Event) Identifier() string { 86 return e.ID 87 } 88 89 func EventFromNewEvent(newEvent *NewEvent, id string) *Event { 90 return &Event{ 91 ID: id, 92 Deactivated: false, 93 Published: newEvent.Published, 94 Parent: newEvent.Parent, 95 ParentListed: newEvent.ParentListed, 96 Name: newEvent.Name, 97 Organizers: newEvent.Organizers, 98 Involved: newEvent.Involved, 99 Location: newEvent.Location, 100 Location2: newEvent.Location2, 101 Description: newEvent.Description, 102 Image: newEvent.Image, 103 Start: newEvent.Start, 104 End: newEvent.End, 105 Tags: newEvent.Tags, 106 OwnedBy: newEvent.OwnedBy, 107 } 108 } 109 110 // IsOwned returns true if the id is in the OwnedBy field. 111 func (e *Event) IsOwned(id string) bool { 112 owned := false 113 for _, owner := range e.OwnedBy { 114 if owner == id { 115 owned = true 116 } 117 } 118 return owned 119 } 120 121 // Indexable indicates wether the event should be indexed in the search. 122 // All events are indexable if they are not deactivated. 123 func (e *Event) Indexable() bool { 124 if e.Deactivated { 125 return false 126 } 127 return true 128 } 129 130 // Listable indicates wether the event should be shown on list pages. 131 func (e *Event) Listable() bool { 132 if !e.Published { 133 return false 134 } 135 if e.Parent != "" { 136 if strings.HasPrefix(e.Parent, "revent") { 137 return true 138 } 139 if !e.ParentListed { 140 return false 141 } 142 } 143 return true 144 } 145 146 // FindFilters defines the possible filters for the find method. 147 type FindFilters struct { 148 ID *string `json:"id,omitempty"` 149 Deactivated *bool `json:"deactivated,omitempty"` 150 Published *bool `json:"published,omitempty"` 151 Canceled *bool `json:"canceled,omitempty"` 152 Parent *string `json:"parent,omitempty"` 153 Name *string `json:"name,omitempty"` 154 LikeName *string `json:"likeName,omitempty"` 155 Organizers []string `json:"organizers,omitempty"` 156 Location *string `json:"location,omitempty"` 157 Location2 *string `json:"location2,omitempty"` 158 Description *string `json:"description,omitempty"` 159 Start *time.Time `json:"start,omitempty"` 160 StartBefore *time.Time `json:"startBefore,omitempty"` 161 StartAfter *time.Time `json:"startAfter,omitempty"` 162 End *time.Time `json:"end,omitempty"` 163 EndBefore *time.Time `json:"endBefore,omitempty"` 164 EndAfter *time.Time `json:"endAfter,omitempty"` 165 Tags []string `json:"tags,omitempty"` 166 OwnedBy []string `json:"ownedBy,omitempty"` 167 OwnedByOrOrganizers []string 168 } 169 170 // Storer defines a service for CRUD operations on the event model. 171 type Storer = crud.Storer[NewEvent, Event, FindFilters] 172 173 // NewService returns a new event service. 174 func NewService(store Storer, groupOwnerService groupOwnerService) *Service { 175 return &Service{store: store, groupOwnerService: groupOwnerService} 176 } 177 178 type Service struct { 179 store Storer 180 groupOwnerService groupOwnerService 181 } 182 183 type groupOwnerService interface { 184 // GroupIDsByOwners returns all group id for any of the owners. 185 GroupIDsByOwners(ctx context.Context, owners []string) ([]string, error) 186 } 187 188 func (s *Service) Create(ctx context.Context, newEvent *NewEvent) (*Event, error) { 189 id, err := auth.UserIDFromContext(ctx) 190 if err != nil { 191 return nil, err 192 } 193 if !newEvent.IsOwned(id) { 194 newEvent.OwnedBy = append(newEvent.OwnedBy, id) 195 } 196 newEvent.Name = strings.TrimSpace(newEvent.Name) 197 return s.store.Create(ctx, newEvent) 198 } 199 200 func (s *Service) Update(ctx context.Context, event *Event) (*Event, error) { 201 return s.store.Update(ctx, event) 202 } 203 204 func (s *Service) Delete(ctx context.Context, id string) error { 205 return s.store.Delete(ctx, id) 206 } 207 208 func (s *Service) FindByID(ctx context.Context, id string) (*Event, error) { 209 return s.store.FindByID(ctx, id) 210 } 211 212 func (s *Service) Find(ctx context.Context, params *crud.FindParams[FindFilters]) ([]*Event, int, error) { 213 if params != nil && params.Filters != nil && params.Filters.OwnedBy != nil { 214 if len(params.Filters.OwnedBy) == 1 && params.Filters.OwnedBy[0] == "self" { 215 id, err := auth.UserIDFromContext(ctx) 216 if err == nil { 217 params.Filters.OwnedBy = []string{id} 218 } 219 } 220 221 ownedGroupIDs, err := s.groupOwnerService.GroupIDsByOwners(ctx, params.Filters.OwnedBy) 222 if err != nil { 223 return nil, 0, err 224 } 225 if len(ownedGroupIDs) > 0 { 226 for i := range ownedGroupIDs { 227 ownedGroupIDs[i] = fmt.Sprintf("id:%s", ownedGroupIDs[i]) 228 } 229 params.Filters.OwnedByOrOrganizers = ownedGroupIDs 230 } 231 } 232 233 events, total, err := s.store.Find(ctx, params) 234 if err != nil { 235 return nil, 0, err 236 } 237 return events, total, nil 238 } 239 240 func containsOwnedBy(ownedBy, filter []string) bool { 241 for _, o := range ownedBy { 242 for _, f := range filter { 243 if o == f { 244 return true 245 } 246 } 247 } 248 return false 249 }