diff --git a/HISTORY.md b/HISTORY.md index 5de66125..951dd8f5 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -28,6 +28,7 @@ The codebase for Dependency Injection, Internationalization and localization and ## Fixes and Improvements +- Add `iris.DirOptions.SPA bool` field to allow [Single Page Applications](https://github.com/kataras/iris/tree/master/_examples/file-server/single-page-application/basic/main.go) under a file server. - A generic User interface, see the `Context.SetUser/User` methods in the New Context Methods section for more. In-short, the basicauth middleware's stored user can now be retrieved through `Context.User()` which provides more information than the native `ctx.Request().BasicAuth()` method one. Third-party authentication middleware creators can benefit of these two methods, plus the Logout below. - A `Context.Logout` method is added, can be used to invalidate [basicauth](https://github.com/kataras/iris/blob/master/_examples/auth/basicauth/main.go) or [jwt](https://github.com/kataras/iris/blob/master/_examples/auth/jwt/blocklist/main.go) client credentials. - Add the ability to [share functions](https://github.com/kataras/iris/tree/master/_examples/routing/writing-a-middleware/share-funcs) between handlers chain and add an [example](https://github.com/kataras/iris/tree/master/_examples/routing/writing-a-middleware/share-services) on sharing Go structures (aka services). diff --git a/_examples/auth/basicauth/main.go b/_examples/auth/basicauth/main.go index 0df1a5e5..39132d6e 100644 --- a/_examples/auth/basicauth/main.go +++ b/_examples/auth/basicauth/main.go @@ -57,7 +57,7 @@ func h(ctx iris.Context) { // OR: user := ctx.User() username, _ := user.GetUsername() - password, _ := user.GetPassword + password, _ := user.GetPassword() ctx.Writef("%s %s:%s", ctx.Path(), username, password) } diff --git a/_examples/auth/jwt/tutorial/go.mod b/_examples/auth/jwt/tutorial/go.mod index 3bcd36bd..d35aad38 100644 --- a/_examples/auth/jwt/tutorial/go.mod +++ b/_examples/auth/jwt/tutorial/go.mod @@ -4,7 +4,7 @@ go 1.15 require ( github.com/google/uuid v1.1.2 - github.com/kataras/iris/v12 v12.2.0-alpha.0.20201031040657-23d4c411cadb + github.com/kataras/iris/v12 v12.2.0-alpha.0.20201106220849-7a19cfb2112f golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 ) diff --git a/_examples/file-server/single-page-application/basic/main.go b/_examples/file-server/single-page-application/basic/main.go index 39315e16..4ed60e4d 100644 --- a/_examples/file-server/single-page-application/basic/main.go +++ b/_examples/file-server/single-page-application/basic/main.go @@ -1,27 +1,15 @@ package main -import ( - "github.com/kataras/iris/v12" -) - -// same as embedded-single-page-application but without go-bindata, the files are "physical" stored in the -// current system directory. - -var page = struct { - Title string -}{"Welcome"} +import "github.com/kataras/iris/v12" func newApp() *iris.Application { app := iris.New() - app.RegisterView(iris.HTML("./public", ".html")) - app.Get("/", func(ctx iris.Context) { - ctx.ViewData("Page", page) - ctx.View("index.html") + app.HandleDir("/", iris.Dir("./public"), iris.DirOptions{ + IndexName: "index.html", + SPA: true, }) - app.HandleDir("/", iris.Dir("./public")) - return app } @@ -29,8 +17,7 @@ func main() { app := newApp() // http://localhost:8080 - // http://localhost:8080/index.html - // http://localhost:8080/app.js - // http://localhost:8080/css/main.css + // http://localhost:8080/about + // http://localhost:8080/a_notfound app.Listen(":8080") } diff --git a/_examples/file-server/single-page-application/basic/main_test.go b/_examples/file-server/single-page-application/basic/main_test.go deleted file mode 100644 index 0ca13efa..00000000 --- a/_examples/file-server/single-page-application/basic/main_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "io/ioutil" - "path/filepath" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -type resource string - -func (r resource) String() string { - return string(r) -} - -func (r resource) strip(strip string) string { - s := r.String() - return strings.TrimPrefix(s, strip) -} - -func (r resource) loadFromBase(dir string) string { - filename := r.String() - - if filename == "/" { - filename = "/index.html" - } - - fullpath := filepath.Join(dir, filename) - - b, err := ioutil.ReadFile(fullpath) - if err != nil { - panic(fullpath + " failed with error: " + err.Error()) - } - - result := string(b) - - return result -} - -var urls = []resource{ - "/", - "/index.html", - "/app.js", - "/css/main.css", -} - -func TestSPA(t *testing.T) { - app := newApp() - e := httptest.New(t, app, httptest.Debug(false)) - - for _, u := range urls { - url := u.String() - contents := u.loadFromBase("./public") - contents = strings.Replace(contents, "{{ .Page.Title }}", page.Title, 1) - - e.GET(url).Expect(). - Status(httptest.StatusOK). - Body().Equal(contents) - } -} diff --git a/_examples/file-server/single-page-application/basic/public/app.js b/_examples/file-server/single-page-application/basic/public/app.js deleted file mode 100644 index c47a1fd5..00000000 --- a/_examples/file-server/single-page-application/basic/public/app.js +++ /dev/null @@ -1 +0,0 @@ -window.alert("app.js loaded from \"/"); \ No newline at end of file diff --git a/_examples/file-server/single-page-application/basic/public/css/main.css b/_examples/file-server/single-page-application/basic/public/css/main.css deleted file mode 100644 index fb72e54a..00000000 --- a/_examples/file-server/single-page-application/basic/public/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/file-server/single-page-application/basic/public/index.html b/_examples/file-server/single-page-application/basic/public/index.html index c52c1613..24cad52e 100644 --- a/_examples/file-server/single-page-application/basic/public/index.html +++ b/_examples/file-server/single-page-application/basic/public/index.html @@ -1,14 +1,19 @@ - + +
-Page not found
' } +const Home = { template: 'home page
' } +const About = { template: 'about page
' } + +const routes = { + '/': Home, + '/about': About +} + +const app = new Vue({ + el: '#app', + data: { + currentRoute: window.location.pathname + }, + computed: { + ViewComponent () { + return routes[this.currentRoute] || NotFound + } + }, + render (h) { return h(this.ViewComponent) } +}) diff --git a/_examples/i18n/basic/main.go b/_examples/i18n/basic/main.go index 51c21cb9..02b80a60 100644 --- a/_examples/i18n/basic/main.go +++ b/_examples/i18n/basic/main.go @@ -44,7 +44,7 @@ func newApp() *iris.Application { // // First parameter: Glob filpath patern, // Second variadic parameter: Optional language tags, the first one is the default/fallback one. - err := app.I18n.Load("./locales/*/*.ini", "en-US", "el-GR", "zh-CN") + err := app.I18n.Load("./locales/*/*", "en-US", "el-GR", "zh-CN") if err != nil { panic(err) } diff --git a/_examples/i18n/template/locales/el-GR/user.ini b/_examples/i18n/template/locales/el-GR/user.ini index 0c05c6dc..0a110cdc 100644 --- a/_examples/i18n/template/locales/el-GR/user.ini +++ b/_examples/i18n/template/locales/el-GR/user.ini @@ -1,5 +1,4 @@ [forms] member = μέλος -register = Γίνε {{tr "forms.member" }} -registered = εγγεγραμμένοι -registered_members = Υπάρχουν {{ concat (plural (tr "forms.member") .count) (tr "forms.registered") }} \ No newline at end of file +register = Γίνε {{uppercase (tr "forms.member") }} +registered = εγγεγραμμένοι \ No newline at end of file diff --git a/_examples/i18n/template/locales/el-GR/welcome.yml b/_examples/i18n/template/locales/el-GR/welcome.yml deleted file mode 100644 index ae535504..00000000 --- a/_examples/i18n/template/locales/el-GR/welcome.yml +++ /dev/null @@ -1 +0,0 @@ -HiDogs: Hi %d {{plural (tr "Dog") .count }} \ No newline at end of file diff --git a/_examples/i18n/template/locales/en-US/user.ini b/_examples/i18n/template/locales/en-US/user.ini index 32329ca5..09d3e3e4 100644 --- a/_examples/i18n/template/locales/en-US/user.ini +++ b/_examples/i18n/template/locales/en-US/user.ini @@ -1,5 +1,4 @@ [forms] member = member -register = Become a {{tr "forms.member" }} -registered = registered -registered_members = There are {{ concat (plural (tr "forms.member") .count) (tr "forms.registered") }} \ No newline at end of file +register = Become a {{uppercase (tr "forms.member") }} +registered = registered \ No newline at end of file diff --git a/_examples/i18n/template/locales/en-US/welcome.yml b/_examples/i18n/template/locales/en-US/welcome.yml deleted file mode 100644 index 6ef4aee6..00000000 --- a/_examples/i18n/template/locales/en-US/welcome.yml +++ /dev/null @@ -1,2 +0,0 @@ -Dog: "dog" -HiDogs: Hi %d {{plural (tr "Dog") .count }} \ No newline at end of file diff --git a/_examples/i18n/template/main.go b/_examples/i18n/template/main.go index c455f409..4d60aa81 100644 --- a/_examples/i18n/template/main.go +++ b/_examples/i18n/template/main.go @@ -5,20 +5,11 @@ import ( "text/template" "github.com/kataras/iris/v12" - - // go get -u golang.org/x/text/message - - "golang.org/x/text/feature/plural" - "golang.org/x/text/language" - "golang.org/x/text/message" ) /* Iris I18n supports text/template inside the translation values. Follow this example to learn how to use that feature. - - This is just an example on how to use template functions. - See the "plurals" example for a more comprehensive pluralization support instead. */ func main() { @@ -29,75 +20,29 @@ func main() { func newApp() *iris.Application { app := iris.New() - // set the printers after load, so they can be done by loop of available languages. - printers := make(map[string]*message.Printer) - - message.Set(language.Greek, "Hello %d dog", - plural.Selectf(1, "%d", - "one", "Γεια σου σκυλί", - "other", "Γεια σας %[1]d σκυλιά", - )) - - /* by variable, single word: - message.Set(language.Greek, "Hi %d dog(s)", - catalog.Var("dogs", plural.Selectf(1, "%d", "one", "σκυλί", "other", "σκυλιά")), - catalog.String("Γεια %[1]d ${dogs}")) - */ - // Set custom functions per locale! app.I18n.Loader.Funcs = func(current iris.Locale) template.FuncMap { return template.FuncMap{ - "plural": func(word string, count int) string { - // Your own implementation or use a 3rd-party package - // like we do here. - return printers[current.Language()].Sprintf(word, count) - }, "uppercase": func(word string) string { return strings.ToUpper(word) }, - "concat": func(words ...string) string { - return strings.Join(words, " ") - }, } } - err := app.I18n.Load("./locales/*/*", "en-US", "el-GR") + err := app.I18n.Load("./locales/*/*.ini", "en-US", "el-GR") if err != nil { panic(err) } - for _, tag := range app.I18n.Tags() { - printers[tag.String()] = message.NewPrinter(tag) - } - - message.NewPrinter(language.Greek).Printf("Hello %d dog", 2) - app.Get("/", func(ctx iris.Context) { - text := ctx.Tr("HiDogs", iris.Map{ - "count": 2, - }) // en-US: prints "Hi 2 dogs". + text := ctx.Tr("forms.register") // en-US: prints "Become a MEMBER". ctx.WriteString(text) }) - app.Get("/singular", func(ctx iris.Context) { - text := ctx.Tr("HiDogs", iris.Map{ - "count": 1, - }) // en-US: prints "Hi 1 dog". + app.Get("/title", func(ctx iris.Context) { + text := ctx.Tr("user.connections.Title") // en-US: prints "Accounts Connections". ctx.WriteString(text) }) - app.Get("/members", func(ctx iris.Context) { - text := ctx.Tr("forms.registered_members", iris.Map{ - "count": 42, - }) // en-US: prints "There are 42 members registered". - ctx.WriteString(text) - }) - - // showcases the other.ini translation file. - app.Get("/other", func(ctx iris.Context) { - ctx.Writef(`AccessLogClear: %s -Title: %s`, ctx.Tr("debug.AccessLogClear"), ctx.Tr("user.connections.Title")) - }) - return app } diff --git a/_examples/i18n/template/main_test.go b/_examples/i18n/template/main_test.go index bb2265d9..3aa2788b 100644 --- a/_examples/i18n/template/main_test.go +++ b/_examples/i18n/template/main_test.go @@ -11,16 +11,11 @@ func TestI18nLoaderFuncMap(t *testing.T) { e := httptest.New(t, app) e.GET("/").Expect().Status(httptest.StatusOK). - Body().Equal("Hi 2 dogs") - e.GET("/singular").Expect().Status(httptest.StatusOK). - Body().Equal("Hi 1 dog") - e.GET("/members").Expect().Status(httptest.StatusOK). - Body().Equal("There are 42 members registered") + Body().Equal("Become a MEMBER") + e.GET("/title").Expect().Status(httptest.StatusOK). + Body().Equal("Account Connections") e.GET("/").WithHeader("Accept-Language", "el").Expect().Status(httptest.StatusOK). - Body().Equal("Γειά 2 σκυλί") - - e.GET("/other").Expect().Status(httptest.StatusOK). - Body().Equal("AccessLogClear: Clear Access Log\nTitle: Account Connections") - e.GET("/other").WithHeader("Accept-Language", "el").Expect().Status(httptest.StatusOK). - Body().Equal("AccessLogClear: Καθαρισμός Πρόσβαση στο αρχείο καταγραφής\nTitle: Λογαριασμός Συνδέσεις") + Body().Equal("Γίνε ΜΈΛΟΣ") + e.GET("/title").WithHeader("Accept-Language", "el").Expect().Status(httptest.StatusOK). + Body().Equal("Λογαριασμός Συνδέσεις") } diff --git a/i18n/i18n.go b/i18n/i18n.go index 3d970a8f..2bcfbefd 100644 --- a/i18n/i18n.go +++ b/i18n/i18n.go @@ -365,7 +365,7 @@ func (i *I18n) getLocaleMessage(loc context.Locale, langInput string, key string } if msg == "" && i.DefaultMessageFunc != nil { - msg = i.DefaultMessageFunc(langInput, langMatched, key, args) + msg = i.DefaultMessageFunc(langInput, langMatched, key, args...) } return