diff --git a/assets/locales/en.po b/assets/locales/en.po index c14f30a2280a22bd45ca49802804953f1385aeb3..289ad68b96c7c50bd0f6fbdc2a9e972ab7735ff6 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -3,6 +3,9 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +msgid "Time Format Long" +msgstr "the Jan 2 2006 at 15h04" + msgid "Tree Administrative" msgstr "Administrative" diff --git a/assets/locales/fr.po b/assets/locales/fr.po index 641c8a5e3b6ae2586fc5d118fff67142136901ee..412f1fd173347fc4f2b10fcd7e8b3ef212b0ad1c 100644 --- a/assets/locales/fr.po +++ b/assets/locales/fr.po @@ -16,6 +16,9 @@ msgstr "" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" +msgid "Time Format Long" +msgstr "le 2 Jan 2006 à 15h04" + msgid "Tree Administrative" msgstr "Administratif" diff --git a/go.mod b/go.mod index c0be49911c33421bf2192eb51a7dc40551e8a9a7..1078e36b065232121ab2317e9dc60c53e88bf3d4 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/gofrs/uuid v3.4.0+incompatible github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f + github.com/goodsign/monday v1.0.0 github.com/google/go-querystring v1.0.0 github.com/google/gops v0.3.14 github.com/gorilla/websocket v1.4.2 diff --git a/go.sum b/go.sum index ca03ae9633db0fbbbd056c65f470785c0bdd74f5..2074f47bff8e0a406a070c682ad154d20e8d69e0 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,6 @@ github.com/cozy/goexif2 v0.0.0-20200819113101-00e1cc8cc9d3 h1:5suSF3q7eNhzpwjs+k github.com/cozy/goexif2 v0.0.0-20200819113101-00e1cc8cc9d3/go.mod h1:PWaQhEQb7UWuVUvXxpFBscfsXmUASdlmMgq97QGcTwU= github.com/cozy/gomail v0.0.0-20170313100128-1395d9a6a6c0 h1:bQVNaGvnUI7m8J8k3hklFVXRT1F+WJcIV6hYHIgjKHE= github.com/cozy/gomail v0.0.0-20170313100128-1395d9a6a6c0/go.mod h1:DlX8Rq7OKA0F9I1e0tz6+PCOXkKZ/l6aD+bWxCC6Qfo= -github.com/cozy/httpcache v0.0.0-20180914105234-d3dc4988de66 h1:b7VTmlsWlhYzJqGLjfhvIiVOpRpNCsOIoV4h0krSkyE= -github.com/cozy/httpcache v0.0.0-20180914105234-d3dc4988de66/go.mod h1:rLnjIcybyvs+PoCzi4+GmpOVp0+q+qdcuZKnKUKJoF4= github.com/cozy/httpcache v0.0.0-20210224123405-3f334f841945 h1:EfeD2CzaZclMHyFxSuaA1BTfqVTLaFwqlASiNNil4nE= github.com/cozy/httpcache v0.0.0-20210224123405-3f334f841945/go.mod h1:rLnjIcybyvs+PoCzi4+GmpOVp0+q+qdcuZKnKUKJoF4= github.com/cozy/prosemirror-go v0.4.9 h1:urukelN1w2qBP+mU2pz2jGEdF/hCJ2C0/1VgjSV1FCw= @@ -201,6 +199,8 @@ github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/goodsign/monday v1.0.0 h1:Yyk/s/WgudMbAJN6UWSU5xAs8jtNewfqtVblAlw0yoc= +github.com/goodsign/monday v1.0.0/go.mod h1:r4T4breXpoFwspQNM+u2sLxJb2zyTaxVGqUfTBjWOu8= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -515,7 +515,6 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -689,7 +688,6 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191224085550-c709ea063b76 h1:Dho5nD6R3PcW2SH1or8vS0dszDaXRxIw55lBX7XiE5g= golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/model/session/login_history.go b/model/session/login_history.go index a386fb43ce5eca6589f999191e21745352ae2ef8..3858cdb4a45d5e559d0f0334c229ea7b51e05062 100644 --- a/model/session/login_history.go +++ b/model/session/login_history.go @@ -1,6 +1,7 @@ package session import ( + "fmt" "net" "net/http" "net/url" @@ -13,6 +14,7 @@ import ( "github.com/cozy/cozy-stack/pkg/consts" "github.com/cozy/cozy-stack/pkg/couchdb" "github.com/cozy/cozy-stack/pkg/couchdb/mango" + "github.com/cozy/cozy-stack/pkg/i18n" "github.com/cozy/cozy-stack/pkg/logger" "github.com/mssola/user_agent" maxminddb "github.com/oschwald/maxminddb-golang" @@ -58,7 +60,7 @@ func (l *LoginEntry) Clone() couchdb.Doc { return &clone } -func lookupIP(ip, locale string) (city, subdivision, country string) { +func lookupIP(ip, locale string) (city, subdivision, country, timezone string) { geodb := config.GetConfig().GeoDB if geodb == "" { return @@ -80,6 +82,9 @@ func lookupIP(ip, locale string) (city, subdivision, country string) { Country struct { Names map[string]string `maxminddb:"names"` } `maxminddb:"country"` + Location struct { + TimeZone string `maxminddb:"time_zone"` + } `maxminddb:"location"` } err = db.Lookup(net.ParseIP(ip), &record) @@ -104,6 +109,7 @@ func lookupIP(ip, locale string) (city, subdivision, country string) { } else if c, ok := record.Country.Names["en"]; ok { country = c } + timezone = record.Location.TimeZone return } @@ -118,12 +124,20 @@ func StoreNewLoginEntry(i *instance.Instance, sessionID, clientID string, req *h ip = strings.Split(req.RemoteAddr, ":")[0] } - city, subdivision, country := lookupIP(ip, i.Locale) + city, subdivision, country, timezone := lookupIP(ip, i.Locale) ua := user_agent.New(req.UserAgent()) browser, _ := ua.Browser() os := ua.OS() + createdAt := time.Now() + if timezone != "" { + if loc, err := time.LoadLocation(timezone); err == nil { + createdAt = createdAt.In(loc) + fmt.Printf("createdAt = %v\n", createdAt) + } + } + l := &LoginEntry{ IP: ip, SessionID: sessionID, @@ -134,7 +148,7 @@ func StoreNewLoginEntry(i *instance.Instance, sessionID, clientID string, req *h OS: os, Browser: browser, ClientRegistration: clientID != "", - CreatedAt: time.Now(), + CreatedAt: createdAt, } if err := couchdb.CreateDoc(i, l); err != nil { @@ -183,8 +197,11 @@ func sendLoginNotification(i *instance.Instance, l *LoginEntry) error { activateTwoFALink = settingsURL.String() } + layout := i.Translate("Time Format Long") + time := i18n.LocalizeTime(l.CreatedAt, i.Locale, layout) + templateValues := map[string]interface{}{ - "Time": l.CreatedAt.Format("2006-01-02 15:04:05Z07:00"), + "Time": time, "IP": l.IP, "Browser": l.Browser, "OS": l.OS, diff --git a/pkg/i18n/i18n.go b/pkg/i18n/i18n.go index 4e6bf32cbe52bee741c30e3a2056bad57cc8c898..41543d7dd280d6168ec90434304fb64b6f7846e3 100644 --- a/pkg/i18n/i18n.go +++ b/pkg/i18n/i18n.go @@ -3,9 +3,11 @@ package i18n import ( "fmt" "strings" + "time" "github.com/cozy/cozy-stack/pkg/consts" "github.com/cozy/cozy-stack/pkg/logger" + "github.com/goodsign/monday" "github.com/leonelquinteros/gotext" ) @@ -46,3 +48,32 @@ func Translate(key, locale string, vars ...interface{}) string { } return fmt.Sprintf(key, vars...) } + +// LocalizeTime transforms a date+time in a string for the given locale. +// The layout is in the same format as the one given to time.Format. +func LocalizeTime(t time.Time, locale, layout string) string { + return monday.Format(t, layout, mondayLocale(locale)) +} + +func mondayLocale(locale string) monday.Locale { + switch locale { + case "de", "de_DE": + return monday.LocaleDeDE + case "es", "es_ES": + return monday.LocaleEsES + case "fr", "fr_FR": + return monday.LocaleFrFR + case "it", "it_IT": + return monday.LocaleItIT + case "ja", "ja_JP": + return monday.LocaleJaJP + case "nl", "nl_NL": + return monday.LocaleNlNL + case "pt", "pt_PT": + return monday.LocalePtPT + case "ru", "ru_RU": + return monday.LocaleRuRU + default: + return monday.LocaleEnUS + } +}