diff --git a/cmd/client/template.go b/cmd/client/template.go index 8f6a58a..8ebf4b8 100644 --- a/cmd/client/template.go +++ b/cmd/client/template.go @@ -40,7 +40,7 @@ var templateCmd = &cobra.Command{ Use: "template", Short: "To compile your application template files into Go source code", Run: func(cmd *cobra.Command, args []string) { - exitAfterCompile := flag.Bool("exit-after-compile", true, "Exit after compiling templates") + exitAfterCompile := flag.Bool("exit-after-compile", false, "Exit after compiling application templates") flag.Parse() if len(args) == 0 { diff --git a/internal/app/views/index.tmpl b/internal/app/views/index.tmpl deleted file mode 100644 index a12e200..0000000 --- a/internal/app/views/index.tmpl +++ /dev/null @@ -1 +0,0 @@ -Template for web application testing \ No newline at end of file diff --git a/internal/main.go b/internal/main.go index 39b74c7..52f8193 100644 --- a/internal/main.go +++ b/internal/main.go @@ -44,28 +44,41 @@ type Product struct { } func main() { - fmt.Println("Bhojpur CMS demo application server, opening SQL database") - demoAppDB, _ := orm.Open("sqlite3", "internal/demo.db") - demoAppDB.AutoMigrate(&User{}, &Product{}) - fmt.Println("Configuring the Bhojpur CMS demo application views") - demoFS := demo.AssetFS - // Register view paths into the demoFS - demoFS.RegisterPath("app/views") - demoFS.RegisterPath("app/vendor/plugin/views") + loginFS := demo.AssetFS.NameSpace("login") + pluginFS := demo.AssetFS.NameSpace("plugin") + // Register view paths into the loginFS + loginFS.RegisterPath("templates/app/views") + pluginFS.RegisterPath("templates/app/vendor/plugin/views") // Compile application templates under registered view paths into binary - demoFS.Compile() + loginFS.Compile() + pluginFS.Compile() + + fmt.Println("Configuring a Bhojpur CMS demo login templates") + // Get file content with registered name + loginContent, err := loginFS.Asset("login.html") + if err != nil { + fmt.Errorf("While configuring demo application login", err) + } + if loginContent != nil { + fmt.Println("Configuring a demo application login.html") + } + fmt.Println("Configuring a Bhojpur CMS demo plugin templates") + pluginContent, err := pluginFS.Asset("index.tmpl") // Get file content with registered name - fileContent, ok := demoFS.Asset("internal/app/views/index.tmpl") - if ok != nil { - fmt.Errorf("While configuring demo application", ok) + if err != nil { + fmt.Errorf("While configuring demo application plugin", err) } - if fileContent != nil { - fmt.Println("Configuring a demo home/index.html") + if pluginContent != nil { + fmt.Println("Configuring a demo application plugin.tmpl") } + fmt.Println("Bhojpur CMS demo application server, opening SQL database") + demoAppDB, _ := orm.Open("sqlite3", "internal/demo.db") + demoAppDB.AutoMigrate(&User{}, &Product{}) + fmt.Println("Configuring a Bhojpur CMS administrator dashboard") // Initialize Bhojpur CMS - Administrator's Dashboard Admin := admin.New(&admin.AdminConfig{DB: demoAppDB}) @@ -77,6 +90,7 @@ func main() { fmt.Println("Configuring an HTTP service request multiplexer") // initialize an HTTP request multiplexer mux := http.NewServeMux() + mux.HandleFunc("/", landingPageFunc) fmt.Println("Mounting administrator dashboard web user interface") // Mount Bhojpur CMS - Administrator's web user interface to mux @@ -86,6 +100,12 @@ func main() { http.ListenAndServe(":3000", mux) } +func landingPageFunc(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("Bhojpur CMS

Welcome to Bhojpur CMS

Login to Administrator's Dashboard




Copyright © 2018 by Bhojpur Consulting Private Limited, India. All rights reserved.
")) + +} + /* func rendering() { Render := render.New(&render.Config{ diff --git a/pkg/admin/README.md b/pkg/admin/README.md new file mode 100644 index 0000000..d8da5a4 --- /dev/null +++ b/pkg/admin/README.md @@ -0,0 +1,249 @@ +# Bhojpur CMS - Administrator Dashboard + +You can instantly create a beautiful, cross platform, configurable System Administrator's web user interface and Backend APIs for managing data in minutes. + +## Core Features + +- Generate [Bhojpur CMS - Admin](https://github.com/bhojpur/cms/pkg/admin) interface for managing data +- RESTful JSON APIs +- Association handling +- Search and filtering +- Actions/Batch Actions +- Authentication and Authorization +- Extendability + +## Quick Start + +```go +package main + +import ( + "fmt" + "net/http" + orm "github.com/bhojpur/orm/pkg/engine" + _ "github.com/mattn/go-sqlite3" + "github.com/bhojpur/cms/pkg/admin" +) + +// Create a simple Bhojpur ORM backend model +type User struct { + orm.Model + Name string +} + +// Create another Bhojpur ORM backend model +type Product struct { + orm.Model + Name string + Description string +} + +func main() { + demoDB, _ := orm.Open("sqlite3", "internal/demo.db") + demoDB.AutoMigrate(&User{}, &Product{}) + + // Initialize + Admin := admin.New(&admin.AdminConfig{DB: demoDB}) + + // Allow to use Admin module to manage User, Product + Admin.AddResource(&User{}) + Admin.AddResource(&Product{}) + + // initialize an HTTP service request multiplexer + mux := http.NewServeMux() + + // Mount Administrator's web user interface to mux + Admin.MountTo("/admin", mux) + + fmt.Println("Listening on: 3000") + http.ListenAndServe(":3000", mux) +} +``` + +`go run main.go` and visit `localhost:3000/admin` to see the result! + +## How to use remoteSelector with publish2.version integrated record + +### **For many relationship** + +Suppose we have two models Factory and Item. Factory **has many** Items. + +In the struct, you need add a field `resource.CompositePrimaryKeyField` to the "many" side, which is `Item` here. + +```go +type Factory struct { + orm.Model + Name string + + publish2.Version + Items []Item `orm:"many2many:factory_items;association_autoupdate:false"` + ItemsSorter sorting.SortableCollection +} + +type Item struct { + orm.Model + Name string + publish2.Version + + // github.com/bhojpur/application/pkg/resource + resource.CompositePrimaryKeyField // Required +} +``` + +then, define a remote resource selector. You need configure the `ID` meta like below to make it support composite primary key, this is mandatory. + +```go +func generateRemoteItemSelector(adm *admin.Admin) (res *admin.Resource) { + res = adm.AddResource(&Item{}, &admin.Config{Name: "ItemSelector"}) + res.IndexAttrs("ID", "Name") + + // Required. Convert single ID into composite primary key + res.Meta(&admin.Meta{ + Name: "ID", + Valuer: func(value interface{}, ctx *appsvr.Context) interface{} { + if r, ok := value.(*Item); ok { + // github.com/bhojpur/application/pkg/resource + return resource.GenCompositePrimaryKey(r.ID, r.GetVersionName()) + } + return "" + }, + }) + + return res +} +``` + +Last, use it in the Factory resource. + +```go +itemSelector := generateRemoteItemSelector(adm) +factoryRes.Meta(&admin.Meta{ + Name: "Items", + Config: &admin.SelectManyConfig{ + RemoteDataResource: itemSelector, + }, +}) +``` + +### **For single relationship** + +Suppose we have two models. Factory and Manager. Factory **has one** Manager. + +Firstly, in the struct, you need add a field `resource.CompositePrimaryKeyField` to the "one" side, which is `Manager` here. + +```go +type Factory struct { + orm.Model + Name string + publish2.Version + + ManagerID uint + ManagerVersionName string // Required. in "xxxVersionName" format. + Manager Manager +} + +type Manager struct { + orm.Model + Name string + publish2.Version + + // github.com/bhojpur/application/pkg/resource + resource.CompositePrimaryKeyField // Required +} +``` + +then, define a remote resource selector. You need configure the `ID` meta like below to make it support composite primary key, this is mandatory. + +```go +func generateRemoteManagerSelector(adm *admin.Admin) (res *admin.Resource) { + res = adm.AddResource(&Manager{}, &admin.Config{Name: "ManagerSelector"}) + res.IndexAttrs("ID", "Name") + + // Required. Convert single ID into composite primary key + res.Meta(&admin.Meta{ + Name: "ID", + Valuer: func(value interface{}, ctx *appsvr.Context) interface{} { + if r, ok := value.(*Manager); ok { + // github.com/bhojpur/application/pkg/resource + return resource.GenCompositePrimaryKey(r.ID, r.GetVersionName()) + } + return "" + }, + }) + + return res +} + +Lastly, use it in the Factory resource. + +```go +managerSelector := generateRemoteManagerSelector(adm) +factoryRes.Meta(&admin.Meta{ + Name: "Manager", + Config: &admin.SelectOneConfig{ + RemoteDataResource: managerSelector, + }, +}) +``` + +If you need to overwrite Collection. you have to pass composite primary key as the first element of the returning array instead of ID. + +```go +factoryRes.Meta(&admin.Meta{ + Name: "Items", + Config: &admin.SelectManyConfig{ + Collection: func(value interface{}, ctx *appsvr.Context) (results [][]string) { + if c, ok := value.(*Factory); ok { + var items []Item + ctx.GetDB().Model(c).Related(&items, "Items") + + for _, p := range items { + // The first element must be the composite primary key instead of ID + results = append(results, []string{resource.GenCompositePrimaryKey(p.ID, p.GetVersionName()), p.Name}) + } + } + return + }, + RemoteDataResource: itemSelector, + }, +}) +``` + +## To support assign associations while creating a new version + +If you want to assign associations while creating a new version of object immediately. You need to define a function called `AssignVersionName` to the versioned struct with **pointer** receiver which should contains the generating new version name's logic and assign the new version name to the object. For example + +```go +func (fac *Factory) AssignVersionName(db *orm.DB) { + var count int + name := time.Now().Format("2006-01-02") + if err := db.Model(&CollectionWithVersion{}).Where("id = ? AND version_name like ?", fac.ID, name+"%").Count(&count).Error; err != nil { + panic(err) + } + fac.VersionName = fmt.Sprintf("%s-v%v", name, count+1) +} +``` + +## Documentation + +To print all registered routes + +```go +// adm is a Bhojpur CMS admin instance +adm.GetRouter().PrintRoutes() +``` + +## ViewPath Note + +Bhojpur CMS still support Go path while finding its template files. The priority is + +1. check vendor, if not found +2. check $GOPATH/pkg/mod/github.com/bhojpur/cms@v0.x/pkg/admin/views. The version would be detected automatically by your go.mod file, if still not found +3. load view path from $GOPATH/src/github.com/bhojpur/cms/pkg/admin/views + + +So, if you want to use the template under the pkg/mod, make sure $GOPATH/src/github.com/bhojpur/cms/pkg/admin is absent. + +## License + +Released under the [MIT License](http://opensource.org/licenses/MIT). \ No newline at end of file diff --git a/pkg/admin/admin.go b/pkg/admin/admin.go index a352c74..3899f97 100644 --- a/pkg/admin/admin.go +++ b/pkg/admin/admin.go @@ -119,8 +119,8 @@ func (admin *Admin) SetAssetFS(assetFS assetfs.Interface) { admin.AssetFS = assetFS globalAssetFSes = append(globalAssetFSes, assetFS) - admin.AssetFS.RegisterPath(filepath.Join(utils.AppRoot, "app/views/bhojpur")) - admin.RegisterViewPath("github.com/bhojpur/cms/pkg/admin/views") + admin.AssetFS.RegisterPath(filepath.Join(utils.AppRoot, "pkg/admin/views")) + admin.RegisterViewPath("pkg/admin/views") for _, viewPath := range globalViewPaths { admin.RegisterViewPath(viewPath) diff --git a/pkg/admin/views/layout.tmpl b/pkg/admin/views/layout.tmpl index 4799054..0daac24 100644 --- a/pkg/admin/views/layout.tmpl +++ b/pkg/admin/views/layout.tmpl @@ -2,7 +2,7 @@ {{$title := page_title}} - {{if $title}}{{$title}} - {{end}}{{if .Admin.SiteName}}{{t .Admin.SiteName}}{{else}}{{t "Bhojpur CMS - Administrator"}}{{end}} + {{if $title}}{{$title}} - {{end}}{{if .Admin.SiteName}}{{t .Admin.SiteName}}{{else}}{{t "Bhojpur CMS"}}{{end}} diff --git a/pkg/media/oss/oss.go b/pkg/media/oss/oss.go index 2b75a53..2b214dc 100644 --- a/pkg/media/oss/oss.go +++ b/pkg/media/oss/oss.go @@ -28,8 +28,8 @@ import ( "github.com/bhojpur/application/pkg/utils" "github.com/bhojpur/cms/pkg/media" - "github.com/bhojpur/cms/pkg/media/filesystem" oss "github.com/bhojpur/drive/pkg/model" + "github.com/bhojpur/drive/pkg/provider/filesystem" ) var ( diff --git a/pkg/media/oss/oss_test.go b/pkg/media/oss/oss_test.go index b9ae583..350a138 100644 --- a/pkg/media/oss/oss_test.go +++ b/pkg/media/oss/oss_test.go @@ -36,8 +36,8 @@ import ( "github.com/bhojpur/application/test/utils" "github.com/bhojpur/cms/pkg/media" + oss "github.com/bhojpur/cms/pkg/media/oss" cfgsvr "github.com/bhojpur/configure/pkg/markup" - oss "github.com/bhojpur/drive/pkg/model" s3 "github.com/bhojpur/drive/pkg/provider/s3" orm "github.com/bhojpur/orm/pkg/engine" ) diff --git a/internal/app/vendor/plugin/views/index.tmpl b/templates/app/vendor/plugin/views/index.tmpl similarity index 100% rename from internal/app/vendor/plugin/views/index.tmpl rename to templates/app/vendor/plugin/views/index.tmpl diff --git a/templates/app/views/login.html b/templates/app/views/login.html new file mode 100644 index 0000000..7a3e7f4 --- /dev/null +++ b/templates/app/views/login.html @@ -0,0 +1,59 @@ + + + + + + + + Bhojpur CMS - Login + + + + + + + +
+
+
+
+
+ + +
+
+ + +
+ +
+
+
+
+ + + \ No newline at end of file