Fix directory searching
This commit is contained in:
parent
e94bd728ec
commit
01bf4aa522
7 changed files with 110 additions and 117 deletions
2
Makefile
2
Makefile
|
@ -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)
|
||||||
|
|
103
directory.go
103
directory.go
|
@ -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]
|
||||||
}
|
}
|
||||||
|
|
2
main.go
2
main.go
|
@ -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)
|
||||||
|
|
|
@ -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}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"> </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">
|
|
||||||
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<script src="/static/javascript/search.js"></script>
|
|
||||||
|
|
||||||
{{end}}
|
<script src="/static/javascript/search.js"></script>
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
|
23
templates/directory_results.html
Normal file
23
templates/directory_results.html
Normal 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}}
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue