github.com/kotovmak/go-admin@v1.1.1/engine/engine.go (about) 1 // Copyright 2019 GoAdmin Core Team. All rights reserved. 2 // Use of this source code is governed by a Apache-2.0 style 3 // license that can be found in the LICENSE file. 4 5 package engine 6 7 import ( 8 "bytes" 9 "encoding/json" 10 errors2 "errors" 11 "fmt" 12 template2 "html/template" 13 "net/http" 14 "runtime/debug" 15 "strings" 16 "sync" 17 18 "github.com/kotovmak/go-admin/modules/language" 19 "github.com/kotovmak/go-admin/template/icon" 20 "github.com/kotovmak/go-admin/template/types/action" 21 22 "github.com/kotovmak/go-admin/adapter" 23 "github.com/kotovmak/go-admin/context" 24 "github.com/kotovmak/go-admin/modules/auth" 25 "github.com/kotovmak/go-admin/modules/config" 26 "github.com/kotovmak/go-admin/modules/db" 27 "github.com/kotovmak/go-admin/modules/errors" 28 "github.com/kotovmak/go-admin/modules/logger" 29 "github.com/kotovmak/go-admin/modules/menu" 30 "github.com/kotovmak/go-admin/modules/service" 31 "github.com/kotovmak/go-admin/modules/system" 32 "github.com/kotovmak/go-admin/modules/ui" 33 "github.com/kotovmak/go-admin/plugins" 34 "github.com/kotovmak/go-admin/plugins/admin" 35 "github.com/kotovmak/go-admin/plugins/admin/models" 36 "github.com/kotovmak/go-admin/plugins/admin/modules/response" 37 "github.com/kotovmak/go-admin/plugins/admin/modules/table" 38 "github.com/kotovmak/go-admin/template" 39 "github.com/kotovmak/go-admin/template/types" 40 ) 41 42 // Engine is the core component of goAdmin. It has two attributes. 43 // PluginList is an array of plugin. Adapter is the adapter of 44 // web framework context and goAdmin context. The relationship of adapter and 45 // plugin is that the adapter use the plugin which contains routers and 46 // controller methods to inject into the framework entity and make it work. 47 type Engine struct { 48 PluginList plugins.Plugins 49 Adapter adapter.WebFrameWork 50 Services service.List 51 NavButtons *types.Buttons 52 config *config.Config 53 announceLock sync.Once 54 } 55 56 // Default return the default engine instance. 57 func Default() *Engine { 58 engine = &Engine{ 59 Adapter: defaultAdapter, 60 Services: service.GetServices(), 61 NavButtons: new(types.Buttons), 62 } 63 return engine 64 } 65 66 // Use enable the adapter. 67 func (eng *Engine) Use(router interface{}) error { 68 if eng.Adapter == nil { 69 emptyAdapterPanic() 70 } 71 72 eng.Services.Add(auth.InitCSRFTokenSrv(eng.DefaultConnection())) 73 eng.initSiteSetting() 74 eng.initJumpNavButtons() 75 eng.initPlugins() 76 77 printInitMsg(language.Get("initialize success")) 78 79 return eng.Adapter.Use(router, eng.PluginList) 80 } 81 82 // AddPlugins add the plugins 83 func (eng *Engine) AddPlugins(plugs ...plugins.Plugin) *Engine { 84 85 if len(plugs) == 0 { 86 return eng 87 } 88 89 for _, plug := range plugs { 90 eng.PluginList = eng.PluginList.Add(plug) 91 } 92 93 return eng 94 } 95 96 // AddPluginList add the plugins 97 func (eng *Engine) AddPluginList(plugs plugins.Plugins) *Engine { 98 99 if len(plugs) == 0 { 100 return eng 101 } 102 103 for _, plug := range plugs { 104 eng.PluginList = eng.PluginList.Add(plug) 105 } 106 107 return eng 108 } 109 110 // FindPluginByName find the register plugin by given name. 111 func (eng *Engine) FindPluginByName(name string) (plugins.Plugin, bool) { 112 for _, plug := range eng.PluginList { 113 if plug.Name() == name { 114 return plug, true 115 } 116 } 117 return nil, false 118 } 119 120 // AddAuthService customize the auth logic with given callback function. 121 func (eng *Engine) AddAuthService(processor auth.Processor) *Engine { 122 eng.Services.Add("auth", auth.NewService(processor)) 123 return eng 124 } 125 126 // ============================ 127 // Config APIs 128 // ============================ 129 130 func (eng *Engine) announce() *Engine { 131 if eng.config.Debug { 132 eng.announceLock.Do(func() { 133 fmt.Printf(language.Get("goadmin is now running. \nrunning in \"debug\" mode. switch to \"release\" mode in production.\n\n")) 134 }) 135 } 136 return eng 137 } 138 139 // AddConfig set the global config. 140 func (eng *Engine) AddConfig(cfg *config.Config) *Engine { 141 return eng.setConfig(cfg).announce().initDatabase() 142 } 143 144 // setConfig set the config of engine. 145 func (eng *Engine) setConfig(cfg *config.Config) *Engine { 146 eng.config = config.Initialize(cfg) 147 sysCheck, themeCheck := template.CheckRequirements() 148 if !sysCheck { 149 logger.Panicf(language.Get("wrong goadmin version, theme %s required goadmin version are %s"), 150 eng.config.Theme, strings.Join(template.Default().GetRequirements(), ",")) 151 } 152 if !themeCheck { 153 logger.Panicf(language.Get("wrong theme version, goadmin %s required version of theme %s is %s"), 154 system.Version(), eng.config.Theme, strings.Join(system.RequireThemeVersion()[eng.config.Theme], ",")) 155 } 156 return eng 157 } 158 159 // AddConfigFromJSON set the global config from json file. 160 func (eng *Engine) AddConfigFromJSON(path string) *Engine { 161 cfg := config.ReadFromJson(path) 162 return eng.setConfig(&cfg).announce().initDatabase() 163 } 164 165 // AddConfigFromYAML set the global config from yaml file. 166 func (eng *Engine) AddConfigFromYAML(path string) *Engine { 167 cfg := config.ReadFromYaml(path) 168 return eng.setConfig(&cfg).announce().initDatabase() 169 } 170 171 // AddConfigFromINI set the global config from ini file. 172 func (eng *Engine) AddConfigFromINI(path string) *Engine { 173 cfg := config.ReadFromINI(path) 174 return eng.setConfig(&cfg).announce().initDatabase() 175 } 176 177 // InitDatabase initialize all database connection. 178 func (eng *Engine) initDatabase() *Engine { 179 printInitMsg(language.Get("initialize database connections")) 180 for driver, databaseCfg := range eng.config.Databases.GroupByDriver() { 181 eng.Services.Add(driver, db.GetConnectionByDriver(driver).InitDB(databaseCfg)) 182 } 183 if defaultAdapter == nil { 184 emptyAdapterPanic() 185 } 186 defaultConnection := db.GetConnection(eng.Services) 187 defaultAdapter.SetConnection(defaultConnection) 188 eng.Adapter.SetConnection(defaultConnection) 189 return eng 190 } 191 192 // AddAdapter add the adapter of engine. 193 func (eng *Engine) AddAdapter(ada adapter.WebFrameWork) *Engine { 194 eng.Adapter = ada 195 defaultAdapter = ada 196 return eng 197 } 198 199 // defaultAdapter is the default adapter of engine. 200 var defaultAdapter adapter.WebFrameWork 201 202 var engine *Engine 203 204 // navButtons is the default buttons in the navigation bar. 205 var navButtons = new(types.Buttons) 206 207 func emptyAdapterPanic() { 208 logger.Panic(language.Get("adapter is nil, import the default adapter or use addadapter method add the adapter")) 209 } 210 211 // Register set default adapter of engine. 212 func Register(ada adapter.WebFrameWork) { 213 if ada == nil { 214 emptyAdapterPanic() 215 } 216 defaultAdapter = ada 217 } 218 219 // User call the User method of defaultAdapter. 220 func User(ctx interface{}) (models.UserModel, bool) { 221 return defaultAdapter.User(ctx) 222 } 223 224 // User call the User method of engine adapter. 225 func (eng *Engine) User(ctx interface{}) (models.UserModel, bool) { 226 return eng.Adapter.User(ctx) 227 } 228 229 // ============================ 230 // DB Connection APIs 231 // ============================ 232 233 // DB return the db connection of given driver. 234 func (eng *Engine) DB(driver string) db.Connection { 235 return db.GetConnectionFromService(eng.Services.Get(driver)) 236 } 237 238 // DefaultConnection return the default db connection. 239 func (eng *Engine) DefaultConnection() db.Connection { 240 return eng.DB(eng.config.Databases.GetDefault().Driver) 241 } 242 243 // MysqlConnection return the mysql db connection of given driver. 244 func (eng *Engine) MysqlConnection() db.Connection { 245 return db.GetConnectionFromService(eng.Services.Get(db.DriverMysql)) 246 } 247 248 // MssqlConnection return the mssql db connection of given driver. 249 func (eng *Engine) MssqlConnection() db.Connection { 250 return db.GetConnectionFromService(eng.Services.Get(db.DriverMssql)) 251 } 252 253 // PostgresqlConnection return the postgresql db connection of given driver. 254 func (eng *Engine) PostgresqlConnection() db.Connection { 255 return db.GetConnectionFromService(eng.Services.Get(db.DriverPostgresql)) 256 } 257 258 // SqliteConnection return the sqlite db connection of given driver. 259 func (eng *Engine) SqliteConnection() db.Connection { 260 return db.GetConnectionFromService(eng.Services.Get(db.DriverSqlite)) 261 } 262 263 // OceanBaseConnection return the OceanBase db connection of given driver. 264 func (eng *Engine) OceanBaseConnection() db.Connection { 265 return db.GetConnectionFromService(eng.Services.Get(db.DriverOceanBase)) 266 } 267 268 type ConnectionSetter func(db.Connection) 269 270 // ResolveConnection resolve the specified driver connection. 271 func (eng *Engine) ResolveConnection(setter ConnectionSetter, driver string) *Engine { 272 setter(eng.DB(driver)) 273 return eng 274 } 275 276 // ResolveMysqlConnection resolve the mysql connection. 277 func (eng *Engine) ResolveMysqlConnection(setter ConnectionSetter) *Engine { 278 eng.ResolveConnection(setter, db.DriverMysql) 279 return eng 280 } 281 282 // ResolveMssqlConnection resolve the mssql connection. 283 func (eng *Engine) ResolveMssqlConnection(setter ConnectionSetter) *Engine { 284 eng.ResolveConnection(setter, db.DriverMssql) 285 return eng 286 } 287 288 // ResolveSqliteConnection resolve the sqlite connection. 289 func (eng *Engine) ResolveSqliteConnection(setter ConnectionSetter) *Engine { 290 eng.ResolveConnection(setter, db.DriverSqlite) 291 return eng 292 } 293 294 // ResolvePostgresqlConnection resolve the postgres connection. 295 func (eng *Engine) ResolvePostgresqlConnection(setter ConnectionSetter) *Engine { 296 eng.ResolveConnection(setter, db.DriverPostgresql) 297 return eng 298 } 299 300 type Setter func(*Engine) 301 302 // Clone copy a new Engine. 303 func (eng *Engine) Clone(e *Engine) *Engine { 304 e = eng 305 return eng 306 } 307 308 // ClonedBySetter copy a new Engine by a setter callback function. 309 func (eng *Engine) ClonedBySetter(setter Setter) *Engine { 310 setter(eng) 311 return eng 312 } 313 314 func (eng *Engine) deferHandler(conn db.Connection) context.Handler { 315 return func(ctx *context.Context) { 316 defer func(ctx *context.Context) { 317 if user, ok := ctx.UserValue["user"].(models.UserModel); ok { 318 var input []byte 319 form := ctx.Request.MultipartForm 320 if form != nil { 321 input, _ = json.Marshal((*form).Value) 322 } 323 324 models.OperationLog().SetConn(conn).New(user.Id, ctx.Path(), ctx.Method(), ctx.LocalIP(), string(input)) 325 } 326 327 if err := recover(); err != nil { 328 logger.Error(err) 329 logger.Error(string(debug.Stack())) 330 331 var ( 332 errMsg string 333 ok bool 334 e error 335 ) 336 337 if errMsg, ok = err.(string); !ok { 338 if e, ok = err.(error); ok { 339 errMsg = e.Error() 340 } 341 } 342 343 if errMsg == "" { 344 errMsg = "system error" 345 } 346 347 if ctx.WantJSON() { 348 response.Error(ctx, errMsg) 349 return 350 } 351 352 eng.errorPanelHTML(ctx, new(bytes.Buffer), errors2.New(errMsg)) 353 } 354 }(ctx) 355 ctx.Next() 356 } 357 } 358 359 // wrapWithAuthMiddleware wrap a auth middleware to the given handler. 360 func (eng *Engine) wrapWithAuthMiddleware(handler context.Handler) context.Handlers { 361 conn := db.GetConnection(eng.Services) 362 return []context.Handler{eng.deferHandler(conn), response.OffLineHandler, auth.Middleware(conn), handler} 363 } 364 365 // wrapWithAuthMiddleware wrap a auth middleware to the given handler. 366 func (eng *Engine) wrap(handler context.Handler) context.Handlers { 367 conn := db.GetConnection(eng.Services) 368 return []context.Handler{eng.deferHandler(conn), response.OffLineHandler, handler} 369 } 370 371 // ============================ 372 // Initialize methods 373 // ============================ 374 375 // AddNavButtons add the nav buttons. 376 func (eng *Engine) AddNavButtons(title template2.HTML, icon string, action types.Action) *Engine { 377 btn := types.GetNavButton(title, icon, action) 378 *eng.NavButtons = append(*eng.NavButtons, btn) 379 return eng 380 } 381 382 // AddNavButtonsRaw add the nav buttons. 383 func (eng *Engine) AddNavButtonsRaw(btns ...types.Button) *Engine { 384 *eng.NavButtons = append(*eng.NavButtons, btns...) 385 return eng 386 } 387 388 type navJumpButtonParam struct { 389 Exist bool 390 Icon string 391 BtnName string 392 URL string 393 Title string 394 TitleScore string 395 } 396 397 func (eng *Engine) addJumpNavButton(param navJumpButtonParam) *Engine { 398 if param.Exist { 399 *eng.NavButtons = (*eng.NavButtons).AddNavButton(param.Icon, param.BtnName, 400 action.JumpInNewTab(config.Url(param.URL), 401 language.GetWithScope(param.Title, param.TitleScore))) 402 } 403 return eng 404 } 405 406 func printInitMsg(msg string) { 407 logger.Info("=====> " + msg) 408 } 409 410 func (eng *Engine) initJumpNavButtons() { 411 printInitMsg(language.Get("initialize navigation buttons")) 412 for _, param := range eng.initNavJumpButtonParams() { 413 eng.addJumpNavButton(param) 414 } 415 navButtons = eng.NavButtons 416 eng.Services.Add(ui.ServiceKey, ui.NewService(eng.NavButtons)) 417 } 418 419 func (eng *Engine) initPlugins() { 420 421 printInitMsg(language.Get("initialize plugins")) 422 423 eng.AddPlugins(admin.NewAdmin()).AddPluginList(plugins.Get()) 424 425 var plugGenerators = make(table.GeneratorList) 426 427 for i := range eng.PluginList { 428 if eng.PluginList[i].Name() != "admin" { 429 printInitMsg("--> " + eng.PluginList[i].Name()) 430 eng.PluginList[i].InitPlugin(eng.Services) 431 if !eng.PluginList[i].GetInfo().SkipInstallation { 432 eng.AddGenerator("plugin_"+eng.PluginList[i].Name(), eng.PluginList[i].GetSettingPage()) 433 } 434 plugGenerators = plugGenerators.Combine(eng.PluginList[i].GetGenerators()) 435 } 436 } 437 adm := eng.AdminPlugin().AddGenerators(plugGenerators) 438 adm.InitPlugin(eng.Services) 439 plugins.Add(adm) 440 } 441 442 func (eng *Engine) initNavJumpButtonParams() []navJumpButtonParam { 443 return []navJumpButtonParam{ 444 { 445 Exist: !eng.config.HideConfigCenterEntrance, 446 Icon: icon.Gear, 447 BtnName: types.NavBtnSiteName, 448 URL: "/info/site/edit", 449 Title: "site setting", 450 TitleScore: "config", 451 }, { 452 Exist: !eng.config.HideToolEntrance && eng.config.IsNotProductionEnvironment(), 453 Icon: icon.Wrench, 454 BtnName: types.NavBtnToolName, 455 URL: "/info/generate/new", 456 Title: "tool", 457 TitleScore: "tool", 458 }, { 459 Exist: !eng.config.HideAppInfoEntrance, 460 Icon: icon.Info, 461 BtnName: types.NavBtnInfoName, 462 URL: "/application/info", 463 Title: "system info", 464 TitleScore: "system", 465 }, { 466 Exist: !eng.config.HidePluginEntrance, 467 Icon: icon.Th, 468 BtnName: types.NavBtnPlugName, 469 URL: "/plugins", 470 Title: "plugin", 471 TitleScore: "plugin", 472 }, 473 } 474 } 475 476 func (eng *Engine) initSiteSetting() { 477 478 printInitMsg(language.Get("initialize configuration")) 479 480 err := eng.config.Update(models.Site(). 481 SetConn(eng.DefaultConnection()). 482 Init(eng.config.ToMap()). 483 AllToMap()) 484 if err != nil { 485 logger.Panic(err) 486 } 487 eng.Services.Add("config", config.SrvWithConfig(eng.config)) 488 489 errors.Init() 490 } 491 492 // ============================ 493 // HTML Content Render APIs 494 // ============================ 495 496 // Content call the Content method of engine adapter. 497 // If adapter is nil, it will panic. 498 func (eng *Engine) Content(ctx interface{}, panel types.GetPanelFn) { 499 if eng.Adapter == nil { 500 emptyAdapterPanic() 501 } 502 eng.Adapter.Content(ctx, panel, eng.AdminPlugin().GetAddOperationFn(), *eng.NavButtons...) 503 } 504 505 // Content call the Content method of defaultAdapter. 506 // If defaultAdapter is nil, it will panic. 507 func Content(ctx interface{}, panel types.GetPanelFn) { 508 if defaultAdapter == nil { 509 emptyAdapterPanic() 510 } 511 defaultAdapter.Content(ctx, panel, engine.AdminPlugin().GetAddOperationFn(), *navButtons...) 512 } 513 514 // Data inject the route and corresponding handler to the web framework. 515 func (eng *Engine) Data(method, url string, handler context.Handler, noAuth ...bool) { 516 if len(noAuth) > 0 && noAuth[0] { 517 eng.Adapter.AddHandler(method, url, eng.wrap(handler)) 518 } else { 519 eng.Adapter.AddHandler(method, url, eng.wrapWithAuthMiddleware(handler)) 520 } 521 } 522 523 // HTML inject the route and corresponding handler wrapped by the given function to the web framework. 524 func (eng *Engine) HTML(method, url string, fn types.GetPanelInfoFn, noAuth ...bool) { 525 526 var handler = func(ctx *context.Context) { 527 panel, err := fn(ctx) 528 if err != nil { 529 panel = template.WarningPanel(err.Error()) 530 } 531 532 eng.AdminPlugin().GetAddOperationFn()(panel.Callbacks...) 533 534 var ( 535 tmpl, tmplName = template.Default().GetTemplate(ctx.IsPjax()) 536 537 user = auth.Auth(ctx) 538 buf = new(bytes.Buffer) 539 ) 540 541 hasError := tmpl.ExecuteTemplate(buf, tmplName, types.NewPage(&types.NewPageParam{ 542 User: user, 543 Menu: menu.GetGlobalMenu(user, eng.Adapter.GetConnection(), ctx.Lang()).SetActiveClass(config.URLRemovePrefix(ctx.Path())), 544 Panel: panel.GetContent(eng.config.IsProductionEnvironment()), 545 Assets: template.GetComponentAssetImportHTML(), 546 Buttons: eng.NavButtons.CheckPermission(user), 547 TmplHeadHTML: template.Default().GetHeadHTML(), 548 TmplFootJS: template.Default().GetFootJS(), 549 Iframe: ctx.IsIframe(), 550 })) 551 552 if hasError != nil { 553 logger.Error(fmt.Sprintf("error: %s adapter content, ", eng.Adapter.Name()), hasError) 554 } 555 556 ctx.HTMLByte(http.StatusOK, buf.Bytes()) 557 } 558 559 if len(noAuth) > 0 && noAuth[0] { 560 eng.Adapter.AddHandler(method, url, eng.wrap(handler)) 561 } else { 562 eng.Adapter.AddHandler(method, url, eng.wrapWithAuthMiddleware(handler)) 563 } 564 } 565 566 // HTMLFile inject the route and corresponding handler which returns the panel content of given html file path 567 // to the web framework. 568 func (eng *Engine) HTMLFile(method, url, path string, data map[string]interface{}, noAuth ...bool) { 569 570 var handler = func(ctx *context.Context) { 571 572 cbuf := new(bytes.Buffer) 573 574 t, err := template2.ParseFiles(path) 575 if err != nil { 576 eng.errorPanelHTML(ctx, cbuf, err) 577 return 578 } else if err := t.Execute(cbuf, data); err != nil { 579 eng.errorPanelHTML(ctx, cbuf, err) 580 return 581 } 582 583 var ( 584 tmpl, tmplName = template.Default().GetTemplate(ctx.IsPjax()) 585 586 user = auth.Auth(ctx) 587 buf = new(bytes.Buffer) 588 ) 589 590 hasError := tmpl.ExecuteTemplate(buf, tmplName, types.NewPage(&types.NewPageParam{ 591 User: user, 592 Menu: menu.GetGlobalMenu(user, eng.Adapter.GetConnection(), ctx.Lang()).SetActiveClass(eng.config.URLRemovePrefix(ctx.Path())), 593 Panel: types.Panel{ 594 Content: template.HTML(cbuf.String()), 595 }, 596 Assets: template.GetComponentAssetImportHTML(), 597 Buttons: eng.NavButtons.CheckPermission(user), 598 TmplHeadHTML: template.Default().GetHeadHTML(), 599 TmplFootJS: template.Default().GetFootJS(), 600 Iframe: ctx.IsIframe(), 601 })) 602 603 if hasError != nil { 604 logger.Error(fmt.Sprintf("error: %s adapter content, ", eng.Adapter.Name()), hasError) 605 } 606 607 ctx.HTMLByte(http.StatusOK, buf.Bytes()) 608 } 609 610 if len(noAuth) > 0 && noAuth[0] { 611 eng.Adapter.AddHandler(method, url, eng.wrap(handler)) 612 } else { 613 eng.Adapter.AddHandler(method, url, eng.wrapWithAuthMiddleware(handler)) 614 } 615 } 616 617 // HTMLFiles inject the route and corresponding handler which returns the panel content of given html files path 618 // to the web framework. 619 func (eng *Engine) HTMLFiles(method, url string, data map[string]interface{}, files ...string) { 620 eng.Adapter.AddHandler(method, url, eng.wrapWithAuthMiddleware(eng.htmlFilesHandler(data, files...))) 621 } 622 623 // HTMLFilesNoAuth inject the route and corresponding handler which returns the panel content of given html files path 624 // to the web framework without auth check. 625 func (eng *Engine) HTMLFilesNoAuth(method, url string, data map[string]interface{}, files ...string) { 626 eng.Adapter.AddHandler(method, url, eng.wrap(eng.htmlFilesHandler(data, files...))) 627 } 628 629 // HTMLFiles inject the route and corresponding handler which returns the panel content of given html files path 630 // to the web framework. 631 func (eng *Engine) htmlFilesHandler(data map[string]interface{}, files ...string) context.Handler { 632 return func(ctx *context.Context) { 633 634 cbuf := new(bytes.Buffer) 635 636 t, err := template2.ParseFiles(files...) 637 if err != nil { 638 eng.errorPanelHTML(ctx, cbuf, err) 639 return 640 } else if err := t.Execute(cbuf, data); err != nil { 641 eng.errorPanelHTML(ctx, cbuf, err) 642 return 643 } 644 645 var ( 646 tmpl, tmplName = template.Default().GetTemplate(ctx.IsPjax()) 647 648 user = auth.Auth(ctx) 649 buf = new(bytes.Buffer) 650 ) 651 652 hasError := tmpl.ExecuteTemplate(buf, tmplName, types.NewPage(&types.NewPageParam{ 653 User: user, 654 Menu: menu.GetGlobalMenu(user, eng.Adapter.GetConnection(), ctx.Lang()).SetActiveClass(eng.config.URLRemovePrefix(ctx.Path())), 655 Panel: types.Panel{ 656 Content: template.HTML(cbuf.String()), 657 }, 658 Assets: template.GetComponentAssetImportHTML(), 659 Buttons: eng.NavButtons.CheckPermission(user), 660 TmplHeadHTML: template.Default().GetHeadHTML(), 661 TmplFootJS: template.Default().GetFootJS(), 662 Iframe: ctx.IsIframe(), 663 })) 664 665 if hasError != nil { 666 logger.Error(fmt.Sprintf("error: %s adapter content, ", eng.Adapter.Name()), hasError) 667 } 668 669 ctx.HTMLByte(http.StatusOK, buf.Bytes()) 670 } 671 } 672 673 // errorPanelHTML add an error panel html to context response. 674 func (eng *Engine) errorPanelHTML(ctx *context.Context, buf *bytes.Buffer, err error) { 675 676 user := auth.Auth(ctx) 677 tmpl, tmplName := template.Default().GetTemplate(ctx.IsPjax()) 678 679 hasError := tmpl.ExecuteTemplate(buf, tmplName, types.NewPage(&types.NewPageParam{ 680 User: user, 681 Menu: menu.GetGlobalMenu(user, eng.Adapter.GetConnection(), ctx.Lang()).SetActiveClass(eng.config.URLRemovePrefix(ctx.Path())), 682 Panel: template.WarningPanel(err.Error()).GetContent(eng.config.IsProductionEnvironment()), 683 Assets: template.GetComponentAssetImportHTML(), 684 Buttons: (*eng.NavButtons).CheckPermission(user), 685 TmplHeadHTML: template.Default().GetHeadHTML(), 686 TmplFootJS: template.Default().GetFootJS(), 687 Iframe: ctx.IsIframe(), 688 })) 689 690 if hasError != nil { 691 logger.Error(fmt.Sprintf("error: %s adapter content, ", eng.Adapter.Name()), hasError) 692 } 693 694 ctx.HTMLByte(http.StatusOK, buf.Bytes()) 695 } 696 697 // ============================ 698 // Admin Plugin APIs 699 // ============================ 700 701 // AddGenerators add the admin generators. 702 func (eng *Engine) AddGenerators(list ...table.GeneratorList) *Engine { 703 plug, exist := eng.FindPluginByName("admin") 704 if exist { 705 plug.(*admin.Admin).AddGenerators(list...) 706 return eng 707 } 708 eng.PluginList = append(eng.PluginList, admin.NewAdmin(list...)) 709 return eng 710 } 711 712 // AdminPlugin get the admin plugin. if not exist, create one. 713 func (eng *Engine) AdminPlugin() *admin.Admin { 714 plug, exist := eng.FindPluginByName("admin") 715 if exist { 716 return plug.(*admin.Admin) 717 } 718 adm := admin.NewAdmin() 719 eng.PluginList = append(eng.PluginList, adm) 720 return adm 721 } 722 723 // SetCaptcha set the captcha config. 724 func (eng *Engine) SetCaptcha(captcha map[string]string) *Engine { 725 eng.AdminPlugin().SetCaptcha(captcha) 726 return eng 727 } 728 729 // SetCaptchaDriver set the captcha config with driver. 730 func (eng *Engine) SetCaptchaDriver(driver string) *Engine { 731 eng.AdminPlugin().SetCaptcha(map[string]string{"driver": driver}) 732 return eng 733 } 734 735 // AddGenerator add table model generator. 736 func (eng *Engine) AddGenerator(key string, g table.Generator) *Engine { 737 eng.AdminPlugin().AddGenerator(key, g) 738 return eng 739 } 740 741 // AddGlobalDisplayProcessFn call types.AddGlobalDisplayProcessFn. 742 func (eng *Engine) AddGlobalDisplayProcessFn(f types.FieldFilterFn) *Engine { 743 types.AddGlobalDisplayProcessFn(f) 744 return eng 745 } 746 747 // AddDisplayFilterLimit call types.AddDisplayFilterLimit. 748 func (eng *Engine) AddDisplayFilterLimit(limit int) *Engine { 749 types.AddLimit(limit) 750 return eng 751 } 752 753 // AddDisplayFilterTrimSpace call types.AddDisplayFilterTrimSpace. 754 func (eng *Engine) AddDisplayFilterTrimSpace() *Engine { 755 types.AddTrimSpace() 756 return eng 757 } 758 759 // AddDisplayFilterSubstr call types.AddDisplayFilterSubstr. 760 func (eng *Engine) AddDisplayFilterSubstr(start int, end int) *Engine { 761 types.AddSubstr(start, end) 762 return eng 763 } 764 765 // AddDisplayFilterToTitle call types.AddDisplayFilterToTitle. 766 func (eng *Engine) AddDisplayFilterToTitle() *Engine { 767 types.AddToTitle() 768 return eng 769 } 770 771 // AddDisplayFilterToUpper call types.AddDisplayFilterToUpper. 772 func (eng *Engine) AddDisplayFilterToUpper() *Engine { 773 types.AddToUpper() 774 return eng 775 } 776 777 // AddDisplayFilterToLower call types.AddDisplayFilterToLower. 778 func (eng *Engine) AddDisplayFilterToLower() *Engine { 779 types.AddToUpper() 780 return eng 781 } 782 783 // AddDisplayFilterXssFilter call types.AddDisplayFilterXssFilter. 784 func (eng *Engine) AddDisplayFilterXssFilter() *Engine { 785 types.AddXssFilter() 786 return eng 787 } 788 789 // AddDisplayFilterXssJsFilter call types.AddDisplayFilterXssJsFilter. 790 func (eng *Engine) AddDisplayFilterXssJsFilter() *Engine { 791 types.AddXssJsFilter() 792 return eng 793 }