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  }