Ispravke: ugnježdena forma, greška čitanja fajla, mrtvi flag, refaktor struct
- podesavanja_opste.html: forma za uklanjanje loga premešena van forme za otpremanje — ugnježdene forme su nevažeći HTML - ProfilOtpremiAvatar: greška iz fajl.Read(buf) se sada proverava (dozvoljava io.EOF, odbacuje pravi problem čitanja) - TopbarLogoTekst uklonjen: iz modela, oba handlera i struct-a u podesavanja.go (podešavanje nije korišćeno ni u jednom šablonu) - korisnici.go: dodeliOpcijeKorisnika prima korisnikOpcije struct umesto dugačke liste parametara; skeniraiKorisnika i Lista ažurirani
This commit is contained in:
@@ -17,43 +17,48 @@ type sqliteKorisniciRepo struct {
|
|||||||
kljuc []byte
|
kljuc []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// dodeliOpcijeKorisnika popunjava bool i opciona polja korisnika iz skeniranih
|
// korisnikOpcije drži NULL vrednosti skeniranih opcionalnih kolona korisnika;
|
||||||
// NULL vrednosti — deljeno između skeniraiKorisnika (jedan red) i Lista (više redova)
|
// dodavanje novog polja zahteva izmenu samo ovog struct-a i relevantnih Scan poziva.
|
||||||
func dodeliOpcijeKorisnika(k *model.Korisnik, aktivan, koristiLokalnuTemu int,
|
type korisnikOpcije struct {
|
||||||
lokalnaTema, lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur,
|
aktivan int
|
||||||
lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity, avatarPutanja sql.NullString,
|
koristiLokalnuTemu int
|
||||||
datumKreiranja time.Time) {
|
datumKreiranja time.Time
|
||||||
k.Aktivan = aktivan == 1
|
lokalnaTema sql.NullString
|
||||||
k.LokalnaTema = lokalnaTema.String
|
lokalnaPozadina sql.NullString
|
||||||
k.KoristiLokalnuTemu = koristiLokalnuTemu == 1
|
lokalnaPozadinaOpacity sql.NullString
|
||||||
k.DatumKreiranja = datumKreiranja
|
lokalnaPozadinaBlur sql.NullString
|
||||||
k.LokalnaPozadina = lokalnaPozadina.String
|
lokalnaPozadinaBlurPozadine sql.NullString
|
||||||
k.LokalnaPozadinaOpacity = lokalnaPozadinaOpacity.String
|
lokalnaPozadinaGlassOpacity sql.NullString
|
||||||
k.LokalnaPozadinaBlur = lokalnaPozadinaBlur.String
|
avatarPutanja sql.NullString
|
||||||
k.LokalnaPozadinaBlurPozadine = lokalnaPozadinaBlurPozadine.String
|
}
|
||||||
k.LokalnaPozadinaGlassOpacity = lokalnaPozadinaGlassOpacity.String
|
|
||||||
k.AvatarPutanja = avatarPutanja.String
|
// dodeliOpcijeKorisnika prenosi vrednosti iz korisnikOpcije na model.Korisnik
|
||||||
|
func dodeliOpcijeKorisnika(k *model.Korisnik, o korisnikOpcije) {
|
||||||
|
k.Aktivan = o.aktivan == 1
|
||||||
|
k.KoristiLokalnuTemu = o.koristiLokalnuTemu == 1
|
||||||
|
k.DatumKreiranja = o.datumKreiranja
|
||||||
|
k.LokalnaTema = o.lokalnaTema.String
|
||||||
|
k.LokalnaPozadina = o.lokalnaPozadina.String
|
||||||
|
k.LokalnaPozadinaOpacity = o.lokalnaPozadinaOpacity.String
|
||||||
|
k.LokalnaPozadinaBlur = o.lokalnaPozadinaBlur.String
|
||||||
|
k.LokalnaPozadinaBlurPozadine = o.lokalnaPozadinaBlurPozadine.String
|
||||||
|
k.LokalnaPozadinaGlassOpacity = o.lokalnaPozadinaGlassOpacity.String
|
||||||
|
k.AvatarPutanja = o.avatarPutanja.String
|
||||||
}
|
}
|
||||||
|
|
||||||
// skeniraiKorisnika čita jedan red iz baze i popunjava model.Korisnik
|
// skeniraiKorisnika čita jedan red iz baze i popunjava model.Korisnik
|
||||||
func skeniraiKorisnika(row interface{ Scan(...any) error }) (*model.Korisnik, error) {
|
func skeniraiKorisnika(row interface{ Scan(...any) error }) (*model.Korisnik, error) {
|
||||||
k := &model.Korisnik{}
|
k := &model.Korisnik{}
|
||||||
var aktivan, koristiLokalnuTemu int
|
var o korisnikOpcije
|
||||||
var lokalnaTema sql.NullString
|
|
||||||
var lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur, lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString
|
|
||||||
var avatarPutanja sql.NullString
|
|
||||||
var datumKreiranja time.Time
|
|
||||||
if err := row.Scan(
|
if err := row.Scan(
|
||||||
&k.ID, &k.KorisnickoIme, &k.LozinkaHash, &k.Uloga, &aktivan, &k.TotpTajna,
|
&k.ID, &k.KorisnickoIme, &k.LozinkaHash, &k.Uloga, &o.aktivan, &k.TotpTajna,
|
||||||
&lokalnaTema, &koristiLokalnuTemu, &datumKreiranja,
|
&o.lokalnaTema, &o.koristiLokalnuTemu, &o.datumKreiranja,
|
||||||
&lokalnaPozadina, &lokalnaPozadinaOpacity, &lokalnaPozadinaBlur,
|
&o.lokalnaPozadina, &o.lokalnaPozadinaOpacity, &o.lokalnaPozadinaBlur,
|
||||||
&lokalnaPozadinaBlurPozadine, &lokalnaPozadinaGlassOpacity, &avatarPutanja,
|
&o.lokalnaPozadinaBlurPozadine, &o.lokalnaPozadinaGlassOpacity, &o.avatarPutanja,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dodeliOpcijeKorisnika(k, aktivan, koristiLokalnuTemu, lokalnaTema,
|
dodeliOpcijeKorisnika(k, o)
|
||||||
lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur,
|
|
||||||
lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity, avatarPutanja, datumKreiranja)
|
|
||||||
return k, nil
|
return k, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,20 +139,16 @@ func (r *sqliteKorisniciRepo) Lista(ctx context.Context) ([]model.Korisnik, erro
|
|||||||
var lista []model.Korisnik
|
var lista []model.Korisnik
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var k model.Korisnik
|
var k model.Korisnik
|
||||||
var aktivan, koristiLokalnuTemu int
|
var o korisnikOpcije
|
||||||
var lokalnaTema sql.NullString
|
if err := rows.Scan(
|
||||||
var lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur, lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString
|
&k.ID, &k.KorisnickoIme, &k.Uloga, &o.aktivan, &k.TotpTajna,
|
||||||
var avatarPutanja sql.NullString
|
&o.lokalnaTema, &o.koristiLokalnuTemu, &o.datumKreiranja,
|
||||||
var datumKreiranja time.Time
|
&o.lokalnaPozadina, &o.lokalnaPozadinaOpacity, &o.lokalnaPozadinaBlur,
|
||||||
if err := rows.Scan(&k.ID, &k.KorisnickoIme, &k.Uloga, &aktivan, &k.TotpTajna,
|
&o.lokalnaPozadinaBlurPozadine, &o.lokalnaPozadinaGlassOpacity, &o.avatarPutanja,
|
||||||
&lokalnaTema, &koristiLokalnuTemu, &datumKreiranja,
|
); err != nil {
|
||||||
&lokalnaPozadina, &lokalnaPozadinaOpacity, &lokalnaPozadinaBlur, &lokalnaPozadinaBlurPozadine,
|
|
||||||
&lokalnaPozadinaGlassOpacity, &avatarPutanja); err != nil {
|
|
||||||
return nil, fmt.Errorf("ntech: korisnici.Lista: %w", err)
|
return nil, fmt.Errorf("ntech: korisnici.Lista: %w", err)
|
||||||
}
|
}
|
||||||
dodeliOpcijeKorisnika(&k, aktivan, koristiLokalnuTemu, lokalnaTema,
|
dodeliOpcijeKorisnika(&k, o)
|
||||||
lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur,
|
|
||||||
lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity, avatarPutanja, datumKreiranja)
|
|
||||||
r.desifrujTotpTajnu(&k)
|
r.desifrujTotpTajnu(&k)
|
||||||
lista = append(lista, k)
|
lista = append(lista, k)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,7 +166,6 @@ func (h *Handler) popuniPodaciStranice(r *http.Request, podesavanja map[string]s
|
|||||||
Podnazlov: podesavanja["podnazlov"],
|
Podnazlov: podesavanja["podnazlov"],
|
||||||
LogoPutanja: podesavanja["logo_putanja"],
|
LogoPutanja: podesavanja["logo_putanja"],
|
||||||
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
|
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
|
||||||
TopbarLogoTekst: podesavanja["topbar_logo_tekst"] == "1",
|
|
||||||
Korisnik: "Admin",
|
Korisnik: "Admin",
|
||||||
}
|
}
|
||||||
var korisnik *model.Korisnik
|
var korisnik *model.Korisnik
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ type PodaciPodesavanja struct {
|
|||||||
PIB string
|
PIB string
|
||||||
LogoPutanja string
|
LogoPutanja string
|
||||||
TopbarLogoSlika bool
|
TopbarLogoSlika bool
|
||||||
TopbarLogoTekst bool
|
|
||||||
// profil firme — pravni/poreski status (Faza 0); određuje koji se zakonski moduli pale
|
// profil firme — pravni/poreski status (Faza 0); određuje koji se zakonski moduli pale
|
||||||
FirmaPravniOblik string
|
FirmaPravniOblik string
|
||||||
FirmaPdvObveznik string
|
FirmaPdvObveznik string
|
||||||
@@ -84,7 +83,6 @@ func (h *Handler) Podesavanja(w http.ResponseWriter, r *http.Request) {
|
|||||||
PIB: podesavanja["pib"],
|
PIB: podesavanja["pib"],
|
||||||
LogoPutanja: podesavanja["logo_putanja"],
|
LogoPutanja: podesavanja["logo_putanja"],
|
||||||
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
|
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
|
||||||
TopbarLogoTekst: podesavanja["topbar_logo_tekst"] == "1",
|
|
||||||
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
|
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
|
||||||
BackupVracen: r.URL.Query().Get("sacuvano") == "vraceno",
|
BackupVracen: r.URL.Query().Get("sacuvano") == "vraceno",
|
||||||
Verzija: h.Verzija,
|
Verzija: h.Verzija,
|
||||||
@@ -240,10 +238,6 @@ func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) {
|
|||||||
if r.FormValue("topbar_logo_slika") == "1" {
|
if r.FormValue("topbar_logo_slika") == "1" {
|
||||||
topbarLogoSlika = "1"
|
topbarLogoSlika = "1"
|
||||||
}
|
}
|
||||||
topbarLogoTekst := "0"
|
|
||||||
if r.FormValue("topbar_logo_tekst") == "1" {
|
|
||||||
topbarLogoTekst = "1"
|
|
||||||
}
|
|
||||||
|
|
||||||
polja := map[string]string{
|
polja := map[string]string{
|
||||||
"naziv_firme": r.FormValue("naziv_firme"),
|
"naziv_firme": r.FormValue("naziv_firme"),
|
||||||
@@ -252,7 +246,6 @@ func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) {
|
|||||||
"telefon": r.FormValue("telefon"),
|
"telefon": r.FormValue("telefon"),
|
||||||
"pib": r.FormValue("pib"),
|
"pib": r.FormValue("pib"),
|
||||||
"topbar_logo_slika": topbarLogoSlika,
|
"topbar_logo_slika": topbarLogoSlika,
|
||||||
"topbar_logo_tekst": topbarLogoTekst,
|
|
||||||
// profil firme (Faza 0) — radio dugmad uvek šalju vrednost, pa se uredno čuvaju
|
// profil firme (Faza 0) — radio dugmad uvek šalju vrednost, pa se uredno čuvaju
|
||||||
"firma_pravni_oblik": r.FormValue("firma_pravni_oblik"),
|
"firma_pravni_oblik": r.FormValue("firma_pravni_oblik"),
|
||||||
"firma_pdv_obveznik": r.FormValue("firma_pdv_obveznik"),
|
"firma_pdv_obveznik": r.FormValue("firma_pdv_obveznik"),
|
||||||
@@ -654,7 +647,6 @@ func (h *Handler) napuniPodaciPodesavanja(r *http.Request, naslov string) (Podac
|
|||||||
PIB: podesavanja["pib"],
|
PIB: podesavanja["pib"],
|
||||||
LogoPutanja: podesavanja["logo_putanja"],
|
LogoPutanja: podesavanja["logo_putanja"],
|
||||||
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
|
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
|
||||||
TopbarLogoTekst: podesavanja["topbar_logo_tekst"] == "1",
|
|
||||||
FirmaPravniOblik: vrednostIliDefault(podesavanja, "firma_pravni_oblik", "pausalac"),
|
FirmaPravniOblik: vrednostIliDefault(podesavanja, "firma_pravni_oblik", "pausalac"),
|
||||||
FirmaPdvObveznik: vrednostIliDefault(podesavanja, "firma_pdv_obveznik", "ne"),
|
FirmaPdvObveznik: vrednostIliDefault(podesavanja, "firma_pdv_obveznik", "ne"),
|
||||||
FirmaFiskalizacija: vrednostIliDefault(podesavanja, "firma_fiskalizacija", "ne"),
|
FirmaFiskalizacija: vrednostIliDefault(podesavanja, "firma_fiskalizacija", "ne"),
|
||||||
|
|||||||
@@ -328,7 +328,12 @@ func (h *Handler) ProfilOtpremiAvatar(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 512)
|
buf := make([]byte, 512)
|
||||||
n, _ := fajl.Read(buf)
|
n, err := fajl.Read(buf)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čitanju fajla.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
stvarniMime := http.DetectContentType(buf[:n])
|
stvarniMime := http.DetectContentType(buf[:n])
|
||||||
if !strings.HasPrefix(stvarniMime, ocekivaniMime) {
|
if !strings.HasPrefix(stvarniMime, ocekivaniMime) {
|
||||||
middleware.SetFlash(w, r, h.DB, "greska", "Sadržaj fajla ne odgovara odabranoj ekstenziji.")
|
middleware.SetFlash(w, r, h.DB, "greska", "Sadržaj fajla ne odgovara odabranoj ekstenziji.")
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ type PodaciStranice struct {
|
|||||||
Podnazlov string
|
Podnazlov string
|
||||||
LogoPutanja string // putanja do slike loga firme
|
LogoPutanja string // putanja do slike loga firme
|
||||||
TopbarLogoSlika bool // prikaži logo sliku u topbaru
|
TopbarLogoSlika bool // prikaži logo sliku u topbaru
|
||||||
TopbarLogoTekst bool // prikaži naziv firme u topbaru
|
|
||||||
AvatarPutanja string // putanja do lične avatar slike korisnika
|
AvatarPutanja string // putanja do lične avatar slike korisnika
|
||||||
Korisnik string
|
Korisnik string
|
||||||
KorisnikIme string // korisničko ime prijavljenog korisnika
|
KorisnikIme string // korisničko ime prijavljenog korisnika
|
||||||
|
|||||||
@@ -9,27 +9,27 @@
|
|||||||
<div class="poruka-uspeh">Podešavanja su uspešno sačuvana.</div>
|
<div class="poruka-uspeh">Podešavanja su uspešno sačuvana.</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<!-- upload loga — posebna forma jer je multipart, mora biti van glavne forme -->
|
<!-- logo kartica: dve forme su sestre unutar div.kartica, nisu ugnježdene -->
|
||||||
<form method="POST" action="/podesavanja/logo" enctype="multipart/form-data">
|
<div class="kartica animiraj" style="margin-bottom:16px;">
|
||||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
||||||
<div class="kartica animiraj" style="margin-bottom:16px;">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--sb-akcent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
||||||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Logo firme</span>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--sb-akcent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
</div>
|
||||||
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Logo firme</span>
|
{{if .LogoPutanja}}
|
||||||
</div>
|
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
|
||||||
{{if .LogoPutanja}}
|
<img src="{{.LogoPutanja}}" alt="Trenutni logo"
|
||||||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
|
style="max-height:60px;max-width:200px;object-fit:contain;border:0.5px solid var(--ivica);border-radius:8px;padding:6px;background:var(--kartica);flex-shrink:0;">
|
||||||
<img src="{{.LogoPutanja}}" alt="Trenutni logo"
|
<form method="POST" action="/podesavanja/logo/ukloni" style="margin:0;">
|
||||||
style="max-height:60px;max-width:200px;object-fit:contain;border:0.5px solid var(--ivica);border-radius:8px;padding:6px;background:var(--kartica);flex-shrink:0;">
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
<form method="POST" action="/podesavanja/logo/ukloni" style="margin:0;">
|
<button type="submit"
|
||||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
style="padding:6px 14px;background:none;border:0.5px solid #dc2626;color:#dc2626;border-radius:8px;font-size:13px;cursor:pointer;white-space:nowrap;">
|
||||||
<button type="submit"
|
Ukloni sliku
|
||||||
style="padding:6px 14px;background:none;border:0.5px solid #dc2626;color:#dc2626;border-radius:8px;font-size:13px;cursor:pointer;white-space:nowrap;">
|
</button>
|
||||||
Ukloni sliku
|
</form>
|
||||||
</button>
|
</div>
|
||||||
</form>
|
{{end}}
|
||||||
</div>
|
<form method="POST" action="/podesavanja/logo" enctype="multipart/form-data">
|
||||||
{{end}}
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
|
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
|
||||||
<input type="file" id="logo-file" name="logo" accept=".png,.jpg,.jpeg,.svg"
|
<input type="file" id="logo-file" name="logo" accept=".png,.jpg,.jpeg,.svg"
|
||||||
style="display:none;"
|
style="display:none;"
|
||||||
@@ -46,8 +46,8 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pomocni-tekst" style="font-size:12px;margin-top:6px;">PNG, JPG ili SVG — maksimum 2 MB</div>
|
<div class="pomocni-tekst" style="font-size:12px;margin-top:6px;">PNG, JPG ili SVG — maksimum 2 MB</div>
|
||||||
</div>
|
</form>
|
||||||
</form>
|
</div>
|
||||||
|
|
||||||
<!-- firma: naziv, podnazlov, adresa, telefon, PIB, logo zona -->
|
<!-- firma: naziv, podnazlov, adresa, telefon, PIB, logo zona -->
|
||||||
<form method="POST" action="/podesavanja/sacuvaj">
|
<form method="POST" action="/podesavanja/sacuvaj">
|
||||||
|
|||||||
Reference in New Issue
Block a user