Fix directory searching
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing

This commit is contained in:
Alex 2021-08-16 16:27:20 +02:00
parent e94bd728ec
commit 01bf4aa522
No known key found for this signature in database
GPG key ID: EDABF9711E244EB1
7 changed files with 110 additions and 117 deletions

View file

@ -1,5 +1,5 @@
BIN=guichet 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 DOCKER=lxpz/guichet_amd64
all: $(BIN) all: $(BIN)

View file

@ -1,13 +1,12 @@
package main package main
import ( import (
"encoding/json"
"html/template" "html/template"
"net/http" "net/http"
"sort"
"strings" "strings"
"github.com/go-ldap/ldap/v3" "github.com/go-ldap/ldap/v3"
"github.com/gorilla/mux"
) )
const FIELD_NAME_PROFILE_PICTURE = "profilePicture" const FIELD_NAME_PROFILE_PICTURE = "profilePicture"
@ -25,28 +24,34 @@ func handleDirectory(w http.ResponseWriter, r *http.Request) {
} }
type SearchResult struct { type SearchResult struct {
Id string `json:"id"` DN string
Displayname string `json:"displayname"` Id string
Email string `json:"email"` DisplayName string
Description string `json:"description"` Email string
DN string `json:"dn"` Description string
ProfilePicture string
} }
type Results struct { type SearchResults struct {
Search []SearchResult `json:"search"` Results []SearchResult
MessageID uint32 `json:"id"`
} }
type UniqueID struct { func handleDirectorySearch(w http.ResponseWriter, r *http.Request) {
Id int `json:"id"` templateDirectoryResults := template.Must(template.ParseFiles("templates/directory_results.html"))
}
func handleSearch(w http.ResponseWriter, r *http.Request) {
//Get input value by user //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 //Log to allow the research
login := checkLogin(w, r) login := checkLogin(w, r)
if login == nil { if login == nil {
http.Error(w, "Login required", http.StatusUnauthorized)
return 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 //Transform the researh's result in a correct struct to send JSON
var result Results results := []SearchResult{}
for _, values := range sr.Entries { for _, values := range sr.Entries {
if ContainsI(values.GetAttributeValue(config.UserNameAttr), input) ||
if strings.Contains(values.GetAttributeValue(config.UserNameAttr), input) || strings.Contains(values.GetAttributeValue("displayname"), input) || ContainsI(values.GetAttributeValue("displayname"), input) ||
(values.GetAttributeValue("email") != "" && strings.Contains(values.GetAttributeValue("email"), input)) { ContainsI(values.GetAttributeValue("mail"), input) {
result = Results{ results = append(results, SearchResult{
Search: append(result.Search, SearchResult{ DN: values.DN,
Id: values.GetAttributeValue(config.UserNameAttr), Id: values.GetAttributeValue(config.UserNameAttr),
Displayname: values.GetAttributeValue("displayname"), DisplayName: values.GetAttributeValue("displayname"),
Email: values.GetAttributeValue("email"), Email: values.GetAttributeValue("mail"),
Description: values.GetAttributeValue("description"), Description: values.GetAttributeValue("description"),
DN: values.DN, ProfilePicture: values.GetAttributeValue(FIELD_NAME_PROFILE_PICTURE),
}), })
}
}
}
if result.Search == nil {
result = Results{
Search: append(result.Search, SearchResult{}),
} }
} }
var id UniqueID search_results := SearchResults{
//Decode JSON body Results: results,
err = json.NewDecoder(r.Body).Decode(&id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} }
result.MessageID = uint32(id.Id) sort.Sort(&search_results)
//Send JSON through xhttp templateDirectoryResults.Execute(w, search_results)
w.Header().Set("Content-Type", "application/json; charset=UTF-8") }
w.WriteHeader(http.StatusCreated)
if err := json.NewEncoder(w).Encode(result); err != nil { func ContainsI(a string, b string) bool {
http.Error(w, err.Error(), http.StatusInternalServerError) 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]
} }

View file

@ -115,8 +115,8 @@ func main() {
r.HandleFunc("/passwd", handlePasswd) r.HandleFunc("/passwd", handlePasswd)
r.HandleFunc("/picture/{name}", handleDownloadPicture) r.HandleFunc("/picture/{name}", handleDownloadPicture)
r.HandleFunc("/directory/search", handleDirectorySearch)
r.HandleFunc("/directory", handleDirectory) r.HandleFunc("/directory", handleDirectory)
r.HandleFunc("/directory/search/{input}", handleSearch)
r.HandleFunc("/invite/new_account", handleInviteNewAccount) r.HandleFunc("/invite/new_account", handleInviteNewAccount)
r.HandleFunc("/invite/send_code", handleInviteSendCode) r.HandleFunc("/invite/send_code", handleInviteSendCode)

View file

@ -1,51 +1,24 @@
var perso_id = 0;
var last_id = 0; var last_id = 0;
function searchDirectory() { function searchDirectory() {
var input = document.getElementById("search").value; var input = document.getElementById("search").value;
if(input){ if(input){
last_id++;
var request_id = last_id;
var data = new FormData();
data.append("query", input);
var xhttp = new XMLHttpRequest(); var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() { xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 201) { if (request_id != last_id) return;
// Typical action to be performed when the document is ready:
//Response from Request Ajax
var jsonResponse = JSON.parse(xhttp.responseText);
if (last_id < jsonResponse.id) { if (this.readyState == 4 && this.status == 200) {
last_id = jsonResponse.id var result_div = document.getElementById("search-results");
//We get the old table element, we create an new table element then we increment this new table. result_div.innerHTML = xhttp.responseText;
//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;");
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
}
old_table.parentNode.replaceChild(table, old_table)
} }
}
}; };
perso_id += 1 xhttp.open("POST", "/directory/search", true);
xhttp.overrideMimeType("application/json"); xhttp.send(data);
xhttp.open("POST", "/search/".concat(input), true);
xhttp.send(JSON.stringify({"id": perso_id}));
} }
} }

View file

@ -1,35 +1,23 @@
{{define "title"}}Directory |{{end}} {{define "title"}}Annuaire |{{end}}
{{define "body"}} {{define "body"}}
<div class="d-flex"> <div class="d-flex">
<h4>Directory</h4> <h4>Annuaire</h4>
<a class="ml-auto btn btn-info" href="/">Menu principal</a> <a class="ml-auto btn btn-info" href="/">Menu principal</a>
</div> </div>
<div class="d-flex"> <form>
<div class="d-flex mx-auto"> <div class="form-group form-row">
<p class="">Name:</p> <div class="col-sm-2">&nbsp;</div>
<form class="px-2" style="width: fit-content;"> <label for="search" class="col-sm-2 col-form-label">Rechercher :</label>
<input id="search" type="text" onkeyup="searchDirectory()" size="20"> <input class="form-control col-sm-4" id="search" name="search" type="text" onkeyup="searchDirectory()" size="20">
</form> </div>
</div> </form>
</div>
<table class="table mt-4"> <div id="search-results"></div>
<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">
<script src="/static/javascript/search.js"></script>
</tbody>
</table>
<script src="/static/javascript/search.js"></script>
{{end}} {{end}}

View file

@ -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}}

View file

@ -5,7 +5,7 @@
<h4>Modifier mon profil</h4> <h4>Modifier mon profil</h4>
<a class="ml-auto btn btn-info" href="/">Retour</a> <a class="ml-auto btn btn-info" href="/">Retour</a>
</div> </div>
<h5>Photo de profil</h5>
{{if .ErrorMessage}} {{if .ErrorMessage}}
<div class="alert alert-danger mt-4">Impossible d'effectuer la modification. <div class="alert alert-danger mt-4">Impossible d'effectuer la modification.
<div style="font-size: 0.8em">{{ .ErrorMessage }}</div> <div style="font-size: 0.8em">{{ .ErrorMessage }}</div>
@ -70,8 +70,8 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="description">Description (180 caractères maximum)</label> <label for="description">Description</label>
<textarea id="description" name="description" class="form-control" maxlength="180">{{ .Description }}</textarea> <textarea id="description" name="description" class="form-control">{{ .Description }}</textarea>
</div> </div>
<button type="submit" class="btn btn-primary">Enregistrer les modifications</button> <button type="submit" class="btn btn-primary">Enregistrer les modifications</button>
</form> </form>