diff options
author | Alex Auvolat <alex@adnab.me> | 2021-08-16 16:27:20 +0200 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2021-08-16 16:27:20 +0200 |
commit | 01bf4aa52279630910ce33d065efcda77e6df7bb (patch) | |
tree | 78dd9c82e09388aae5fd9f4847c0b854f9846de5 | |
parent | e94bd728ec7f709883fb232b2eb123543ba5660e (diff) | |
download | guichet-01bf4aa52279630910ce33d065efcda77e6df7bb.tar.gz guichet-01bf4aa52279630910ce33d065efcda77e6df7bb.zip |
Fix directory searching
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | directory.go | 103 | ||||
-rw-r--r-- | main.go | 2 | ||||
-rw-r--r-- | static/javascript/search.js | 53 | ||||
-rw-r--r-- | templates/directory.html | 38 | ||||
-rw-r--r-- | templates/directory_results.html | 23 | ||||
-rw-r--r-- | templates/profile.html | 6 |
7 files changed, 110 insertions, 117 deletions
@@ -1,5 +1,5 @@ BIN=guichet -SRC=main.go ssha.go profile.go admin.go invite.go +SRC=main.go ssha.go profile.go admin.go invite.go directory.go picture.go DOCKER=lxpz/guichet_amd64 all: $(BIN) diff --git a/directory.go b/directory.go index b1e563d..f3fa0fe 100644 --- a/directory.go +++ b/directory.go @@ -1,13 +1,12 @@ package main import ( - "encoding/json" "html/template" "net/http" + "sort" "strings" "github.com/go-ldap/ldap/v3" - "github.com/gorilla/mux" ) const FIELD_NAME_PROFILE_PICTURE = "profilePicture" @@ -25,28 +24,34 @@ func handleDirectory(w http.ResponseWriter, r *http.Request) { } type SearchResult struct { - Id string `json:"id"` - Displayname string `json:"displayname"` - Email string `json:"email"` - Description string `json:"description"` - DN string `json:"dn"` + DN string + Id string + DisplayName string + Email string + Description string + ProfilePicture string } -type Results struct { - Search []SearchResult `json:"search"` - MessageID uint32 `json:"id"` +type SearchResults struct { + Results []SearchResult } -type UniqueID struct { - Id int `json:"id"` -} +func handleDirectorySearch(w http.ResponseWriter, r *http.Request) { + templateDirectoryResults := template.Must(template.ParseFiles("templates/directory_results.html")) -func handleSearch(w http.ResponseWriter, r *http.Request) { //Get input value by user - input := mux.Vars(r)["input"] + r.ParseMultipartForm(1024) + input := strings.TrimSpace(strings.Join(r.Form["query"], "")) + + if r.Method != "POST" || input == "" { + http.Error(w, "Invalid request", http.StatusBadRequest) + return + } + //Log to allow the research login := checkLogin(w, r) if login == nil { + http.Error(w, "Login required", http.StatusUnauthorized) return } @@ -71,42 +76,46 @@ func handleSearch(w http.ResponseWriter, r *http.Request) { } //Transform the researh's result in a correct struct to send JSON - var result Results - for _, values := range sr.Entries { + results := []SearchResult{} - if strings.Contains(values.GetAttributeValue(config.UserNameAttr), input) || strings.Contains(values.GetAttributeValue("displayname"), input) || - (values.GetAttributeValue("email") != "" && strings.Contains(values.GetAttributeValue("email"), input)) { - result = Results{ - Search: append(result.Search, SearchResult{ - Id: values.GetAttributeValue(config.UserNameAttr), - Displayname: values.GetAttributeValue("displayname"), - Email: values.GetAttributeValue("email"), - Description: values.GetAttributeValue("description"), - DN: values.DN, - }), - } - } - - } - if result.Search == nil { - result = Results{ - Search: append(result.Search, SearchResult{}), + for _, values := range sr.Entries { + if ContainsI(values.GetAttributeValue(config.UserNameAttr), input) || + ContainsI(values.GetAttributeValue("displayname"), input) || + ContainsI(values.GetAttributeValue("mail"), input) { + results = append(results, SearchResult{ + DN: values.DN, + Id: values.GetAttributeValue(config.UserNameAttr), + DisplayName: values.GetAttributeValue("displayname"), + Email: values.GetAttributeValue("mail"), + Description: values.GetAttributeValue("description"), + ProfilePicture: values.GetAttributeValue(FIELD_NAME_PROFILE_PICTURE), + }) } } - var id UniqueID - //Decode JSON body - err = json.NewDecoder(r.Body).Decode(&id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + search_results := SearchResults{ + Results: results, } - result.MessageID = uint32(id.Id) + sort.Sort(&search_results) - //Send JSON through xhttp - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusCreated) - if err := json.NewEncoder(w).Encode(result); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + templateDirectoryResults.Execute(w, search_results) +} + +func ContainsI(a string, b string) bool { + return strings.Contains( + strings.ToLower(a), + strings.ToLower(b), + ) +} + +func (r *SearchResults) Len() int { + return len(r.Results) +} + +func (r *SearchResults) Less(i, j int) bool { + return r.Results[i].Id < r.Results[j].Id +} + +func (r *SearchResults) Swap(i, j int) { + r.Results[i], r.Results[j] = r.Results[j], r.Results[i] } @@ -115,8 +115,8 @@ func main() { r.HandleFunc("/passwd", handlePasswd) r.HandleFunc("/picture/{name}", handleDownloadPicture) + r.HandleFunc("/directory/search", handleDirectorySearch) r.HandleFunc("/directory", handleDirectory) - r.HandleFunc("/directory/search/{input}", handleSearch) r.HandleFunc("/invite/new_account", handleInviteNewAccount) r.HandleFunc("/invite/send_code", handleInviteSendCode) diff --git a/static/javascript/search.js b/static/javascript/search.js index ea98e34..2a75889 100644 --- a/static/javascript/search.js +++ b/static/javascript/search.js @@ -1,51 +1,24 @@ -var perso_id = 0; var last_id = 0; function searchDirectory() { var input = document.getElementById("search").value; if(input){ - var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if (this.readyState == 4 && this.status == 201) { - // Typical action to be performed when the document is ready: - //Response from Request Ajax - var jsonResponse = JSON.parse(xhttp.responseText); + last_id++; + var request_id = last_id; - if (last_id < jsonResponse.id) { - last_id = jsonResponse.id - //We get the old table element, we create an new table element then we increment this new table. - //After the new add, we replace the old table by the new one. - var old_table = document.getElementById("users"); - var table = document.createElement('tbody'); - table.setAttribute("id","users"); - - for (let i =0; i < Object.keys(jsonResponse.search).length; i++) { - var row = table.insertRow(0); - var urlName = row.insertCell(0); - var identifiant = row.insertCell(1); - var displayname = row.insertCell(2); - var email = row.insertCell(3); - var description = row.insertCell(4); - description.setAttribute("style", "word-break: break-all;"); + var data = new FormData(); + data.append("query", input); - if (jsonResponse.search[i].dn.localeCompare("")!=0) { - urlName.innerText = `<object data="/image/${jsonResponse.search[i].dn}/little" class=".img-thumbnail"><image src="/image/unknown_profile/little" class=".img-thumbnail"></object>` - }else { - urlName.innerText="" - } - identifiant.innerText = `<a href="/admin/ldap/${jsonResponse.search[i].dn}">${jsonResponse.search[i].id}</a>` - displayname.innerText = jsonResponse.search[i].displayname - email.innerText = jsonResponse.search[i].email - description.innerText = jsonResponse.search[i].description + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (request_id != last_id) return; - } - old_table.parentNode.replaceChild(table, old_table) + if (this.readyState == 4 && this.status == 200) { + var result_div = document.getElementById("search-results"); + result_div.innerHTML = xhttp.responseText; } - } }; - perso_id += 1 - xhttp.overrideMimeType("application/json"); - xhttp.open("POST", "/search/".concat(input), true); - xhttp.send(JSON.stringify({"id": perso_id})); + xhttp.open("POST", "/directory/search", true); + xhttp.send(data); } -}
\ No newline at end of file +} diff --git a/templates/directory.html b/templates/directory.html index f9bad61..d995fb2 100644 --- a/templates/directory.html +++ b/templates/directory.html @@ -1,35 +1,23 @@ -{{define "title"}}Directory |{{end}} +{{define "title"}}Annuaire |{{end}} {{define "body"}} <div class="d-flex"> - <h4>Directory</h4> + <h4>Annuaire</h4> <a class="ml-auto btn btn-info" href="/">Menu principal</a> </div> -<div class="d-flex"> - <div class="d-flex mx-auto"> - <p class="">Name:</p> - <form class="px-2" style="width: fit-content;"> - <input id="search" type="text" onkeyup="searchDirectory()" size="20"> - </form> - </div> -</div> +<form> + <div class="form-group form-row"> + <div class="col-sm-2"> </div> + <label for="search" class="col-sm-2 col-form-label">Rechercher :</label> + <input class="form-control col-sm-4" id="search" name="search" type="text" onkeyup="searchDirectory()" size="20"> + </div> +</form> + +<div id="search-results"></div> -<table class="table mt-4"> - <thead> - <th scope="col">Profil image</th> - <th scope="col">Identifiant</th> - <th scope="col">Nom complet</th> - <th scope="col">Email</th> - <th scope="col">Description</th> - </thead> - <tbody id="users"> - - - </tbody> - </table> - <script src="/static/javascript/search.js"></script> +<script src="/static/javascript/search.js"></script> -{{end}}
\ No newline at end of file +{{end}} diff --git a/templates/directory_results.html b/templates/directory_results.html new file mode 100644 index 0000000..c7dd715 --- /dev/null +++ b/templates/directory_results.html @@ -0,0 +1,23 @@ +{{if .Results}} + {{range .Results}} + <div class="card mt-4"> + <div class="card-body"> + <div class="float-right"> + {{if .ProfilePicture}} + <a href="/picture/{{.ProfilePicture}}"> + <img src="/picture/{{.ProfilePicture}}-thumb"/> + </a> + {{else}} + {{end}} + </div> + <h5 class="card-title"> + {{.DisplayName}} + <code>{{.Id}}@</code> + </h5> + <p class="card-text">{{.Description}}</p> + </div> + </div> + {{end}} +{{else}} + Aucun résultat. +{{end}} diff --git a/templates/profile.html b/templates/profile.html index 8704e23..edf9d76 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -5,7 +5,7 @@ <h4>Modifier mon profil</h4> <a class="ml-auto btn btn-info" href="/">Retour</a> </div> -<h5>Photo de profil</h5> + {{if .ErrorMessage}} <div class="alert alert-danger mt-4">Impossible d'effectuer la modification. <div style="font-size: 0.8em">{{ .ErrorMessage }}</div> @@ -70,8 +70,8 @@ </div> <div class="form-group"> - <label for="description">Description (180 caractères maximum)</label> - <textarea id="description" name="description" class="form-control" maxlength="180">{{ .Description }}</textarea> + <label for="description">Description</label> + <textarea id="description" name="description" class="form-control">{{ .Description }}</textarea> </div> <button type="submit" class="btn btn-primary">Enregistrer les modifications</button> </form> |