From 32823cd243ea73cfcdf51c300ad51bb2b362a909 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sat, 12 May 2018 16:53:47 -0700 Subject: [PATCH 1/8] go-aah/aah#156 integrate view lib with vfs --- .travis.yml | 3 ++ anti_csrf_field.go | 39 +++++++++++++++----------- anti_csrf_field_test.go | 6 ++-- go_engine.go | 36 ++++++++++++------------ go_engine_test.go | 29 +++++++++---------- version.go | 2 +- view.go | 62 ++++++++++++++++++++--------------------- view_test.go | 23 ++++++++++++++- 8 files changed, 114 insertions(+), 86 deletions(-) diff --git a/.travis.yml b/.travis.yml index e1e04f3..b70c781 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,9 @@ go: go_import_path: aahframework.org/view.v0 +before_install: + - bash <(curl -s https://aahframework.org/base-before-install) "vfs forge config essentials log" + install: - go get -t -v ./... diff --git a/anti_csrf_field.go b/anti_csrf_field.go index 8f000a6..717f1eb 100644 --- a/anti_csrf_field.go +++ b/anti_csrf_field.go @@ -7,31 +7,40 @@ package view import ( "fmt" "io/ioutil" + "path" "path/filepath" "strings" "aahframework.org/essentials.v0" "aahframework.org/log.v0" + "aahframework.org/vfs.v0" ) -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // type AntiCSRFField and methods -//________________________________________ +//______________________________________________________________________________ // AntiCSRFField is used to insert Anti-CSRF HTML field dynamically // while parsing templates on view engine. type AntiCSRFField struct { engineName string field string - inserter *strings.Replacer leftDelim string rightDelim string + inserter *strings.Replacer + vfs *vfs.VFS } // NewAntiCSRFField method creates new instance of Anti-CSRF HTML field // parser. func NewAntiCSRFField(engineName, leftDelim, rightDelim string) *AntiCSRFField { - csft := &AntiCSRFField{engineName: engineName, leftDelim: leftDelim, rightDelim: rightDelim} + return NewAntiCSRFFieldWithVFS(nil, engineName, leftDelim, rightDelim) +} + +// NewAntiCSRFFieldWithVFS method creates new instance of Anti-CSRF HTML field +// parser with given VFS instance. +func NewAntiCSRFFieldWithVFS(fs *vfs.VFS, engineName, leftDelim, rightDelim string) *AntiCSRFField { + csft := &AntiCSRFField{vfs: fs, engineName: engineName, leftDelim: leftDelim, rightDelim: rightDelim} csft.field = fmt.Sprintf(` `, csft.leftDelim, csft.rightDelim) @@ -63,7 +72,7 @@ func (ft *AntiCSRFField) InsertOnFiles(files ...string) []string { func (ft *AntiCSRFField) InsertOnFile(file string) (string, error) { tmpDir, _ := ioutil.TempDir("", ft.engineName+"_anti_csrf") - fileBytes, err := ioutil.ReadFile(file) + fileBytes, err := vfs.ReadFile(ft.vfs, file) if err != nil { return "", err } @@ -71,21 +80,17 @@ func (ft *AntiCSRFField) InsertOnFile(file string) (string, error) { fileStr := string(fileBytes) f := StripPathPrefixAt(file, "views") fpath := filepath.Join(tmpDir, f) - if strings.Contains(fileStr, "") { - log.Tracef("Inserting Anti-CSRF field for file: %s", filepath.Join("views", f)) - fileStr = ft.InsertOnString(fileStr) - if err = ess.MkDirAll(filepath.Dir(fpath), 0755); err != nil { - return "", err - } - - if err = ioutil.WriteFile(fpath, []byte(fileStr), 0755); err != nil { - return "", err - } + log.Tracef("Inserting Anti-CSRF field for file: %s", path.Join("views", f)) + fileStr = ft.InsertOnString(fileStr) + if err = ess.MkDirAll(filepath.Dir(fpath), 0755); err != nil { + return "", err + } - return fpath, nil + if err = ioutil.WriteFile(fpath, []byte(fileStr), 0755); err != nil { + return "", err } - return file, nil + return fpath, nil } // InsertOnString method inserts the Anti-CSRF HTML field on diff --git a/anti_csrf_field_test.go b/anti_csrf_field_test.go index 5c88d40..4d8e788 100644 --- a/anti_csrf_field_test.go +++ b/anti_csrf_field_test.go @@ -15,7 +15,7 @@ import ( func TestAntiCSRFFieldNoFormTag(t *testing.T) { acsrf := NewAntiCSRFField("go", "{{", "}}") - fpath := filepath.Join(getTestdataPath(), "anti-csrf-field", "testhtml-noform.html") + fpath := filepath.Join(testdataBaseDir(), "anti-csrf-field", "testhtml-noform.html") files := acsrf.InsertOnFiles(fpath) bytes, err := ioutil.ReadFile(files[0]) @@ -25,7 +25,7 @@ func TestAntiCSRFFieldNoFormTag(t *testing.T) { func TestAntiCSRFFieldFormTag(t *testing.T) { acsrf := NewAntiCSRFField("go", "%%", "%%") - fpath := filepath.Join(getTestdataPath(), "anti-csrf-field", "testhtml-form.html") + fpath := filepath.Join(testdataBaseDir(), "anti-csrf-field", "testhtml-form.html") files := acsrf.InsertOnFiles(fpath) bytes, err := ioutil.ReadFile(files[0]) @@ -35,7 +35,7 @@ func TestAntiCSRFFieldFormTag(t *testing.T) { func TestAntiCSRFFieldFormTagDelim(t *testing.T) { acsrf := NewAntiCSRFField("go", "[[", "]]") - fpath := filepath.Join(getTestdataPath(), "anti-csrf-field", "not-exists.html") + fpath := filepath.Join(testdataBaseDir(), "anti-csrf-field", "not-exists.html") files := acsrf.InsertOnFiles(fpath) assert.NotNil(t, files) diff --git a/go_engine.go b/go_engine.go index 477b130..38998e3 100644 --- a/go_engine.go +++ b/go_engine.go @@ -7,14 +7,14 @@ package view import ( "bytes" "html/template" - "io/ioutil" + "path" "path/filepath" "strings" "sync" "aahframework.org/config.v0" - "aahframework.org/essentials.v0" "aahframework.org/log.v0" + "aahframework.org/vfs.v0" ) const noLayout = "nolayout" @@ -24,9 +24,9 @@ var ( bufPool *sync.Pool ) -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // type GoViewEngine and its method -//___________________________________ +//______________________________________________________________________________ // GoViewEngine implements the partial inheritance support with Go templates. type GoViewEngine struct { @@ -35,12 +35,12 @@ type GoViewEngine struct { // Init method initialize a template engine with given aah application config // and application views base path. -func (e *GoViewEngine) Init(appCfg *config.Config, baseDir string) error { +func (e *GoViewEngine) Init(fs *vfs.VFS, appCfg *config.Config, baseDir string) error { if e.EngineBase == nil { - e.EngineBase = &EngineBase{} + e.EngineBase = new(EngineBase) } - if err := e.EngineBase.Init(appCfg, baseDir, "go", ".html"); err != nil { + if err := e.EngineBase.Init(fs, appCfg, baseDir, "go", ".html"); err != nil { return err } @@ -71,7 +71,7 @@ func (e *GoViewEngine) Init(appCfg *config.Config, baseDir string) error { _ = e.loadNonLayoutTemplates("pages") } - if ess.IsFileExists(filepath.Join(e.BaseDir, "errors")) { + if e.VFS.IsExists(filepath.Join(e.BaseDir, "errors")) { if err = e.loadNonLayoutTemplates("errors"); err != nil { return err } @@ -80,9 +80,9 @@ func (e *GoViewEngine) Init(appCfg *config.Config, baseDir string) error { return nil } -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // GoViewEngine unexported methods -//___________________________________ +//______________________________________________________________________________ func (e *GoViewEngine) loadCommonTemplates() error { commons, err := e.FilesPath("common") @@ -92,7 +92,7 @@ func (e *GoViewEngine) loadCommonTemplates() error { commonTemplates = &Templates{} bufPool = &sync.Pool{New: func() interface{} { return &bytes.Buffer{} }} - prefix := filepath.Dir(e.BaseDir) + prefix := path.Dir(e.BaseDir) for _, file := range commons { if !strings.HasSuffix(file, e.FileExt) { log.Warnf("goviewengine: not a valid template extension[%s]: %s", e.FileExt, TrimPathPrefix(prefix, file)) @@ -102,7 +102,7 @@ func (e *GoViewEngine) loadCommonTemplates() error { tmplKey := StripPathPrefixAt(filepath.ToSlash(file), "views/") tmpl := e.NewTemplate(tmplKey) - tbytes, err := ioutil.ReadFile(file) + tbytes, err := vfs.ReadFile(e.VFS, file) if err != nil { return err } @@ -126,13 +126,13 @@ func (e *GoViewEngine) loadLayoutTemplates(layouts []string) error { return err } - prefix := filepath.Dir(e.BaseDir) + prefix := path.Dir(e.BaseDir) var errs []error for _, layout := range layouts { - layoutKey := strings.ToLower(filepath.Base(layout)) + layoutKey := strings.ToLower(path.Base(layout)) for _, dir := range dirs { - files, err := filepath.Glob(filepath.Join(dir, "*"+e.FileExt)) + files, err := e.VFS.Glob(path.Join(dir, "*"+e.FileExt)) if err != nil { errs = append(errs, err) continue @@ -167,10 +167,10 @@ func (e *GoViewEngine) loadNonLayoutTemplates(scope string) error { return err } - prefix := filepath.Dir(e.BaseDir) + prefix := path.Dir(e.BaseDir) var errs []error for _, dir := range dirs { - files, err := filepath.Glob(filepath.Join(dir, "*"+e.FileExt)) + files, err := e.VFS.Glob(path.Join(dir, "*"+e.FileExt)) if err != nil { errs = append(errs, err) continue @@ -179,7 +179,7 @@ func (e *GoViewEngine) loadNonLayoutTemplates(scope string) error { for _, file := range files { tmplKey := noLayout + "-" + StripPathPrefixAt(filepath.ToSlash(file), "views/") tmpl := e.NewTemplate(tmplKey) - fileBytes, _ := ioutil.ReadFile(file) + fileBytes, _ := e.VFS.ReadFile(file) fileStr := e.AntiCSRFField.InsertOnString(string(fileBytes)) log.Tracef("Parsing file: %s", TrimPathPrefix(prefix, file)) diff --git a/go_engine_test.go b/go_engine_test.go index e7be7f4..111d28e 100644 --- a/go_engine_test.go +++ b/go_engine_test.go @@ -8,7 +8,6 @@ import ( "bytes" "errors" "html/template" - "path/filepath" "strings" "testing" @@ -105,23 +104,23 @@ func TestViewUserPagesNoLayout(t *testing.T) { } func TestViewBaseDirNotExists(t *testing.T) { - viewsDir := filepath.Join(getTestdataPath(), "views1") + viewsDir := join("testdata", "views1") ge := &GoViewEngine{} cfg, _ := config.ParseString(`view { }`) - err := ge.Init(cfg, viewsDir) + err := ge.Init(newVFS(), cfg, viewsDir) assert.NotNil(t, err) assert.True(t, strings.HasPrefix(err.Error(), "goviewengine: views base dir is not exists:")) } func TestViewDelimitersError(t *testing.T) { - viewsDir := filepath.Join(getTestdataPath(), "views") + viewsDir := join("testdata", "views") ge := &GoViewEngine{} cfg, _ := config.ParseString(`view { delimiters = "{{." }`) - err := ge.Init(cfg, viewsDir) + err := ge.Init(newVFS(), cfg, viewsDir) assert.NotNil(t, err) assert.Equal(t, "goviewengine: config 'view.delimiters' value is invalid", err.Error()) } @@ -132,24 +131,26 @@ func TestViewErrors(t *testing.T) { default_layout = false }`) + fs := newVFS() + // No layout directiry - viewsDir := filepath.Join(getTestdataPath(), "views-no-layouts-dir") + viewsDir := join("testdata", "views-no-layouts-dir") ge := &GoViewEngine{} - err := ge.Init(cfg, viewsDir) + err := ge.Init(fs, cfg, viewsDir) assert.NotNil(t, err) assert.True(t, strings.HasPrefix(err.Error(), "goviewengine: layouts base dir is not exists:")) // No Common directory - viewsDir = filepath.Join(getTestdataPath(), "views-no-common-dir") + viewsDir = join("testdata", "views-no-common-dir") ge = &GoViewEngine{} - err = ge.Init(cfg, viewsDir) + err = ge.Init(fs, cfg, viewsDir) assert.NotNil(t, err) assert.True(t, strings.HasPrefix(err.Error(), "goviewengine: common base dir is not exists:")) // No Pages directory - viewsDir = filepath.Join(getTestdataPath(), "views-no-pages-dir") + viewsDir = join("testdata", "views-no-pages-dir") ge = &GoViewEngine{} - err = ge.Init(cfg, viewsDir) + err = ge.Init(fs, cfg, viewsDir) assert.NotNil(t, err) assert.True(t, strings.HasPrefix(err.Error(), "goviewengine: pages base dir is not exists:")) @@ -167,17 +168,17 @@ func loadGoViewEngine(t *testing.T, cfg *config.Config, dir string) *GoViewEngin }, }) - viewsDir := filepath.Join(getTestdataPath(), dir) + viewsDir := join("testdata", dir) ge := &GoViewEngine{} - err := ge.Init(cfg, viewsDir) + err := ge.Init(newVFS(), cfg, viewsDir) assert.FailNowOnError(t, err, "") assert.Equal(t, viewsDir, ge.BaseDir) assert.NotNil(t, ge.AppConfig) assert.NotNil(t, ge.Templates) - assert.NotNil(t, (&EngineBase{}).Init(nil, "", "", "")) + assert.NotNil(t, (&EngineBase{}).Init(nil, nil, "", "", "")) return ge } diff --git a/version.go b/version.go index f8863b2..fbffb41 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package view // Version no. of aah framework view library -const Version = "0.8.2" +const Version = "0.9.0-edge" diff --git a/view.go b/view.go index 4a4fd0a..48c00b3 100644 --- a/view.go +++ b/view.go @@ -11,13 +11,14 @@ import ( "errors" "fmt" "html/template" + "path" "path/filepath" - "reflect" "strings" "aahframework.org/config.v0" "aahframework.org/essentials.v0" "aahframework.org/log.v0" + "aahframework.org/vfs.v0" ) var ( @@ -39,13 +40,13 @@ var ( // Enginer interface defines a methods for pluggable view engine. type Enginer interface { - Init(appCfg *config.Config, baseDir string) error + Init(fs *vfs.VFS, appCfg *config.Config, baseDir string) error Get(layout, path, tmplName string) (*template.Template, error) } -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Package methods -//___________________________________ +//______________________________________________________________________________ // AddTemplateFunc method adds given Go template funcs into function map. func AddTemplateFunc(funcMap template.FuncMap) { @@ -72,16 +73,13 @@ func AddEngine(name string, engine Enginer) error { // GetEngine method returns the view engine from store by name otherwise nil. func GetEngine(name string) (Enginer, bool) { - if engine, found := viewEngines[name]; found { - ty := reflect.TypeOf(engine) - return reflect.New(ty.Elem()).Interface().(Enginer), found - } - return nil, false + engine, found := viewEngines[name] + return engine, found } -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // type Templates, methods -//___________________________________ +//______________________________________________________________________________ // Templates hold template reference of lowercase key and case sensitive key // with reference to compliled template. @@ -125,35 +123,37 @@ func (t *Templates) Keys() []string { return keys } -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // type EngineBase, methods -//___________________________________ +//______________________________________________________________________________ // EngineBase struct is to create common and repurpose the implementation. -// Could used for custom view implementation. +// Could be used for custom view engine implementation. type EngineBase struct { + CaseSensitive bool + IsLayoutEnabled bool Name string - AppConfig *config.Config BaseDir string - Templates map[string]*Templates FileExt string - CaseSensitive bool - IsLayoutEnabled bool LeftDelim string RightDelim string + AppConfig *config.Config + Templates map[string]*Templates AntiCSRFField *AntiCSRFField + VFS *vfs.VFS } // Init method is to initialize the base fields values. -func (eb *EngineBase) Init(appCfg *config.Config, baseDir, defaultEngineName, defaultFileExt string) error { +func (eb *EngineBase) Init(fs *vfs.VFS, appCfg *config.Config, baseDir, defaultEngineName, defaultFileExt string) error { if appCfg == nil { return fmt.Errorf("view: app config is nil") } + eb.VFS = fs eb.Name = appCfg.StringDefault("view.engine", defaultEngineName) // check base directory - if !ess.IsFileExists(baseDir) { + if !eb.VFS.IsExists(baseDir) { return fmt.Errorf("%sviewengine: views base dir is not exists: %s", eb.Name, baseDir) } @@ -171,7 +171,7 @@ func (eb *EngineBase) Init(appCfg *config.Config, baseDir, defaultEngineName, de eb.LeftDelim, eb.RightDelim = delimiter[0], delimiter[1] // Anti CSRF - eb.AntiCSRFField = NewAntiCSRFField("go", eb.LeftDelim, eb.RightDelim) + eb.AntiCSRFField = NewAntiCSRFFieldWithVFS(eb.VFS, "go", eb.LeftDelim, eb.RightDelim) return nil } @@ -223,34 +223,32 @@ func (eb *EngineBase) ParseErrors(errs []error) error { // LayoutFiles method returns the all layout files from `/layouts`. // If layout directory doesn't exists it returns error. func (eb *EngineBase) LayoutFiles() ([]string, error) { - baseDir := filepath.Join(eb.BaseDir, "layouts") - if !ess.IsFileExists(baseDir) { + baseDir := path.Join(eb.BaseDir, "layouts") + if !eb.VFS.IsExists(baseDir) { return nil, fmt.Errorf("%sviewengine: layouts base dir is not exists: %s", eb.Name, baseDir) } - return filepath.Glob(filepath.Join(baseDir, "*"+eb.FileExt)) + return eb.VFS.Glob(path.Join(baseDir, "*"+eb.FileExt)) } // DirsPath method returns all sub directories from `/`. // if it not exists returns error. func (eb *EngineBase) DirsPath(subDir string) ([]string, error) { - baseDir := filepath.Join(eb.BaseDir, subDir) - if !ess.IsFileExists(baseDir) { + baseDir := path.Join(eb.BaseDir, subDir) + if !eb.VFS.IsExists(baseDir) { return nil, fmt.Errorf("%sviewengine: %s base dir is not exists: %s", eb.Name, subDir, baseDir) } - - return ess.DirsPath(baseDir, true) + return eb.VFS.Dirs(baseDir) } // FilesPath method returns all file path from `/`. // if it not exists returns error. func (eb *EngineBase) FilesPath(subDir string) ([]string, error) { - baseDir := filepath.Join(eb.BaseDir, subDir) - if !ess.IsFileExists(baseDir) { + baseDir := path.Join(eb.BaseDir, subDir) + if !eb.VFS.IsExists(baseDir) { return nil, fmt.Errorf("%sviewengine: %s base dir is not exists: %s", eb.Name, subDir, baseDir) } - - return ess.FilesPath(baseDir, true) + return eb.VFS.Files(baseDir) } // NewTemplate method return new instance on `template.Template` initialized with diff --git a/view_test.go b/view_test.go index f116af5..b9b2c7a 100644 --- a/view_test.go +++ b/view_test.go @@ -6,12 +6,17 @@ package view import ( "html/template" + "io/ioutil" "os" + "path" "path/filepath" "strings" "testing" + "aahframework.org/config.v0" + "aahframework.org/log.v0" "aahframework.org/test.v0/assert" + "aahframework.org/vfs.v0" ) func TestViewAddTemplateFunc(t *testing.T) { @@ -61,7 +66,23 @@ func TestViewTemplates(t *testing.T) { assert.False(t, tmpls.IsExists("not-exixts")) } -func getTestdataPath() string { +func testdataBaseDir() string { wd, _ := os.Getwd() return filepath.Join(wd, "testdata") } + +func newVFS() *vfs.VFS { + fs := new(vfs.VFS) + fs.AddMount("/testdata", testdataBaseDir()) + return fs +} + +func join(s ...string) string { + return "/" + path.Join(s...) +} + +func newLog() log.Loggerer { + l, _ := log.New(config.NewEmpty()) + l.SetWriter(ioutil.Discard) + return l +} From 47351c3e6e0633ac0f0de8494ac3c3a364810d21 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Mon, 28 May 2018 22:52:32 -0700 Subject: [PATCH 2/8] corrected typo in view func anticsrftoken --- anti_csrf_field.go | 2 +- anti_csrf_field_test.go | 2 +- go_engine_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/anti_csrf_field.go b/anti_csrf_field.go index 717f1eb..ea55b53 100644 --- a/anti_csrf_field.go +++ b/anti_csrf_field.go @@ -42,7 +42,7 @@ func NewAntiCSRFField(engineName, leftDelim, rightDelim string) *AntiCSRFField { func NewAntiCSRFFieldWithVFS(fs *vfs.VFS, engineName, leftDelim, rightDelim string) *AntiCSRFField { csft := &AntiCSRFField{vfs: fs, engineName: engineName, leftDelim: leftDelim, rightDelim: rightDelim} - csft.field = fmt.Sprintf(` + csft.field = fmt.Sprintf(` `, csft.leftDelim, csft.rightDelim) csft.inserter = strings.NewReplacer("", csft.field) diff --git a/anti_csrf_field_test.go b/anti_csrf_field_test.go index 4d8e788..60a698b 100644 --- a/anti_csrf_field_test.go +++ b/anti_csrf_field_test.go @@ -30,7 +30,7 @@ func TestAntiCSRFFieldFormTag(t *testing.T) { files := acsrf.InsertOnFiles(fpath) bytes, err := ioutil.ReadFile(files[0]) assert.Nil(t, err) - assert.True(t, strings.Contains(string(bytes), "%% anitcsrftoken . %%")) + assert.True(t, strings.Contains(string(bytes), "%% anticsrftoken . %%")) } func TestAntiCSRFFieldFormTagDelim(t *testing.T) { diff --git a/go_engine_test.go b/go_engine_test.go index 111d28e..5994388 100644 --- a/go_engine_test.go +++ b/go_engine_test.go @@ -163,7 +163,7 @@ func TestViewErrors(t *testing.T) { func loadGoViewEngine(t *testing.T, cfg *config.Config, dir string) *GoViewEngine { // dummy func for test AddTemplateFunc(template.FuncMap{ - "anitcsrftoken": func(arg interface{}) string { + "anticsrftoken": func(arg interface{}) string { return "" }, }) From 95bb9ae34f054bd1e085a3498f6a4dcefd3b3587 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 13 Jun 2018 03:18:51 -0700 Subject: [PATCH 3/8] view engine ehnacement for auto insertionf fields --- anti_csrf_field.go | 100 ------------------- anti_csrf_field_test.go | 43 -------- go_engine.go | 24 ++--- go_engine_test.go | 6 ++ testdata/views/pages/app/testhtml-form.html | 28 ++++++ testdata/views/pages/user/testhtml-form.html | 28 ++++++ view.go | 68 ++++++++++++- 7 files changed, 136 insertions(+), 161 deletions(-) delete mode 100644 anti_csrf_field.go delete mode 100644 anti_csrf_field_test.go create mode 100644 testdata/views/pages/app/testhtml-form.html create mode 100644 testdata/views/pages/user/testhtml-form.html diff --git a/anti_csrf_field.go b/anti_csrf_field.go deleted file mode 100644 index ea55b53..0000000 --- a/anti_csrf_field.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/view source code and usage is governed by a MIT style -// license that can be found in the LICENSE file. - -package view - -import ( - "fmt" - "io/ioutil" - "path" - "path/filepath" - "strings" - - "aahframework.org/essentials.v0" - "aahframework.org/log.v0" - "aahframework.org/vfs.v0" -) - -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// type AntiCSRFField and methods -//______________________________________________________________________________ - -// AntiCSRFField is used to insert Anti-CSRF HTML field dynamically -// while parsing templates on view engine. -type AntiCSRFField struct { - engineName string - field string - leftDelim string - rightDelim string - inserter *strings.Replacer - vfs *vfs.VFS -} - -// NewAntiCSRFField method creates new instance of Anti-CSRF HTML field -// parser. -func NewAntiCSRFField(engineName, leftDelim, rightDelim string) *AntiCSRFField { - return NewAntiCSRFFieldWithVFS(nil, engineName, leftDelim, rightDelim) -} - -// NewAntiCSRFFieldWithVFS method creates new instance of Anti-CSRF HTML field -// parser with given VFS instance. -func NewAntiCSRFFieldWithVFS(fs *vfs.VFS, engineName, leftDelim, rightDelim string) *AntiCSRFField { - csft := &AntiCSRFField{vfs: fs, engineName: engineName, leftDelim: leftDelim, rightDelim: rightDelim} - - csft.field = fmt.Sprintf(` - `, csft.leftDelim, csft.rightDelim) - csft.inserter = strings.NewReplacer("", csft.field) - - return csft -} - -// InsertOnFile method inserts the Anti-CSRF HTML field for given HTML file and -// writes a processed file into temp directory then return the new file path. -func (ft *AntiCSRFField) InsertOnFiles(files ...string) []string { - var ofiles []string - - for _, f := range files { - fpath, err := ft.InsertOnFile(f) - if err != nil { - log.Errorf("anitcsrffield: unable to insert Anti-CSRF field for file: %s", f) - ofiles = append(ofiles, f) - continue - } - ofiles = append(ofiles, fpath) - } - - return ofiles -} - -// InsertOnFile method inserts the Anti-CSRF HTML filed for given HTML file and -// writes a processed file into temp directory then return the new file path. -func (ft *AntiCSRFField) InsertOnFile(file string) (string, error) { - tmpDir, _ := ioutil.TempDir("", ft.engineName+"_anti_csrf") - - fileBytes, err := vfs.ReadFile(ft.vfs, file) - if err != nil { - return "", err - } - - fileStr := string(fileBytes) - f := StripPathPrefixAt(file, "views") - fpath := filepath.Join(tmpDir, f) - log.Tracef("Inserting Anti-CSRF field for file: %s", path.Join("views", f)) - fileStr = ft.InsertOnString(fileStr) - if err = ess.MkDirAll(filepath.Dir(fpath), 0755); err != nil { - return "", err - } - - if err = ioutil.WriteFile(fpath, []byte(fileStr), 0755); err != nil { - return "", err - } - - return fpath, nil -} - -// InsertOnString method inserts the Anti-CSRF HTML field on -// given HTML string and returns the processed HTML string. -func (ft *AntiCSRFField) InsertOnString(str string) string { - return ft.inserter.Replace(str) -} diff --git a/anti_csrf_field_test.go b/anti_csrf_field_test.go deleted file mode 100644 index 60a698b..0000000 --- a/anti_csrf_field_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/view source code and usage is governed by a MIT style -// license that can be found in the LICENSE file. - -package view - -import ( - "io/ioutil" - "path/filepath" - "strings" - "testing" - - "aahframework.org/test.v0/assert" -) - -func TestAntiCSRFFieldNoFormTag(t *testing.T) { - acsrf := NewAntiCSRFField("go", "{{", "}}") - fpath := filepath.Join(testdataBaseDir(), "anti-csrf-field", "testhtml-noform.html") - - files := acsrf.InsertOnFiles(fpath) - bytes, err := ioutil.ReadFile(files[0]) - assert.Nil(t, err) - assert.False(t, strings.Contains(string(bytes), "{{ anti_csrf_token . }}")) -} - -func TestAntiCSRFFieldFormTag(t *testing.T) { - acsrf := NewAntiCSRFField("go", "%%", "%%") - fpath := filepath.Join(testdataBaseDir(), "anti-csrf-field", "testhtml-form.html") - - files := acsrf.InsertOnFiles(fpath) - bytes, err := ioutil.ReadFile(files[0]) - assert.Nil(t, err) - assert.True(t, strings.Contains(string(bytes), "%% anticsrftoken . %%")) -} - -func TestAntiCSRFFieldFormTagDelim(t *testing.T) { - acsrf := NewAntiCSRFField("go", "[[", "]]") - fpath := filepath.Join(testdataBaseDir(), "anti-csrf-field", "not-exists.html") - - files := acsrf.InsertOnFiles(fpath) - assert.NotNil(t, files) - assert.Equal(t, fpath, files[0]) -} diff --git a/go_engine.go b/go_engine.go index 38998e3..16fcf71 100644 --- a/go_engine.go +++ b/go_engine.go @@ -1,5 +1,5 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/view source code and usage is governed by a MIT style +// aahframework.org/view source code and usage is governed by a MIT style // license that can be found in the LICENSE file. package view @@ -102,16 +102,14 @@ func (e *GoViewEngine) loadCommonTemplates() error { tmplKey := StripPathPrefixAt(filepath.ToSlash(file), "views/") tmpl := e.NewTemplate(tmplKey) - tbytes, err := vfs.ReadFile(e.VFS, file) + log.Tracef("Parsing file: %s", TrimPathPrefix(prefix, file)) + tstr, err := e.Open(file) if err != nil { return err } - - tstr := e.AntiCSRFField.InsertOnString(string(tbytes)) - if tmpl, err = tmpl.Parse(tstr); err != nil { + if _, err = tmpl.Parse(tstr); err != nil { return err } - if err = commonTemplates.Add(tmplKey, tmpl); err != nil { return err } @@ -139,17 +137,15 @@ func (e *GoViewEngine) loadLayoutTemplates(layouts []string) error { } for _, file := range files { - tfiles := []string{layout, file} tmplKey := StripPathPrefixAt(filepath.ToSlash(file), "views/") tmpl := e.NewTemplate(tmplKey) - tmplfiles := e.AntiCSRFField.InsertOnFiles(tfiles...) + tfiles := []string{layout, file} log.Tracef("Parsing files: %s", TrimPathPrefix(prefix, tfiles...)) - if tmpl, err = tmpl.ParseFiles(tmplfiles...); err != nil { + if _, err = e.ParseFiles(tmpl, tfiles...); err != nil { errs = append(errs, err) continue } - if err = e.AddTemplate(layoutKey, tmplKey, tmpl); err != nil { errs = append(errs, err) continue @@ -179,11 +175,13 @@ func (e *GoViewEngine) loadNonLayoutTemplates(scope string) error { for _, file := range files { tmplKey := noLayout + "-" + StripPathPrefixAt(filepath.ToSlash(file), "views/") tmpl := e.NewTemplate(tmplKey) - fileBytes, _ := e.VFS.ReadFile(file) - fileStr := e.AntiCSRFField.InsertOnString(string(fileBytes)) log.Tracef("Parsing file: %s", TrimPathPrefix(prefix, file)) - if tmpl, err = tmpl.Parse(fileStr); err != nil { + tstr, err := e.Open(file) + if err != nil { + return err + } + if tmpl, err = tmpl.Parse(tstr); err != nil { errs = append(errs, err) continue } diff --git a/go_engine_test.go b/go_engine_test.go index 5994388..01c384c 100644 --- a/go_engine_test.go +++ b/go_engine_test.go @@ -166,6 +166,12 @@ func loadGoViewEngine(t *testing.T, cfg *config.Config, dir string) *GoViewEngin "anticsrftoken": func(arg interface{}) string { return "" }, + "rurl": func(args map[string]interface{}, key string) string { + return "//localhost:8080/login" + }, + "qparam": func(args map[string]interface{}, key string) string { + return "/index" + }, }) viewsDir := join("testdata", dir) diff --git a/testdata/views/pages/app/testhtml-form.html b/testdata/views/pages/app/testhtml-form.html new file mode 100644 index 0000000..188cc69 --- /dev/null +++ b/testdata/views/pages/app/testhtml-form.html @@ -0,0 +1,28 @@ + + + + pageTitle + + + +

node template engine

+
+
+ {{ if .Welcome }} +

You are amazing

+ {{ else }} +

Get on it!

+ {{ end }} +

+ one is terse and simple + templating language with a + focus on performance + and powerful features. +

+
+ + diff --git a/testdata/views/pages/user/testhtml-form.html b/testdata/views/pages/user/testhtml-form.html new file mode 100644 index 0000000..bf5f2fb --- /dev/null +++ b/testdata/views/pages/user/testhtml-form.html @@ -0,0 +1,28 @@ + + + + pageTitle + + + +

node template engine

+
+
+ {{ if .Welcome }} +

You are amazing

+ {{ else }} +

Get on it!

+ {{ end }} +

+ one is terse and simple + templating language with a + focus on performance + and powerful features. +

+
+ + diff --git a/view.go b/view.go index 48c00b3..ad8b728 100644 --- a/view.go +++ b/view.go @@ -1,5 +1,5 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/view source code and usage is governed by a MIT style +// aahframework.org/view source code and usage is governed by a MIT style // license that can be found in the LICENSE file. // Package view is implementation of aah framework view engine using Go @@ -8,11 +8,13 @@ package view import ( + "bytes" "errors" "fmt" "html/template" "path" "path/filepath" + "regexp" "strings" "aahframework.org/config.v0" @@ -124,7 +126,7 @@ func (t *Templates) Keys() []string { } //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// type EngineBase, methods +// type EngineBase, its methods //______________________________________________________________________________ // EngineBase struct is to create common and repurpose the implementation. @@ -139,8 +141,9 @@ type EngineBase struct { RightDelim string AppConfig *config.Config Templates map[string]*Templates - AntiCSRFField *AntiCSRFField VFS *vfs.VFS + + loginFormRegex *regexp.Regexp } // Init method is to initialize the base fields values. @@ -170,11 +173,66 @@ func (eb *EngineBase) Init(fs *vfs.VFS, appCfg *config.Config, baseDir, defaultE } eb.LeftDelim, eb.RightDelim = delimiter[0], delimiter[1] - // Anti CSRF - eb.AntiCSRFField = NewAntiCSRFFieldWithVFS(eb.VFS, "go", eb.LeftDelim, eb.RightDelim) + eb.loginFormRegex = regexp.MustCompile(`()`) + return nil } +// Open method reads template from VFS if not found resolve from physical +// file system. Also does auto field insertion such as +// Anti-CSRF(anti_csrf_token) and requested page URL (_rt). +func (eb *EngineBase) Open(filename string) (string, error) { + b, err := vfs.ReadFile(eb.VFS, filename) + if err != nil { + return "", err + } + + fc := string(b) + // process auto field insertion, if form tag exists + // anti_csrf_token field + if bytes.Contains(b, []byte("")) { + fc = strings.Replace(string(b), "", fmt.Sprintf(` + `, eb.LeftDelim, eb.RightDelim), -1) + } + + // _rt field + if matches := eb.loginFormRegex.FindAllStringIndex(fc, -1); len(matches) > 0 { + for _, m := range matches { + ts := fc[m[0]:m[1]] + fc = strings.Replace(fc, ts, fmt.Sprintf(`%s + `, ts), 1) + } + } + + return fc, nil +} + +// ParseFiles method parses given files with given template instance. +func (eb *EngineBase) ParseFiles(t *template.Template, filenames ...string) (*template.Template, error) { + for _, filename := range filenames { + s, err := eb.Open(filename) + if err != nil { + return nil, err + } + + name := filepath.Base(filename) + var tmpl *template.Template + if t == nil { + t = eb.NewTemplate(name) + } + if name == t.Name() { + tmpl = t + } else { + tmpl = t.New(name) + } + if _, err = tmpl.Parse(s); err != nil { + return nil, err + } + } + + return t, nil +} + // Get method returns the template based given name if found, otherwise nil. func (eb *EngineBase) Get(layout, path, tmplName string) (*template.Template, error) { if ess.IsStrEmpty(layout) { From ed332a505632e5f0e0ec9c9c876582588ed4ff10 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 13 Jun 2018 03:31:06 -0700 Subject: [PATCH 4/8] adding trace log for auto insertion field and build config update --- .travis.yml | 2 +- go_engine_test.go | 15 +++++++++++---- view.go | 2 ++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index b70c781..b15be07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ branches: go: - 1.9 - - "1.10" + - 1.x - tip go_import_path: aahframework.org/view.v0 diff --git a/go_engine_test.go b/go_engine_test.go index 01c384c..12e7dd5 100644 --- a/go_engine_test.go +++ b/go_engine_test.go @@ -8,6 +8,7 @@ import ( "bytes" "errors" "html/template" + "io/ioutil" "strings" "testing" @@ -17,7 +18,8 @@ import ( ) func TestViewAppPages(t *testing.T) { - _ = log.SetLevel("trace") + // _ = log.SetLevel("trace") + log.SetWriter(ioutil.Discard) cfg, _ := config.ParseString(`view { }`) ge := loadGoViewEngine(t, cfg, "views") @@ -45,7 +47,8 @@ func TestViewAppPages(t *testing.T) { } func TestViewUserPages(t *testing.T) { - _ = log.SetLevel("trace") + // _ = log.SetLevel("trace") + log.SetWriter(ioutil.Discard) cfg, _ := config.ParseString(`view { delimiters = "{{.}}" }`) @@ -78,7 +81,8 @@ func TestViewUserPages(t *testing.T) { } func TestViewUserPagesNoLayout(t *testing.T) { - _ = log.SetLevel("trace") + // _ = log.SetLevel("trace") + log.SetWriter(ioutil.Discard) cfg, _ := config.ParseString(`view { delimiters = "{{.}}" default_layout = false @@ -126,7 +130,8 @@ func TestViewDelimitersError(t *testing.T) { } func TestViewErrors(t *testing.T) { - _ = log.SetLevel("trace") + // _ = log.SetLevel("trace") + log.SetWriter(ioutil.Discard) cfg, _ := config.ParseString(`view { default_layout = false }`) @@ -186,5 +191,7 @@ func loadGoViewEngine(t *testing.T, cfg *config.Config, dir string) *GoViewEngin assert.NotNil(t, (&EngineBase{}).Init(nil, nil, "", "", "")) + log.SetWriter(ioutil.Discard) + return ge } diff --git a/view.go b/view.go index ad8b728..866c35f 100644 --- a/view.go +++ b/view.go @@ -191,12 +191,14 @@ func (eb *EngineBase) Open(filename string) (string, error) { // process auto field insertion, if form tag exists // anti_csrf_token field if bytes.Contains(b, []byte("")) { + log.Tracef("Adding field 'anti_csrf_token' into all forms: %s", filename) fc = strings.Replace(string(b), "", fmt.Sprintf(` `, eb.LeftDelim, eb.RightDelim), -1) } // _rt field if matches := eb.loginFormRegex.FindAllStringIndex(fc, -1); len(matches) > 0 { + log.Tracef("Adding field '_rt' into login form: %s", filename) for _, m := range matches { ts := fc[m[0]:m[1]] fc = strings.Replace(fc, ts, fmt.Sprintf(`%s From 53b8da6ec05bd6adc14f8b22a094614d3c9fe190 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 14 Jun 2018 00:41:13 -0700 Subject: [PATCH 5/8] go-aah/aah#189 view file hotreload without watcher --- funcs.go | 26 ++++++++++++++++++-------- go_engine.go | 20 +++++--------------- go_engine_test.go | 9 +++++---- view.go | 39 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 64 insertions(+), 30 deletions(-) diff --git a/funcs.go b/funcs.go index 5fa22fa..33b69dd 100644 --- a/funcs.go +++ b/funcs.go @@ -13,30 +13,40 @@ import ( ) // tmplSafeHTML method outputs given HTML as-is, use it with care. -func tmplSafeHTML(str string) template.HTML { +func (e *GoViewEngine) tmplSafeHTML(str string) template.HTML { return template.HTML(str) } // tmplInclude method renders given template with View Args and imports into // current template. -func tmplInclude(name string, viewArgs map[string]interface{}) template.HTML { +func (e *GoViewEngine) tmplInclude(name string, viewArgs map[string]interface{}) template.HTML { if !strings.HasPrefix(name, "common") { name = "common/" + name } + name = filepath.ToSlash(name) + var err error + var tmpl *template.Template + if e.hotReload { + if tmpl, err = e.ParseFile(name); err != nil { + log.Errorf("goviewengine: %s", err) + return e.tmplSafeHTML("") + } + } else { + tmpl = commonTemplates.Lookup(name) + } - tmpl := commonTemplates.Lookup(name) if tmpl == nil { log.Warnf("goviewengine: common template not found: %s", name) - return tmplSafeHTML("") + return e.tmplSafeHTML("") } buf := acquireBuffer() defer releaseBuffer(buf) - if err := tmpl.Execute(buf, viewArgs); err != nil { - log.Error(err) - return template.HTML("") + if err = tmpl.Execute(buf, viewArgs); err != nil { + log.Errorf("goviewengine: %s", err) + return e.tmplSafeHTML("") } - return tmplSafeHTML(buf.String()) + return e.tmplSafeHTML(buf.String()) } diff --git a/go_engine.go b/go_engine.go index 16fcf71..c6ed1cc 100644 --- a/go_engine.go +++ b/go_engine.go @@ -46,8 +46,9 @@ func (e *GoViewEngine) Init(fs *vfs.VFS, appCfg *config.Config, baseDir string) // Add template func AddTemplateFunc(template.FuncMap{ - "import": tmplInclude, - "include": tmplInclude, // alias for import + "safeHTML": e.tmplSafeHTML, + "import": e.tmplInclude, + "include": e.tmplInclude, // alias for import }) // load common templates @@ -99,18 +100,12 @@ func (e *GoViewEngine) loadCommonTemplates() error { continue } - tmplKey := StripPathPrefixAt(filepath.ToSlash(file), "views/") - tmpl := e.NewTemplate(tmplKey) - log.Tracef("Parsing file: %s", TrimPathPrefix(prefix, file)) - tstr, err := e.Open(file) + tmpl, err := e.ParseFile(file) if err != nil { return err } - if _, err = tmpl.Parse(tstr); err != nil { - return err - } - if err = commonTemplates.Add(tmplKey, tmpl); err != nil { + if err = commonTemplates.Add(tmpl.Name(), tmpl); err != nil { return err } } @@ -198,9 +193,4 @@ func (e *GoViewEngine) loadNonLayoutTemplates(scope string) error { func init() { _ = AddEngine("go", &GoViewEngine{}) - - // Add template func - AddTemplateFunc(template.FuncMap{ - "safeHTML": tmplSafeHTML, - }) } diff --git a/go_engine_test.go b/go_engine_test.go index 12e7dd5..8b5c7b1 100644 --- a/go_engine_test.go +++ b/go_engine_test.go @@ -21,7 +21,7 @@ func TestViewAppPages(t *testing.T) { // _ = log.SetLevel("trace") log.SetWriter(ioutil.Discard) cfg, _ := config.ParseString(`view { }`) - ge := loadGoViewEngine(t, cfg, "views") + ge := loadGoViewEngine(t, cfg, "views", false) data := map[string]interface{}{ "GreetName": "aah framework", @@ -52,7 +52,7 @@ func TestViewUserPages(t *testing.T) { cfg, _ := config.ParseString(`view { delimiters = "{{.}}" }`) - ge := loadGoViewEngine(t, cfg, "views") + ge := loadGoViewEngine(t, cfg, "views", true) data := map[string]interface{}{ "GreetName": "aah framework", @@ -87,7 +87,7 @@ func TestViewUserPagesNoLayout(t *testing.T) { delimiters = "{{.}}" default_layout = false }`) - ge := loadGoViewEngine(t, cfg, "views") + ge := loadGoViewEngine(t, cfg, "views", false) data := map[string]interface{}{ "GreetName": "aah framework", @@ -165,7 +165,7 @@ func TestViewErrors(t *testing.T) { assert.Equal(t, "goviewengine: error processing templates, please check the log", err.Error()) } -func loadGoViewEngine(t *testing.T, cfg *config.Config, dir string) *GoViewEngine { +func loadGoViewEngine(t *testing.T, cfg *config.Config, dir string, hotreload bool) *GoViewEngine { // dummy func for test AddTemplateFunc(template.FuncMap{ "anticsrftoken": func(arg interface{}) string { @@ -184,6 +184,7 @@ func loadGoViewEngine(t *testing.T, cfg *config.Config, dir string) *GoViewEngin err := ge.Init(newVFS(), cfg, viewsDir) assert.FailNowOnError(t, err, "") + ge.hotReload = hotreload assert.Equal(t, viewsDir, ge.BaseDir) assert.NotNil(t, ge.AppConfig) diff --git a/view.go b/view.go index 866c35f..6627390 100644 --- a/view.go +++ b/view.go @@ -143,6 +143,7 @@ type EngineBase struct { Templates map[string]*Templates VFS *vfs.VFS + hotReload bool loginFormRegex *regexp.Regexp } @@ -192,7 +193,7 @@ func (eb *EngineBase) Open(filename string) (string, error) { // anti_csrf_token field if bytes.Contains(b, []byte("")) { log.Tracef("Adding field 'anti_csrf_token' into all forms: %s", filename) - fc = strings.Replace(string(b), "", fmt.Sprintf(` + fc = strings.Replace(fc, "", fmt.Sprintf(` `, eb.LeftDelim, eb.RightDelim), -1) } @@ -209,6 +210,19 @@ func (eb *EngineBase) Open(filename string) (string, error) { return fc, nil } +// ParseFile method parses given single file. +func (eb *EngineBase) ParseFile(filename string) (*template.Template, error) { + if !strings.HasPrefix(filename, eb.BaseDir) { + filename = path.Join(eb.BaseDir, filename) + } + tmpl := eb.NewTemplate(StripPathPrefixAt(filepath.ToSlash(filename), "views/")) + tstr, err := eb.Open(filename) + if err != nil { + return nil, err + } + return tmpl.Parse(tstr) +} + // ParseFiles method parses given files with given template instance. func (eb *EngineBase) ParseFiles(t *template.Template, filenames ...string) (*template.Template, error) { for _, filename := range filenames { @@ -236,13 +250,27 @@ func (eb *EngineBase) ParseFiles(t *template.Template, filenames ...string) (*te } // Get method returns the template based given name if found, otherwise nil. -func (eb *EngineBase) Get(layout, path, tmplName string) (*template.Template, error) { +func (eb *EngineBase) Get(layout, tpath, tmplName string) (*template.Template, error) { + if eb.hotReload { + key := path.Join(tpath, tmplName) + if !eb.CaseSensitive { + key = strings.ToLower(key) + } + + if ess.IsStrEmpty(layout) { + return eb.ParseFile(path.Join(eb.BaseDir, key)) + } + return eb.ParseFiles(eb.NewTemplate(key), + path.Join(eb.BaseDir, "layouts", layout), + path.Join(eb.BaseDir, key)) + } + if ess.IsStrEmpty(layout) { layout = noLayout } if tmpls, found := eb.Templates[layout]; found { - key := filepath.Join(path, tmplName) + key := path.Join(tpath, tmplName) if layout == noLayout { key = noLayout + "-" + key } @@ -259,6 +287,11 @@ func (eb *EngineBase) Get(layout, path, tmplName string) (*template.Template, er return nil, ErrTemplateNotFound } +// SetHotReload method set teh view engine mode into hot reload without watcher. +func (eb *EngineBase) SetHotReload(r bool) { + eb.hotReload = r +} + // AddTemplate method adds the given template for layout and key. func (eb *EngineBase) AddTemplate(layout, key string, tmpl *template.Template) error { if eb.Templates[layout] == nil { From 9e7ae25f2d59790271e7223055834b8efb818ad8 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Fri, 15 Jun 2018 10:17:35 -0700 Subject: [PATCH 6/8] anti-csrf form field name red from app config --- funcs.go | 2 +- view.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/funcs.go b/funcs.go index 33b69dd..b372173 100644 --- a/funcs.go +++ b/funcs.go @@ -1,5 +1,5 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/view source code and usage is governed by a MIT style +// aahframework.org/view source code and usage is governed by a MIT style // license that can be found in the LICENSE file. package view diff --git a/view.go b/view.go index 6627390..9dacb54 100644 --- a/view.go +++ b/view.go @@ -193,8 +193,9 @@ func (eb *EngineBase) Open(filename string) (string, error) { // anti_csrf_token field if bytes.Contains(b, []byte("")) { log.Tracef("Adding field 'anti_csrf_token' into all forms: %s", filename) - fc = strings.Replace(fc, "", fmt.Sprintf(` - `, eb.LeftDelim, eb.RightDelim), -1) + fieldName := eb.AppConfig.StringDefault("security.anti_csrf.form_field_name", "anti_csrf_token") + fc = strings.Replace(fc, "", fmt.Sprintf(` + `, fieldName, eb.LeftDelim, eb.RightDelim), -1) } // _rt field From 3c684a2995a90bf9ec15e5e52c49d5ff469c1cf8 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 21 Jun 2018 20:37:25 -0700 Subject: [PATCH 7/8] extracted auto field insertion as a method in base engine --- view.go | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/view.go b/view.go index 9dacb54..70ae7d1 100644 --- a/view.go +++ b/view.go @@ -8,7 +8,6 @@ package view import ( - "bytes" "errors" "fmt" "html/template" @@ -89,7 +88,7 @@ type Templates struct { set map[string]*template.Template } -// Get method return the template for given key. +// Lookup method return the template for given key. func (t *Templates) Lookup(key string) *template.Template { return t.set[filepath.ToSlash(key)] } @@ -134,6 +133,7 @@ func (t *Templates) Keys() []string { type EngineBase struct { CaseSensitive bool IsLayoutEnabled bool + hotReload bool Name string BaseDir string FileExt string @@ -142,9 +142,7 @@ type EngineBase struct { AppConfig *config.Config Templates map[string]*Templates VFS *vfs.VFS - - hotReload bool - loginFormRegex *regexp.Regexp + loginFormRegex *regexp.Regexp } // Init method is to initialize the base fields values. @@ -187,28 +185,31 @@ func (eb *EngineBase) Open(filename string) (string, error) { if err != nil { return "", err } + return eb.AutoFieldInsertion(filename, string(b)), nil +} - fc := string(b) +// AutoFieldInsertion method processes the aah view's to auto insert the field. +func (eb *EngineBase) AutoFieldInsertion(name, v string) string { // process auto field insertion, if form tag exists // anti_csrf_token field - if bytes.Contains(b, []byte("")) { - log.Tracef("Adding field 'anti_csrf_token' into all forms: %s", filename) + if strings.Contains(v, "") { + log.Tracef("Adding field 'anti_csrf_token' into all forms: %s", name) fieldName := eb.AppConfig.StringDefault("security.anti_csrf.form_field_name", "anti_csrf_token") - fc = strings.Replace(fc, "", fmt.Sprintf(` + v = strings.Replace(v, "", fmt.Sprintf(` `, fieldName, eb.LeftDelim, eb.RightDelim), -1) } // _rt field - if matches := eb.loginFormRegex.FindAllStringIndex(fc, -1); len(matches) > 0 { - log.Tracef("Adding field '_rt' into login form: %s", filename) + if matches := eb.loginFormRegex.FindAllStringIndex(v, -1); len(matches) > 0 { + log.Tracef("Adding field '_rt' into login form: %s", name) for _, m := range matches { - ts := fc[m[0]:m[1]] - fc = strings.Replace(fc, ts, fmt.Sprintf(`%s + ts := v[m[0]:m[1]] + v = strings.Replace(v, ts, fmt.Sprintf(`%s `, ts), 1) } } - return fc, nil + return v } // ParseFile method parses given single file. @@ -252,7 +253,7 @@ func (eb *EngineBase) ParseFiles(t *template.Template, filenames ...string) (*te // Get method returns the template based given name if found, otherwise nil. func (eb *EngineBase) Get(layout, tpath, tmplName string) (*template.Template, error) { - if eb.hotReload { + if eb.hotReload && eb.Name == "go" { key := path.Join(tpath, tmplName) if !eb.CaseSensitive { key = strings.ToLower(key) From 64b129486c68af0a05aab9e86a32be4d3cc2f984 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Fri, 6 Jul 2018 22:19:18 -0700 Subject: [PATCH 8/8] readme update and version bump for v0.9.0 release --- README.md | 22 +++++++++++++--------- version.go | 4 ++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c8dd29b..b74f283 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,21 @@ -# view - aah framework -[![Build Status](https://travis-ci.org/go-aah/view.svg?branch=master)](https://travis-ci.org/go-aah/view) [![codecov](https://codecov.io/gh/go-aah/view/branch/master/graph/badge.svg)](https://codecov.io/gh/go-aah/view/branch/master) [![Go Report Card](https://goreportcard.com/badge/aahframework.org/view.v0)](https://goreportcard.com/report/aahframework.org/view.v0) [![Version](https://img.shields.io/badge/version-0.8.2-blue.svg)](https://github.com/go-aah/view/releases/latest) [![GoDoc](https://godoc.org/aahframework.org/view.v0?status.svg)](https://godoc.org/aahframework.org/view.v0) [![License](https://img.shields.io/github/license/go-aah/view.svg)](LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@aahframework-55acee.svg)](https://twitter.com/aahframework) +

+ +

View Engine library by aah framework

+

+

+

Build Status Code Coverage Go Report Card Release Version Godoc Twitter @aahframework

+

-***v0.8.2 [released](https://github.com/go-aah/view/releases/latest) and tagged on Apr 25, 2018*** +View Engine library provides enhanced Go template engine which supports partial template inheritance, imports, etc. -Go HTML template library which supports partial template inheritance, imports, etc. +### News -*`view` developed for aah framework. However, it's an independent library, can be used separately with any `Go` language project. Feel free to use it.* + * `v0.9.0` [released](https://github.com/go-aah/view/releases/latest) and tagged on Jul 06, 2018. + +## Installation -# Installation -#### Stable Version - Production Ready ```bash -# install the library go get -u aahframework.org/view.v0 ``` -Visit official website https://aahframework.org to learn more. +Visit official website https://aahframework.org to learn more about `aah` framework. diff --git a/version.go b/version.go index fbffb41..cacaec6 100644 --- a/version.go +++ b/version.go @@ -1,8 +1,8 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/view source code and usage is governed by a MIT style +// aahframework.org/view source code and usage is governed by a MIT style // license that can be found in the LICENSE file. package view // Version no. of aah framework view library -const Version = "0.9.0-edge" +const Version = "0.9.0"