github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/server/core/utils/joomeo/jommeo.go (about) 1 package joomeo 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 8 rd "github.com/benoitkugler/goACVE/server/core/rawdata" 9 10 "github.com/benoitkugler/goACVE/logs" 11 "github.com/benoitkugler/xmlrpc" 12 ) 13 14 const ( 15 URL = "https://api.joomeo.com/xmlrpc.php" 16 RootFolder = "SEJOURS" 17 folderType = "folder" // différencie un dossier d'un album 18 messageDirecteur = `Vous avez maintenant accès à l'album de votre camp (permission lecture/écriture). 19 Vous pouvez retrouver les informations liées à cette album sur votre espace directeur (https://acve.fr/directeurs). 20 ` 21 messageParents = `Bonjour, 22 23 Envie d'avoir un aperçu des activités du %s édition %d? 24 Des photos sont disponibles (ou le seront très prochainement) sur un espace (Joomeo) dédié. 25 Vous y avez accès en suivant le lien ci-dessous. 26 27 Vous pouvez retrouver les informations de connexion sur votre espace personnel ACVE. 28 29 Bon visionnage ! 30 ` 31 ) 32 33 type dict = map[string]interface{} 34 type mclist = []xmlrpc.MulticallArg 35 36 type repSuccess struct { 37 Success int `xmlrpc:"success"` 38 } 39 40 type repNbFiles struct { 41 NbFiles int `xmlrpc:"nbfiles"` 42 } 43 44 func (r repSuccess) Ok() bool { 45 return r.Success == 1 46 } 47 48 type Date float64 49 50 func (d Date) Time() rd.Date { 51 return rd.Date(time.Unix(int64(d/1000), 0)) 52 } 53 54 type ApiJoomeo struct { 55 client *xmlrpc.Client 56 baseArgs dict 57 SpaceURL string 58 } 59 60 type Album struct { 61 AlbumId string `xmlrpc:"albumid" json:"album_id,omitempty"` 62 Label string `xmlrpc:"label" json:"label,omitempty"` 63 Date Date `xmlrpc:"date" json:"-,omitempty"` 64 FolderId string `xmlrpc:"folderid" json:"folder_id,omitempty"` 65 NbFiles int `json:"nb_files,omitempty"` 66 id int64 67 DateParsed rd.Date `json:"date"` 68 } 69 70 // Contact représente un contact Joomeo 71 type Contact struct { 72 ContactId string `xmlrpc:"contactid" json:"contact_id,omitempty"` 73 Mail string `xmlrpc:"email" json:"mail,omitempty"` 74 Login string `xmlrpc:"login" json:"login,omitempty"` 75 Password string `xmlrpc:"password" json:"password,omitempty"` 76 } 77 78 // ContactPermission ajoute à un contact les permissions 79 // pour un album donné. 80 type ContactPermission struct { 81 Contact 82 Allowupload int `json:"allowupload,omitempty"` 83 Allowdownload int `json:"allowdownload,omitempty"` 84 } 85 86 type permission struct { 87 ContactId string `xmlrpc:"contactid"` 88 Allowupload int `xmlrpc:"allowupload"` 89 Allowdownload int `xmlrpc:"allowdownload"` 90 } 91 92 // args ajoute les arguments donnés aux arguments de base 93 // et renvoi le dictionnaire correspondant 94 func (api *ApiJoomeo) args(addArgs dict) dict { 95 out := make(dict) 96 for i, v := range api.baseArgs { 97 out[i] = v 98 } 99 for i, v := range addArgs { 100 out[i] = v 101 } 102 return out 103 } 104 105 func InitApi(credences logs.Joomeo) (*ApiJoomeo, error) { 106 client, err := xmlrpc.NewClient(URL, nil) 107 if err != nil { 108 return nil, err 109 } 110 args := map[string]string{ 111 "apikey": credences.Apikey, 112 "login": credences.Login, 113 "password": credences.Password, 114 } 115 rep := struct { 116 SessionId string `xmlrpc:"sessionid"` 117 SpaceURL string `xmlrpc:"spaceURL"` 118 }{} 119 err = client.Call("joomeo.session.init", args, &rep) 120 if err != nil { 121 return nil, err 122 } 123 api := ApiJoomeo{ 124 client, 125 dict{"sessionid": rep.SessionId, "apikey": credences.Apikey}, 126 rep.SpaceURL, 127 } 128 return &api, nil 129 } 130 131 // Kill termine la session, rendant `api` inutilisable. 132 func (api *ApiJoomeo) Kill() { 133 rep := struct { 134 Success string `xmlrpc:"success"` 135 }{} 136 _ = api.client.Call("joomeo.session.kill", api.args(nil), &rep) // fail silencieux 137 } 138 139 // newMCArgs ajoute les arguments de bases sont ajoutés. 140 func (api *ApiJoomeo) newMCArgs(methodName string, args dict) xmlrpc.MulticallArg { 141 return xmlrpc.NewMulticallArg(methodName, api.args(args)) 142 } 143 144 // GetAlbumMetadatas renvoi un dictionnaire contenant un résumé des informations 145 // de l'album demandé 146 func (api *ApiJoomeo) GetAlbumMetadatas(albumid string) (Album, error) { 147 calls := mclist{ 148 api.newMCArgs("joomeo.user.getAlbumList", nil), 149 api.newMCArgs("joomeo.user.album.getNumberOfFiles", dict{"albumid": albumid}), 150 } 151 var ( 152 albumsList []Album 153 nbFilesRep repNbFiles 154 ) 155 err := api.client.Multicall(calls, &albumsList, &nbFilesRep) 156 if err != nil { 157 return Album{}, err 158 } 159 160 for _, alb := range albumsList { 161 alb.DateParsed = alb.Date.Time() 162 if alb.AlbumId == albumid { 163 alb.NbFiles = nbFilesRep.NbFiles 164 return alb, nil 165 } 166 } 167 return Album{}, errors.New("Album non trouvé sur le serveur Joomeo.") 168 } 169 170 type Folder struct { 171 FolderId string `xmlrpc:"folderid"` 172 Type string `xmlrpc:"type"` 173 Label string `xmlrpc:"label"` 174 id int64 175 childs []Album 176 } 177 178 func (api *ApiJoomeo) GetAlbumsContacts() (map[string]Folder, map[string]Album, map[string]Contact, error) { 179 calls := mclist{ 180 api.newMCArgs("joomeo.user.getFolderChildren", nil), 181 api.newMCArgs("joomeo.user.getAlbumList", nil), 182 api.newMCArgs("joomeo.user.getContactList", nil), 183 } 184 var ( 185 albumsList []Album 186 contactsList []Contact 187 rootFolders []Folder 188 ) 189 err := api.client.Multicall(calls, &rootFolders, &albumsList, &contactsList) 190 if err != nil { 191 return nil, nil, nil, err 192 } 193 var campsFolderid string 194 for _, folder := range rootFolders { 195 if folder.Type != folderType { 196 continue 197 } 198 if folder.Label == RootFolder { 199 campsFolderid = folder.FolderId 200 } 201 } 202 if campsFolderid == "" { 203 return nil, nil, nil, fmt.Errorf("Aucun dossier %s trouvé sur le serveur Joomeo !", RootFolder) 204 } 205 206 calls = mclist{ 207 api.newMCArgs("joomeo.user.getFolderChildren", dict{"folderid": campsFolderid}), 208 } 209 var repFolders []Folder 210 repNbFiles := make([]repNbFiles, len(albumsList)) 211 outs := []interface{}{&repFolders} 212 for index, album := range albumsList { 213 calls = append(calls, api.newMCArgs("joomeo.user.album.getNumberOfFiles", dict{"albumid": album.AlbumId})) 214 outs = append(outs, &repNbFiles[index]) 215 } 216 217 err = api.client.Multicall(calls, outs...) 218 if err != nil { 219 return nil, nil, nil, err 220 } 221 222 folders := make(map[string]Folder) 223 for i, folder := range repFolders { 224 if folder.Type == folderType { 225 folder.id = int64(i) 226 folders[folder.FolderId] = folder 227 } 228 } 229 230 albums := make(map[string]Album, len(albumsList)) 231 for i, alb := range albumsList { 232 alb.NbFiles = repNbFiles[i].NbFiles 233 if folder, isIn := folders[alb.FolderId]; isIn { 234 alb.id = int64(i) 235 albums[alb.AlbumId] = alb 236 237 folder.childs = append(folder.childs, alb) 238 folders[alb.FolderId] = folder 239 } 240 } 241 242 contacts := make(map[string]Contact, len(contactsList)) 243 for _, c := range contactsList { 244 contacts[c.ContactId] = c 245 } 246 return folders, albums, contacts, nil 247 } 248 249 // AjouteDirecteur ajoute un directeur en contact avec droit d'écriture. 250 // Renvoie le contact créé. 251 // EleveSuperContact devrait être appelée ensuite. 252 func (api *ApiJoomeo) AjouteDirecteur(albumid, mailDirecteur string, envoiMail bool) (Contact, error) { 253 var contact Contact 254 err := api.client.Call("joomeo.user.addContact", api.args(dict{"email": mailDirecteur}), &contact) 255 if err != nil { 256 return Contact{}, err 257 } 258 var successAllow, succesMail repSuccess 259 outs := []interface{}{&successAllow} 260 calls := mclist{ 261 api.newMCArgs("joomeo.user.album.allowContactAccess", dict{ 262 "contactid": contact.ContactId, 263 "albumid": albumid, "allowupload": 1, "allowdownload": 1, 264 "allowprintorder": 1, "allowsendcomments": 1, 265 }), 266 } 267 268 if envoiMail { 269 calls = append(calls, api.newMCArgs("joomeo.user.contact.sendInvite", dict{ 270 "contactid": contact.ContactId, "subject": "Album photos Joomeo", 271 "message": messageDirecteur, 272 })) 273 outs = append(outs, &succesMail) 274 } 275 276 err = api.client.Multicall(calls, outs...) 277 if err != nil { 278 return Contact{}, err 279 } 280 if !successAllow.Ok() { 281 return Contact{}, errors.New("Erreur côté Joomeo : impossible d'ajouter le directeur.") 282 } 283 284 if envoiMail && !succesMail.Ok() { 285 return Contact{}, errors.New("Erreur côté Joomeo : impossible d'envoyer un mail d'invitation.") 286 } 287 288 return contact, nil 289 } 290 291 // AjouteContacts imite AjouteDirecteur mais pour plusieurs contacts, 292 // avec une permission lecture et un message adapté. 293 // Renvoie une liste d'erreurs (mail par mail) et une erreur globale. 294 func (api *ApiJoomeo) AjouteContacts(campNom string, campAnnee int, albumid string, mails []string, envoiMail bool) ([]string, error) { 295 Nc := len(mails) 296 calls := make(mclist, Nc) 297 outs, addedContacts := make([]interface{}, Nc), make([]Contact, Nc) 298 for index, mail := range mails { 299 calls[index] = api.newMCArgs("joomeo.user.addContact", dict{"email": mail}) 300 outs[index] = &addedContacts[index] 301 } 302 err := api.client.Multicall(calls, outs...) 303 if err != nil { 304 return nil, err 305 } 306 307 calls = make(mclist, Nc) 308 success, successMail := make([]repSuccess, Nc), make([]repSuccess, Nc) 309 for index, contact := range addedContacts { 310 calls[index] = api.newMCArgs("joomeo.user.album.allowContactAccess", dict{ 311 "albumid": albumid, "contactid": contact.ContactId, 312 "allowupload": 0, "allowdownload": 1, "allowprintorder": 1, "allowsendcomments": 1, 313 }) 314 outs[index] = &success[index] 315 } 316 if envoiMail { 317 message := fmt.Sprintf(messageParents, campNom, campAnnee) 318 319 for index, contact := range addedContacts { 320 calls = append(calls, api.newMCArgs("joomeo.user.contact.sendInvite", dict{ 321 "contactid": contact.ContactId, "subject": "Album photos ACVE", 322 "message": message})) 323 outs = append(outs, &successMail[index]) 324 } 325 } 326 327 err = api.client.Multicall(calls, outs...) 328 if err != nil { 329 return nil, err 330 } 331 var errs []string 332 for i := 0; i < Nc; i++ { 333 mail := mails[i] 334 if !success[i].Ok() { 335 errs = append(errs, fmt.Sprintf("Ajout du contact %s impossible (erreur côté Joomeo).", mail)) 336 } 337 if envoiMail && !successMail[i].Ok() { 338 errs = append(errs, fmt.Sprintf("Envoi d'un mail d'invitation à %s impossible (erreur côté Joomeo).", mail)) 339 } 340 } 341 return errs, nil 342 } 343 344 // GetContacts renvoi les contacts de l'album demandé, 345 // avec les permissions correspondantes. 346 func (api *ApiJoomeo) GetContacts(albumid string) ([]ContactPermission, error) { 347 calls := mclist{ 348 api.newMCArgs("joomeo.user.album.getAllowedContactAccessList", dict{"albumid": albumid}), 349 api.newMCArgs("joomeo.user.getContactList", nil), 350 } 351 var ( 352 albContacts []permission 353 contacts []Contact 354 ) 355 356 err := api.client.Multicall(calls, &albContacts, &contacts) 357 if err != nil { 358 return nil, err 359 } 360 dicCont := make(map[string]Contact) 361 for _, c := range contacts { 362 dicCont[c.ContactId] = c 363 } 364 rep := make([]ContactPermission, len(albContacts)) 365 for index, c := range albContacts { 366 rep[index] = ContactPermission{dicCont[c.ContactId], c.Allowupload, c.Allowdownload} 367 } 368 369 return rep, nil 370 } 371 372 func (api *ApiJoomeo) RemoveContact(albumid, contactid string) error { 373 var out repSuccess 374 err := api.client.Call("joomeo.user.album.removeContactAccess", 375 api.args(dict{"albumid": albumid, "contactid": contactid}), &out) 376 if err != nil { 377 return err 378 } 379 if !out.Ok() { 380 return errors.New("Suppression du contact impossible (erreur côté Joomeo).") 381 } 382 return nil 383 } 384 385 // SetContactUploader ajoute le contact comme uploader. 386 // EleveSuperContact devrait être appelée ensuite. 387 func (api *ApiJoomeo) SetContactUploader(albumid, contactid string) error { 388 var out repSuccess 389 err := api.client.Call("joomeo.user.album.allowContactAccess", 390 api.args(dict{"albumid": albumid, "contactid": contactid, 391 "allowupload": 1, "allowdownload": 1, "allowprintorder": 1, "allowsendcomments": 1}), &out) 392 if err != nil { 393 return err 394 } 395 if !out.Ok() { 396 return errors.New("Ajout du droit en écriture impossible (erreur côté Joomeo).") 397 } 398 return nil 399 } 400 401 func (api *ApiJoomeo) EleveSuperContact(contactid string) error { 402 var out repSuccess 403 // On demande les droits de super contacts 404 err := api.client.Call("joomeo.user.contact.update", api.args(dict{"contactid": contactid, "type": 1}), &out) 405 if err != nil || !out.Ok() { 406 return errors.New("L'élévation vers un super contact a échoué. Est-ce que le compte Joomeo dispose d'une offre suffisante ?") 407 } 408 // Ici, on est sur que le compte a les droits. 409 err = api.client.Call("joomeo.user.contact.updatePermissions", 410 api.args(dict{"contactid": contactid, "allowFileDelete": 1, "allowFileEditCaption": 1}), &out) 411 if err != nil { 412 return err 413 } 414 if !out.Ok() { 415 return errors.New("Impossible d'attribuer le droit de suppression (erreur côté Joomeo).") 416 } 417 return nil 418 } 419 420 // Renvoi le contact et la liste des albums authorisés du contact associé à l'adresse mail fournie. 421 // Le contact peut être zéro. 422 func (api *ApiJoomeo) GetLoginFromMail(mail string) (Contact, []Album, error) { 423 var ( 424 albums []Album 425 contacts []Contact 426 contact Contact 427 ) 428 calls := mclist{ 429 api.newMCArgs("joomeo.user.getAlbumList", nil), 430 api.newMCArgs("joomeo.user.getContactList", nil), 431 } 432 433 err := api.client.Multicall(calls, &albums, &contacts) 434 if err != nil { 435 return contact, nil, err 436 } 437 438 albumsMap, albumsId := make(map[string]Album), make([]string, len(albums)) 439 for index, album := range albums { 440 albId := album.AlbumId 441 albumsMap[albId] = album 442 albumsId[index] = albId 443 } 444 445 for _, cont := range contacts { 446 if cont.Mail == mail { 447 contact = cont 448 break 449 } 450 } 451 if contact.ContactId == "" { // contact inconnu 452 return contact, nil, nil 453 } 454 455 N := len(albumsId) 456 allowedContactsLists := make([][]Contact, N) 457 repNbFiles := make([]repNbFiles, N) 458 calls, outs := make(mclist, 2*N), make([]interface{}, 2*N) 459 for index, albId := range albumsId { 460 calls[2*index] = api.newMCArgs("joomeo.user.album.getAllowedContactAccessList", dict{"albumid": albId}) 461 calls[2*index+1] = api.newMCArgs("joomeo.user.album.getNumberOfFiles", dict{"albumid": albId}) 462 463 outs[2*index] = &allowedContactsLists[index] 464 outs[2*index+1] = &repNbFiles[index] 465 } 466 err = api.client.Multicall(calls, outs...) 467 if err != nil { 468 return contact, nil, err 469 } 470 var albumsLinked []Album 471 for index, cList := range allowedContactsLists { 472 albId := albumsId[index] 473 album := albumsMap[albId] 474 album.NbFiles = repNbFiles[index].NbFiles 475 for _, cont := range cList { 476 if cont.ContactId == contact.ContactId { 477 albumsLinked = append(albumsLinked, album) 478 } 479 } 480 } 481 return contact, albumsLinked, nil 482 }