forked from Deuxfleurs/guichet
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
|
||||
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)
|
||||
|
|
93
directory.go
93
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
|
||||
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"),
|
||||
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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
search_results := SearchResults{
|
||||
Results: results,
|
||||
}
|
||||
if result.Search == nil {
|
||||
result = Results{
|
||||
Search: append(result.Search, SearchResult{}),
|
||||
}
|
||||
sort.Sort(&search_results)
|
||||
|
||||
templateDirectoryResults.Execute(w, search_results)
|
||||
}
|
||||
|
||||
var id UniqueID
|
||||
//Decode JSON body
|
||||
err = json.NewDecoder(r.Body).Decode(&id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
func ContainsI(a string, b string) bool {
|
||||
return strings.Contains(
|
||||
strings.ToLower(a),
|
||||
strings.ToLower(b),
|
||||
)
|
||||
}
|
||||
result.MessageID = uint32(id.Id)
|
||||
|
||||
//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)
|
||||
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("/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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 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>
|
||||
</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">
|
||||
<div id="search-results"></div>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<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>
|
||||
<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>
|
||||
|
|
Loading…
Reference in a new issue