Fix directory searching
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details

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
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)

View File

@ -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
results := []SearchResult{}
for _, values := range sr.Entries {
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{}),
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]
}

View File

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

View File

@ -1,51 +1,24 @@
var perso_id = 0;
var last_id = 0;
function searchDirectory() {
var input = document.getElementById("search").value;
if(input){
last_id++;
var request_id = last_id;
var data = new FormData();
data.append("query", 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);
if (request_id != last_id) return;
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;");
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)
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);
}
}
}

View File

@ -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">&nbsp;</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>
<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>
<div id="search-results"></div>
{{end}}
<script src="/static/javascript/search.js"></script>
{{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>
<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>