diff --git a/README.md b/README.md index e9824de..cbe727e 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,12 @@ import "github.com/webability-go/xmodules/ingredient" import "github.com/webability-go/xmodules/material" +v2022-06-09: +- Go 1.17 and Xamboo 1.6 compatibility: Now uses embed FS, languages, and functions entry table, not anymore a bridge + +v2021-03-12: +- Go 1.16.1 compatibility: modified global variables assignment to be sure they are set correctly, now into init() + v2021-02-16: - All the modules have been changed to meet new Xamboo applications interfaces diff --git a/adminmenu/assets/moduleentries.go b/adminmenu/assets/moduleentries.go index 0eb1148..c726c09 100644 --- a/adminmenu/assets/moduleentries.go +++ b/adminmenu/assets/moduleentries.go @@ -4,8 +4,19 @@ import ( "github.com/webability-go/xdominion" "github.com/webability-go/xamboo/applications" + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" +) + +const ( + MODULEID = "adminmenu" + VERSION = "0.0.1" + DATASOURCE = "adminmenudatasource" ) +var Needs = []string{"base", "user"} + type ModuleEntries struct { AddGroup func(ds applications.Datasource, key string, name string) error GetGroup func(ds applications.Datasource, key string) (*xdominion.XRecord, error) @@ -14,3 +25,15 @@ type ModuleEntries struct { GetMenu func(ds applications.Datasource, group string, father string) (*xdominion.XRecords, error) } + +func GetEntries(ctx *context.Context) *ModuleEntries { + me := base.GetEntries(ctx, MODULEID) + if me == nil { + return nil + } + lme, ok := me.(*ModuleEntries) + if !ok { + return nil + } + return lme +} diff --git a/adminmenu/bridge/link.go b/adminmenu/bridge/link.go deleted file mode 100644 index b062ab2..0000000 --- a/adminmenu/bridge/link.go +++ /dev/null @@ -1,30 +0,0 @@ -package bridge - -import ( - "errors" - "plugin" - - "github.com/webability-go/xmodules/adminmenu/assets" -) - -var ModuleAdminMenu *assets.ModuleEntries - -var linked = false - -func Link(lib *plugin.Plugin) error { - if linked { - return nil - } - - obj, err := lib.Lookup("ModuleAdminMenu") - if err != nil { - return errors.New("Error: The application library does not contain the ModuleUserAdmin object") - } - ok := false - ModuleAdminMenu, ok = obj.(*assets.ModuleEntries) - if ModuleAdminMenu == nil || !ok { - return errors.New("Error: The application library does not contain a ModuleAdminMenu valid object") - } - linked = true - return nil -} diff --git a/adminmenu/init.go b/adminmenu/init.go index f6a2be9..9c5dea6 100644 --- a/adminmenu/init.go +++ b/adminmenu/init.go @@ -4,8 +4,12 @@ package adminmenu import ( + "embed" + "golang.org/x/text/language" + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xamboo/applications" "github.com/webability-go/xamboo/cms/context" @@ -14,35 +18,31 @@ import ( "github.com/webability-go/xmodules/tools" ) -const ( - MODULEID = "adminmenu" - VERSION = "0.0.1" - DATASOURCE = "adminmenudatasource" -) - -var Needs = []string{"base", "user"} -var ModuleAdminMenu = assets.ModuleEntries{ - AddGroup: AddGroup, - GetGroup: GetGroup, - AddOption: AddOption, - GetOption: GetOption, - GetMenu: GetMenu, -} +//go:embed languages/*.language +var fsmessages embed.FS +var messages *map[language.Tag]*xcore.XLanguage func init() { - messages = tools.BuildMessages(smessages) + messages = tools.BuildMessagesFS(fsmessages, "languages") m := &base.Module{ - ID: MODULEID, - Version: VERSION, + ID: assets.MODULEID, + Version: assets.VERSION, Languages: map[language.Tag]string{ language.English: tools.Message(messages, "MODULENAME", language.English), language.Spanish: tools.Message(messages, "MODULENAME", language.Spanish), language.French: tools.Message(messages, "MODULENAME", language.French), }, - Needs: Needs, + Needs: assets.Needs, FSetup: Setup, // Called once at the main system startup, once PER CREATED xmodule CONTEXT (if set) FSynchronize: Synchronize, // Called only to create/rebuild database objects and others on demand (if set) FStartContext: StartContext, // Called each time a new Server context is created (if set) + Entries: &assets.ModuleEntries{ + AddGroup: AddGroup, + GetGroup: GetGroup, + AddOption: AddOption, + GetOption: GetOption, + GetMenu: GetMenu, + }, } base.ModulesList.Register(m) } @@ -52,7 +52,7 @@ func init() { func Setup(ds applications.Datasource, prefix string) ([]string, error) { linkTables(ds) - ds.SetModule(MODULEID, VERSION) + ds.SetModule(assets.MODULEID, assets.VERSION) return []string{}, nil } @@ -61,13 +61,13 @@ func Synchronize(ds applications.Datasource, prefix string) ([]string, error) { result := []string{} - ok, res := base.VerifyNeeds(ds, Needs) + ok, res := base.VerifyNeeds(ds, assets.Needs) result = append(result, res...) if !ok { return result, nil } - installed := base.ModuleInstalledVersion(ds, MODULEID) + installed := base.ModuleInstalledVersion(ds, assets.MODULEID) // synchro tables err, r := synchroTables(ds, installed) @@ -93,9 +93,9 @@ func Synchronize(ds applications.Datasource, prefix string) ([]string, error) { } result = append(result, r...) if err == nil { - err = base.AddModule(cds, MODULEID, tools.Message(messages, "MODULENAME"), VERSION) + err = base.AddModule(cds, assets.MODULEID, tools.Message(messages, "MODULENAME"), assets.VERSION) if err == nil { - result = append(result, tools.Message(messages, "modulemodified", MODULEID)) + result = append(result, tools.Message(messages, "modulemodified", assets.MODULEID)) result = append(result, tools.Message(messages, "commit")) err = cds.Commit() if err != nil { diff --git a/adminmenu/languages/adminmenu.en.language b/adminmenu/languages/adminmenu.en.language new file mode 100644 index 0000000..8a72c58 --- /dev/null +++ b/adminmenu/languages/adminmenu.en.language @@ -0,0 +1,31 @@ + + + + + + Administration menu + The entry %s was modified successfully in the base_module table. + Installation successfull. + Installation aborted with error: %s + + + Default Administration menu + Access group for menu administration + Group of all accesses for menu administration + Access for menu administration + Access for menu administration + Menu constructor + Click on this line to see the different options for the administration of menus + Menu groups and options + Menu groups and options + + Error modifying the entry %s in the base_module table: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/adminmenu/languages/adminmenu.es.language b/adminmenu/languages/adminmenu.es.language new file mode 100644 index 0000000..b28a91c --- /dev/null +++ b/adminmenu/languages/adminmenu.es.language @@ -0,0 +1,31 @@ + + + + + + Menu de administración + La entrada %s fue modificada con exito en la tabla base_module. + Instalación exitosa. + Instalación con error: %s + + + Menú de administración por defecto + Grupos de accesos de la administración de menús + Grupos de accesos de la administración de menús + Acceso de la administración de menús + Acceso de la administración de menús + Constructor de menús + Haz clic sobre esta linea para ver las diferentes opciones para la administración de menús. + Grupos y opciones de menús + Grupos y opciones de menús + + Error modificando la entrada %s en la tabla base_module: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/adminmenu/languages/adminmenu.fr.language b/adminmenu/languages/adminmenu.fr.language new file mode 100644 index 0000000..46f1ad3 --- /dev/null +++ b/adminmenu/languages/adminmenu.fr.language @@ -0,0 +1,31 @@ + + + + + + Menu pour l'administration + L'entrée %s a été modifiée avec succès dans la table base_module. + Instalation réussie. + Instalation avec erreur: %s + + + Menu d'administration par défaut + Groupe des accès d'administration de menus + Groupe des accès d'administration de menus + Accès d'administration de menus + Accès d'administration de menus + Constructeur de menus + Constructeur de menus + Groupes et options de menus + Groupes et options de menus + + Erreur en modifiant l'entrée %s dans la table base_module: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/adminmenu/messages.go b/adminmenu/messages.go deleted file mode 100644 index 3a0432e..0000000 --- a/adminmenu/messages.go +++ /dev/null @@ -1,106 +0,0 @@ -package adminmenu - -import ( - "golang.org/x/text/language" - - "github.com/webability-go/xcore/v2" -) - -// Do no forget to call tools.BuildMessages from the init -var messages *map[language.Tag]*xcore.XLanguage - -var smessages = map[language.Tag]map[string]string{ - language.English: { - // Module installation messages - // init.go - "MODULENAME": "Administration menu", - "modulemodified": "The entry %s was modified successfully in the base_module table.", - "commit": "Installation successfull.", - "rollback": "Installation aborted with error: %s", - - // util.go - "MAINMENU": "Default Administration menu", - "accessgroup.name": "Access group for menu administration", - "accessgroup.description": "Group of all accesses for menu administration", - "access.name": "Access for menu administration", - "access.description": "Access for menu administration", - "menufolder.name": "Menu constructor", - "menufolder.description1": "Click on this line to see the different options for the administration of menus", - "adminmenugroup.name": "Menu groups", - "adminmenugroup.description1": "Menu groups", - "adminmenuoption.name": "Menu options", - "adminmenuoption.description1": "Menu options", - - "moduleerror": "Error modifying the entry %s in the base_module table: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, - language.Spanish: { - // Module installation messages - // init.go - "MODULENAME": "Menu de administración", - "modulemodified": "La entrada %s fue modificada con exito en la tabla base_module.", - "commit": "Instalación exitosa.", - "rollback": "Instalación con error: %s", - - // util.go - "MAINMENU": "Menú de administración por defecto", - "accessgroup.name": "Grupos de accesos de la administración de menús", - "accessgroup.description": "Grupos de accesos de la administración de menús", - "access.name": "Acceso de la administración de menús", - "access.description": "Acceso de la administración de menús", - "menufolder.name": "Constructor de menús", - "menufolder.description1": "Haz clic sobre esta linea para ver las diferentes opciones para la administración de menús.", - "adminmenugroup.name": "Grupos de menús", - "adminmenugroup.description1": "Grupos de menús", - "adminmenuoption.name": "Opciones de menús", - "adminmenuoption.description1": "Opciones de menús", - - "moduleerror": "Error modificando la entrada %s en la tabla base_module: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, - language.French: { - // Module installation messages - // init.go - "MODULENAME": "Menu pour l'administration", - "modulemodified": "L'entrée %s a été modifiée avec succès dans la table base_module.", - "commit": "Instalation réussie.", - "rollback": "Instalation avec erreur: %s", - - // util.go - "MAINMENU": "Menu d'administration par défaut", - "accessgroup.name": "Groupe des accès d'administration de menus", - "accessgroup.description": "Groupe des accès d'administration de menus", - "access.name": "Accès d'administration de menus", - "access.description": "Accès d'administration de menus", - "menufolder.name": "Constructeur de menus", - "menufolder.description1": "Constructeur de menus", - "adminmenugroup.name": "Groupes de menus", - "adminmenugroup.description1": "Groupes de menus", - "adminmenuoption.name": "Options de menus", - "adminmenuoption.description1": "Options de menus", - - "moduleerror": "Erreur en modifiant l'entrée %s dans la table base_module: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, -} diff --git a/adminmenu/util.go b/adminmenu/util.go index b49f69c..27afd6c 100644 --- a/adminmenu/util.go +++ b/adminmenu/util.go @@ -112,21 +112,6 @@ func install(ds applications.Datasource) (error, []string) { return err, result } - option = xdominion.XRecord{ - "key": "_adminmenu_option", - "group": "_admin", - "father": "_adminmenu", - "name": tools.Message(messages, "adminmenuoption.name"), - "access": "_adminmenu", - "icon1": "option.png", - "call1": "adminmenu/option", - "description1": tools.Message(messages, "adminmenuoption.description1"), - } - err = AddOption(ds, &option) - if err != nil { - return err, result - } - return nil, []string{ fmt.Sprint(" admin menu options added"), } diff --git a/base/assets/moduleentries.go b/base/assets/moduleentries.go deleted file mode 100644 index 6497d9a..0000000 --- a/base/assets/moduleentries.go +++ /dev/null @@ -1,10 +0,0 @@ -package assets - -import ( - "github.com/webability-go/xamboo/applications" - "github.com/webability-go/xamboo/cms/context" -) - -type ModuleEntries struct { - TryDatasource func(ctx *context.Context, datasourcename string) applications.Datasource -} diff --git a/base/bridge/link.go b/base/bridge/link.go deleted file mode 100644 index bff2b22..0000000 --- a/base/bridge/link.go +++ /dev/null @@ -1,30 +0,0 @@ -package bridge - -import ( - "errors" - "plugin" - - "github.com/webability-go/xmodules/base/assets" -) - -var ModuleBase *assets.ModuleEntries - -var linked = false - -func Link(lib *plugin.Plugin) error { - if linked { - return nil - } - - obj, err := lib.Lookup("ModuleBase") - if err != nil { - return errors.New("Error: The application library does not contain the ModuleBase object") - } - ok := false - ModuleBase, ok = obj.(*assets.ModuleEntries) - if ModuleBase == nil || !ok { - return errors.New("Error: The application library does not contain a ModuleBase valid object") - } - linked = true - return nil -} diff --git a/base/container.go b/base/container.go index b23950e..1b59225 100644 --- a/base/container.go +++ b/base/container.go @@ -42,8 +42,6 @@ type Container struct { type ContainersList map[string]*Container -var Containers = &ContainersList{} - func (cntl *ContainersList) AddContainer(id string, cnt *Container) { (*cntl)[id] = cnt } @@ -52,6 +50,12 @@ func (cntl *ContainersList) GetContainer(id string) *Container { return (*cntl)[id] } +func (cntl *ContainersList) RegisterModule(mod applications.Module) { + for _, cnt := range *cntl { + cnt.RegisterModule(mod) + } +} + func (cnt *Container) SetDatasource(id string, ds applications.Datasource) { cnt.mdatasources.Lock() cnt.datasources[id] = ds @@ -178,6 +182,12 @@ func (cnt *Container) CreateDatasource(name string, config *xconfig.XConfig) (ap return ds, nil } +func (cnt *Container) RegisterModule(mod applications.Module) { + for _, ds := range cnt.datasources { + ds.RegisterModule(mod) + } +} + // TryDatasource will create a new datasource, link databases and logs based on XConfig data func (cnt *Container) TryDatasource(ctx *context.Context, datasourcename string) applications.Datasource { @@ -249,44 +259,3 @@ func TryDatasource(ctx *context.Context, datasourcename string) applications.Dat } return nil } - -// Analyze a datasource and gets back the main data -func GetDatasourceStats(ds *Datasource) *xcore.XDataset { - - subdata := xcore.XDataset{} - subdata["languages"] = ds.GetLanguages() - subdata["database"] = ds.GetDatabase() - subdata["logs"] = ds.GetLogs() - - caches := []string{} - for id := range ds.GetCaches() { - caches = append(caches, id) - } - subdata["xcaches"] = caches - - tables := map[string]string{} - for id, table := range ds.GetTables() { - if table.Base != nil { - db := table.Base.Database - tables[id] = db - } else { - tables[id] = "N/A" - } - } - subdata["tables"] = tables - - subdata["config"] = tools.BuildConfigSet(ds.Config) - - // analiza los módulos instalados - modules := map[string]interface{}{} - for id, v := range ds.GetModules() { - md := struct { - Version string - InstalledVersion string - }{v, ModuleInstalledVersion(ds, id)} - modules[id] = md - } - subdata["modules"] = modules - - return &subdata -} diff --git a/base/datasource.go b/base/datasource.go index c95f0ba..6a4e806 100644 --- a/base/datasource.go +++ b/base/datasource.go @@ -5,6 +5,7 @@ package base import ( "errors" "log" + "strings" "sync" "golang.org/x/text/language" @@ -25,7 +26,7 @@ import ( type Datasource struct { // The name of the datasource (informative only) Name string - // A configuration for the datasource: (does not need lock to be accessed since it's a pointer) + // A configuration for the datasource: (does not need lock to be accessed since it's a pointer) lock yourself for now (next XConfig version will be TS) Config *xconfig.XConfig // Only one database per datasource database *xdominion.XBase @@ -91,6 +92,7 @@ func (ds *Datasource) GetLogs() map[string]*log.Logger { } func (ds *Datasource) Log(id string, messages ...interface{}) { + ds.mlogs.RLock() l := ds.logs[id] if l == nil { @@ -160,6 +162,23 @@ func (ds *Datasource) SetModule(moduleid string, moduleversion string) { ds.mmodules.Unlock() } +func (ds *Datasource) RegisterModule(mod applications.Module) { + + modulelist, _ := ds.Config.GetStringCollection("module") + for _, m := range modulelist { + xm := strings.Split(m, "|") + modid := xm[0] + if modid != mod.GetID() { + continue + } + modprefix := "" + if len(xm) > 1 { + modprefix = xm[1] + } + mod.Setup(ds, modprefix) + } +} + func (ds *Datasource) GetModule(moduleid string) string { ds.mmodules.RLock() m := ds.modules[moduleid] @@ -252,3 +271,44 @@ func (ds *Datasource) Rollback() error { ds.transaction = nil return nil } + +// Analyze a datasource and gets back the main data +func GetDatasourceStats(ds *Datasource) *xcore.XDataset { + + subdata := xcore.XDataset{} + subdata["languages"] = ds.GetLanguages() + subdata["database"] = ds.GetDatabase() + subdata["logs"] = ds.GetLogs() + + caches := []string{} + for id := range ds.GetCaches() { + caches = append(caches, id) + } + subdata["xcaches"] = caches + + tables := map[string]string{} + for id, table := range ds.GetTables() { + if table.Base != nil { + db := table.Base.Database + tables[id] = db + } else { + tables[id] = "N/A" + } + } + subdata["tables"] = tables + + subdata["config"] = tools.BuildConfigSet(ds.Config) + + // analiza los módulos instalados + modules := map[string]interface{}{} + for id, v := range ds.GetModules() { + md := struct { + Version string + InstalledVersion string + }{v, ModuleInstalledVersion(ds, id)} + modules[id] = md + } + subdata["modules"] = modules + + return &subdata +} diff --git a/base/hooks.go b/base/hooks.go new file mode 100644 index 0000000..4a2d1ae --- /dev/null +++ b/base/hooks.go @@ -0,0 +1,23 @@ +package base + +type Hook func(ds *Datasource, data ...interface{}) + +type Hooks struct { + Hooks map[string]Hook +} + +func NewHooks() *Hooks { + return &Hooks{Hooks: map[string]Hook{}} +} + +func (h *Hooks) Register(id string, f Hook) { + h.Hooks[id] = f +} + +func (h *Hooks) Call(ds *Datasource, data ...interface{}) { + + for _, f := range h.Hooks { + f(ds, data...) + } + +} diff --git a/base/init.go b/base/init.go index ca9694f..e6dc4a4 100644 --- a/base/init.go +++ b/base/init.go @@ -4,12 +4,16 @@ package base import ( + "embed" + "sync" + "golang.org/x/text/language" + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xamboo/applications" "github.com/webability-go/xamboo/cms/context" - "github.com/webability-go/xmodules/base/assets" "github.com/webability-go/xmodules/tools" ) @@ -19,12 +23,19 @@ const ( ) var Needs = []string{} -var ModuleBase = assets.ModuleEntries{ - TryDatasource: TryDatasource, -} + +// var Containers = &ContainersList{} +var Containers *ContainersList +var ContainersLock sync.RWMutex + +//go:embed languages/*.language +var fsmessages embed.FS +var messages *map[language.Tag]*xcore.XLanguage func init() { - messages = tools.BuildMessages(smessages) + // Creates the Containers into the init() to avoid the GO 1.16.1 error on creating global variables on plugins and not using them directly + Containers = &ContainersList{} + messages = tools.BuildMessagesFS(fsmessages, "languages") m := &Module{ ID: MODULEID, Version: VERSION, diff --git a/base/installation.go b/base/installation.go index f62aaaa..46f3287 100644 --- a/base/installation.go +++ b/base/installation.go @@ -1,7 +1,11 @@ package base import ( + // "embed" "errors" + "io/fs" + "os" + "path/filepath" "github.com/webability-go/xamboo/applications" @@ -24,7 +28,7 @@ func VerifyNeeds(ds applications.Datasource, needs []string) (bool, []string) { } } - return flag, []string{} + return flag, result } func SynchroTable(ds applications.Datasource, tablename string) (error, []string) { @@ -54,3 +58,42 @@ func SynchroTable(ds applications.Datasource, tablename string) (error, []string } return nil, result } + +func SynchroFiles(origin fs.FS, destination string) (error, []string) { + + result := []string{} + + err := fs.WalkDir(origin, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + result = append(result, "Error:") + return err + } + + // if subdirectory, do nothing, the fs already have all the files into the structure + if d.IsDir() { + return nil + } + + // read file in buffer + result = append(result, "Copy file: "+path+" to "+destination+path) + data, err := fs.ReadFile(origin, path) + if err != nil { + return err + } + + dir := filepath.Dir(path) + if dir != "." { + os.MkdirAll(destination+dir, 0700) + } + err = os.WriteFile(destination+path, data, 0644) + if err != nil { + return err + } + return nil + }) + if err != nil { + return err, result + } + + return nil, result +} diff --git a/base/languages/base.en.language b/base/languages/base.en.language new file mode 100644 index 0000000..57661ad --- /dev/null +++ b/base/languages/base.en.language @@ -0,0 +1,28 @@ + + + + + XModules base + The entry %s was modified successfully in the base_module table. + Installation successfull. + Installation aborted with error: %s + + Verifying '%s': The module '%s' is not installed: ERROR. + Verifying '%s': The module '%s' is installed: PASSED. + Analysing %s table. + Critical Error: the module 'base' table '%s' does not exist. + The table %s does not exist in the database: %s + The table %s was not created: %s + The table %s was created (again). + The table %s was not created because it contains data. + + Error modifying the entry %s in the base_module table: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/base/languages/base.es.language b/base/languages/base.es.language new file mode 100644 index 0000000..e968d25 --- /dev/null +++ b/base/languages/base.es.language @@ -0,0 +1,27 @@ + + + + base XModules + La entrada %s fue modificada con exito en la tabla base_module. + Instalación exitosa. + Instalación con error: %s + + Verificando '%s': El módulo '%s' no esta instalado: ERROR. + Verificando '%s': El módulo '%s' esta instalado: OK. + Analizando la tabla %s. + Error crítico: la tabla del módulo 'base', '%s' no existe. + La tabla %s no existe en base de datos: %s + La tabla %s no pudo ser creada: %s + La tabla %s fue creada (de nuevo). + La tabla %s no fue creada porque ya existe y contiene datos. + + Error modificando la entrada %s en la tabla base_module: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/base/languages/base.fr.language b/base/languages/base.fr.language new file mode 100644 index 0000000..7c1b375 --- /dev/null +++ b/base/languages/base.fr.language @@ -0,0 +1,27 @@ + + + + base XModules + L'entrée %s a été modifiée avec succès dans la table base_module. + Instalation réussie. + Instalation avec erreur: %s + + Verification '%s': Le module '%s' n'est pas installé: ERREUR. + Verification '%s': Le module '%s' est installé: OK. + Analyze de la table %s. + Erreur critique: la table du module 'base', '%s' n'existe pas. + La table %s n'existe pas dans la base de données: %s + La table %s pe peux pas être créée: %s + La table %s a été créée (de nouveau). + La table %s n'a pas été créée car elle existe déjà et contient des données. + + Erreur en modifiant l'entrée %s dans la table base_module: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/base/messages.go b/base/messages.go deleted file mode 100644 index e85a951..0000000 --- a/base/messages.go +++ /dev/null @@ -1,92 +0,0 @@ -package base - -import ( - "golang.org/x/text/language" - - "github.com/webability-go/xcore/v2" -) - -// Do no forget to call tools.BuildMessages from the init -var messages *map[language.Tag]*xcore.XLanguage - -var smessages = map[language.Tag]map[string]string{ - language.English: { - // Module installation messages - // init.go - "MODULENAME": "XModules base", - "modulemodified": "The entry %s was modified successfully in the base_module table.", - "commit": "Installation successfull.", - "rollback": "Installation aborted with error: %s", - // installatin.go - "moduleneeded": "Verifying '%s': The module '%s' is not installed: ERROR.", - "moduleok": "Verifying '%s': The module '%s' is installed: PASSED.", - "analyze": "Analysing %s table.", - "notable": "Critical Error: the module 'base' table '%s' does not exist.", - "tablenoexist": "The table %s does not exist in the database: %s", - "tableerror": "The table %s was not created: %s", - "tablecreated": "The table %s was created (again).", - "tablenotmodified": "The table %s was not created because it contains data.", - - "moduleerror": "Error modifying the entry %s in the base_module table: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, - language.Spanish: { - // Module installation messages - "MODULENAME": "base XModules", - "modulemodified": "La entrada %s fue modificada con exito en la tabla base_module.", - "commit": "Instalación exitosa.", - "rollback": "Instalación con error: %s", - // installatin.go - "moduleneeded": "Verificando '%s': El módulo '%s' no esta instalado: ERROR.", - "moduleok": "Verificando '%s': El módulo '%s' esta instalado: OK.", - "analyze": "Analizando la tabla %s.", - "notable": "Error crítico: la tabla del módulo 'base', '%s' no existe.", - "tablenoexist": "La tabla %s no existe en base de datos: %s", - "tableerror": "La tabla %s no pudo ser creada: %s", - "tablecreated": "La tabla %s fue creada (de nuevo).", - "tablenotmodified": "La tabla %s no fue creada porque ya existe y contiene datos.", - - "moduleerror": "Error modificando la entrada %s en la tabla base_module: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, - language.French: { - // Module installation messages - "MODULENAME": "base XModules", - "modulemodified": "L'entrée %s a été modifiée avec succès dans la table base_module.", - "commit": "Instalation réussie.", - "rollback": "Instalation avec erreur: %s", - // installatin.go - "moduleneeded": "Verification '%s': Le module '%s' n'est pas installé: ERREUR.", - "moduleok": "Verification '%s': Le module '%s' est installé: OK.", - "analyze": "Analyze de la table %s.", - "notable": "Erreur critique: la table du module 'base', '%s' n'existe pas.", - "tablenoexist": "La table %s n'existe pas dans la base de données: %s", - "tableerror": "La table %s pe peux pas être créée: %s", - "tablecreated": "La table %s a été créée (de nouveau).", - "tablenotmodified": "La table %s n'a pas été créée car elle existe déjà et contient des données.", - - "moduleerror": "Erreur en modifiant l'entrée %s dans la table base_module: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, -} diff --git a/base/module.go b/base/module.go index ee8fe8a..9971217 100644 --- a/base/module.go +++ b/base/module.go @@ -21,6 +21,10 @@ type Modules map[string]applications.Module func (ml *Modules) Register(m applications.Module) { id := m.GetID() (*ml)[id] = m + + if Containers != nil { + Containers.RegisterModule(m) + } } func (ml *Modules) Get(id string) applications.Module { @@ -37,6 +41,8 @@ type Module struct { FSetup func(applications.Datasource, string) ([]string, error) FSynchronize func(applications.Datasource, string) ([]string, error) FStartContext func(applications.Datasource, *context.Context) error + + Entries interface{} } func (m *Module) GetID() string { @@ -60,6 +66,7 @@ func (m *Module) GetInstalledVersion(ds applications.Datasource) string { } func (m *Module) Setup(ds applications.Datasource, prefix string) ([]string, error) { + if m.FSetup != nil { return m.FSetup(ds, prefix) } @@ -122,3 +129,14 @@ func AddModule(ds applications.Datasource, id string, name string, version strin }, ds.GetTransaction()) return err } + +func GetEntries(ctx *context.Context, moduleid string) interface{} { + + // scan ctx.Plugins to find the correct module and ModuleEntries + module := ModulesList.Get(moduleid) + if module == nil { + ctx.LoggerError.Println("base::module::GetEntries - Module ", moduleid, " not found in module list") + return nil + } + return (module.(*Module)).Entries +} diff --git a/go.mod b/go.mod index 3f68949..7d77aa5 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/webability-go/xmodules -go 1.14 +go 1.17 diff --git a/tools/files.go b/tools/files.go new file mode 100644 index 0000000..1b8f3d2 --- /dev/null +++ b/tools/files.go @@ -0,0 +1,167 @@ +package tools + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" +) + +/* +func FileVerify(url string) bool { + + response, err := http.Get(url) + if err != nil { + return false + } + defer response.Body.Close() + + if response.StatusCode == 200 { + return true + } + return false +} + +func FileDownload(url string, path string) error { + + response, err := http.Get(url) + if err != nil { + return err + } + defer response.Body.Close() + + //open a file for writing + file, err := os.Create(path) + if err != nil { + return err + } + defer file.Close() + + // Usa io.Copy para copiar los streams. No hay problema de tamano. (se puede usar para video o lo que sea) + _, err = io.Copy(file, response.Body) + if err != nil { + return err + } + return nil +} +*/ + +var typesImagesAccepted = map[string]string{ + "image/jpeg": ".jpg", + "image/png": ".png", + "image/gif": ".gif", + "image/webp": ".webp", +} + +// ImageDownload : Receive the url for get the image and the path of the folder target +func ImageDownload(url, path, name string) (string, error) { + response, err := http.Get(url) + if err != nil { + return "", err + } + defer response.Body.Close() + extension := typesImagesAccepted[response.Header.Get("Content-Type")] // verificar la extension + if extension == "" { + return "", errors.New("Error: La imagen no tiene un formato aceptable: " + response.Header.Get("Content-Type")) // + } + + // verificar el directorio + if _, err := os.Stat(path); os.IsNotExist(err) { + err = os.MkdirAll(path, 0766) + if err != nil { + return "", err + } + } + + //open a file for writing + file, err := os.Create(path + name + extension) + if err != nil { + return "", err + } + defer file.Close() + // Usa io.Copy para copiar los streams. No hay problema de tamano. (se puede usar para video o lo que sea) + _, err = io.Copy(file, response.Body) + if err != nil { + return "", err + } + return extension, nil +} + +// MoveFile : copy the file fron source path to destiny dir, it is possible delete the file from the source dir +func MoveFile(sourcePath, names, destPath, named string, deleteold bool) error { + + inputFile, err := os.Open(sourcePath + names) + if err != nil { + return errors.New("Couldn't open source file: " + err.Error()) + } + + // verificar directorio destino + if _, err := os.Stat(destPath); os.IsNotExist(err) { + err = os.MkdirAll(destPath, 0644) + if err != nil { + return err + } + } + + outputFile, err := os.Create(destPath + named) + if err != nil { + inputFile.Close() + return errors.New("Couldn't open dest file: " + err.Error()) + } + defer outputFile.Close() + _, err = io.Copy(outputFile, inputFile) + inputFile.Close() + if err != nil { + return errors.New("Writing to output file failed: " + err.Error()) + } + if deleteold { + err = os.Remove(sourcePath + names) + if err != nil { + return errors.New("Failed removing original file: " + err.Error()) + } + } + return nil +} + +// FilePutContents : create the file if not exist and put a new line +func FilePutContents(path, filename string, content string) error { + if _, err := os.Stat(path); os.IsNotExist(err) { + err = os.MkdirAll(path, os.ModeDir) + if err != nil { + //fmt.Println("error on open dir: ", path) + return err + } + } + fp, err := os.OpenFile(path+filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) + if err != nil { + //fmt.Println("error on open file: ", path+filename) + return err + } + defer fp.Close() + _, err = fp.WriteString(content + "\n") + //fmt.Fprintln(fp, content) + return err +} + +func FileGetContents(filename string) (string, error) { + data, err := ioutil.ReadFile(filename) + return string(data), err +} + +// CreateDirectoryObject crea directorio objeto retornar prefix + "/" + dirnivel +func CreateDirectoryObject(clave, nivel int, prefix string) (string, error) { + + switch nivel { + case 1: + return prefix + "/" + fmt.Sprint(clave) + "/", nil + case 2: + sclave := fmt.Sprintf("%02d", clave) + subd := sclave[0:2] + "/" + fmt.Sprint(clave) + return prefix + "/" + subd + "/", nil + case 3: + return prefix + "/" + GetMD5Hash("kl123kl"+fmt.Sprint(clave)) + "/", nil + } + return "", errors.New("error nivel") +} diff --git a/tools/lang.go b/tools/lang.go index 645b12f..e07fbd3 100644 --- a/tools/lang.go +++ b/tools/lang.go @@ -1,11 +1,15 @@ package tools import ( + "embed" "fmt" + "log" "golang.org/x/text/language" "github.com/webability-go/xcore/v2" + + "github.com/webability-go/xamboo/loggers" ) // Language is the default system language to search messages in this language in priority @@ -25,7 +29,7 @@ func Message(messages *map[language.Tag]*xcore.XLanguage, id string, params ...i } } msg := (*messages)[lang].Get(id) - if msg == "" && lang != language.English { + if msg == "" && lang != language.English && (*messages)[language.English] != nil { msg = (*messages)[language.English].Get(id) } if msg == "" { @@ -49,3 +53,47 @@ func BuildMessages(data map[language.Tag]map[string]string) *map[language.Tag]*x } return &bdata } + +func LogMessage(log *log.Logger, lang *xcore.XLanguage, id string, params ...interface{}) string { + + msg := lang.Get(id) + if msg == "" { + msg = id + } + + if len(params) > 0 { + msg = fmt.Sprintf(msg, params...) + } + log.Println(msg) + return msg +} + +func WajafLogErrorMessage(log *log.Logger, lang *xcore.XLanguage, id string, params ...interface{}) string { + + msg := LogMessage(log, lang, id, params...) + return "{\"error\":true,\"message\":\"" + msg + "\"}" +} + +func BuildMessagesFS(fs embed.FS, path string) *map[language.Tag]*xcore.XLanguage { + + slg := loggers.GetCoreLogger("errors") + messages := &map[language.Tag]*xcore.XLanguage{} + + files, _ := fs.ReadDir(path) + for _, file := range files { + name := file.Name() + pathname := name + if path != "." { + pathname = path + "/" + pathname + } + data, _ := fs.ReadFile(pathname) + xlanguage, err := xcore.NewXLanguageFromXMLString(string(data)) + if err != nil { + slg.Println("Error reading module messages:", err) + continue + } + lang := xlanguage.GetLanguage() + (*messages)[lang] = xlanguage + } + return messages +} diff --git a/translation/assets/moduleentries.go b/translation/assets/moduleentries.go index fc31a2f..ab03496 100644 --- a/translation/assets/moduleentries.go +++ b/translation/assets/moduleentries.go @@ -1,4 +1,28 @@ package assets +import ( + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" +) + +const ( + MODULEID = "translation" + VERSION = "0.0.1" +) + type ModuleEntries struct { + TranslateOne func(input string) (string, error) +} + +func GetEntries(ctx *context.Context) *ModuleEntries { + me := base.GetEntries(ctx, MODULEID) + if me == nil { + return nil + } + lme, ok := me.(*ModuleEntries) + if !ok { + return nil + } + return lme } diff --git a/translation/init.go b/translation/init.go index 743f795..577c40d 100644 --- a/translation/init.go +++ b/translation/init.go @@ -13,17 +13,14 @@ import ( "github.com/webability-go/xmodules/translation/assets" ) -const ( - MODULEID = "translation" - VERSION = "0.0.1" -) - -var ModuleTranslation = assets.ModuleEntries{} +var ModuleEntries = assets.ModuleEntries{ + TranslateOne: TranslateOne, +} func init() { m := &base.Module{ - ID: MODULEID, - Version: VERSION, + ID: assets.MODULEID, + Version: assets.VERSION, Languages: map[language.Tag]string{language.English: "Translation tables", language.Spanish: "Tablas de traducción", language.French: "Tables de traduction"}, Needs: []string{"base"}, FSetup: Setup, @@ -38,7 +35,7 @@ func Setup(ds applications.Datasource, prefix string) ([]string, error) { ctx := ds.(*base.Datasource) buildTables(ctx) - ctx.SetModule(MODULEID, VERSION) + ctx.SetModule(assets.MODULEID, assets.VERSION) return []string{}, nil } @@ -66,11 +63,11 @@ func Synchronize(ds applications.Datasource, prefix string) ([]string, error) { // Inserting into base-modules // Be sure base module is on db: fill base module (we should get this from xmodule.conf) - err := base.AddModule(ctx, MODULEID, "Multilanguages translation tables for Xamboo", VERSION) + err := base.AddModule(ctx, assets.MODULEID, "Multilanguages translation tables for Xamboo", assets.VERSION) if err == nil { - messages = append(messages, "The entry "+MODULEID+" was modified successfully in the modules table.") + messages = append(messages, "The entry "+assets.MODULEID+" was modified successfully in the modules table.") } else { - messages = append(messages, "Error modifying the entry "+MODULEID+" in the modules table: "+err.Error()) + messages = append(messages, "Error modifying the entry "+assets.MODULEID+" in the modules table: "+err.Error()) } return messages, nil @@ -79,3 +76,7 @@ func Synchronize(ds applications.Datasource, prefix string) ([]string, error) { func StartContext(ds applications.Datasource, ctx *context.Context) error { return nil } + +func TranslateOne(input string) (string, error) { + return "TRANSLATED: {" + input + "}", nil +} diff --git a/user/README.md b/user/README.md index 4dcd72a..3430bc1 100644 --- a/user/README.md +++ b/user/README.md @@ -16,6 +16,10 @@ The user package is used to build a set of administration users with all the sec Version Changes Control ======================= +v0.1.0 - 2022-03- +- First official release. Working on the xmodules/base, Xamboo, XCore, XDominion and XMask of this date. +- Admin pages integrated in xmodule + v0.0.1 - 2020-05-08 - Compatible with xmodule context standard @@ -32,8 +36,6 @@ v0.0.0 - 2020-01-03 v0.0.0 - 2019-12-18 - First release of module - - Manual: ======================= @@ -59,3 +61,23 @@ import "github.com/webability-go/xmodules/user" To use the package: import "github.com/webability-go/xmodules/user" + +----------------------- +3. Normalization + +All the concept of the module must be called by se same ywa among messages, screens and data. +English: +- Group of right accesses +- Right access +- User Profile +- Administration User +Spanish: +- Grupo de permisos de accesos +- Permiso de Acceso +- Perfil de usuario +- Usuario Administrador +French: +- Groupe d'accès +- Droit d'accès +- Profil d'utilisateur +- Utilisateur administrateur diff --git a/user/access.go b/user/access.go index e4ce81b..55ac07f 100644 --- a/user/access.go +++ b/user/access.go @@ -11,33 +11,44 @@ import ( "github.com/webability-go/xmodules/user/assets" ) -func AddAccessGroup(ds applications.Datasource, accessgroup *assets.AccessGroup) error { +func GetAccessByQuery(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder) *xdominion.XRecord { - user_accessgroup := ds.GetTable("user_accessgroup") - if user_accessgroup == nil { - errmsg := tools.Message(messages, "notable", "user_accessgroup", ds.GetName()) - ds.Log(errmsg) - return errors.New(errmsg) + user_access := ds.GetTable("user_access") + if user_access == nil { + errmsg := tools.Message(messages, "error.notable", "access", "GetAccessByQuery", "user_access", ds.GetName()) + ds.Log("error", errmsg) + return nil + } + data, err := user_access.SelectOne(*cond, *order) + if err != nil { + ds.Log("error", tools.Message(messages, "error.select", "access", "GetAccessByQuery", "user_access", err)) + return nil } + return data +} - _, err := user_accessgroup.Upsert(accessgroup.Key, xdominion.XRecord{ - "key": accessgroup.Key, - "name": accessgroup.Name, - "description": accessgroup.Description, - }) +func GetAccessByKey(ds applications.Datasource, key string) *xdominion.XRecord { + + user_access := ds.GetTable("user_access") + if user_access == nil { + errmsg := tools.Message(messages, "error.notable", "access", "GetAccessByKey", "user_access", ds.GetName()) + ds.Log("error", errmsg) + return nil + } + data, err := user_access.SelectOne(key) if err != nil { - ds.Log("main", tools.Message(messages, "errorupsert", "user_accessgroup", err)) - return err + ds.Log("error", tools.Message(messages, "error.select", "access", "GetAccessByKey", "user_access", err)) + return nil } - return nil + return data } func AddAccess(ds applications.Datasource, access *assets.Access) error { user_access := ds.GetTable("user_access") if user_access == nil { - errmsg := tools.Message(messages, "notable", "user_access", ds.GetName()) - ds.Log(errmsg) + errmsg := tools.Message(messages, "error.notable", "access", "AddAccess", "user_access", ds.GetName()) + ds.Log("error", errmsg) return errors.New(errmsg) } @@ -48,30 +59,106 @@ func AddAccess(ds applications.Datasource, access *assets.Access) error { "description": access.Description, }) if err != nil { - ds.Log("main", tools.Message(messages, "errorupsert", "user_access", err)) - return err + ds.Log("error", tools.Message(messages, "error.upsert", "access", "AddAccess", "user_access", err)) } - return nil + return err } -func GetCountAccesses(ds applications.Datasource, cond *xdominion.XConditions) int { +func GetAccessesCount(ds applications.Datasource, cond *xdominion.XConditions) int { user_access := ds.GetTable("user_access") if user_access == nil { - ds.Log("xmodules::user::GetCountAccesses: Error, the user_access table is not available on this datasource") + errmsg := tools.Message(messages, "error.notable", "access", "GetAccessesCount", "user_access", ds.GetName()) + ds.Log("error", errmsg) + return 0 + } + cnt, err := user_access.Count(cond) + if err != nil { + ds.Log("error", tools.Message(messages, "error.select", "access", "GetAccessesCount", "user_access", err)) return 0 } - cnt, _ := user_access.Count() return cnt } -func GetAccessesList(ds applications.Datasource, cond *xdominion.XConditions, orderby *xdominion.XOrderBy, quantity int, first int) *xdominion.XRecords { +func GetAccessesList(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder, quantity int, first int) *xdominion.XRecords { user_access := ds.GetTable("user_access") if user_access == nil { - ds.Log("xmodules::user::GetAccessesList: Error, the user_access table is not available on this datasource") + errmsg := tools.Message(messages, "error.notable", "access", "GetAccessesList", "user_access", ds.GetName()) + ds.Log("error", errmsg) + return nil + } + data, err := user_access.SelectAll(cond, order, quantity, first) + if err != nil { + ds.Log("error", tools.Message(messages, "error.select", "access", "GetAccessesList", "user_access", err)) return nil } - data, _ := user_access.SelectAll(cond, orderby, quantity, first) return data } + +func GetAccessProfiles(ds applications.Datasource, access string, quantity int) (*xdominion.XRecords, error) { + + user_profileaccess := ds.GetTable("user_profileaccess") + if user_profileaccess == nil { + errmsg := tools.Message(messages, "error.notable", "access", "GetAccessProfiles", "user_profileaccess", ds.GetName()) + ds.Log("error", errmsg) + return nil, errors.New(errmsg) + } + cond := xdominion.NewXCondition("access", "=", access) + orderby := xdominion.NewXOrderBy("profile", xdominion.ASC) + data, err := user_profileaccess.SelectAll(cond, orderby, quantity) + if err != nil { + ds.Log("error", tools.Message(messages, "error.select", "access", "GetAccessProfiles", "user_profileaccess", err)) + return nil, err + } + return data, nil +} + +// GetCountry to get the data of a country from cache/db in the specified language +func GetAccessUsers(ds applications.Datasource, access string, quantity int) (*xdominion.XRecords, error) { + + user_useraccess := ds.GetTable("user_useraccess") + if user_useraccess == nil { + errmsg := tools.Message(messages, "error.notable", "access", "GetAccessUsers", "user_useraccess", ds.GetName()) + ds.Log("error", errmsg) + return nil, errors.New(errmsg) + } + cond := xdominion.NewXCondition("access", "=", access) + orderby := xdominion.NewXOrderBy("user", xdominion.ASC) + data, err := user_useraccess.SelectAll(cond, orderby, quantity) + if err != nil { + ds.Log("error", tools.Message(messages, "error.select", "access", "GetAccessUsers", "user_useraccess", err)) + return nil, err + } + return data, nil +} + +func DeleteAccessChildren(ds applications.Datasource, skey string) error { + + user_access := ds.GetTable("user_access") + if user_access == nil { + errmsg := tools.Message(messages, "error.notable", "access", "DeleteAccessChildren", "user_access", ds.GetName()) + ds.Log("error", errmsg) + return errors.New(errmsg) + } + // _, err := user_access.Delete(xdominion.NewXCondition("group", "=", skey)) + // if err != nil { + // ds.Log("error", tools.Message(messages, "error.delete", "accessgroup", "DeleteAccessGroupChildren", "user_access", err)) + // } + return nil +} + +func PruneAccessChildren(ds applications.Datasource, skey string, group string) error { + + user_access := ds.GetTable("user_access") + if user_access == nil { + errmsg := tools.Message(messages, "error.notable", "access", "PruneAccessChildren", "user_access", ds.GetName()) + ds.Log("error", errmsg) + return errors.New(errmsg) + } + // _, err := user_access.Update(xdominion.NewXCondition("group", "=", skey), xdominion.XRecord{"group": group}) + // if err != nil { + // ds.Log("error", tools.Message(messages, "error.delete", "accessgroup", "PruneAccessGroupChildren", "user_access", err)) + // } + return nil +} diff --git a/user/accessgroup.go b/user/accessgroup.go new file mode 100644 index 0000000..f1f82af --- /dev/null +++ b/user/accessgroup.go @@ -0,0 +1,111 @@ +package user + +import ( + "errors" + + "github.com/webability-go/xdominion" + + "github.com/webability-go/xamboo/applications" + + "github.com/webability-go/xmodules/tools" + "github.com/webability-go/xmodules/user/assets" +) + +// AddAccessGroup is generally used by xmodules installers +func AddAccessGroup(ds applications.Datasource, accessgroup *assets.AccessGroup) error { + + user_accessgroup := ds.GetTable("user_accessgroup") + if user_accessgroup == nil { + errmsg := tools.Message(messages, "error.notable", "accessgroup", "AddAccessGroup", "user_accessgroup", ds.GetName()) + ds.Log("error", errmsg) + return errors.New(errmsg) + } + + _, err := user_accessgroup.Upsert(accessgroup.Key, xdominion.XRecord{ + "key": accessgroup.Key, + "name": accessgroup.Name, + "description": accessgroup.Description, + }) + if err != nil { + ds.Log("error", tools.Message(messages, "error.upsert", "accessgroup", "AddAccessGroup", "user_accessgroup", err)) + } + return err +} + +func GetAccessGroupsCount(ds applications.Datasource, cond *xdominion.XConditions) int { + + user_accessgroup := ds.GetTable("user_accessgroup") + if user_accessgroup == nil { + errmsg := tools.Message(messages, "error.notable", "accessgroup", "GetAccessGroupsCount", "user_accessgroup", ds.GetName()) + ds.Log("error", errmsg) + return 0 + } + cnt, err := user_accessgroup.Count(cond) + if err != nil { + ds.Log("error", tools.Message(messages, "error.select", "accessgroup", "GetAccessGroupsCount", "user_accessgroup", err)) + return 0 + } + return cnt +} + +func GetAccessGroupsList(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder, quantity int, first int) *xdominion.XRecords { + + user_accessgroup := ds.GetTable("user_accessgroup") + if user_accessgroup == nil { + errmsg := tools.Message(messages, "error.notable", "accessgroup", "GetAccessGroupsList", "user_accessgroup", ds.GetName()) + ds.Log("error", errmsg) + return nil + } + data, err := user_accessgroup.SelectAll(cond, order, quantity, first) + if err != nil { + ds.Log("error", tools.Message(messages, "error.select", "accessgroup", "GetAccessGroupsList", "user_accessgroup", err)) + return nil + } + return data +} + +func DeleteAccessGroupChildren(ds applications.Datasource, skey string) error { + + user_access := ds.GetTable("user_access") + if user_access == nil { + errmsg := tools.Message(messages, "error.notable", "accessgroup", "DeleteAccessGroupChildren", "user_access", ds.GetName()) + ds.Log("error", errmsg) + return errors.New(errmsg) + } + accesses, err := user_access.SelectAll(xdominion.NewXCondition("group", "=", skey)) + if err != nil { + ds.Log("error", tools.Message(messages, "error.delete", "accessgroup", "DeleteAccessGroupChildren", "user_access", err)) + return err + } + if accesses == nil { + return nil + } + for _, r := range *accesses { + akey, _ := r.GetString("key") + err = DeleteAccessChildren(ds, akey) + if err != nil { + ds.Log("error", tools.Message(messages, "error.delete", "accessgroup", "DeleteAccessGroupChildren", "user_access", err)) + return err + } + } + _, err = user_access.Delete(xdominion.NewXCondition("group", "=", skey)) + if err != nil { + ds.Log("error", tools.Message(messages, "error.delete", "accessgroup", "DeleteAccessGroupChildren", "user_access", err)) + } + return err +} + +func PruneAccessGroupChildren(ds applications.Datasource, skey string, group string) error { + + user_access := ds.GetTable("user_access") + if user_access == nil { + errmsg := tools.Message(messages, "error.notable", "accessgroup", "PruneAccessGroupChildren", "user_access", ds.GetName()) + ds.Log("error", errmsg) + return errors.New(errmsg) + } + _, err := user_access.Update(xdominion.NewXCondition("group", "=", skey), xdominion.XRecord{"group": group}) + if err != nil { + ds.Log("error", tools.Message(messages, "error.delete", "accessgroup", "PruneAccessGroupChildren", "user_access", err)) + } + return err +} diff --git a/user/assets/moduleentries.go b/user/assets/moduleentries.go index f27f644..a3893b5 100644 --- a/user/assets/moduleentries.go +++ b/user/assets/moduleentries.go @@ -4,12 +4,55 @@ import ( "github.com/webability-go/xdominion" "github.com/webability-go/xamboo/applications" + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" +) + +const ( + MODULEID = "user" + VERSION = "0.0.1" + DATASOURCE = "userdatasource" ) +var Needs = []string{"base"} + type ModuleEntries struct { + // Access Groups + GetAccessGroupsCount func(ds applications.Datasource, cond *xdominion.XConditions) int + GetAccessGroupsList func(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder, quantity int, first int) *xdominion.XRecords + DeleteAccessGroupChildren func(ds applications.Datasource, skey string) error + PruneAccessGroupChildren func(ds applications.Datasource, skey string, group string) error + // Accesses - GetAccessesCount func(ds applications.Datasource, cond *xdominion.XConditions) int - GetAccessesList func(ds applications.Datasource, cond *xdominion.XConditions, orderby *xdominion.XOrderBy, quantity int, first int) *xdominion.XRecords + GetAccessByKey func(ds applications.Datasource, key string) *xdominion.XRecord + // GetAccessByQuery func(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder) *xdominion.XRecord + GetAccessesCount func(ds applications.Datasource, cond *xdominion.XConditions) int + GetAccessesList func(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder, quantity int, first int) *xdominion.XRecords + DeleteAccessChildren func(ds applications.Datasource, skey string) error + PruneAccessChildren func(ds applications.Datasource, skey string, access string) error + GetAccessProfiles func(ds applications.Datasource, key string, quantity int) (*xdominion.XRecords, error) + GetAccessUsers func(ds applications.Datasource, key string, quantity int) (*xdominion.XRecords, error) + + // profiles + GetProfilesCount func(ds applications.Datasource, cond *xdominion.XConditions) int + GetProfilesList func(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder, quantity int, first int) *xdominion.XRecords + DeleteProfileChildren func(ds applications.Datasource, skey string) error + PruneProfileChildren func(ds applications.Datasource, skey string, profile string) error + GetProfileAccesses func(ds applications.Datasource, key string, quantity int) (*xdominion.XRecords, error) + SetProfileAccess func(ds applications.Datasource, key string, access string, status bool) error + GetProfileUsers func(ds applications.Datasource, key string, quantity int) (*xdominion.XRecords, error) + + // users + GetUserByKey func(ds applications.Datasource, key int) *xdominion.XRecord + GetUsersCount func(ds applications.Datasource, cond *xdominion.XConditions) int + GetUsersList func(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder, quantity int, first int) *xdominion.XRecords + DeleteUserChildren func(ds applications.Datasource, key int) error + PruneUserChildren func(ds applications.Datasource, key int, user int) error + GetUserAccesses func(ds applications.Datasource, key int, quantity int) (*xdominion.XRecords, error) + SetUserAccess func(ds applications.Datasource, key int, access string, status int) error + GetUserProfiles func(ds applications.Datasource, key int, quantity int) (*xdominion.XRecords, error) + SetUserProfile func(ds applications.Datasource, key int, profile string, status bool) error // User Params SetUserParam func(ds applications.Datasource, user int, param string, value interface{}) @@ -17,5 +60,18 @@ type ModuleEntries struct { GetUserParam func(ds applications.Datasource, user int, param string) string DelUserParam func(ds applications.Datasource, user int, param string) - HasAccess func(ds applications.Datasource, user int, access string, extra string) bool + // security + HasAccess func(ds applications.Datasource, userid int, args ...interface{}) bool +} + +func GetEntries(ctx *context.Context) *ModuleEntries { + me := base.GetEntries(ctx, MODULEID) + if me == nil { + return nil + } + lme, ok := me.(*ModuleEntries) + if !ok { + return nil + } + return lme } diff --git a/user/bridge/link.go b/user/bridge/link.go deleted file mode 100644 index 7a31ea0..0000000 --- a/user/bridge/link.go +++ /dev/null @@ -1,30 +0,0 @@ -package bridge - -import ( - "errors" - "plugin" - - "github.com/webability-go/xmodules/user/assets" -) - -var ModuleUser *assets.ModuleEntries - -var linked = false - -func Link(lib *plugin.Plugin) error { - if linked { - return nil - } - - obj, err := lib.Lookup("ModuleUser") - if err != nil { - return errors.New("Error: The application library does not contain the ModuleUser object") - } - ok := false - ModuleUser, ok = obj.(*assets.ModuleEntries) - if ModuleUser == nil || !ok { - return errors.New("Error: The application library does not contain a ModuleUser valid object") - } - linked = true - return nil -} diff --git a/user/init.go b/user/init.go index 9597989..34a6d74 100644 --- a/user/init.go +++ b/user/init.go @@ -4,8 +4,12 @@ package user import ( + "embed" + "golang.org/x/text/language" + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xamboo/applications" "github.com/webability-go/xamboo/cms/context" @@ -14,41 +18,70 @@ import ( "github.com/webability-go/xmodules/user/assets" ) -const ( - MODULEID = "user" - VERSION = "0.0.1" -) - -var Needs = []string{"base"} -var ModuleUser = assets.ModuleEntries{ - // accesses - GetAccessesCount: GetCountAccesses, - GetAccessesList: GetAccessesList, - - // Security - HasAccess: HasAccess, - - // Params - SetUserParam: SetUserParam, - AddUserParam: AddUserParam, - GetUserParam: GetUserParam, - DelUserParam: DelUserParam, -} +//go:embed languages/*.language +var fsmessages embed.FS +var messages *map[language.Tag]*xcore.XLanguage func init() { - messages = tools.BuildMessages(smessages) + messages = tools.BuildMessagesFS(fsmessages, "languages") m := &base.Module{ - ID: MODULEID, - Version: VERSION, + ID: assets.MODULEID, + Version: assets.VERSION, Languages: map[language.Tag]string{ language.English: tools.Message(messages, "MODULENAME", language.English), language.Spanish: tools.Message(messages, "MODULENAME", language.Spanish), language.French: tools.Message(messages, "MODULENAME", language.French), }, - Needs: Needs, + Needs: assets.Needs, FSetup: Setup, // Called once at the main system startup, once PER CREATED xmodule CONTEXT (if set) FSynchronize: Synchronize, // Called only to create/rebuild database objects and others on demand (if set) FStartContext: StartContext, // Called each time a new Server context is created (if set) + Entries: &assets.ModuleEntries{ + // access groups + GetAccessGroupsCount: GetAccessGroupsCount, + GetAccessGroupsList: GetAccessGroupsList, + DeleteAccessGroupChildren: DeleteAccessGroupChildren, + PruneAccessGroupChildren: PruneAccessGroupChildren, + + // accesses + GetAccessByKey: GetAccessByKey, + // GetAccessByQuery: GetAccessByQuery, + GetAccessesCount: GetAccessesCount, + GetAccessesList: GetAccessesList, + DeleteAccessChildren: DeleteAccessChildren, + PruneAccessChildren: PruneAccessChildren, + GetAccessUsers: GetAccessUsers, + GetAccessProfiles: GetAccessProfiles, + + // profiles + GetProfilesCount: GetProfilesCount, + GetProfilesList: GetProfilesList, + DeleteProfileChildren: DeleteProfileChildren, + PruneProfileChildren: PruneProfileChildren, + GetProfileAccesses: GetProfileAccesses, + SetProfileAccess: SetProfileAccess, + GetProfileUsers: GetProfileUsers, + + // users + GetUserByKey: GetUserByKey, + GetUsersCount: GetUsersCount, + GetUsersList: GetUsersList, + DeleteUserChildren: DeleteUserChildren, + PruneUserChildren: PruneUserChildren, + GetUserAccesses: GetUserAccesses, + SetUserAccess: SetUserAccess, + GetUserProfiles: GetUserProfiles, + SetUserProfile: SetUserProfile, + + // Params + SetUserParam: SetUserParam, + AddUserParam: AddUserParam, + GetUserParam: GetUserParam, + DelUserParam: DelUserParam, + + // Security + HasAccess: HasAccess, + }, } base.ModulesList.Register(m) } @@ -59,7 +92,7 @@ func Setup(ds applications.Datasource, prefix string) ([]string, error) { linkTables(ds) createCache(ds) - ds.SetModule(MODULEID, VERSION) + ds.SetModule(assets.MODULEID, assets.VERSION) go buildCache(ds) @@ -70,13 +103,13 @@ func Synchronize(ds applications.Datasource, prefix string) ([]string, error) { result := []string{} - ok, res := base.VerifyNeeds(ds, Needs) + ok, res := base.VerifyNeeds(ds, assets.Needs) result = append(result, res...) if !ok { return result, nil } - installed := base.ModuleInstalledVersion(ds, MODULEID) + installed := base.ModuleInstalledVersion(ds, assets.MODULEID) // synchro tables err, r := synchroTables(ds, installed) @@ -100,9 +133,9 @@ func Synchronize(ds applications.Datasource, prefix string) ([]string, error) { } result = append(result, r...) if err == nil { - err = base.AddModule(cds, MODULEID, tools.Message(messages, "MODULENAME"), VERSION) + err = base.AddModule(cds, assets.MODULEID, tools.Message(messages, "MODULENAME"), assets.VERSION) if err == nil { - result = append(result, tools.Message(messages, "modulemodified", MODULEID)) + result = append(result, tools.Message(messages, "modulemodified", assets.MODULEID)) result = append(result, tools.Message(messages, "commit")) err = cds.Commit() if err != nil { diff --git a/user/languages/user.en.language b/user/languages/user.en.language new file mode 100644 index 0000000..8a44768 --- /dev/null +++ b/user/languages/user.en.language @@ -0,0 +1,25 @@ + + + + + Users for administration + The entry %s was modified successfully in the base_module table. + Installation successfull. + Installation aborted with error: %s + + + user::%s::%s - Error, the table %s is not available on the datasource %s. + user::%s::%s - Error, upserting in %s: %s + user::%s::%s - Error, selecting in %s: %s + user::%s::%s - Error, deleting in %s: %s + + Error modifying the entry %s in the base_module table: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/user/languages/user.es.language b/user/languages/user.es.language new file mode 100644 index 0000000..c63dfec --- /dev/null +++ b/user/languages/user.es.language @@ -0,0 +1,19 @@ + + + + + Usuarios administradores + La entrada %s fue modificada con exito en la tabla base_module. + Instalación exitosa. + Instalación con error: %s + + Error modificando la entrada %s en la tabla base_module: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/user/languages/user.fr.language b/user/languages/user.fr.language new file mode 100644 index 0000000..31df01e --- /dev/null +++ b/user/languages/user.fr.language @@ -0,0 +1,19 @@ + + + + + Utilisateurs pour l'administration + L'entrée %s a été modifiée avec succès dans la table base_module. + Instalation réussie. + Instalation avec erreur: %s + + Erreur en modifiant l'entrée %s dans la table base_module: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/user/messages.go b/user/messages.go deleted file mode 100644 index 5b3609f..0000000 --- a/user/messages.go +++ /dev/null @@ -1,71 +0,0 @@ -package user - -import ( - "golang.org/x/text/language" - - "github.com/webability-go/xcore/v2" -) - -// Do no forget to call tools.BuildMessages from the init -var messages *map[language.Tag]*xcore.XLanguage - -var smessages = map[language.Tag]map[string]string{ - language.English: { - // Module installation messages - // init.go - "MODULENAME": "Users for administration", - "modulemodified": "The entry %s was modified successfully in the base_module table.", - "commit": "Installation successfull.", - "rollback": "Installation aborted with error: %s", - - // access.go - "notable": "Error, the table %s is not available on the datasource %s.", - "errorupsert": "Error upserting in %s: %s", - - "moduleerror": "Error modifying the entry %s in the base_module table: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, - language.Spanish: { - // Module installation messages - // init.go - "MODULENAME": "Usuarios administradores", - "modulemodified": "La entrada %s fue modificada con exito en la tabla base_module.", - "commit": "Instalación exitosa.", - "rollback": "Instalación con error: %s", - - "moduleerror": "Error modificando la entrada %s en la tabla base_module: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, - language.French: { - // Module installation messages - // init.go - "MODULENAME": "Utilisateurs pour l'administration", - "modulemodified": "L'entrée %s a été modifiée avec succès dans la table base_module.", - "commit": "Instalation réussie.", - "rollback": "Instalation avec erreur: %s", - - "moduleerror": "Erreur en modifiant l'entrée %s dans la table base_module: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, -} diff --git a/user/profile.go b/user/profile.go new file mode 100644 index 0000000..e7fe5e5 --- /dev/null +++ b/user/profile.go @@ -0,0 +1,115 @@ +package user + +import ( + "errors" + + "github.com/webability-go/xdominion" + + "github.com/webability-go/xamboo/applications" +) + +func GetProfilesCount(ds applications.Datasource, cond *xdominion.XConditions) int { + + user_profile := ds.GetTable("user_profile") + if user_profile == nil { + ds.Log("xmodules::user::GetCountProfilees: Error, the user_profile table is not available on this datasource") + return 0 + } + cnt, _ := user_profile.Count(cond) + return cnt +} + +func GetProfilesList(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder, quantity int, first int) *xdominion.XRecords { + + user_profile := ds.GetTable("user_profile") + if user_profile == nil { + ds.Log("xmodules::user::GetProfileesList: Error, the user_profile table is not available on this datasource") + return nil + } + data, _ := user_profile.SelectAll(cond, order, quantity, first) + return data +} + +func DeleteProfileChildren(ds applications.Datasource, skey string) error { + + /* + user_profile := ds.GetTable("user_profile") + if user_profile == nil { + errmsg := tools.Message(messages, "notable", "user_profile", ds.GetName()) + ds.Log(errmsg) + return errors.New(errmsg) + } + _, err := user_profile.Delete(xdominion.NewXCondition("group", "=", skey)) + return err + */ + return nil +} + +func PruneProfileChildren(ds applications.Datasource, skey string, profile string) error { + + /* + user_profile := ds.GetTable("user_profile") + if user_profile == nil { + errmsg := tools.Message(messages, "notable", "user_profile", ds.GetName()) + ds.Log(errmsg) + return errors.New(errmsg) + } + _, err := user_profile.Update(xdominion.NewXCondition("group", "=", skey), xdominion.XRecord{"group": group}) + return err + */ + return nil +} + +// GetCountry to get the data of a country from cache/db in the specified language +func GetProfileAccesses(ds applications.Datasource, profile string, quantity int) (*xdominion.XRecords, error) { + + user_profileaccess := ds.GetTable("user_profileaccess") + if user_profileaccess == nil { + ds.Log("xmodules::user::GetProfilesOfAccess: Error, the user_profileaccess table is not available on this datasource") + return nil, errors.New("xmodules::user::GetProfilesOfAccess: Error, the user_profileaccess table is not available on this datasource") + } + cond := xdominion.NewXCondition("profile", "=", profile) + orderby := xdominion.NewXOrderBy("access", xdominion.ASC) + data, err := user_profileaccess.SelectAll(cond, orderby, quantity) + return data, err +} + +// GetCountry to get the data of a country from cache/db in the specified language +func SetProfileAccess(ds applications.Datasource, profile string, access string, status bool) error { + + user_profileaccess := ds.GetTable("user_profileaccess") + if user_profileaccess == nil { + return errors.New("xmodules::user::GetProfilesOfAccess: Error, the user_profileaccess table is not available on this datasource") + } + cond := xdominion.XConditions{xdominion.NewXCondition("profile", "=", profile), xdominion.NewXCondition("access", "=", access, "and")} + data, err := user_profileaccess.SelectOne(cond) + if err != nil { + return err + } + if data != nil && !status { + // delete entry + _, err = user_profileaccess.Delete(cond) + } + if data == nil && status { + // insert entry + _, err = user_profileaccess.Insert(xdominion.XRecord{ + "profile": profile, + "access": access, + }) + } + return err +} + +// GetCountry to get the data of a country from cache/db in the specified language +func GetProfileUsers(ds applications.Datasource, profilekey string, quantity int) (*xdominion.XRecords, error) { + + user_userprofile := ds.GetTable("user_userprofile") + if user_userprofile == nil { + ds.Log("xmodules::user::GetProfilesOfAccess: Error, the user_userprofile table is not available on this datasource") + return nil, errors.New("xmodules::user::GetProfilesOfAccess: Error, the user_userprofile table is not available on this datasource") + } + cond := xdominion.NewXCondition("profile", "=", profilekey) + orderby := xdominion.NewXOrderBy("user", xdominion.ASC) + data, err := user_userprofile.SelectAll(cond, orderby, quantity) + return data, err +} diff --git a/user/security.go b/user/security.go index 587680f..812b4bb 100644 --- a/user/security.go +++ b/user/security.go @@ -1,6 +1,7 @@ package user import ( + "fmt" "net/http" "regexp" @@ -77,14 +78,14 @@ func VerifyUserSession(ctx *context.Context, xds applications.Datasource, origin // ctx.Sessionparams.Set("clientid", clientid) userkey, _ := sessiondata.GetInt("user") - userdata := GetUser(ds, userkey) - username, _ := userdata.Data.GetString("name") + userdata := GetUserByKey(ds, userkey) + username, _ := userdata.GetString("name") ctx.Sessionparams.Set("usersessionid", sessionid) ctx.Sessionparams.Set("userkey", userkey) ctx.Sessionparams.Set("username", username) ctx.Sessionparams.Set("usersession", sessiondata) - ctx.Sessionparams.Set("userdata", userdata.Data) + ctx.Sessionparams.Set("userdata", userdata) } func CreateSessionUser(ctx *context.Context, xds applications.Datasource, sessionid string, IP string, origin string, device string, user *StructureUser) string { @@ -122,180 +123,86 @@ func DestroySessionUser(ctx *context.Context, xds applications.Datasource, sessi CloseSession(ds, sessionid) } -/* Verify cookie session against DB -func LinkSessionUser(ctx *assets.Context) { - - sitecontextname, _ := ctx.Sysparams.GetString("sitecontext") - sitecontext := base.Sites.GetContext(sitecontextname) - if sitecontext == nil { - return - } - // kiwi-gr, kiwi-im, crafto-gr, crafto-im are authorized normally. control, kiwi7, crafto7, central, cdn, are not - if !context.IsModuleAuthorized(sitecontext, "client") { - return - } - - var clientsession *xdominion.XRecord - - sessionid := ctx.Request.Form.Get("token") - if sessionid != "" { - clientsession = client.GetSession(sessionid) - } - - config := base.Sites.GetContext("kiwi-gr").Config +// SECURITY Access +func HasAccess(ds applications.Datasource, userid int, args ...interface{}) bool { - if clientsession == nil { - // 1. check the cookie session - cookiename, _ := config.GetString("cookiename") - cookie, _ := ctx.Request.Cookie(cookiename) - // 1.bis If there is no cookie, there is no session - if cookie == nil || len(cookie.Value) == 0 { - return - } - sessionid = cookie.Value - clientsession = client.GetSession(sessionid) + // 1. profile + // 2. direct acceses + userdata := GetUserByKey(ds, userid) + if userdata == nil { + return false } - if clientsession == nil { - // Si no hay sesion repertoriada, destruye todo - DestroySessionClient(ctx, sessionid) - return + // superuser is always all access + super, _ := userdata.GetString("status") + if super == "S" { + return true } - - checkIP, _ := ctx.Sysparams.GetBool("checkip") - IP, _ := clientsession.GetString("ip") - IPClient := ctx.Writer.(*server.CoreWriter).RequestStat.IP - if checkIP && IP != IPClient { - DestroySessionClient(ctx, sessionid) - return + if super == "X" { // desactivated user, no access + return false } - // Actualiza el fin de la sesion con el tiempo actual - client.SetSessionTime(sessionid) - - clave, _ := clientsession.GetInt("chef") - if clave != 0 { - client.SetClientTime(clave) - clientdata, _ := graph.GetClient(sitecontext, clave) - if clientdata != nil { - ctx.Sessionparams.Set("sessionid", sessionid) - ctx.Sessionparams.Set("clientid", clave) - ctx.Sessionparams.Set("clientdata", clientdata.Data) + access := "" + extendedaccess := "" + ok := false + // any args ? + if len(args) > 0 { + access, ok = args[0].(string) + if !ok { + // http.Error(ctx.Writer, "Error on user access verification", http.StatusUnauthorized) + return false } } -} - -/* connect clientid and force cookie -func ForceUser(ctx *assets.Context, clientid int, longlogin int, origin string, source string, device string) { - - sitecontextname, _ := ctx.Sysparams.GetString("sitecontext") - sitecontext := base.Sites.GetContext(sitecontextname) - - // NO podemos conectar un cliente que no existe - clientdata, _ := graph.GetClient(sitecontext, clientid) - if clientdata == nil { - return + if len(args) > 1 { + extendedaccess = args[1].(string) + if !ok { + // http.Error(ctx.Writer, "Error on user extended access verification", http.StatusUnauthorized) + return false + } } + /* + // is access into user accesses ? + acc := GetUserAccess(ds, userid, access) - config := base.Sites.GetContext("kiwi-gr").Config - - cookiename, _ := config.GetString("cookiename") - cookiedomain, _ := config.GetString("cookiedomain") - - sessionid := CreateSessionClient(ctx, clientid, longlogin, origin, source, device) - ctx.Sessionparams.Set("clientid", clientid) - ctx.Sessionparams.Set("clientdata", clientdata.Data) - - http.SetCookie(ctx.Writer, &http.Cookie{Name: cookiename, Value: sessionid, Path: "/", Domain: cookiedomain}) + // is access in profile ? + pr := GetUserProfiles(ds, userid) + // loop sobre progiles + accpr := GetProfileAccess(ds, userid, access) + */ + fmt.Println("Access to check:", access, extendedaccess) - // fmt.Println("Forzando el cliente=", clientid, "con la cookie", cookiename, "=", sessionid) + return false } /* -func CreateSessionUser(ctx *assets.Context, clientid int, longlogin int, origin string, source string, device string) string { - - config := base.Sites.GetContext("kiwi-gr").Config - - cookiesize, _ := config.GetInt("cookiesize") - - // usuario actualmente conectado (cargado al principio del hit por cookie), o nada - clientcnx, _ := ctx.Sessionparams.GetInt("clientid") - sessionid, _ := ctx.Sessionparams.GetString("sessionid") - - // Si ya estabamos conectados, crea una nueva sessionid - if sessionid != "" && clientcnx != clientid { - sessionid = "" - } - // crea sessionid - ip, _, _ := net.SplitHostPort(ctx.Request.RemoteAddr) - if sessionid == "" { - // busca un ID disponible - for { - sessionid = util.CreateKey(cookiesize, -1) - num, _ := client.KL_chefsesion.Count(xdominion.NewXCondition("clave", "=", sessionid)) - if num == 0 { - break - } - } - - _, err := client.KL_chefsesion.Insert(xdominion.XRecord{ - "clave": sessionid, - "chef": clientid, - "fechainicio": time.Now(), - "fechafin": time.Now(), - "ip": ip, - "longlogin": longlogin, - "origen": origin + source, - "device": device, - }) - if err != nil { - fmt.Println("Error insertando sesion:", err) - } - ctx.Sessionparams.Set("sessionid", sessionid) - ctx.Sessionparams.Set("clientid", clientid) - } else { - _, err := client.KL_chefsesion.Update(sessionid, xdominion.XRecord{ - "fechafin": time.Now(), - "origen": origin + source, - "device": device, - }) - if err != nil { - fmt.Println("Error modificando sesion:", err) +public function hasAccess($claveusuario, $derecho) +{ + if (!$claveusuario) + return false; + $datausuario = $this->getUsuarioDataByKey($claveusuario); + if (!$datausuario) + return false; + if ($datausuario->estatus == "S") // super usuario + return true; + + // 1. check access or extended + $data = $this->kl_adminderechousuario->doSelectCondition(array(new \dominion\DB_Condition('usuario', '=', $claveusuario), new \dominion\DB_Condition('derecho', '=', $derecho, 'and'))); + + if ($data && $data[0]->estatus == 1) // Authorized + return true; // if denied for sure we return false + if ($data && $data[0]->estatus == 2) // Denied + return false; + + $profiles = $this->kl_adminperfilusuario->doSelectCondition(array(new \dominion\DB_Condition('usuario', '=', $claveusuario))); + if ($profiles) + { + foreach($profiles as $pr) + { + $prof = $this->kl_adminperfilderecho->doSelectCondition(array(new \dominion\DB_Condition('perfil', '=', $pr->perfil), new \dominion\DB_Condition('derecho', '=', $derecho, 'and'))); + if ($prof) + return true; // found: authorized } } - - ipdata := geoip.GetGeoData(ip) - _, err := client.KL_chef.Update(clientid, xdominion.XRecord{ - "ultimopais": ipdata.Country.IsoCode, - "ultimaconexion": time.Now(), - "intento": 0, - }) - if err != nil { - fmt.Println("Error modificando chef:", err) - } - return sessionid -} - -func DestroySessionUser(ctx *assets.Context, sessionid string) { - /* - $this->siteSesion = null; - $this->clientid = null; - $this->chefData = null; - SetCookie($this->base->config->cookiename, null, 0, '/', $this->base->config->COOKIEDOMAIN); - - // comparte con Base - $this->base->clientid = $this->clientid; - $this->chefEntity->chefData = $this->chefData; - $this->base->siteSesion = $this->siteSesion; - * / + return false; } */ - -// SECURITY Access -func HasAccess(ds applications.Datasource, clientid int, access string, extra string) bool { - - // 1. profile - // 2. direct acceses - - return true -} diff --git a/user/security/verify.go b/user/security/verify.go new file mode 100644 index 0000000..6d11305 --- /dev/null +++ b/user/security/verify.go @@ -0,0 +1,84 @@ +package security + +import ( + "net/http" + + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" + "github.com/webability-go/xmodules/user/assets" +) + +const ( + // DOES NOT MATTER IF THE USER IS OR NOT CONNECTED + ANY = 1 + // THE USER MUST BE CONNECTED TO USE THE LIBARY + USER = 2 + // No access needed + FREEACCESS = "" +) + +// Verify connected user, verify security accesses +func Verify(ctx *context.Context, connection int, args ...interface{}) bool { + + // is appname is empty: search for "app" entry in ctx + appname, _ := ctx.Sysparams.GetString("adminapp") + if appname == "" { + http.Error(ctx.Writer, "Admin Library name is not available in config file (parameter adminapp missing)", http.StatusInternalServerError) + return false + } + + // Verification of session is done during StartContext of application, and set the Sessionparam + switch connection { + case ANY: + return true + case USER: + sessionid, _ := ctx.Sessionparams.GetString("usersessionid") + if sessionid == "" { + http.Error(ctx.Writer, "Error, user not connected", http.StatusForbidden) + return false + } + } + + access := "" + ok := false + // any args ? + if len(args) > 0 { + access, ok = args[0].(string) + if !ok { + http.Error(ctx.Writer, "Error on user access verification", http.StatusInternalServerError) + return false + } + } + + // security access + if access != "" { + // Check security + acc := HasAccess(ctx, args...) + if !acc { + http.Error(ctx.Writer, "Error, user not authorized", http.StatusForbidden) + return false + } + } + + return true +} + +// SECURITY Access +func HasAccess(ctx *context.Context, args ...interface{}) bool { + + userentries := assets.GetEntries(ctx) + if userentries == nil { + http.Error(ctx.Writer, "Error, no security module", http.StatusInternalServerError) + return false + } + + ds := base.TryDatasource(ctx, assets.DATASOURCE) + if ds == nil { + http.Error(ctx.Writer, "Error, no security datasource", http.StatusInternalServerError) + return false + } + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + return userentries.HasAccess(ds, userkey, args...) +} diff --git a/user/user.go b/user/user.go index d162f68..204861f 100644 --- a/user/user.go +++ b/user/user.go @@ -1,20 +1,20 @@ package user import ( + "errors" "fmt" "strconv" + "github.com/webability-go/xamboo/applications" "github.com/webability-go/xdominion" - - "github.com/webability-go/xmodules/base" ) // GetCountry to get the data of a country from cache/db in the specified language -func GetUser(ds *base.Datasource, key int) *StructureUser { +func GetUserByKey(ds applications.Datasource, key int) *xdominion.XRecord { cache := ds.GetCache("user:users") if cache == nil { - ds.Log("main", "xmodules::user::GetUser: Error, the user cache is not available on this site datasource") + ds.Log("main", "xmodules::user::GetUserByKey: Error, the user cache is not available on this site datasource") return nil } @@ -22,28 +22,16 @@ func GetUser(ds *base.Datasource, key int) *StructureUser { if data == nil { sm := CreateStructureUserByKey(ds, key) if sm == nil { - ds.Log("graph", "xmodules::user::GetUser: There is no user created: ", key) + ds.Log("graph", "xmodules::user::GetUserByKey: There is no user created: ", key) return nil } cache.Set(strconv.Itoa(key), sm) - return sm.(*StructureUser) - } - return data.(*StructureUser) -} - -// GetUsersList to get a list of all the users -func GetUsersList(ds *base.Datasource) *xdominion.XRecords { - - user_user := ds.GetTable("user_user") - if user_user == nil { - ds.Log("xmodules::user::GetUsersList: Error, the user_user table is not available on this datasource") - return nil + return sm.(*StructureUser).Data } - data, _ := user_user.SelectAll() - return data + return data.(*StructureUser).Data } -func GetUserByCredentials(ds *base.Datasource, username string, password string) *StructureUser { +func GetUserByCredentials(ds applications.Datasource, username string, password string) *StructureUser { user_user := ds.GetTable("user_user") if user_user == nil { @@ -68,3 +56,148 @@ func GetUserByCredentials(ds *base.Datasource, username string, password string) } return sm.(*StructureUser) } + +func GetUsersCount(ds applications.Datasource, cond *xdominion.XConditions) int { + + user_user := ds.GetTable("user_user") + if user_user == nil { + ds.Log("xmodules::user::GetCountProfilees: Error, the user_user table is not available on this datasource") + return 0 + } + cnt, _ := user_user.Count(cond) + return cnt +} + +func GetUsersList(ds applications.Datasource, cond *xdominion.XConditions, order *xdominion.XOrder, quantity int, first int) *xdominion.XRecords { + + user_user := ds.GetTable("user_user") + if user_user == nil { + ds.Log("xmodules::user::GetProfileesList: Error, the user_user table is not available on this datasource") + return nil + } + data, _ := user_user.SelectAll(cond, order, quantity, first) + return data +} + +func DeleteUserChildren(ds applications.Datasource, key int) error { + + /* + user_profile := ds.GetTable("user_profile") + if user_profile == nil { + errmsg := tools.Message(messages, "notable", "user_profile", ds.GetName()) + ds.Log(errmsg) + return errors.New(errmsg) + } + _, err := user_profile.Delete(xdominion.NewXCondition("group", "=", skey)) + return err + */ + return nil +} + +func PruneUserChildren(ds applications.Datasource, key int, user int) error { + + /* + user_profile := ds.GetTable("user_profile") + if user_profile == nil { + errmsg := tools.Message(messages, "notable", "user_profile", ds.GetName()) + ds.Log(errmsg) + return errors.New(errmsg) + } + _, err := user_profile.Update(xdominion.NewXCondition("group", "=", skey), xdominion.XRecord{"group": group}) + return err + */ + return nil +} + +// GetCountry to get the data of a country from cache/db in the specified language +func GetUserAccessByKeys(ds applications.Datasource, userkey int, accesskey string) *xdominion.XRecord { + + return nil +} + +// GetCountry to get the data of a country from cache/db in the specified language +func GetUserAccesses(ds applications.Datasource, userkey int, quantity int) (*xdominion.XRecords, error) { + + user_useraccess := ds.GetTable("user_useraccess") + if user_useraccess == nil { + ds.Log("xmodules::user::GetProfilesOfAccess: Error, the user_useraccess table is not available on this datasource") + return nil, errors.New("xmodules::user::GetProfilesOfAccess: Error, the user_userprofile table is not available on this datasource") + } + cond := xdominion.NewXCondition("user", "=", userkey) + orderby := xdominion.NewXOrderBy("access", xdominion.ASC) + data, err := user_useraccess.SelectAll(cond, orderby, quantity) + return data, err +} + +// GetCountry to get the data of a country from cache/db in the specified language +func SetUserAccess(ds applications.Datasource, user int, access string, status int) error { + + user_useraccess := ds.GetTable("user_useraccess") + if user_useraccess == nil { + return errors.New("xmodules::user::GetProfilesOfAccess: Error, the user_useraccess table is not available on this datasource") + } + cond := xdominion.XConditions{xdominion.NewXCondition("user", "=", user), xdominion.NewXCondition("access", "=", access, "and")} + data, err := user_useraccess.SelectOne(cond) + if err != nil { + return err + } + if data != nil && status == -1 { + // delete entry + _, err = user_useraccess.Delete(cond) + } + if data == nil && status != -1 { + // insert entry + _, err = user_useraccess.Insert(xdominion.XRecord{ + "user": user, + "access": access, + "denied": status, + }) + } + if data != nil && status != -1 { + // insert entry + _, err = user_useraccess.Update(cond, xdominion.XRecord{ + "denied": status, + }) + } + return err +} + +// GetCountry to get the data of a country from cache/db in the specified language +func GetUserProfiles(ds applications.Datasource, userkey int, quantity int) (*xdominion.XRecords, error) { + + user_userprofile := ds.GetTable("user_userprofile") + if user_userprofile == nil { + ds.Log("xmodules::user::GetProfilesOfAccess: Error, the user_userprofile table is not available on this datasource") + return nil, errors.New("xmodules::user::GetProfilesOfAccess: Error, the user_userprofile table is not available on this datasource") + } + cond := xdominion.NewXCondition("user", "=", userkey) + orderby := xdominion.NewXOrderBy("profile", xdominion.ASC) + data, err := user_userprofile.SelectAll(cond, orderby, quantity) + return data, err +} + +// GetCountry to get the data of a country from cache/db in the specified language +func SetUserProfile(ds applications.Datasource, user int, profile string, status bool) error { + + user_userprofile := ds.GetTable("user_userprofile") + if user_userprofile == nil { + return errors.New("xmodules::user::GetProfilesOfAccess: Error, the user_userprofile table is not available on this datasource") + } + cond := xdominion.XConditions{xdominion.NewXCondition("user", "=", user), xdominion.NewXCondition("profile", "=", profile, "and")} + data, err := user_userprofile.SelectOne(cond) + if err != nil { + return err + } + if data != nil && !status { + // delete entry + _, err = user_userprofile.Delete(cond) + } + if data == nil && status { + // insert entry + _, err = user_userprofile.Insert(xdominion.XRecord{ + "user": user, + "profile": profile, + }) + } + return err +} diff --git a/user/user_useraccess.go b/user/user_useraccess.go index 47f3f8d..288fd44 100644 --- a/user/user_useraccess.go +++ b/user/user_useraccess.go @@ -25,7 +25,7 @@ func userUserAccess() *xdominion.XTable { xdominion.XConstraint{Type: xdominion.DC}, }}) - // 1 if this access is denied + // 1 if this access is denied, or 0 t.AddField(xdominion.XFieldInteger{Name: "denied", Constraints: xdominion.XConstraints{ xdominion.XConstraint{Type: xdominion.NN}, }}) diff --git a/user/util.go b/user/util.go index b5b3709..5247b94 100644 --- a/user/util.go +++ b/user/util.go @@ -116,7 +116,7 @@ func install(ds applications.Datasource) (error, []string) { // insert super admin _, err := user_user.Upsert(1, xdominion.XRecord{ "key": 1, - "status": "A", + "status": "S", "name": "System Manager", "un": "system", "pw": hex.EncodeToString(bmd5[:]), diff --git a/useradmin/assets/moduleentries.go b/useradmin/assets/moduleentries.go index fe0f07f..7e1e3b7 100644 --- a/useradmin/assets/moduleentries.go +++ b/useradmin/assets/moduleentries.go @@ -1,11 +1,11 @@ package assets -import ( -//"github.com/webability-go/xdominion" - -// "github.com/webability-go/xamboo/assets" -// "github.com/webability-go/xmodules/base" +const ( + MODULEID = "useradmin" + VERSION = "0.0.4" + DATASOURCE = "useradmindatasource" + ACCESS = "_useradmin_access" + PREFIX = "useradmin" ) -type ModuleEntries struct { -} +var Needs = []string{"base", "user", "adminmenu"} diff --git a/useradmin/bridge/link.go b/useradmin/bridge/link.go deleted file mode 100644 index 6281f8a..0000000 --- a/useradmin/bridge/link.go +++ /dev/null @@ -1,30 +0,0 @@ -package bridge - -import ( - "errors" - "plugin" - - "github.com/webability-go/xmodules/useradmin/assets" -) - -var ModuleUserAdmin *assets.ModuleEntries - -var linked = false - -func Link(lib *plugin.Plugin) error { - if linked { - return nil - } - - obj, err := lib.Lookup("ModuleUserAdmin") - if err != nil { - return errors.New("Error: The application library does not contain the ModuleUserAdmin object") - } - ok := false - ModuleUserAdmin, ok = obj.(*assets.ModuleEntries) - if ModuleUserAdmin == nil || !ok { - return errors.New("Error: The application library does not contain a ModuleUserAdmin valid object") - } - linked = true - return nil -} diff --git a/useradmin/init.go b/useradmin/init.go index e8a95e4..d87014e 100644 --- a/useradmin/init.go +++ b/useradmin/init.go @@ -4,6 +4,9 @@ package useradmin import ( + "embed" + "io/fs" + "golang.org/x/text/language" "github.com/webability-go/xamboo/applications" @@ -14,25 +17,24 @@ import ( "github.com/webability-go/xmodules/useradmin/assets" ) -const ( - MODULEID = "useradmin" - VERSION = "0.0.1" -) +//go:embed languages/*.language +var fsmessages embed.FS +var messages *map[language.Tag]*xcore.XLanguage -var Needs = []string{"base", "user", "adminmenu"} -var ModuleUserAdmin = assets.ModuleEntries{} +//go:embed pages static +var files embed.FS func init() { - messages = tools.BuildMessages(smessages) + messages = tools.BuildMessagesFS(fsmessages, "languages") m := &base.Module{ - ID: MODULEID, - Version: VERSION, + ID: assets.MODULEID, + Version: assets.VERSION, Languages: map[language.Tag]string{ language.English: tools.Message(messages, "MODULENAME", language.English), language.Spanish: tools.Message(messages, "MODULENAME", language.Spanish), language.French: tools.Message(messages, "MODULENAME", language.French), }, - Needs: Needs, + Needs: assets.Needs, FSetup: Setup, // Called once at the main system startup, once PER CREATED xmodule CONTEXT (if set) FSynchronize: Synchronize, // Called only to create/rebuild database objects and others on demand (if set) FStartContext: StartContext, // Called each time a new Server context is created (if set) @@ -45,7 +47,7 @@ func init() { func Setup(ds applications.Datasource, prefix string) ([]string, error) { // no tables on this module - ds.SetModule(MODULEID, VERSION) + ds.SetModule(assets.MODULEID, assets.VERSION) return []string{}, nil } @@ -54,13 +56,13 @@ func Synchronize(ds applications.Datasource, prefix string) ([]string, error) { result := []string{} - ok, res := base.VerifyNeeds(ds, Needs) + ok, res := base.VerifyNeeds(ds, assets.Needs) result = append(result, res...) if !ok { return result, nil } - installed := base.ModuleInstalledVersion(ds, MODULEID) + installed := base.ModuleInstalledVersion(ds, assets.MODULEID) cds := ds.CloneShell() _, err := cds.StartTransaction() @@ -78,9 +80,9 @@ func Synchronize(ds applications.Datasource, prefix string) ([]string, error) { } result = append(result, r...) if err == nil { - err = base.AddModule(cds, MODULEID, tools.Message(messages, "MODULENAME"), VERSION) + err = base.AddModule(cds, assets.MODULEID, tools.Message(messages, "MODULENAME"), assets.VERSION) if err == nil { - result = append(result, tools.Message(messages, "modulemodified", MODULEID)) + result = append(result, tools.Message(messages, "modulemodified", assets.MODULEID)) result = append(result, tools.Message(messages, "commit")) err = cds.Commit() if err != nil { @@ -95,6 +97,24 @@ func Synchronize(ds applications.Datasource, prefix string) ([]string, error) { } } + // copy files + bcds := cds.(*base.Datasource) + pathadmin, _ := bcds.Config.GetString("pathinstalladmin") + pages, _ := fs.Sub(files, "pages") + err, rsf := base.SynchroFiles(pages, pathadmin) + result = append(result, rsf...) + if err != nil { + result = append(result, err.Error()) + } + + pathadminstatic, _ := bcds.Config.GetString("pathinstalladminstatic") + static, _ := fs.Sub(files, "static") + err, rsf = base.SynchroFiles(static, pathadminstatic) + result = append(result, rsf...) + if err != nil { + result = append(result, err.Error()) + } + return result, nil } diff --git a/useradmin/languages/useradmin.en.language b/useradmin/languages/useradmin.en.language new file mode 100644 index 0000000..f00f5a0 --- /dev/null +++ b/useradmin/languages/useradmin.en.language @@ -0,0 +1,39 @@ + + + + + Users Administration tools + The entry %s was modified successfully in the base_module table. + Installation successfull. + Installation aborted with error: %s + + Access group for user administration + Group of all accesses for user administration + Main access for user administration + Main access for user administration + Access for user access administration + Access for user access administration + Access for user profiles administration + Access for user profiles administration + Access for user administration + Access for user administration + + Users administration + Accesses, Profiles and Users + Accesses + All the available user accesses + Profiles + All the available profiles + Users + List and edit users + + Error modifying the entry %s in the base_module table: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/useradmin/languages/useradmin.es.language b/useradmin/languages/useradmin.es.language new file mode 100644 index 0000000..85373d0 --- /dev/null +++ b/useradmin/languages/useradmin.es.language @@ -0,0 +1,39 @@ + + + + + Herramientas para administrar usuarios + La entrada %s fue modificada con exito en la tabla base_module. + Instalación exitosa. + Instalación con error: %s + + Grupos de accesos de la administración de usuarios + Grupos de accesos de la administración de usuarios + Acceso principal para la administración de usuarios + Acceso principal para la administración de usuarios + Acceso de la administración de accesos de usuarios + Acceso de la administración de accesos de usuarios + Acceso de la administración de perfiles de usuarios + Acceso de la administración de perfiles de usuarios + Acceso de la administración de usuarios + Acceso de la administración de usuarios + + Administración de los usuarios + Derechos, Perfiles y Usuarios + Derechos de acceso + Todos los accesos + Perfiles + Todos los perfiles + Usuarios + Lista y edición de los usuarios + + Error modificando la entrada %s en la tabla base_module: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/useradmin/languages/useradmin.fr.language b/useradmin/languages/useradmin.fr.language new file mode 100644 index 0000000..9343425 --- /dev/null +++ b/useradmin/languages/useradmin.fr.language @@ -0,0 +1,39 @@ + + + + + Outils pour administrer les utilisateurs + L'entrée %s a été modifiée avec succès dans la table base_module. + Instalation réussie. + Instalation avec erreur: %s + + Groupe des accès d'administration des utilisateurs + Groupe des accès d'administration des utilisateurs + Accès principal d'administration des utilisateurs + Accès principal d'administration des utilisateurs + Accès d'administration des accès utilisateurs + Accès d'administration des accès utilisateurs + Accès d'administration des profils utilisateurs + Accès d'administration des profils utilisateurs + Accès d'administration des utilisateurs + Accès d'administration des utilisateurs + + Administration des utilisateurs + Administration des utilisateurs + Accès + Droits d'accès + Profils + Profils + Utilisateurs + Utilisateurs + + Erreur en modifiant l'entrée %s dans la table base_module: %s + + Error creating a transaction: There is already a started transaction. + Error searching the transaction: There is no available transaction. + Error searching the transaction to commit: There is no available transaction. + Error searching the transaction to rollback: There is no available transaction. + Error in the transaction: %s + + There is no available database in the datasource + diff --git a/useradmin/messages.go b/useradmin/messages.go deleted file mode 100644 index 8e1c8ec..0000000 --- a/useradmin/messages.go +++ /dev/null @@ -1,67 +0,0 @@ -package useradmin - -import ( - "golang.org/x/text/language" - - "github.com/webability-go/xcore/v2" -) - -// Do no forget to call tools.BuildMessages from the init -var messages *map[language.Tag]*xcore.XLanguage - -var smessages = map[language.Tag]map[string]string{ - language.English: { - // Module installation messages - // init.go - "MODULENAME": "Users Administration tools", - "modulemodified": "The entry %s was modified successfully in the base_module table.", - "commit": "Installation successfull.", - "rollback": "Installation aborted with error: %s", - - "moduleerror": "Error modifying the entry %s in the base_module table: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, - language.Spanish: { - // Module installation messages - // init.go - "MODULENAME": "Herramientas para administrar usuarios", - "modulemodified": "La entrada %s fue modificada con exito en la tabla base_module.", - "commit": "Instalación exitosa.", - "rollback": "Instalación con error: %s", - - "moduleerror": "Error modificando la entrada %s en la tabla base_module: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, - language.French: { - // Module installation messages - // init.go - "MODULENAME": "Outils pour administrer les utilisateurs", - "modulemodified": "L'entrée %s a été modifiée avec succès dans la table base_module.", - "commit": "Instalation réussie.", - "rollback": "Instalation avec erreur: %s", - - "moduleerror": "Erreur en modifiant l'entrée %s dans la table base_module: %s", - // Datasources transactions - "transaction.exist": "Error creating a transaction: There is already a started transaction.", - "transaction.none": "Error searching the transaction: There is no available transaction.", - "transaction.commitnone": "Error searching the transaction to commit: There is no available transaction.", - "transaction.rollbacknone": "Error searching the transaction to rollback: There is no available transaction.", - "transaction.error": "Error in the transaction: %s", - // Containers - "database.none": "There is no available database in the datasource", - }, -} diff --git a/useradmin/pages/README.md b/useradmin/pages/README.md index eda4755..87b3ea5 100644 --- a/useradmin/pages/README.md +++ b/useradmin/pages/README.md @@ -1,11 +1,4 @@ -Those are pages for Xamboo. +Those are pages for Xamboo administration system. -You may copy them to your site pages folder, or just call them from your pages library code. - -There are 2 ways to use the pages, one is with the user module included directly in the page (highly not recommended since there are no xcontext directly in the pages), and the other is calling the user methods through an APP bridge. - -We consider a standard bridge with Setup function to link and call the the bridge. - -Then we consider each function of the user and structure are implemented in the APP and linked in the bridge with the same syntax - -You should include and call the bridge sub module to include the User in your APP +The pages are copied to the main admin/pages environment directory at installation/upgrade of the module +The bridge is linked into the admin.so app module and is used by the administration pages diff --git a/useradmin/pages/useradmin/access/access.en.language b/useradmin/pages/useradmin/access/access.en.language new file mode 100644 index 0000000..e5add3e --- /dev/null +++ b/useradmin/pages/useradmin/access/access.en.language @@ -0,0 +1,9 @@ + + + Reload the list + New access + Admin groups of accesses + Accesses for users in the system. + Click on a line to edit the information: + + diff --git a/useradmin/pages/useradmin/access/access.es.language b/useradmin/pages/useradmin/access/access.es.language new file mode 100644 index 0000000..00f84e4 --- /dev/null +++ b/useradmin/pages/useradmin/access/access.es.language @@ -0,0 +1,28 @@ + + + + adminuser/access::%s - Error: no hay fuente de datos '%s' + adminuser/access::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + + Recargar la lista + Nuevo acceso + Administrar grupos de accesos + Derechos de acceso del sistema. + Haz click sobre un renglón para visualizarlo: + Ver los derechos del sistema + + Nuevo + Derecho de acceso + Ver + Editar + Borrar + + Grupo + Clave + Nombre + Descripción + Perfiles + Usuarios + Comandos + + diff --git a/useradmin/pages/useradmin/access/access.fr.language b/useradmin/pages/useradmin/access/access.fr.language new file mode 100644 index 0000000..34b849e --- /dev/null +++ b/useradmin/pages/useradmin/access/access.fr.language @@ -0,0 +1,9 @@ + + + Recharger la liste + Nouvel accès + Administrer les groupes d'accès + Droits d'accès du système. + Fais clic sur une ligne pour l'éditer: + + diff --git a/useradmin/pages/useradmin/access/access.go b/useradmin/pages/useradmin/access/access.go new file mode 100644 index 0000000..f4b6715 --- /dev/null +++ b/useradmin/pages/useradmin/access/access.go @@ -0,0 +1,183 @@ +package main + +import ( + // "fmt" + "strings" + + "encoding/json" + + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xdominion" + + "github.com/webability-go/xamboo/applications" + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" + userassets "github.com/webability-go/xmodules/user/assets" + "github.com/webability-go/xmodules/user/security" + "github.com/webability-go/xmodules/useradmin/assets" +) + +const ( + ACCESS_SHOWSYSTEM = "ACCESS_SHOWSYSTEM" +) + +func Run(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + showsystem := userentries.GetUserParam(ds, userkey, ACCESS_SHOWSYSTEM) + checked := "" + if showsystem == "true" { + checked = "checked " + } + + params := &xcore.XDataset{ + "CHECKED": checked, + "#": language, + } + + return template.Execute(params) +} + +func Accessdata(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + var dataarray [][]int + data := ctx.Request.Form.Get("data") + if data != "" { + json.Unmarshal([]byte(data), &dataarray) + } + if dataarray == nil { + dataarray = [][]int{{0, 49}} + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + + showsystem := userentries.GetUserParam(ds, userkey, ACCESS_SHOWSYSTEM) + + filter := xdominion.XConditions{xdominion.NewXCondition("key", "!=", "")} + if showsystem != "true" { + filter = append(filter, xdominion.NewXCondition("key", "not like", "\\_%", "and")) + } + order := xdominion.XOrder{xdominion.NewXOrderBy("group", xdominion.ASC), xdominion.NewXOrderBy("key", xdominion.ASC)} + + total := userentries.GetAccessesCount(ds, &filter) + + result := map[string]interface{}{ + "total": total, + "row": map[int]xdominion.XRecordDef{}, + "subtotal": 0.1, + "time": 0.1, + "subtime": 0.1, + } + + for _, rg := range dataarray { + row := rg[0] + + accesses := userentries.GetAccessesList(ds, &filter, &order, rg[1]-rg[0]+1, rg[0]) + if accesses != nil { + for _, access := range *accesses { + key, _ := access.GetString("key") + name, _ := access.GetString("name") + group, _ := access.GetString("group") + profiles := searchProfiles(ctx, ds, key) + users := searchUsers(ctx, ds, key) + + update := "" + delete := "" + if key[0] != '_' { + update = "" + delete = "" + } + + rec := &xdominion.XRecord{ + "group": group, + "key": key, + "name": name, + "profiles": profiles, + "users": users, + "commands": " " + update + " " + delete, + } + + result["row"].(map[int]xdominion.XRecordDef)[row] = rec + row++ + } + } + } + + return result +} + +func Filter(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + checked := ctx.Request.Form.Get("checked") + userentries.SetUserParam(ds, userkey, ACCESS_SHOWSYSTEM, checked) + + return "OK" +} + +func searchProfiles(ctx *context.Context, ds applications.Datasource, key string) string { + + userentries := userassets.GetEntries(ctx) + recs, err := userentries.GetAccessProfiles(ds, key, 10) + if recs == nil || err != nil || len(*recs) == 0 { + return "--" + } + result := []string{} + for _, r := range *recs { + profilekey, _ := r.GetString("profile") + result = append(result, "["+profilekey+"]") + } + return strings.Join(result, " ") +} + +func searchUsers(ctx *context.Context, ds applications.Datasource, key string) string { + + userentries := userassets.GetEntries(ctx) + recs, err := userentries.GetAccessUsers(ds, key, 10) + if recs == nil || err != nil || len(*recs) == 0 { + return "--" + } + result := []string{} + for _, r := range *recs { + userkey, _ := r.GetInt("user") + userdata := userentries.GetUserByKey(ds, userkey) + username, _ := userdata.GetString("name") + status, _ := userdata.GetString("status") + denied, _ := r.GetInt("denied") + if status == "S" { + result = append(result, "["+username+"]") + } else { + if denied == 1 { + result = append(result, "["+username+"]") + } else { + result = append(result, "["+username+"]") + } + } + } + return strings.Join(result, " ") +} diff --git a/adminmenu/pages/adminmenu/adminmenu.go b/useradmin/pages/useradmin/access/access.instance similarity index 100% rename from adminmenu/pages/adminmenu/adminmenu.go rename to useradmin/pages/useradmin/access/access.instance diff --git a/useradmin/pages/users/accesses/accesses.page b/useradmin/pages/useradmin/access/access.page similarity index 100% rename from useradmin/pages/users/accesses/accesses.page rename to useradmin/pages/useradmin/access/access.page diff --git a/useradmin/pages/useradmin/access/access.template b/useradmin/pages/useradmin/access/access.template new file mode 100644 index 0000000..7a7335b --- /dev/null +++ b/useradmin/pages/useradmin/access/access.template @@ -0,0 +1,109 @@ + + + + + + + ##title##
+ ##hint##
+ + + ##showsystem## + + + ]]>
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
diff --git a/adminmenu/pages/adminmenu/adminmenu.instance b/useradmin/pages/useradmin/access/editor/editor.en.language similarity index 100% rename from adminmenu/pages/adminmenu/adminmenu.instance rename to useradmin/pages/useradmin/access/editor/editor.en.language diff --git a/useradmin/pages/useradmin/access/editor/editor.es.language b/useradmin/pages/useradmin/access/editor/editor.es.language new file mode 100644 index 0000000..668e3f7 --- /dev/null +++ b/useradmin/pages/useradmin/access/editor/editor.es.language @@ -0,0 +1,55 @@ + + + + adminuser/access/editor::%s - Error: no hay fuente de datos '%s' + adminuser/access/editor::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + adminuser/access/editor::%s - Error: la tabla '%s' no existe en el datasource + + Derechos de acceso del sistema. + Haz click sobre un renglón para editarlo: + + + + + Información agregada con éxito. Ya puede cerrar esta pantalla. + + Información modificada con éxito. Ya puede cerrar esta pantalla. + + Información borrada con éxito. Ya puede cerrar esta pantalla. + + + Clave + La clave del derecho de acceso contiene de 1 a 30 carácteres, en minusculas. Puede usar también cifras, '-' y '_'. Las claves empezando por _ son derechos de acceso de sistema y no deberian de ser creados por el usuario. + La clave no puede estar vacia. + La clave no tiene un formato autorizado. + + Nombre + El nomnbre del derecho de acceso es de 1 hasta 255 carácteres máximo. + El nombre no puede estar vacio. + + Descripción + La descripción es un texto libre, hasta 4000 carácteres. + + Grupo + Seleccionar un grupo al cual pertenece este derecho. Los grupos empezando por _ son reservados para los módulos (no deberia de usarlos para derechos de acceso capturados a mano). + + ¿Borrar tambien los hijos? + Si selecciona Sí, entonces toda informacion relacionada con este derechos será borrada también. Si selecciona No, entonces la información será movida al acceso seleccionado en el campo siguiente. + Sí, borrar los hijos + No, seleccionar el acceso receptor + + Grupo receptor: + Si seleccionó No en el campo anterior, entonces especifique el acceso que recibirá la información relacionada al acceso a borrar. + + Grabar el nuevo grupo + Guardar las modificaciones + Si, borrar este grupo + Reinicinar + Cancelar + + Perfiles que usan este acceso: + Usuarios que usan este acceso: + Heredado + Otorgado + Denegado + diff --git a/adminmenu/pages/adminmenu/adminmenu.page b/useradmin/pages/useradmin/access/editor/editor.fr.language similarity index 100% rename from adminmenu/pages/adminmenu/adminmenu.page rename to useradmin/pages/useradmin/access/editor/editor.fr.language diff --git a/useradmin/pages/useradmin/access/editor/editor.go b/useradmin/pages/useradmin/access/editor/editor.go new file mode 100644 index 0000000..7300e25 --- /dev/null +++ b/useradmin/pages/useradmin/access/editor/editor.go @@ -0,0 +1,427 @@ +package main + +import ( + "encoding/xml" + "errors" + "strconv" + "strings" + + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xdominion" + "github.com/webability-go/xdommask" + + // "github.com/webability-go/xamboo/applications" + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" + "github.com/webability-go/xmodules/tools" + userassets "github.com/webability-go/xmodules/user/assets" + "github.com/webability-go/xmodules/user/security" + "github.com/webability-go/xmodules/useradmin/assets" +) + +var language *xcore.XLanguage + +func Run(ctx *context.Context, template *xcore.XTemplate, xlanguage *xcore.XLanguage, e interface{}) interface{} { + + if language == nil { + language = xlanguage + } + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + mode := ctx.Request.Form.Get("mode") + key := ctx.Request.Form.Get("Key") + if key == "" { + key = "new" + } + + params := &xcore.XDataset{ + "FORMACCESS": createXMLMask("formaccess", mode, ctx), + "KEY": key, + "#": language, + } + + return template.Execute(params) +} + +func createMask(id string, ctx *context.Context) (*xdommask.Mask, error) { + + hooks := xdommask.MaskHooks{ + Build: build, + PreInsert: preinsert, + PreUpdate: preupdate, + PreDelete: predelete, + } + return xdommask.NewMask(id, hooks, ctx) +} + +func createXMLMask(id string, mode string, ctx *context.Context) string { + mask, _ := createMask(id, ctx) + cmask := mask.Compile(mode, ctx) + xmlmask, _ := xml.Marshal(cmask) + return string(xmlmask) +} + +func build(mask *xdommask.Mask, ctx *context.Context) error { + + // Check security + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + if ds == nil { + return errors.New(tools.LogMessage(ctx.LoggerError, language, "errordatasource", "build", userassets.DATASOURCE)) + } + + user_access := ds.GetTable("user_access") + if user_access == nil { + return errors.New(tools.LogMessage(ctx.LoggerError, language, "errortable", "build", "user_access")) + } + + user_accessgroup := ds.GetTable("user_accessgroup") + if user_accessgroup == nil { + return errors.New(tools.LogMessage(ctx.LoggerError, language, "errortable", "build", "user_accessgroup")) + } + + mode := ctx.Request.Form.Get("mode") + mask.Table = user_access + key := ctx.Request.Form.Get(mask.VarKey) + if key != "" { + mask.Key = key + } + + mask.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + mask.KeyField = "key" + + mask.AlertMessage = "##mask.errormessage##" + mask.ServerMessage = "##mask.servermessage##" + mask.InsertTitle = "##mask.titleinsert##" + mask.DoInsertMessage = "##mask.titleinsert.message##" + mask.UpdateTitle = "##mask.titleupdate##" + mask.DoUpdateMessage = "##mask.titleupdate.message##" + mask.DeleteTitle = "##mask.titledelete##" + mask.DoDeleteMessage = "##mask.titledelete.message##" + mask.ViewTitle = "##mask.titleview##" + mask.FailureJS = "function(params) { this.icontainer.setMessages(params); }" + + // key + f10 := xdommask.NewTextField("key") + f10.Title = "##key.title##" + f10.HelpDescription = "##key.help.description##" + f10.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f10.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f10.HelpModes = xdommask.INSERT | xdommask.UPDATE + f10.ViewModes = xdommask.DELETE | xdommask.VIEW + f10.StatusNotNull = "##key.status.notnull##" + f10.StatusBadFormat = "##key.status.badformat##" + f10.MaxLength = 30 + f10.Size = "400" + f10.URLVariable = "key" + f10.Format = "^[a-z][a-z0-9-_]{1,29}$" + f10.FormatJS = "^[a-z][a-z0-9-_]{1,29}$" + f10.DefaultValue = "" + mask.AddField(f10) + + // name + f11 := xdommask.NewTextField("name") + f11.Title = "##name.title##" + f11.HelpDescription = "##name.help.description##" + f11.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f11.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f11.HelpModes = xdommask.INSERT | xdommask.UPDATE + f11.ViewModes = xdommask.DELETE | xdommask.VIEW + f11.StatusNotNull = "##name.status.notnull##" + f11.MaxLength = 255 + f11.Size = "400" + f11.URLVariable = "name" + f11.DefaultValue = "" + mask.AddField(f11) + + // description + f12 := xdommask.NewTextAreaField("description") + f12.Title = "##description.title##" + f12.HelpDescription = "##description.help.description##" + f12.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f12.HelpModes = xdommask.INSERT | xdommask.UPDATE + f12.ViewModes = xdommask.DELETE | xdommask.VIEW + f12.MaxLength = 4000 + f12.Width = 400 + f12.Height = 100 + f12.URLVariable = "description" + f12.DefaultValue = "" + mask.AddField(f12) + + // group + f13 := xdommask.NewLOVField("group") + f13.Title = "##group.title##" + f13.HelpDescription = "##group.help.description##" + f13.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f13.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f13.HelpModes = xdommask.INSERT | xdommask.UPDATE + f13.ViewModes = xdommask.DELETE | xdommask.VIEW + f13.Table = user_accessgroup + if mode != "4" { + f13.Conditions = &xdominion.XConditions{xdominion.NewXCondition("key", "not like", "\\_%")} + } + f13.Order = &xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + f13.FieldSet = &xdominion.XFieldSet{"key", "name"} + f13.URLVariable = "group" + mask.AddField(f13) + + // Ask what to delete + f21 := xdommask.NewLOVField("prune") + f21.Title = "##prune.title##" + f21.HelpDescription = "##prune.help.description##" + f21.NotNullModes = xdommask.DELETE + f21.AuthModes = xdommask.DELETE + f21.HelpModes = xdommask.DELETE + f21.Options = map[string]string{ + "1": "##prune.yes##", + "2": "##prune.no##", + } + f21.CheckJS = "n = WA.$N('useradmin/access/editor|" + key + "|accesstoprune'); n.domNodeField.disabled = (value==1); n.checkAll();" + // f12.ChangeJS = "function(p) { if (p.id == 'prune') { n = WA.$N('useradmin/accessgroup/editor|" + key + "|grouptoprune'); n.domNodeField.disabled = (p.value==1); n.checkAll(); } }" + f21.URLVariable = "prune" + mask.AddField(f21) + + // group + f22 := xdommask.NewLOVField("accesstoprune") + f22.Title = "##accesstoprune.title##" + f22.HelpDescription = "##accesstoprune.help.description##" + f22.NotNullModes = xdommask.DELETE + f22.AuthModes = xdommask.DELETE + f22.HelpModes = xdommask.DELETE + f22.Table = user_access + f22.Conditions = &xdominion.XConditions{xdominion.NewXCondition("key", "!=", key), xdominion.NewXCondition("key", "not like", "\\_%", "and")} + f22.Order = &xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + f22.FieldSet = &xdominion.XFieldSet{"key", "name"} + f22.URLVariable = "accesstoprune" + mask.AddField(f22) + + // Submit + f8 := xdommask.NewButtonField("", "submit") + f8.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE + f8.TitleInsert = "##formsubmit.insert.title##" + f8.TitleUpdate = "##formsubmit.update.title##" + f8.TitleDelete = "##formsubmit.delete.title##" + mask.AddField(f8) + + // Reset + f9 := xdommask.NewButtonField("", "reset") + f9.AuthModes = xdommask.INSERT | xdommask.UPDATE + f9.TitleInsert = "##formreset.title##" + f9.TitleUpdate = "##formreset.title##" + mask.AddField(f9) + + // View + f91 := xdommask.NewButtonField("", "view") + f91.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE + f91.TitleInsert = "##formview.title##" + f91.TitleUpdate = "##formview.title##" + f91.TitleDelete = "##formview.title##" + mask.AddField(f91) + + return nil +} + +func preinsert(m *xdommask.Mask, ctx *context.Context, rec *xdominion.XRecord) error { + key, _ := rec.GetString("key") + if len(key) > 1 && key[0] == '_' { + return errors.New("Error: you cannot insert a key starting with _") + } + return nil +} + +func preupdate(m *xdommask.Mask, ctx *context.Context, key interface{}, oldrec *xdominion.XRecord, newrec *xdominion.XRecord) error { + skey := key.(string) + if len(skey) > 1 && skey[0] == '_' { + return errors.New("Error: you cannot update a key starting with _") + } + newkey, _ := newrec.GetString("key") + if len(newkey) > 1 && newkey[0] == '_' { + return errors.New("Error: you cannot update a key starting with _") + } + return nil +} + +func predelete(m *xdommask.Mask, ctx *context.Context, key interface{}, oldrec *xdominion.XRecord, rec *xdominion.XRecord) error { + skey := key.(string) + if len(skey) > 1 && skey[0] == '_' { + return errors.New("Error: you cannot delete a key starting with _") + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + + prune, _ := rec.GetString("prune") + if prune == "1" { + // delete children + err := userentries.DeleteAccessChildren(ds, skey) + if err != nil { + return err + } + } else { + // prune children + group, _ := rec.GetString("accesstoprune") + if len(group) > 1 && group[0] == '_' { + return errors.New("Error: you cannot paste access children to a system access starting with _") + } + err := userentries.PruneAccessChildren(ds, skey, group) + if err != nil { + return err + } + } + return nil +} + +func Formaccess(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + mask, _ := createMask("formaccess", ctx) + data, _ := mask.Run(ctx) + return data +} + +func Getprofiles(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + result := []string{} + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + + inprofiles, _ := userentries.GetAccessProfiles(ds, key, 0) + order := xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + profiles := userentries.GetProfilesList(ds, nil, &order, 0, 0) + if profiles == nil || len(*profiles) == 0 { + return "Error" + } + + // make an array of inprofiles + ainprofiles := map[string]bool{} + if inprofiles != nil { + for _, ip := range *inprofiles { + pr, _ := ip.GetString("profile") + ainprofiles[pr] = true + } + } + + for _, r := range *profiles { + profilekey, _ := r.GetString("key") + profilename, _ := r.GetString("name") + checked := "" + if ainprofiles[profilekey] { + checked = " checked=\"checked\"" + } + result = append(result, " "+profilekey+" - "+profilename) + } + return strings.Join(result, "
") +} + +func Getusers(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + result := []string{} + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + + inusers, _ := userentries.GetAccessUsers(ds, key, 0) + order := xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + users := userentries.GetUsersList(ds, nil, &order, 0, 0) + if users == nil || len(*users) == 0 { + return "Error" + } + + // make an array of inprofiles + ainusers := map[int]int{} + if inusers != nil { + for _, ip := range *inusers { + us, _ := ip.GetInt("user") + denied, _ := ip.GetInt("denied") + ainusers[us] = denied + } + } + + bydefault := language.Get("users.hierarchy") + authorized := language.Get("users.yes") + denied := language.Get("users.no") + for _, r := range *users { + userkey, _ := r.GetInt("key") + suserkey, _ := r.GetString("key") + userdata := userentries.GetUserByKey(ds, userkey) + if userdata == nil { + return "Error" + } + username, _ := userdata.GetString("name") + checked1 := "" + checked2 := "" + checked3 := "" + v, ok := ainusers[userkey] + if !ok { + checked1 = " checked=\"true\"" + } else if v == 0 { + checked2 = " checked=\"true\"" + } else { // v == 1 + checked3 = " checked=\"true\"" + } + result = append(result, " "+bydefault+ + " "+authorized+ + " "+denied+" | "+ + " "+suserkey+" - "+username) + } + return strings.Join(result, "
") +} + +func Setprofile(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + profile := ctx.Request.Form.Get("id") + checked := ctx.Request.Form.Get("checked") + + userentries.SetProfileAccess(ds, profile, key, checked == "true") + + return "{\"status\":\"OK\"}" +} + +func Setuser(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + user := ctx.Request.Form.Get("id") + iuser, _ := strconv.Atoi(user) + val := ctx.Request.Form.Get("val") + ival, _ := strconv.Atoi(val) + + userentries.SetUserAccess(ds, iuser, key, ival) + + return "{\"status\":\"OK\"}" +} diff --git a/adminmenu/pages/adminmenu/adminmenu.template b/useradmin/pages/useradmin/access/editor/editor.instance similarity index 100% rename from adminmenu/pages/adminmenu/adminmenu.template rename to useradmin/pages/useradmin/access/editor/editor.instance diff --git a/useradmin/pages/users/users.page b/useradmin/pages/useradmin/access/editor/editor.page similarity index 100% rename from useradmin/pages/users/users.page rename to useradmin/pages/useradmin/access/editor/editor.page diff --git a/useradmin/pages/useradmin/access/editor/editor.template b/useradmin/pages/useradmin/access/editor/editor.template new file mode 100644 index 0000000..b0afa29 --- /dev/null +++ b/useradmin/pages/useradmin/access/editor/editor.template @@ -0,0 +1,98 @@ + + + + + +{{FORMACCESS}} + + + + + ##profiles.title## + + + + ##users.title## + + + + + + + + + + + + + diff --git a/useradmin/pages/useradmin/accessgroup/accessgroup.en.language b/useradmin/pages/useradmin/accessgroup/accessgroup.en.language new file mode 100644 index 0000000..c103831 --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/accessgroup.en.language @@ -0,0 +1,23 @@ + + + + adminuser/accessgroup::%s - Error: there is no datasource '%s' + adminuser/accessgroup::%s - Error: the user '%d' does not have access to this screen + + Reload the list + New access group + Groups of right accesses in the system. + Click on a line to view the information + View the system groups + + New + Groups of right accesses + View + Edit + Delete + + Key + Name + Commands + + diff --git a/useradmin/pages/useradmin/accessgroup/accessgroup.es.language b/useradmin/pages/useradmin/accessgroup/accessgroup.es.language new file mode 100644 index 0000000..5784776 --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/accessgroup.es.language @@ -0,0 +1,23 @@ + + + + adminuser/accessgroup::%s - Error: no hay fuente de datos '%s' + adminuser/accessgroup::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + + Recargar la lista + Nuevo grupo de accesos + Grupos de derechos de acceso del sistema. + Haz click sobre un renglón para visualizarlo + Ver los derechos del sistema + + Nuevo + Grupos de derechos + Ver + Editar + Borrar + + Clave + Nombre + Comandos + + diff --git a/useradmin/pages/useradmin/accessgroup/accessgroup.fr.language b/useradmin/pages/useradmin/accessgroup/accessgroup.fr.language new file mode 100644 index 0000000..f47021a --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/accessgroup.fr.language @@ -0,0 +1,23 @@ + + + + adminuser/accessgroup::%s - Erreur: il n'y a pas de source de données '%s' + adminuser/accessgroup::%s - Erreur: l'utilisateur '%d' n'a pas accès à cet écran + + Recharger la liste + Nouveau groupe d'accès + Groupes de droits d'accès du système. + Fais clic sur une ligne pour la visualiser + Voir les groupes du système + + Nouveau + Groupes de droits d'accès + Voir + Editer + Effacer + + Clef + Nom + Commandes + + diff --git a/useradmin/pages/useradmin/accessgroup/accessgroup.go b/useradmin/pages/useradmin/accessgroup/accessgroup.go new file mode 100644 index 0000000..8f08a71 --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/accessgroup.go @@ -0,0 +1,128 @@ +package main + +import ( + "encoding/json" + + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xdominion" + + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" + userassets "github.com/webability-go/xmodules/user/assets" + "github.com/webability-go/xmodules/user/security" + "github.com/webability-go/xmodules/useradmin/assets" +) + +const ( + ACCESSGROUP_SHOWSYSTEM = "ACCESSGROUP_SHOWSYSTEM" +) + +func Run(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + showsystem := userentries.GetUserParam(ds, userkey, ACCESSGROUP_SHOWSYSTEM) + checked := "" + if showsystem == "true" { + checked = "checked " + } + + params := &xcore.XDataset{ + "CHECKED": checked, + "#": language, + } + + return template.Execute(params) +} + +func Accessgroupdata(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + var dataarray [][]int + data := ctx.Request.Form.Get("data") + if data != "" { + json.Unmarshal([]byte(data), &dataarray) + } + if dataarray == nil { + dataarray = [][]int{{0, 49}} + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + showsystem := userentries.GetUserParam(ds, userkey, ACCESSGROUP_SHOWSYSTEM) + + filter := xdominion.XConditions{xdominion.NewXCondition("key", "!=", "")} + if showsystem != "true" { + filter = append(filter, xdominion.NewXCondition("key", "not like", "\\_%", "and")) + } + order := xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + + total := userentries.GetAccessGroupsCount(ds, &filter) + + result := map[string]interface{}{ + "total": total, + "row": map[int]xdominion.XRecordDef{}, + "time": 0.1, + } + + for _, rg := range dataarray { + row := rg[0] + + accessgroups := userentries.GetAccessGroupsList(ds, &filter, &order, rg[1]-rg[0]+1, rg[0]) + if accessgroups != nil { + for _, accessgroup := range *accessgroups { + key, _ := accessgroup.GetString("key") + name, _ := accessgroup.GetString("name") + + update := "" + delete := "" + if key[0] != '_' { + update = "" + delete = "" + } + + rec := &xdominion.XRecord{ + "key": key, + "name": name, + "commands": " " + update + " " + delete, + } + + result["row"].(map[int]xdominion.XRecordDef)[row] = rec + row++ + } + } + } + + return result +} + +func Filter(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + checked := ctx.Request.Form.Get("checked") + userentries.SetUserParam(ds, userkey, ACCESSGROUP_SHOWSYSTEM, checked) + + return "OK" +} diff --git a/useradmin/pages/users/accesses/accesses.instance b/useradmin/pages/useradmin/accessgroup/accessgroup.instance similarity index 100% rename from useradmin/pages/users/accesses/accesses.instance rename to useradmin/pages/useradmin/accessgroup/accessgroup.instance diff --git a/useradmin/pages/useradmin/accessgroup/accessgroup.page b/useradmin/pages/useradmin/accessgroup/accessgroup.page new file mode 100644 index 0000000..deaecd2 --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/accessgroup.page @@ -0,0 +1,4 @@ +type=wajafapp +status=published +template= +acceptpathparameters=yes diff --git a/useradmin/pages/useradmin/accessgroup/accessgroup.template b/useradmin/pages/useradmin/accessgroup/accessgroup.template new file mode 100644 index 0000000..1853c30 --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/accessgroup.template @@ -0,0 +1,90 @@ + + + + + + + ##title##
+ ##hint##
+ + ##showsystem## + + + ]]>
+
+ + + + + + + + + + + + + + + + + +
+ + + +
diff --git a/useradmin/pages/useradmin/accessgroup/editor/editor.en.language b/useradmin/pages/useradmin/accessgroup/editor/editor.en.language new file mode 100644 index 0000000..0852f3d --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/editor/editor.en.language @@ -0,0 +1,46 @@ + + + + adminuser/accessgroup/editor::%s - Error: no hay fuente de datos '%s' + adminuser/accessgroup/editor::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + adminuser/access/editor::%s - Error: la tabla '%s' no existe en el datasource + + Grupos de derechos de acceso del sistema. + Haz click sobre un renglón para editarlo: + + + + + Información agregada con éxito. Ya puede cerrar esta pantalla. + + Información modificada con éxito. Ya puede cerrar esta pantalla. + + Información borrada con éxito. Ya puede cerrar esta pantalla. + + + Clave: + La clave del grupo de derechos de acceso contiene de 1 a 30 carácteres, en minúsculas. La clave tiene que empezar por una letra. Puede usar también cifras, '-' y '_'. Las claves empezando por _ son grupos de derechos de acceso de sistema y no pueden ser creados o modificados por el usuario. + La clave no puede estar vacia. + La clave tiene un formato erroneo. + La clave de un grupo de derechos editado por el usuario no puede empezar por _. + No puede pegar los hijos en un grupo cuya clave empieza por _. + + Nombre: + El nombre del grupo de derechos de acceso es de 1 hasta 255 carácteres máximo. + El nombre no puede estar vacio. + + ¿Borrar tambien los hijos? + Si selecciona Sí, entonces todos los derechos en este grupo serán borrados también. Si selecciona No, entonces los derechos en este grupo serán movidos al grupo seleccionado en el campo siguiente. + Sí, borrar los hijos + No, seleccionar el grupo receptor + + Grupo receptor: + Si seleccionó No en el campo anterior, entonces especifique el grupo que recibirá los derechos del grupo a borrar. + + Grabar el nuevo grupo + Guardar las modificaciones + Si, borrar este grupo + Deshacer los cambios + Cancelar + + diff --git a/useradmin/pages/useradmin/accessgroup/editor/editor.es.language b/useradmin/pages/useradmin/accessgroup/editor/editor.es.language new file mode 100644 index 0000000..b1b409a --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/editor/editor.es.language @@ -0,0 +1,46 @@ + + + + adminuser/accessgroup/editor::%s - Error: no hay fuente de datos '%s' + adminuser/accessgroup/editor::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + adminuser/access/editor::%s - Error: la tabla '%s' no existe en el datasource + + Grupos de derechos de acceso del sistema. + Haz click sobre un renglón para editarlo: + + + + + Información agregada con éxito. Ya puede cerrar esta pantalla. + + Información modificada con éxito. Ya puede cerrar esta pantalla. + + Información borrada con éxito. Ya puede cerrar esta pantalla. + + + Clave: + La clave del grupo de derechos de acceso contiene de 1 a 30 carácteres, en minúsculas. La clave tiene que empezar por una letra. Puede usar también cifras, '-' y '_'. Las claves empezando por _ son grupos de derechos de acceso de sistema y no pueden ser creados o modificados por el usuario. + La clave no puede estar vacia. + La clave tiene un formato erroneo. + La clave de un grupo de derechos editado por el usuario no puede empezar por _. + No puede pegar los hijos en un grupo cuya clave empieza por _. + + Nombre: + El nombre del grupo de derechos de acceso es de 1 hasta 255 carácteres máximo. + El nombre no puede estar vacio. + + ¿Borrar tambien los hijos? + Si selecciona Sí, entonces todos los derechos en este grupo serán borrados también. Si selecciona No, entonces los derechos en este grupo serán movidos al grupo seleccionado en el campo siguiente. + Sí, borrar los hijos + No, seleccionar el grupo receptor + + Grupo receptor: + Si seleccionó No en el campo anterior, entonces especifique el grupo que recibirá los derechos del grupo a borrar. + + Grabar el nuevo grupo + Guardar las modificaciones + Si, borrar este grupo + Deshacer los cambios + Cancelar + + diff --git a/useradmin/pages/useradmin/accessgroup/editor/editor.fr.language b/useradmin/pages/useradmin/accessgroup/editor/editor.fr.language new file mode 100644 index 0000000..f18ec20 --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/editor/editor.fr.language @@ -0,0 +1,46 @@ + + + + adminuser/accessgroup/editor::%s - Error: no hay fuente de datos '%s' + adminuser/accessgroup/editor::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + adminuser/access/editor::%s - Error: la tabla '%s' no existe en el datasource + + Grupos de derechos de acceso del sistema. + Haz click sobre un renglón para editarlo: + + + + + Información agregada con éxito. Ya puede cerrar esta pantalla. + + Información modificada con éxito. Ya puede cerrar esta pantalla. + + Información borrada con éxito. Ya puede cerrar esta pantalla. + + + Clave: + La clave del grupo de derechos de acceso contiene de 1 a 30 carácteres, en minúsculas. La clave tiene que empezar por una letra. Puede usar también cifras, '-' y '_'. Las claves empezando por _ son grupos de derechos de acceso de sistema y no pueden ser creados o modificados por el usuario. + La clave no puede estar vacia. + La clave tiene un formato erroneo. + La clave de un grupo de derechos editado por el usuario no puede empezar por _. + No puede pegar los hijos en un grupo cuya clave empieza por _. + + Nombre: + El nombre del grupo de derechos de acceso es de 1 hasta 255 carácteres máximo. + El nombre no puede estar vacio. + + ¿Borrar tambien los hijos? + Si selecciona Sí, entonces todos los derechos en este grupo serán borrados también. Si selecciona No, entonces los derechos en este grupo serán movidos al grupo seleccionado en el campo siguiente. + Sí, borrar los hijos + No, seleccionar el grupo receptor + + Grupo receptor: + Si seleccionó No en el campo anterior, entonces especifique el grupo que recibirá los derechos del grupo a borrar. + + Grabar el nuevo grupo + Guardar las modificaciones + Si, borrar este grupo + Deshacer los cambios + Cancelar + + diff --git a/useradmin/pages/useradmin/accessgroup/editor/editor.go b/useradmin/pages/useradmin/accessgroup/editor/editor.go new file mode 100644 index 0000000..f670ca3 --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/editor/editor.go @@ -0,0 +1,259 @@ +package main + +import ( + "encoding/xml" + "errors" + // "fmt" + + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xdominion" + "github.com/webability-go/xdommask" + + // "github.com/webability-go/xamboo/applications" + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" + "github.com/webability-go/xmodules/tools" + userassets "github.com/webability-go/xmodules/user/assets" + "github.com/webability-go/xmodules/user/security" + "github.com/webability-go/xmodules/useradmin/assets" +) + +var language *xcore.XLanguage + +func Run(ctx *context.Context, template *xcore.XTemplate, xlanguage *xcore.XLanguage, e interface{}) interface{} { + + if language == nil { + language = xlanguage + } + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + mode := ctx.Request.Form.Get("mode") + key := ctx.Request.Form.Get("Key") + if key == "" { + key = "new" + } + + params := &xcore.XDataset{ + "FORMACCESS": createXMLMask("formaccessgroup", mode, ctx), + "KEY": key, + "#": language, + } + + return template.Execute(params) +} + +func createMask(id string, ctx *context.Context) (*xdommask.Mask, error) { + + hooks := xdommask.MaskHooks{ + Build: build, + PreInsert: preinsert, + PreUpdate: preupdate, + PreDelete: predelete, + } + return xdommask.NewMask(id, hooks, ctx) +} + +func createXMLMask(id string, mode string, ctx *context.Context) string { + mask, _ := createMask(id, ctx) + cmask := mask.Compile(mode, ctx) + xmlmask, _ := xml.Marshal(cmask) + return string(xmlmask) +} + +func build(mask *xdommask.Mask, ctx *context.Context) error { + + // Check security + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + if ds == nil { + return errors.New(tools.LogMessage(ctx.LoggerError, language, "errordatasource", "build", userassets.DATASOURCE)) + } + + user_accessgroup := ds.GetTable("user_accessgroup") + if user_accessgroup == nil { + return errors.New(tools.LogMessage(ctx.LoggerError, language, "errortable", "build", "user_access")) + } + + mask.Table = user_accessgroup + key := ctx.Request.Form.Get(mask.VarKey) + if key != "" { + mask.Key = key + } + + mask.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + mask.KeyField = "key" + + mask.AlertMessage = "##mask.errormessage##" + mask.ServerMessage = "##mask.servermessage##" + mask.InsertTitle = "##mask.titleinsert##" + mask.DoInsertMessage = "##mask.titleinsert.message##" + mask.UpdateTitle = "##mask.titleupdate##" + mask.DoUpdateMessage = "##mask.titleupdate.message##" + mask.DeleteTitle = "##mask.titledelete##" + mask.DoDeleteMessage = "##mask.titledelete.message##" + mask.ViewTitle = "##mask.titleview##" + mask.FailureJS = "function(params) { this.icontainer.setMessages(params); }" + + // key + f10 := xdommask.NewTextField("key") + f10.Title = "##key.title##" + f10.HelpDescription = "##key.help.description##" + f10.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f10.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f10.HelpModes = xdommask.INSERT | xdommask.UPDATE + f10.ViewModes = xdommask.DELETE | xdommask.VIEW + f10.StatusNotNull = "##key.status.notnull##" + f10.StatusBadFormat = "##key.status.badformat##" + f10.MaxLength = 30 + f10.Size = "400" + f10.URLVariable = "key" + f10.Format = "^[a-z][a-z0-9-_]{1,29}$" + f10.FormatJS = "^[a-z][a-z0-9-_]{1,29}$" + f10.DefaultValue = "" + mask.AddField(f10) + + // name + f11 := xdommask.NewTextField("name") + f11.Title = "##name.title##" + f11.HelpDescription = "##name.help.description##" + f11.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f11.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f11.HelpModes = xdommask.INSERT | xdommask.UPDATE + f11.ViewModes = xdommask.DELETE | xdommask.VIEW + f11.StatusNotNull = "##name.status.notnull##" + f11.MaxLength = 255 + f11.Size = "400" + f11.URLVariable = "name" + f11.DefaultValue = "" + mask.AddField(f11) + + // Ask what to delete + f21 := xdommask.NewLOVField("prune") + f21.Title = "##prune.title##" + f21.HelpDescription = "##prune.help.description##" + f21.NotNullModes = xdommask.DELETE + f21.AuthModes = xdommask.DELETE + f21.HelpModes = xdommask.DELETE + f21.Options = map[string]string{ + "1": "##prune.yes##", + "2": "##prune.no##", + } + f21.CheckJS = "n = WA.$N('useradmin/accessgroup/editor|" + key + "|grouptoprune'); n.domNodeField.disabled = (value==1); n.checkAll();" + // f12.ChangeJS = "function(p) { if (p.id == 'prune') { n = WA.$N('useradmin/accessgroup/editor|" + key + "|grouptoprune'); n.domNodeField.disabled = (p.value==1); n.checkAll(); } }" + f21.URLVariable = "prune" + mask.AddField(f21) + + // group + f22 := xdommask.NewLOVField("grouptoprune") + f22.Title = "##grouptoprune.title##" + f22.HelpDescription = "##grouptoprune.help.description##" + f22.NotNullModes = xdommask.DELETE + f22.AuthModes = xdommask.DELETE + f22.HelpModes = xdommask.DELETE + f22.Table = user_accessgroup + f22.Conditions = &xdominion.XConditions{xdominion.NewXCondition("key", "!=", key), xdominion.NewXCondition("key", "not like", "\\_%", "and")} + f22.Order = &xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + f22.FieldSet = &xdominion.XFieldSet{"key", "name"} + f22.URLVariable = "grouptoprune" + mask.AddField(f22) + + // Submit + f8 := xdommask.NewButtonField("", "submit") + f8.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE + f8.TitleInsert = "##formsubmit.insert.title##" + f8.TitleUpdate = "##formsubmit.update.title##" + f8.TitleDelete = "##formsubmit.delete.title##" + mask.AddField(f8) + + // Reset + f9 := xdommask.NewButtonField("", "reset") + f9.AuthModes = xdommask.INSERT | xdommask.UPDATE + f9.TitleInsert = "##formreset.title##" + f9.TitleUpdate = "##formreset.title##" + mask.AddField(f9) + + // View + f91 := xdommask.NewButtonField("", "view") + f91.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE + f91.TitleInsert = "##formview.title##" + f91.TitleUpdate = "##formview.title##" + f91.TitleDelete = "##formview.title##" + mask.AddField(f91) + + return nil +} + +func preinsert(m *xdommask.Mask, ctx *context.Context, rec *xdominion.XRecord) error { + key, _ := rec.GetString("key") + if len(key) > 1 && key[0] == '_' { + keyerror := language.Get("key.error") + return errors.New(keyerror) + } + return nil +} + +func preupdate(m *xdommask.Mask, ctx *context.Context, key interface{}, oldrec *xdominion.XRecord, newrec *xdominion.XRecord) error { + skey := key.(string) + if len(skey) > 1 && skey[0] == '_' { + keyerror := language.Get("key.error") + return errors.New(keyerror) + } + newkey, _ := newrec.GetString("key") + if len(newkey) > 1 && newkey[0] == '_' { + keyerror := language.Get("key.error") + return errors.New(keyerror) + } + return nil +} + +func predelete(m *xdommask.Mask, ctx *context.Context, key interface{}, oldrec *xdominion.XRecord, rec *xdominion.XRecord) error { + skey := key.(string) + if len(skey) > 1 && skey[0] == '_' { + keyerror := language.Get("key.error") + return errors.New(keyerror) + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + + prune, _ := rec.GetString("prune") + if prune == "1" { + // delete children + err := userentries.DeleteAccessGroupChildren(ds, skey) + if err != nil { + return err + } + } else { + // prune children + group, _ := rec.GetString("grouptoprune") + if len(group) > 1 && group[0] == '_' { + keyerror := language.Get("key.pruneerror") + return errors.New(keyerror) + } + err := userentries.PruneAccessGroupChildren(ds, skey, group) + if err != nil { + return err + } + } + return nil +} + +func Formaccessgroup(ctx *context.Context, template *xcore.XTemplate, xlanguage *xcore.XLanguage, e interface{}) interface{} { + + if language == nil { + language = xlanguage + } + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + mask, _ := createMask("formaccess", ctx) + data, _ := mask.Run(ctx) + return data +} diff --git a/useradmin/pages/users/accesses/editorgroup/editorgroup.go b/useradmin/pages/useradmin/accessgroup/editor/editor.instance similarity index 100% rename from useradmin/pages/users/accesses/editorgroup/editorgroup.go rename to useradmin/pages/useradmin/accessgroup/editor/editor.instance diff --git a/useradmin/pages/useradmin/accessgroup/editor/editor.page b/useradmin/pages/useradmin/accessgroup/editor/editor.page new file mode 100644 index 0000000..deaecd2 --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/editor/editor.page @@ -0,0 +1,4 @@ +type=wajafapp +status=published +template= +acceptpathparameters=yes diff --git a/useradmin/pages/useradmin/accessgroup/editor/editor.template b/useradmin/pages/useradmin/accessgroup/editor/editor.template new file mode 100644 index 0000000..76f9489 --- /dev/null +++ b/useradmin/pages/useradmin/accessgroup/editor/editor.template @@ -0,0 +1,28 @@ + + + +{{FORMACCESS}} + + + + + + + + diff --git a/useradmin/pages/users/accesses/editorgroup/editorgroup.instance b/useradmin/pages/useradmin/profile/editor/editor.en.language similarity index 100% rename from useradmin/pages/users/accesses/editorgroup/editorgroup.instance rename to useradmin/pages/useradmin/profile/editor/editor.en.language diff --git a/useradmin/pages/useradmin/profile/editor/editor.es.language b/useradmin/pages/useradmin/profile/editor/editor.es.language new file mode 100644 index 0000000..52dd961 --- /dev/null +++ b/useradmin/pages/useradmin/profile/editor/editor.es.language @@ -0,0 +1,53 @@ + + + + adminuser/profile/editor::%s - Error: no hay fuente de datos '%s' + adminuser/profile/editor::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + adminuser/profile/editor::%s - Error: la tabla '%s' no existe en el datasource + + Perfil de derechos. + Haz click sobre un renglón para editarlo: + + + + + Información agregada con éxito. Ya puede cerrar esta pantalla. + + Información modificada con éxito. Ya puede cerrar esta pantalla. + + Información borrada con éxito. Ya puede cerrar esta pantalla. + + + Clave + La clave del perfil contiene de 1 a 30 carácteres, en minusculas. Puede usar también cifras, '-' y '_'. Las claves solo pueden empezar por una letra. + La clave no puede estar vacia. + + Nombre + El nomnbre perfil es de 1 hasta 255 carácteres máximo. + El nombre no puede estar vacio. + + Descripción + La descripción es un texto libre, hasta 4000 carácteres. + + Estatus + Puede activar o desactivar un perfil en cualquier momento. Si el perfil esta desactivado, su conjunto de usuarios/derechos es ignorado por el sistema. + Activado + Desactivado + + ¿Borrar tambien los hijos? + Si selecciona Sí, entonces toda informacion relacionada con este perfil será borrada también. Si selecciona No, entonces la información será movida al perfil seleccionado en el campo siguiente. + Sí, borrar los hijos + No, seleccionar el perfil receptor + + Perfil receptor: + Si seleccionó No en el campo anterior, entonces especifique el perfil que recibirá la información relacionada al perfil a borrar. + + Grabar el nuevo perfil + Guardar las modificaciones + Si, borrar este perfil + Reinicinar + Cancelar + + Accesos que usan este perfil: + Usuarios que usan este perfil: + diff --git a/useradmin/pages/users/accesses/editorgroup/editorgroup.page b/useradmin/pages/useradmin/profile/editor/editor.fr.language similarity index 100% rename from useradmin/pages/users/accesses/editorgroup/editorgroup.page rename to useradmin/pages/useradmin/profile/editor/editor.fr.language diff --git a/useradmin/pages/useradmin/profile/editor/editor.go b/useradmin/pages/useradmin/profile/editor/editor.go new file mode 100644 index 0000000..dca5ebb --- /dev/null +++ b/useradmin/pages/useradmin/profile/editor/editor.go @@ -0,0 +1,377 @@ +package main + +import ( + "encoding/xml" + "errors" + "strconv" + "strings" + + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xdominion" + "github.com/webability-go/xdommask" + + // "github.com/webability-go/xamboo/applications" + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" + "github.com/webability-go/xmodules/tools" + userassets "github.com/webability-go/xmodules/user/assets" + "github.com/webability-go/xmodules/user/security" + "github.com/webability-go/xmodules/useradmin/assets" +) + +var language *xcore.XLanguage + +func Run(ctx *context.Context, template *xcore.XTemplate, xlanguage *xcore.XLanguage, e interface{}) interface{} { + + if language == nil { + language = xlanguage + } + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + mode := ctx.Request.Form.Get("mode") + key := ctx.Request.Form.Get("Key") + if key == "" { + key = "new" + } + + params := &xcore.XDataset{ + "FORMPROFILE": createXMLMask("formprofile", mode, ctx), + "KEY": key, + "#": language, + } + + return template.Execute(params) +} + +func createMask(id string, ctx *context.Context) (*xdommask.Mask, error) { + + hooks := xdommask.MaskHooks{ + Build: build, + PreDelete: predelete, + } + return xdommask.NewMask(id, hooks, ctx) +} + +func createXMLMask(id string, mode string, ctx *context.Context) string { + mask, _ := createMask(id, ctx) + cmask := mask.Compile(mode, ctx) + xmlmask, _ := xml.Marshal(cmask) + return string(xmlmask) +} + +func build(mask *xdommask.Mask, ctx *context.Context) error { + + // Check security + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + if ds == nil { + return errors.New(tools.LogMessage(ctx.LoggerError, language, "errordatasource", "build", userassets.DATASOURCE)) + } + + user_profile := ds.GetTable("user_profile") + if user_profile == nil { + return errors.New(tools.LogMessage(ctx.LoggerError, language, "errortable", "build", "user_profile")) + } + + mask.Table = user_profile + key := ctx.Request.Form.Get(mask.VarKey) + if key != "" { + mask.Key = key + } + + mask.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + mask.KeyField = "key" + + mask.AlertMessage = "##mask.errormessage##" + mask.ServerMessage = "##mask.servermessage##" + mask.InsertTitle = "##mask.titleinsert##" + mask.DoInsertMessage = "##mask.titleinsert.message##" + mask.UpdateTitle = "##mask.titleupdate##" + mask.DoUpdateMessage = "##mask.titleupdate.message##" + mask.DeleteTitle = "##mask.titledelete##" + mask.DoDeleteMessage = "##mask.titledelete.message##" + mask.ViewTitle = "##mask.titleview##" + mask.FailureJS = "function(params) { this.icontainer.setMessages(params); }" + + // key + f10 := xdommask.NewTextField("key") + f10.Title = "##key.title##" + f10.HelpDescription = "##key.help.description##" + f10.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f10.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f10.HelpModes = xdommask.INSERT | xdommask.UPDATE + f10.ViewModes = xdommask.DELETE | xdommask.VIEW + f10.StatusNotNull = "##key.status.notnull##" + f10.MaxLength = 30 + f10.Size = "400" + f10.URLVariable = "key" + f10.Format = "^[a-z][a-z0-9-_]{1,29}$" + f10.FormatJS = "^[a-z][a-z0-9-_]{1,29}$" + f10.DefaultValue = "" + mask.AddField(f10) + + // name + f11 := xdommask.NewTextField("name") + f11.Title = "##name.title##" + f11.HelpDescription = "##name.help.description##" + f11.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f11.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f11.HelpModes = xdommask.INSERT | xdommask.UPDATE + f11.ViewModes = xdommask.DELETE | xdommask.VIEW + f11.StatusNotNull = "##name.status.notnull##" + f11.MaxLength = 255 + f11.Size = "400" + f11.URLVariable = "name" + f11.DefaultValue = "" + mask.AddField(f11) + + // description + f12 := xdommask.NewTextAreaField("description") + f12.Title = "##description.title##" + f12.HelpDescription = "##description.help.description##" + f12.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f12.HelpModes = xdommask.INSERT | xdommask.UPDATE + f12.ViewModes = xdommask.DELETE | xdommask.VIEW + f12.MaxLength = 4000 + f12.Width = 400 + f12.Height = 100 + f12.URLVariable = "description" + f12.DefaultValue = "" + mask.AddField(f12) + + // Ask what to delete + f13 := xdommask.NewLOVField("status") + f13.Title = "##status.title##" + f13.HelpDescription = "##status.help.description##" + f13.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f13.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f13.HelpModes = xdommask.INSERT | xdommask.UPDATE + f13.ViewModes = xdommask.DELETE | xdommask.VIEW + f13.Options = map[string]string{ + "1": "##status.yes##", + "2": "##status.no##", + } + f13.URLVariable = "status" + mask.AddField(f13) + + // Ask what to delete + f21 := xdommask.NewLOVField("prune") + f21.Title = "##prune.title##" + f21.HelpDescription = "##prune.help.description##" + f21.NotNullModes = xdommask.DELETE + f21.AuthModes = xdommask.DELETE + f21.HelpModes = xdommask.DELETE + f21.Options = map[string]string{ + "1": "##prune.yes##", + "2": "##prune.no##", + } + f21.CheckJS = "n = WA.$N('useradmin/profile/editor|" + key + "|profiletoprune'); n.domNodeField.disabled = (value==1); n.checkAll();" + // f12.ChangeJS = "function(p) { if (p.id == 'prune') { n = WA.$N('useradmin/profile/editor|" + key + "|grouptoprune'); n.domNodeField.disabled = (p.value==1); n.checkAll(); } }" + f21.URLVariable = "prune" + mask.AddField(f21) + + // profile + f22 := xdommask.NewLOVField("profiletoprune") + f22.Title = "##profiletoprune.title##" + f22.HelpDescription = "##profiletoprune.help.description##" + f22.NotNullModes = xdommask.DELETE + f22.AuthModes = xdommask.DELETE + f22.HelpModes = xdommask.DELETE + f22.Table = user_profile + f22.Conditions = &xdominion.XConditions{xdominion.NewXCondition("key", "!=", key)} + f22.Order = &xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + f22.FieldSet = &xdominion.XFieldSet{"key", "name"} + f22.URLVariable = "profiletoprune" + mask.AddField(f22) + + // Submit + f8 := xdommask.NewButtonField("", "submit") + f8.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE + f8.TitleInsert = "##formsubmit.insert.title##" + f8.TitleUpdate = "##formsubmit.update.title##" + f8.TitleDelete = "##formsubmit.delete.title##" + mask.AddField(f8) + + // Reset + f9 := xdommask.NewButtonField("", "reset") + f9.AuthModes = xdommask.INSERT | xdommask.UPDATE + f9.TitleInsert = "##formreset.title##" + f9.TitleUpdate = "##formreset.title##" + mask.AddField(f9) + + // View + f91 := xdommask.NewButtonField("", "view") + f91.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE + f91.TitleInsert = "##formview.title##" + f91.TitleUpdate = "##formview.title##" + f91.TitleDelete = "##formview.title##" + mask.AddField(f91) + + return nil +} + +func predelete(m *xdommask.Mask, ctx *context.Context, key interface{}, oldrec *xdominion.XRecord, rec *xdominion.XRecord) error { + + skey := key.(string) + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + + prune, _ := rec.GetString("prune") + if prune == "1" { + // delete children + err := userentries.DeleteProfileChildren(ds, skey) + if err != nil { + return err + } + } else { + // prune children + profile, _ := rec.GetString("profiletoprune") + err := userentries.PruneProfileChildren(ds, skey, profile) + if err != nil { + return err + } + } + return nil +} + +func Formprofile(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + mask, _ := createMask("formprofile", ctx) + data, _ := mask.Run(ctx) + return data +} + +func Getaccesses(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + result := []string{} + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + + inaccesses, _ := userentries.GetProfileAccesses(ds, key, 0) + order := xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + accesses := userentries.GetAccessesList(ds, nil, &order, 0, 0) + if accesses == nil || len(*accesses) == 0 { + return "Error" + } + + // make an array of inprofiles + ainaccesses := map[string]bool{} + if inaccesses != nil { + for _, ia := range *inaccesses { + ac, _ := ia.GetString("access") + ainaccesses[ac] = true + } + } + + for _, r := range *accesses { + accesskey, _ := r.GetString("key") + accessname, _ := r.GetString("name") + checked := "" + if ainaccesses[accesskey] { + checked = " checked=\"checked\"" + } + result = append(result, " "+accesskey+" - "+accessname) + } + return strings.Join(result, "
") +} + +func Getusers(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + result := []string{} + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + + inusers, _ := userentries.GetProfileUsers(ds, key, 0) + order := xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + users := userentries.GetUsersList(ds, nil, &order, 0, 0) + if users == nil || len(*users) == 0 { + return "Error" + } + + // make an array of inprofiles + ainusers := map[int]bool{} + if inusers != nil { + for _, ip := range *inusers { + us, _ := ip.GetInt("user") + ainusers[us] = true + } + } + + for _, r := range *users { + userkey, _ := r.GetInt("key") + suserkey, _ := r.GetString("key") + userdata := userentries.GetUserByKey(ds, userkey) + if userdata == nil { + return "Error" + } + username, _ := userdata.GetString("name") + checked := "" + v := ainusers[userkey] + if v { + checked = " checked=\"true\"" + } + result = append(result, " "+suserkey+" - "+username) + } + return strings.Join(result, "
") +} + +func Setaccess(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + access := ctx.Request.Form.Get("id") + checked := ctx.Request.Form.Get("checked") + + userentries.SetProfileAccess(ds, key, access, checked == "true") + + return "{\"status\":\"OK\"}" +} + +func Setuser(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + user := ctx.Request.Form.Get("id") + iuser, _ := strconv.Atoi(user) + checked := ctx.Request.Form.Get("checked") + + userentries.SetUserProfile(ds, iuser, key, checked == "true") + + return "{\"status\":\"OK\"}" +} diff --git a/useradmin/pages/users/accesses/editorgroup/editorgroup.template b/useradmin/pages/useradmin/profile/editor/editor.instance similarity index 100% rename from useradmin/pages/users/accesses/editorgroup/editorgroup.template rename to useradmin/pages/useradmin/profile/editor/editor.instance diff --git a/useradmin/pages/useradmin/profile/editor/editor.page b/useradmin/pages/useradmin/profile/editor/editor.page new file mode 100644 index 0000000..deaecd2 --- /dev/null +++ b/useradmin/pages/useradmin/profile/editor/editor.page @@ -0,0 +1,4 @@ +type=wajafapp +status=published +template= +acceptpathparameters=yes diff --git a/useradmin/pages/useradmin/profile/editor/editor.template b/useradmin/pages/useradmin/profile/editor/editor.template new file mode 100644 index 0000000..1e6b1c7 --- /dev/null +++ b/useradmin/pages/useradmin/profile/editor/editor.template @@ -0,0 +1,99 @@ + + + + + +{{FORMPROFILE}} + + + + + ##accesses.title## + + + + ##users.title## + + + + + + + + + + + + + diff --git a/useradmin/pages/useradmin/profile/profile.en.language b/useradmin/pages/useradmin/profile/profile.en.language new file mode 100644 index 0000000..2e14dfe --- /dev/null +++ b/useradmin/pages/useradmin/profile/profile.en.language @@ -0,0 +1,9 @@ + + + Reload the list + New access + Admin groups of accesses + Accesses for users in the system. + Click on a line to edit the information: + + diff --git a/useradmin/pages/useradmin/profile/profile.es.language b/useradmin/pages/useradmin/profile/profile.es.language new file mode 100644 index 0000000..001c6b7 --- /dev/null +++ b/useradmin/pages/useradmin/profile/profile.es.language @@ -0,0 +1,29 @@ + + + + adminuser/profile::%s - Error: no hay fuente de datos '%s' + adminuser/profile::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + + Recargar la lista + Nuevo perfil + Perfiles de usuarios del sistema. + Haz click sobre un renglón para visualizarlo: + Ver los perfiles desactivados + + Nuevo + Perfil + Ver + Editar + Borrar + + Clave + Estatus + Act. + Desact. + Nombre + Descripción + Accesos + Usuarios + Comandos + + diff --git a/useradmin/pages/useradmin/profile/profile.fr.language b/useradmin/pages/useradmin/profile/profile.fr.language new file mode 100644 index 0000000..98f9413 --- /dev/null +++ b/useradmin/pages/useradmin/profile/profile.fr.language @@ -0,0 +1,9 @@ + + + Recharger la liste + Nouvel accès + Administrer les groupes d'accès + Droits d'accès du système. + Fais clic sur une ligne pour l'éditer: + + diff --git a/useradmin/pages/useradmin/profile/profile.go b/useradmin/pages/useradmin/profile/profile.go new file mode 100644 index 0000000..aa8864d --- /dev/null +++ b/useradmin/pages/useradmin/profile/profile.go @@ -0,0 +1,178 @@ +package main + +import ( + // "fmt" + "strings" + + "encoding/json" + + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xdominion" + + "github.com/webability-go/xamboo/applications" + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" + userassets "github.com/webability-go/xmodules/user/assets" + "github.com/webability-go/xmodules/user/security" + "github.com/webability-go/xmodules/useradmin/assets" +) + +const ( + PROFILE_SHOWALL = "PROFILE_SHOWALL" +) + +func Run(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + showall := userentries.GetUserParam(ds, userkey, PROFILE_SHOWALL) + checked := "" + if showall == "true" { + checked = "checked " + } + + params := &xcore.XDataset{ + "CHECKED": checked, + "#": language, + } + + return template.Execute(params) +} + +func Profiledata(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + var dataarray [][]int + data := ctx.Request.Form.Get("data") + if data != "" { + json.Unmarshal([]byte(data), &dataarray) + } + if dataarray == nil { + dataarray = [][]int{{0, 49}} + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + showall := userentries.GetUserParam(ds, userkey, PROFILE_SHOWALL) + + filter := xdominion.XConditions{xdominion.NewXCondition("key", "!=", "")} + if showall != "true" { + filter = append(filter, xdominion.NewXCondition("status", "=", 1, "and")) + } + order := xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + + total := userentries.GetProfilesCount(ds, &filter) + + result := map[string]interface{}{ + "total": total, + "row": map[int]xdominion.XRecordDef{}, + "subtotal": 0.1, + "time": 0.1, + "subtime": 0.1, + } + + for _, rg := range dataarray { + row := rg[0] + + profiles := userentries.GetProfilesList(ds, &filter, &order, rg[1]-rg[0]+1, rg[0]) + if profiles != nil { + for _, profile := range *profiles { + key, _ := profile.GetString("key") + name, _ := profile.GetString("name") + status, _ := profile.GetString("status") + sstatus := "" + if status == "1" { + sstatus = language.Get("status.yes") + } else { + sstatus = language.Get("status.no") + } + accesses := searchAccesses(ctx, ds, key) + users := searchUsers(ctx, ds, key) + + update := "" + delete := "" + if key[0] != '_' { + update = "" + delete = "" + } + + rec := &xdominion.XRecord{ + "key": key, + "status": sstatus, + "name": name, + "accesses": accesses, + "users": users, + "commands": " " + update + " " + delete, + } + + result["row"].(map[int]xdominion.XRecordDef)[row] = rec + row++ + } + } + } + + return result +} + +func Filter(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + checked := ctx.Request.Form.Get("checked") + userentries.SetUserParam(ds, userkey, PROFILE_SHOWALL, checked) + + return "OK" +} + +func searchAccesses(ctx *context.Context, ds applications.Datasource, key string) string { + + userentries := userassets.GetEntries(ctx) + recs, err := userentries.GetProfileAccesses(ds, key, 10) + if recs == nil || err != nil || len(*recs) == 0 { + return "--" + } + result := []string{} + for _, r := range *recs { + accesskey, _ := r.GetString("access") + result = append(result, "["+accesskey+"]") + } + return strings.Join(result, " ") +} + +func searchUsers(ctx *context.Context, ds applications.Datasource, key string) string { + + userentries := userassets.GetEntries(ctx) + recs, err := userentries.GetProfileUsers(ds, key, 10) + if recs == nil || err != nil || len(*recs) == 0 { + return "--" + } + result := []string{} + for _, r := range *recs { + userkey, _ := r.GetInt("user") + userdata := userentries.GetUserByKey(ds, userkey) + username, _ := userdata.GetString("name") + result = append(result, "["+username+"]") + } + return strings.Join(result, " ") +} diff --git a/useradmin/pages/users/users.instance b/useradmin/pages/useradmin/profile/profile.instance similarity index 100% rename from useradmin/pages/users/users.instance rename to useradmin/pages/useradmin/profile/profile.instance diff --git a/useradmin/pages/useradmin/profile/profile.page b/useradmin/pages/useradmin/profile/profile.page new file mode 100644 index 0000000..deaecd2 --- /dev/null +++ b/useradmin/pages/useradmin/profile/profile.page @@ -0,0 +1,4 @@ +type=wajafapp +status=published +template= +acceptpathparameters=yes diff --git a/useradmin/pages/useradmin/profile/profile.template b/useradmin/pages/useradmin/profile/profile.template new file mode 100644 index 0000000..f6a9664 --- /dev/null +++ b/useradmin/pages/useradmin/profile/profile.template @@ -0,0 +1,96 @@ + + + + + + + ##title##
+ ##hint##
+ + ##showall## + + + ]]>
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
diff --git a/useradmin/pages/useradmin/user/editor/editor.en.language b/useradmin/pages/useradmin/user/editor/editor.en.language new file mode 100644 index 0000000..e69de29 diff --git a/useradmin/pages/useradmin/user/editor/editor.es.language b/useradmin/pages/useradmin/user/editor/editor.es.language new file mode 100644 index 0000000..c78917c --- /dev/null +++ b/useradmin/pages/useradmin/user/editor/editor.es.language @@ -0,0 +1,74 @@ + + + + adminuser/user/editor::%s - Error: no hay fuente de datos '%s' + adminuser/user/editor::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + adminuser/user/editor::%s - Error: la tabla '%s' no existe en el datasource + + Edición del usuario administrador. + Haz click sobre un renglón para editarlo: + + + + + Información agregada con éxito. Ya puede cerrar esta pantalla. + + Información modificada con éxito. Ya puede cerrar esta pantalla. + + Información borrada con éxito. Ya puede cerrar esta pantalla. + + + Clave: + La clave del usuario adiministrador es un entero auto incrementado y manejado por el sistema. + Asignación automática + + Estatus: + El estatus del usuario administrador puede ser uno de los tres valores: A = Activado, es sujeto al filtro de seguridad de derechos de acceso y perfile. S = Super usuario, tiene por defecto todos los derechos de acceso del sistema. X = Desactivado. No tendrá ningún acceso, ni de conectarse. + El estatus no puede estar vacio. + Activado + Super usuario + Desactivado + + Nombre: + El nombre del usuario es de 1 hasta 200 carácteres máximo. + El nombre no puede estar vacio. + + Usuario de acceso: + El usuario de acceso se usará como usuario para conectarte, es de 1 hasta 200 carácteres máximo. Puede ser el correo electrónico del usuario también. + El usuario de acceso no puede estar vacio. + + Contraseña: + Es la contraseña para conectarse que va de par con el usuario de acceso, es de 1 hasta 200 carácteres máximo. + La contraseña no puede estar vacia. + + Correo electrónico: + El nombre del usuario es de 1 hasta 200 carácteres máximo. + El nombre no puede estar vacio. + + Sexo: + El sexo del usuario, feminino o masculino. + El sexo no puede estar vacio. + Masculino + Feminino + + Campos de datos: + Los campos de datos se ponen un por linea con el formato Paramtro=Valor. + + Información extra: + Puede capturar aqui cualquier información complementaria sobre el usuario. + + Usuario responsable: + Puede asignar otro usuario responsabe de este usuario. El usuario responsable puede administrar sus derechos y sus datos. + + Grabar el nuevo usuario + Guardar las modificaciones + Sí, borrar este usuario + Reinicinar + Cancelar + + Perfiles suscritos por este usuario: + Accessos otorgados a este usuario: + Heredado + Otorgado + Denegado + diff --git a/useradmin/pages/useradmin/user/editor/editor.fr.language b/useradmin/pages/useradmin/user/editor/editor.fr.language new file mode 100644 index 0000000..e69de29 diff --git a/useradmin/pages/useradmin/user/editor/editor.go b/useradmin/pages/useradmin/user/editor/editor.go new file mode 100644 index 0000000..058fb16 --- /dev/null +++ b/useradmin/pages/useradmin/user/editor/editor.go @@ -0,0 +1,458 @@ +package main + +import ( + "encoding/xml" + "errors" + "fmt" + "strconv" + "strings" + "time" + + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xdominion" + "github.com/webability-go/xdommask" + + // "github.com/webability-go/xamboo/applications" + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" + "github.com/webability-go/xmodules/tools" + userassets "github.com/webability-go/xmodules/user/assets" + "github.com/webability-go/xmodules/user/security" + "github.com/webability-go/xmodules/useradmin/assets" +) + +var language *xcore.XLanguage + +func Run(ctx *context.Context, template *xcore.XTemplate, xlanguage *xcore.XLanguage, e interface{}) interface{} { + + if language == nil { + language = xlanguage + } + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + mode := ctx.Request.Form.Get("mode") + key := ctx.Request.Form.Get("Key") + if key == "" { + key = "new" + } + + params := &xcore.XDataset{ + "FORMUSER": createXMLMask("formuser", mode, ctx), + "KEY": key, + "#": language, + } + + return template.Execute(params) +} + +func createMask(id string, ctx *context.Context) (*xdommask.Mask, error) { + + hooks := xdommask.MaskHooks{ + Build: build, + PreInsert: preinsert, + PreUpdate: preupdate, + PreDelete: predelete, + } + return xdommask.NewMask(id, hooks, ctx) +} + +func createXMLMask(id string, mode string, ctx *context.Context) string { + mask, _ := createMask(id, ctx) + cmask := mask.Compile(mode, ctx) + xmlmask, _ := xml.Marshal(cmask) + return string(xmlmask) +} + +func build(mask *xdommask.Mask, ctx *context.Context) error { + + // Check security + ds := base.TryDatasource(ctx, assets.DATASOURCE) + if ds == nil { + return errors.New(tools.LogMessage(ctx.LoggerError, language, "errordatasource", "build", assets.DATASOURCE)) + } + + user_user := ds.GetTable("user_user") + if user_user == nil { + return errors.New(tools.LogMessage(ctx.LoggerError, language, "errortable", "build", "user_user")) + } + + mask.Table = user_user + key := ctx.Request.Form.Get(mask.VarKey) + if key != "" { + mask.Key = key + } + + mask.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + mask.KeyField = "key" + + mask.AlertMessage = "##mask.errormessage##" + mask.ServerMessage = "##mask.servermessage##" + mask.InsertTitle = "##mask.titleinsert##" + mask.DoInsertMessage = "##mask.titleinsert.message##" + mask.UpdateTitle = "##mask.titleupdate##" + mask.DoUpdateMessage = "##mask.titleupdate.message##" + mask.DeleteTitle = "##mask.titledelete##" + mask.DoDeleteMessage = "##mask.titledelete.message##" + mask.ViewTitle = "##mask.titleview##" + mask.FailureJS = "function(params) { this.icontainer.setMessages(params); }" + + // key + f10 := xdommask.NewIntegerField("key") + f10.Title = "##key.title##" + f10.HelpDescription = "##key.help.description##" + f10.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f10.HelpModes = xdommask.INSERT | xdommask.UPDATE + f10.ViewModes = xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f10.Auto = true + f10.AutoMessage = "##key.auto##" + f10.Size = "400" + f10.URLVariable = "key" + f10.DefaultValue = 0 + mask.AddField(f10) + + // user status + f11 := xdommask.NewLOVField("status") + f11.Title = "##status.title##" + f11.HelpDescription = "##status.help.description##" + f11.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f11.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f11.HelpModes = xdommask.INSERT | xdommask.UPDATE + f11.ViewModes = xdommask.DELETE | xdommask.VIEW + f11.StatusNotNull = "##status.status.notnull##" + f11.Options = map[string]string{ + "A": "A / ##status.active##", + "S": "S / ##status.superuser##", + "X": "X / ##status.down##", + } + f11.URLVariable = "status" + f11.DefaultValue = "A" + mask.AddField(f11) + + // name + f12 := xdommask.NewTextField("name") + f12.Title = "##name.title##" + f12.HelpDescription = "##name.help.description##" + f12.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f12.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f12.HelpModes = xdommask.INSERT | xdommask.UPDATE + f12.ViewModes = xdommask.DELETE | xdommask.VIEW + f12.StatusNotNull = "##name.status.notnull##" + f12.MaxLength = 200 + f12.Size = "400" + f12.URLVariable = "name" + f12.DefaultValue = "" + mask.AddField(f12) + + // username + f13 := xdommask.NewTextField("un") + f13.Title = "##un.title##" + f13.HelpDescription = "##un.help.description##" + f13.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f13.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f13.HelpModes = xdommask.INSERT | xdommask.UPDATE + f13.ViewModes = xdommask.DELETE | xdommask.VIEW + f13.StatusNotNull = "##un.status.notnull##" + f13.MaxLength = 200 + f13.Size = "400" + f13.URLVariable = "un" + f13.DefaultValue = "" + mask.AddField(f13) + + // password + f14 := xdommask.NewMaskedField("pw") + f14.Title = "##pw.title##" + f14.HelpDescription = "##pw.help.description##" + f14.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f14.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f14.HelpModes = xdommask.INSERT | xdommask.UPDATE + f14.ViewModes = xdommask.DELETE | xdommask.VIEW + f14.StatusNotNull = "##pw.status.notnull##" + f14.MaxLength = 200 + f14.Size = "400" + f14.URLVariable = "pw" + f14.DefaultValue = "" + mask.AddField(f14) + + // mail + f15 := xdommask.NewTextField("mail") + f15.Title = "##mail.title##" + f15.HelpDescription = "##mail.help.description##" + f15.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f15.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f15.HelpModes = xdommask.INSERT | xdommask.UPDATE + f15.ViewModes = xdommask.DELETE | xdommask.VIEW + f15.StatusNotNull = "##mail.status.notnull##" + f15.MaxLength = 255 + f15.Size = "400" + f15.URLVariable = "mail" + f15.DefaultValue = "" + mask.AddField(f15) + + // gender + f16 := xdommask.NewLOVField("sex") + f16.Title = "##sex.title##" + f16.HelpDescription = "##sex.help.description##" + f16.NotNullModes = xdommask.INSERT | xdommask.UPDATE + f16.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f16.HelpModes = xdommask.INSERT | xdommask.UPDATE + f16.ViewModes = xdommask.DELETE | xdommask.VIEW + f16.StatusNotNull = "##sex.status.notnull##" + f16.Options = map[string]string{ + "M": "##sex.masc##", + "F": "##sex.fem##", + } + f16.URLVariable = "sex" + f16.DefaultValue = "M" + mask.AddField(f16) + + // fields + f17 := xdommask.NewTextAreaField("fields") + f17.Title = "##fields.title##" + f17.HelpDescription = "##fields.help.description##" + f17.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f17.HelpModes = xdommask.INSERT | xdommask.UPDATE + f17.ViewModes = xdommask.DELETE | xdommask.VIEW + f17.MaxLength = 4000 + f17.Width = 400 + f17.Height = 150 + f17.URLVariable = "fields" + f17.DefaultValue = "" + mask.AddField(f17) + + // info + f18 := xdommask.NewTextAreaField("info") + f18.Title = "##info.title##" + f18.HelpDescription = "##info.help.description##" + f18.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f18.HelpModes = xdommask.INSERT | xdommask.UPDATE + f18.ViewModes = xdommask.DELETE | xdommask.VIEW + f18.MaxLength = 4000 + f18.Width = 400 + f18.Height = 150 + f18.URLVariable = "info" + f18.DefaultValue = "" + mask.AddField(f18) + + // father, responsible + f19 := xdommask.NewLOVField("father") + f19.Title = "##father.title##" + f19.HelpDescription = "##father.help.description##" + f19.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE | xdommask.VIEW + f19.HelpModes = xdommask.INSERT | xdommask.UPDATE + f19.ViewModes = xdommask.DELETE | xdommask.VIEW + f19.Table = user_user + if key != "" { + f19.Conditions = &xdominion.XConditions{xdominion.NewXCondition("key", "!=", key)} + } + f19.Order = &xdominion.XOrder{xdominion.NewXOrderBy("name", xdominion.ASC)} + f19.FieldSet = &xdominion.XFieldSet{"key", "name"} + f19.NullOnEmpty = true + f19.URLVariable = "father" + mask.AddField(f19) + + // Submit + f8 := xdommask.NewButtonField("", "submit") + f8.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE + f8.TitleInsert = "##formsubmit.insert.title##" + f8.TitleUpdate = "##formsubmit.update.title##" + f8.TitleDelete = "##formsubmit.delete.title##" + mask.AddField(f8) + + // Reset + f9 := xdommask.NewButtonField("", "reset") + f9.AuthModes = xdommask.INSERT | xdommask.UPDATE + f9.TitleInsert = "##formreset.title##" + f9.TitleUpdate = "##formreset.title##" + mask.AddField(f9) + + // View + f91 := xdommask.NewButtonField("", "view") + f91.AuthModes = xdommask.INSERT | xdommask.UPDATE | xdommask.DELETE + f91.TitleInsert = "##formview.title##" + f91.TitleUpdate = "##formview.title##" + f91.TitleDelete = "##formview.title##" + mask.AddField(f91) + + return nil +} + +func preinsert(m *xdommask.Mask, ctx *context.Context, rec *xdominion.XRecord) error { + rec.Set("creationdate", time.Now()) + rec.Set("lastmodif", time.Now()) + return nil +} + +func preupdate(m *xdommask.Mask, ctx *context.Context, key interface{}, oldrec *xdominion.XRecord, newrec *xdominion.XRecord) error { + newrec.Set("lastmodif", time.Now()) + return nil +} + +func predelete(m *xdommask.Mask, ctx *context.Context, key interface{}, oldrec *xdominion.XRecord, rec *xdominion.XRecord) error { + ikey := key.(int) + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, assets.DATASOURCE) + + // delete children + err := userentries.DeleteUserChildren(ds, ikey) + return err +} + +func Formuser(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + mask, _ := createMask("formuser", ctx) + data, _ := mask.Run(ctx) + return data +} + +func Getprofiles(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + result := []string{} + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + ikey, _ := strconv.Atoi(key) + + inprofiles, err := userentries.GetUserProfiles(ds, ikey, 10) + fmt.Println("IN PROFILES:", key, ikey, inprofiles, err) + order := xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + profiles := userentries.GetProfilesList(ds, nil, &order, 0, 0) + if profiles == nil || len(*profiles) == 0 { + return "Error" + } + + // make an array of inprofiles + ainprofiles := map[string]bool{} + if inprofiles != nil { + for _, ip := range *inprofiles { + pr, _ := ip.GetString("profile") + ainprofiles[pr] = true + } + } + fmt.Println("A IN PROFILES:", ainprofiles) + for _, r := range *profiles { + profilekey, _ := r.GetString("key") + profilename, _ := r.GetString("name") + checked := "" + if ainprofiles[profilekey] { + checked = " checked=\"checked\"" + } + result = append(result, " "+profilekey+" - "+profilename) + } + return strings.Join(result, "
") +} + +func Getaccesses(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + result := []string{} + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + ikey, _ := strconv.Atoi(key) + + inusers, _ := userentries.GetUserAccesses(ds, ikey, 0) + order := xdominion.XOrder{xdominion.NewXOrderBy("key", xdominion.ASC)} + accesses := userentries.GetAccessesList(ds, nil, &order, 0, 0) + if accesses == nil || len(*accesses) == 0 { + return "Error" + } + + // make an array of inprofiles + ainusers := map[string]int{} + if inusers != nil { + for _, ip := range *inusers { + ac, _ := ip.GetString("access") + denied, _ := ip.GetInt("denied") + ainusers[ac] = denied + } + } + + bydefault := language.Get("accesses.hierarchy") + authorized := language.Get("accesses.yes") + denied := language.Get("accesses.no") + for _, r := range *accesses { + accesskey, _ := r.GetString("key") + accessdata := userentries.GetAccessByKey(ds, accesskey) + if accessdata == nil { + return "Error" + } + accessname, _ := accessdata.GetString("name") + checked1 := "" + checked2 := "" + checked3 := "" + v, ok := ainusers[accesskey] + if !ok { + checked1 = " checked=\"true\"" + } else if v == 0 { + checked2 = " checked=\"true\"" + } else { // v == 1 + checked3 = " checked=\"true\"" + } + result = append(result, " "+bydefault+ + " "+authorized+ + " "+denied+" | "+ + " "+accesskey+" - "+accessname) + } + return strings.Join(result, "
") +} + +func Setprofile(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + key := ctx.Request.Form.Get("key") + ikey, _ := strconv.Atoi(key) + profile := ctx.Request.Form.Get("id") + checked := ctx.Request.Form.Get("checked") + + userentries.SetUserProfile(ds, ikey, profile, checked == "true") + + return "{\"status\":\"OK\"}" +} + +func Setaccess(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + user := ctx.Request.Form.Get("key") + iuser, _ := strconv.Atoi(user) + access := ctx.Request.Form.Get("id") + val := ctx.Request.Form.Get("val") + ival, _ := strconv.Atoi(val) + + userentries.SetUserAccess(ds, iuser, access, ival) + + return "{\"status\":\"OK\"}" +} diff --git a/useradmin/pages/useradmin/user/editor/editor.instance b/useradmin/pages/useradmin/user/editor/editor.instance new file mode 100644 index 0000000..e69de29 diff --git a/useradmin/pages/useradmin/user/editor/editor.page b/useradmin/pages/useradmin/user/editor/editor.page new file mode 100644 index 0000000..deaecd2 --- /dev/null +++ b/useradmin/pages/useradmin/user/editor/editor.page @@ -0,0 +1,4 @@ +type=wajafapp +status=published +template= +acceptpathparameters=yes diff --git a/useradmin/pages/useradmin/user/editor/editor.template b/useradmin/pages/useradmin/user/editor/editor.template new file mode 100644 index 0000000..662b3af --- /dev/null +++ b/useradmin/pages/useradmin/user/editor/editor.template @@ -0,0 +1,98 @@ + + + + + +{{FORMUSER}} + + + + + ##profiles.title## + + + + ##accesses.title## + + + + + + + + + + + + + diff --git a/useradmin/pages/useradmin/user/user.en.language b/useradmin/pages/useradmin/user/user.en.language new file mode 100644 index 0000000..ee406c7 --- /dev/null +++ b/useradmin/pages/useradmin/user/user.en.language @@ -0,0 +1,9 @@ + + + Reload the list + New access + Admin groups of accesses + Accesses for users in the system. + Click on a line to edit the information: + + diff --git a/useradmin/pages/useradmin/user/user.es.language b/useradmin/pages/useradmin/user/user.es.language new file mode 100644 index 0000000..0625b9a --- /dev/null +++ b/useradmin/pages/useradmin/user/user.es.language @@ -0,0 +1,29 @@ + + + + adminuser/user::%s - Error: no hay fuente de datos '%s' + adminuser/user::%s - Error: el usuario '%d' no tiene acceso a esta pantalla + + Recargar la lista + Nuevo usuario + Usuarios del sistema. + Haz click sobre un renglón para visualizarlo: + Ver los usuarios desactivados + + Nuevo + Usuario + Ver + Editar + Borrar + + Clave + Estatus + Act. + Desact. + Nombre + Descripción + Accesos + Perfiles + Comandos + + diff --git a/useradmin/pages/useradmin/user/user.fr.language b/useradmin/pages/useradmin/user/user.fr.language new file mode 100644 index 0000000..c9ed6b1 --- /dev/null +++ b/useradmin/pages/useradmin/user/user.fr.language @@ -0,0 +1,9 @@ + + + Recharger la liste + Nouvel accès + Administrer les groupes d'accès + Droits d'accès du système. + Fais clic sur une ligne pour l'éditer: + + diff --git a/useradmin/pages/useradmin/user/user.go b/useradmin/pages/useradmin/user/user.go new file mode 100644 index 0000000..74b40d6 --- /dev/null +++ b/useradmin/pages/useradmin/user/user.go @@ -0,0 +1,170 @@ +package main + +import ( + "encoding/json" + "strings" + + "github.com/webability-go/xcore/v2" + "github.com/webability-go/xdominion" + + "github.com/webability-go/xamboo/applications" + "github.com/webability-go/xamboo/cms/context" + + "github.com/webability-go/xmodules/base" + userassets "github.com/webability-go/xmodules/user/assets" + "github.com/webability-go/xmodules/user/security" + "github.com/webability-go/xmodules/useradmin/assets" +) + +const ( + USER_SHOWALL = "USER_SHOWALL" +) + +func Run(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + showall := userentries.GetUserParam(ds, userkey, USER_SHOWALL) + checked := "" + if showall == "true" { + checked = "checked " + } + + params := &xcore.XDataset{ + "CHECKED": checked, + "#": language, + } + + return template.Execute(params) +} + +func Userdata(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + var dataarray [][]int + data := ctx.Request.Form.Get("data") + if data != "" { + json.Unmarshal([]byte(data), &dataarray) + } + if dataarray == nil { + dataarray = [][]int{{0, 49}} + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + showall := userentries.GetUserParam(ds, userkey, USER_SHOWALL) + + filter := xdominion.XConditions{xdominion.NewXCondition("key", "!=", 0)} + if showall != "true" { + filter = append(filter, xdominion.NewXCondition("status", "in", "('A', 'S')", "and")) + } + order := xdominion.XOrder{xdominion.NewXOrderBy("name", xdominion.ASC)} + + total := userentries.GetUsersCount(ds, &filter) + + result := map[string]interface{}{ + "total": total, + "row": map[int]xdominion.XRecordDef{}, + "subtotal": 0.1, + "time": 0.1, + "subtime": 0.1, + } + + for _, rg := range dataarray { + row := rg[0] + + users := userentries.GetUsersList(ds, &filter, &order, rg[1]-rg[0]+1, rg[0]) + if users != nil { + for _, user := range *users { + key, _ := user.GetInt("key") + skey, _ := user.GetString("key") + name, _ := user.GetString("name") + status, _ := user.GetString("status") + accesses := searchAccesses(ctx, ds, key) + profiles := searchProfiles(ctx, ds, key) + + update := "" + delete := "" + + rec := &xdominion.XRecord{ + "key": key, + "status": status, + "name": name, + "accesses": accesses, + "profiles": profiles, + "commands": " " + update + " " + delete, + } + + result["row"].(map[int]xdominion.XRecordDef)[row] = rec + row++ + } + } + } + + return result +} + +func Filter(ctx *context.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { + + ok := security.Verify(ctx, security.USER, assets.ACCESS) + if !ok { + return "" + } + + userentries := userassets.GetEntries(ctx) + ds := base.TryDatasource(ctx, userassets.DATASOURCE) + userkey, _ := ctx.Sessionparams.GetInt("userkey") + + checked := ctx.Request.Form.Get("checked") + userentries.SetUserParam(ds, userkey, USER_SHOWALL, checked) + + return "OK" +} + +func searchAccesses(ctx *context.Context, ds applications.Datasource, key int) string { + + userentries := userassets.GetEntries(ctx) + recs, err := userentries.GetUserAccesses(ds, key, 10) + if recs == nil || err != nil || len(*recs) == 0 { + return "--" + } + result := []string{} + for _, r := range *recs { + accesskey, _ := r.GetString("access") + denied, _ := r.GetInt("denied") + if denied == 1 { + result = append(result, "["+accesskey+"]") + } else { + result = append(result, "["+accesskey+"]") + } + } + return strings.Join(result, "") +} + +func searchProfiles(ctx *context.Context, ds applications.Datasource, key int) string { + + userentries := userassets.GetEntries(ctx) + recs, err := userentries.GetUserProfiles(ds, key, 10) + if recs == nil || err != nil || len(*recs) == 0 { + return "--" + } + result := []string{} + for _, r := range *recs { + profilekey, _ := r.GetString("profile") + result = append(result, "["+profilekey+"]") + } + return strings.Join(result, "") +} diff --git a/useradmin/pages/useradmin/user/user.instance b/useradmin/pages/useradmin/user/user.instance new file mode 100644 index 0000000..e69de29 diff --git a/useradmin/pages/useradmin/user/user.page b/useradmin/pages/useradmin/user/user.page new file mode 100644 index 0000000..deaecd2 --- /dev/null +++ b/useradmin/pages/useradmin/user/user.page @@ -0,0 +1,4 @@ +type=wajafapp +status=published +template= +acceptpathparameters=yes diff --git a/useradmin/pages/useradmin/user/user.template b/useradmin/pages/useradmin/user/user.template new file mode 100644 index 0000000..a019264 --- /dev/null +++ b/useradmin/pages/useradmin/user/user.template @@ -0,0 +1,96 @@ + + + + + + + ##title##
+ ##hint##
+ + ##showall## + + + ]]>
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
diff --git a/useradmin/pages/users/accesses/accesses.es.language b/useradmin/pages/users/accesses/accesses.es.language deleted file mode 100644 index 080b4da..0000000 --- a/useradmin/pages/users/accesses/accesses.es.language +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - Clave - Nombre / Correo - Perfiles - Usuarios - Comandos - diff --git a/useradmin/pages/users/accesses/accesses.go b/useradmin/pages/users/accesses/accesses.go deleted file mode 100644 index c2e9705..0000000 --- a/useradmin/pages/users/accesses/accesses.go +++ /dev/null @@ -1,269 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/webability-go/xamboo/assets" - "github.com/webability-go/xcore/v2" - "github.com/webability-go/xdominion" - "github.com/webability-go/xmodules/user/bridge" -) - -func Run(ctx *assets.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { - - ok := bridge.Setup(ctx, "", bridge.USER) - if !ok { - return "" - } - - params := &xcore.XDataset{ - "#": language, - } - - return template.Execute(params) -} - -func Accessescontainer(ctx *assets.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { - - ok := bridge.Setup(ctx, "", bridge.USER) - if !ok { - return "" - } - - // init := time.Now() - jsondata := ctx.Request.Form.Get("data") - var data [][]int - if jsondata != "" { - err := json.Unmarshal([]byte(jsondata), &data) - if err != nil { - fmt.Println("ARRAY ERROR:", err) - } - } - - if data == nil { - data = [][]int{{0, 49}} - } - - t1 := time.Now() - total := bridge.ModuleUser.GetAccessesCount(ctx, nil) - t2 := time.Now() - - result := map[string]interface{}{ - "total": total, - "subtimetotal": t2.Sub(t1), - } - - // We load ALL the groups AND accesses - - for _, set := range data { - min := set[0] - max := set[1] - - t1 = time.Now() - accesses := bridge.ModuleUser.GetAccessesList(ctx, nil, &xdominion.XOrderBy{Field: "name", Operator: xdominion.ASC}, max-min+1, min) - t2 = time.Now() - result["subtime"] = t2.Sub(t1) - - if accesses != nil { - num := min - for _, access := range *accesses { - profiles := "profiles" - users := "users" - buttons := "buttons" - - fmt.Println(num, access, profiles, users, buttons) - num++ - } - - /* - array('clave' => $entrada->clave, - 'nombre' => $entrada->nombre, - 'perfiles' => $perfiles, - 'usuarios' => $usuarios, - 'comandos' => $botones - ); - */ - - } - } - fmt.Println("RESULT:", result) - return "DATA" - /* - - // condicionar por orden y lista - foreach($data as $set) - { - - if ($entradas) - { - $turn = true; - foreach($entradas as $entrada) - { - // Busca perfiles con este derechos - $perfiles = $this->searchperfiles($entrada->clave); - $usuarios = $this->searchusuarios($entrada->clave); - - $botones = << - - EOF; - $rec['row'][$num++] = array('clave' => $entrada->clave, - 'nombre' => $entrada->nombre, - 'perfiles' => $perfiles, - 'usuarios' => $usuarios, - 'comandos' => $botones - ); - $turn = !$turn; - } - } - } - // flag "fullload" is not set since we load only a part of the dynamic data - $end = microtime(true); - $rec['time'] = $end - $init; - - return $rec; - */ -} - -/* - public function contenedoresdata() - { - $init = microtime(true); - $data = null; - if (isset($_POST['data'])) - $data = json_decode($_POST['data']); - - if (!$data) - { - $data = array(array(0, 49)); - } - - $t1 = microtime(true); - $total = $this->usuarioEntity->countAdminDerecho(null); - $t2 = microtime(true); - $rec = array('total' => $total, 'row' => array()); - $rec['subtimetotal'] = $t2 - $t1; - - // condicionar por orden y lista - foreach($data as $set) - { - $min = $set[0]; - $max = $set[1]; - - $t1 = microtime(true); - $entradas = $this->usuarioEntity->readAdminDerecho(null, new \dominion\DB_OrderBy('nombre', \dominion\DB_OrderBy::ASC), $max-$min+1, $min); - $t2 = microtime(true); - $rec['subtime'] = $t2 - $t1; - $num = $set[0]; - - if ($entradas) - { - $turn = true; - foreach($entradas as $entrada) - { - // Busca perfiles con este derechos - $perfiles = $this->searchperfiles($entrada->clave); - $usuarios = $this->searchusuarios($entrada->clave); - - $botones = << - -EOF; - $rec['row'][$num++] = array('clave' => $entrada->clave, - 'nombre' => $entrada->nombre, - 'perfiles' => $perfiles, - 'usuarios' => $usuarios, - 'comandos' => $botones - ); - $turn = !$turn; - } - } - } - // flag "fullload" is not set since we load only a part of the dynamic data - $end = microtime(true); - $rec['time'] = $end - $init; - - return $rec; - } - - private function searchperfiles($key) - { - $resultado = array(); - $recs = $this->usuarioEntity->selectAdminperfilderecho(array(new \dominion\DB_Condition('derecho', '=', $key)), new \dominion\DB_OrderBy('perfil', \dominion\DB_OrderBy::ASC)); - if ($recs) - { - foreach($recs as $rec) - { - $perfil = $this->usuarioEntity->selectAdminperfil($rec->perfil); - $resultado[] = '[' . $perfil->clave.']'; - } - } - if (!$resultado) - return '--'; - - return implode(', ', $resultado); - } - - private function searchusuarios($key) - { - $resultado = array(); - $usuarios = array(); - - // usuarios directos con este derecho SI/NO - $recs = $this->usuarioEntity->selectAdminderechousuario(array(new \dominion\DB_Condition('derecho', '=', $key)), new \dominion\DB_OrderBy('usuario', \dominion\DB_OrderBy::ASC)); - if ($recs) - { - foreach($recs as $rec) - { - $usuario = $this->usuarioEntity->selectUsuario($rec->usuario); - if ($usuario->estatus == 'X') - continue; - if ($rec->estatus == 1) - $color = 'green'; - elseif ($rec->estatus == 2) - $color = 'red'; - else - $color = 'black'; - - $usuarios[$usuario->nombre] = $color; - } - } - - // usuarios indirectos a traves del perfil - $recs = $this->usuarioEntity->selectAdminperfilderecho(array(new \dominion\DB_Condition('derecho', '=', $key)), new \dominion\DB_OrderBy('perfil', \dominion\DB_OrderBy::ASC)); - if ($recs) - { - foreach($recs as $rec) - { - $subrecs = $this->usuarioEntity->selectAdminPerfilUsuario(array(new \dominion\DB_Condition('perfil', '=', $rec->perfil)), new \dominion\DB_OrderBy('usuario', \dominion\DB_OrderBy::ASC)); - if ($subrecs) - { - foreach($subrecs as $subrec) - { - $usuario = $this->usuarioEntity->selectUsuario($subrec->usuario); - if ($usuario->estatus == 'X') - continue; - if ($rec->estatus == 1) - $color = 'green'; - elseif ($rec->estatus == 2) - $color = 'red'; - else - $color = 'black'; - $usuarios[$usuario->nombre] = $color; - } - } - } - } - - if (!$usuarios) - return '--'; - - foreach($usuarios as $usuario => $color) - $resultado[] = '[' . $usuario.']'; - - return implode(', ', $resultado); - } - -*/ diff --git a/useradmin/pages/users/accesses/accesses.template b/useradmin/pages/users/accesses/accesses.template deleted file mode 100644 index a827ed3..0000000 --- a/useradmin/pages/users/accesses/accesses.template +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - ##maintitle##
- ##filtertitle##
- - - - - - ]]>
-
- - - - - - - - - - - - - - - - - - - - - - -
- - - -
diff --git a/useradmin/pages/users/accesses/editorgroup/editorgroup.es.language b/useradmin/pages/users/accesses/editorgroup/editorgroup.es.language deleted file mode 100644 index ca9ccab..0000000 --- a/useradmin/pages/users/accesses/editorgroup/editorgroup.es.language +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Clave - Nombre / Correo - Perfiles - Usuarios - - diff --git a/useradmin/pages/users/users.es.language b/useradmin/pages/users/users.es.language deleted file mode 100644 index a07a18d..0000000 --- a/useradmin/pages/users/users.es.language +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - Insertar un nuevo usuario - Ver solamente los activos - - Clave - Estatus - Nombre / Correo - Perfiles - Derechos - Comandos - - diff --git a/useradmin/pages/users/users.go b/useradmin/pages/users/users.go deleted file mode 100644 index 1a7f5f4..0000000 --- a/useradmin/pages/users/users.go +++ /dev/null @@ -1,298 +0,0 @@ -package main - -import ( - "github.com/webability-go/xcore/v2" - - // We include the master/app bridge, where the xmodules are supposed to live - // You may change this to you own APP and bridge upon needs - "github.com/webability-go/xamboo/applications/xmodules/app/bridge" - "github.com/webability-go/xamboo/assets" -) - -func Run(ctx *assets.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { - - bridge.Setup(ctx) - - // bridge.EntityLog_LogStat(ctx) - params := &xcore.XDataset{ - "#": language, - } - - return template.Execute(params) -} - -func Userslist(ctx *assets.Context, template *xcore.XTemplate, language *xcore.XLanguage, e interface{}) interface{} { - - bridge.Setup(ctx) - - init := time.Now() - data := [][]int{} - fdata := ctx.Request.Form.Get("data") - if fdata != "" { - json.Unmarshal([]byte(fdata), &data) - } - if len(data) == 0 { - data = append(data, []int{0, 49}) - } - - query := "" - filtroestatus := bridge.GetUserParamInt("filtroestatus") - if filtroestatus == 1 { - query = array(new \dominion\DB_Condition('estatus', 'in', "('A', 'S')")); - } - - t1 := time.Now(); - totalusers := bridge.GetUsersListCount("xmodules", query) - t2 := time.Now(); - - result := map[string]interface{}{ - "total": totalusers, - "row": map[int]interface{}, - "subtimetotal": t2-t1, - } - - // condicionar por orden y lista - for _, set := range data { - min = set.min - max = set.max - - $t1 = microtime(true); - $entradas = $this->usuarioEntity->readUsuario($query, new \dominion\DB_OrderBy('clave', \dominion\DB_OrderBy::ASC), $max-$min+1, $min); - $t2 = microtime(true); - $rec['subtime'] = $t2 - $t1; - $num = $set[0]; - - if ($entradas) - { - $turn = true; - foreach($entradas as $entrada) - { - // Busca perfiles con este derechos - $perfiles = $this->searchperfiles($entrada->clave); - $derechos = $this->searchderechos($entrada->clave); - - $botones = << - -EOF; - $rec['row'][$num++] = array('clave' => $entrada->clave, - 'estatus' => $entrada->estatus, - 'nombre' => $entrada->nombre . ' [' . $entrada->chef . ']
' . $entrada->correo, - 'perfiles' => $perfiles, - 'derechos' => $derechos, - 'comandos' => $botones - ); - $turn = !$turn; - } - } - } - // flag "fullload" is not set since we load only a part of the dynamic data - $end = microtime(true); - $rec['time'] = $end - $init; - - return $rec; - - - - - - - - return map[string]interface{}{ - "row": users, - } -} - -/* -usuarioEntity = \entities\usuarioEntity::getInstance(); - - $this->FILTROESTATUS = $this->usuarioEntity->getParam('USUARIOS_ESTATUS'); - if (!$this->FILTROESTATUS) - { - $this->FILTROESTATUS = 1; - $this->usuarioEntity->setParam('USUARIOS_ESTATUS', $this->FILTROESTATUS); - } - } - - public function code($engine, $params) - { - $check = ''; - if ($this->FILTROESTATUS == 1) - { - $check = ' checked="checked"'; - } - - $this->template->metaElements( - array( - 'checkfiltro' => $check, - ) - ); - $code = $this->template->resolve(); - return $code; - } - - public function filtro($engine, $params) - { - if ($this->FILTROESTATUS == 1) - $this->FILTROESTATUS = 2; - else - $this->FILTROESTATUS = 1; - $this->usuarioEntity->setParam('USUARIOS_ESTATUS', $this->FILTROESTATUS ); - - return array('estatus' => 'OK', 'filtro' => $this->FILTROESTATUS); - } - - public function contenedoresdata() - { - $init = microtime(true); - $data = null; - if (isset($_POST['data'])) - $data = json_decode($_POST['data']); - - if (!$data) - { - $data = array(array(0, 49)); - } - - $query = null; - if ($this->FILTROESTATUS == 1) - { - $query = array(new \dominion\DB_Condition('estatus', 'in', "('A', 'S')")); - } - - $t1 = microtime(true); - $total = $this->usuarioEntity->countUsuario($query); - $t2 = microtime(true); - $rec = array('total' => $total, 'row' => array()); - $rec['subtimetotal'] = $t2 - $t1; - - // condicionar por orden y lista - foreach($data as $set) - { - $min = $set[0]; - $max = $set[1]; - - $t1 = microtime(true); - $entradas = $this->usuarioEntity->readUsuario($query, new \dominion\DB_OrderBy('clave', \dominion\DB_OrderBy::ASC), $max-$min+1, $min); - $t2 = microtime(true); - $rec['subtime'] = $t2 - $t1; - $num = $set[0]; - - if ($entradas) - { - $turn = true; - foreach($entradas as $entrada) - { - // Busca perfiles con este derechos - $perfiles = $this->searchperfiles($entrada->clave); - $derechos = $this->searchderechos($entrada->clave); - - $botones = << - -EOF; - $rec['row'][$num++] = array('clave' => $entrada->clave, - 'estatus' => $entrada->estatus, - 'nombre' => $entrada->nombre . ' [' . $entrada->chef . ']
' . $entrada->correo, - 'perfiles' => $perfiles, - 'derechos' => $derechos, - 'comandos' => $botones - ); - $turn = !$turn; - } - } - } - // flag "fullload" is not set since we load only a part of the dynamic data - $end = microtime(true); - $rec['time'] = $end - $init; - - return $rec; - } - - private function searchperfiles($key) - { - $resultado = array(); - $recs = $this->usuarioEntity->selectAdminperfilusuario(array(new \dominion\DB_Condition('usuario', '=', $key)), new \dominion\DB_OrderBy('perfil', \dominion\DB_OrderBy::ASC)); - if ($recs) - { - foreach($recs as $rec) - { - $perfil = $this->usuarioEntity->selectAdminperfil($rec->perfil); - $resultado[] = '[' . $perfil->clave.']'; - } - } - if (!$resultado) - return '--'; - - return implode(', ', $resultado); - } - - private function searchderechos($key) - { - $resultado = array(); - $derechos = array(); - - // derechos directos con este derecho SI/NO - $recs = $this->usuarioEntity->selectAdminderechousuario(array(new \dominion\DB_Condition('usuario', '=', $key)), new \dominion\DB_OrderBy('derecho', \dominion\DB_OrderBy::ASC)); - if ($recs) - { - foreach($recs as $rec) - { - if ($rec->estatus == 1) - $color = 'green'; - elseif ($rec->estatus == 2) - $color = 'red'; - else - $color = 'black'; - - $derechos[$rec->derecho] = $color; - } - } - - // derechos indirectos a traves del perfil - $recs = $this->usuarioEntity->selectAdminperfilusuario(array(new \dominion\DB_Condition('usuario', '=', $key)), new \dominion\DB_OrderBy('perfil', \dominion\DB_OrderBy::ASC)); - if ($recs) - { - foreach($recs as $rec) - { - $subrecs = $this->usuarioEntity->selectAdminPerfilderecho(array(new \dominion\DB_Condition('perfil', '=', $rec->perfil)), new \dominion\DB_OrderBy('derecho', \dominion\DB_OrderBy::ASC)); - if ($subrecs) - { - foreach($subrecs as $subrec) - { - if ($subrec->estatus == 1) - $color = 'green'; - elseif ($subrec->estatus == 2) - $color = 'red'; - else - $color = 'black'; - $derechos[$subrec->derecho] = $color; - } - } - } - } - - if (!$derechos) - return '--'; - - foreach($derechos as $derecho => $color) - $resultado[] = '[' . $derecho.']'; - - return implode(', ', $resultado); - } -} - -?> -*/ diff --git a/useradmin/pages/users/users.template b/useradmin/pages/users/users.template deleted file mode 100644 index cb2fe92..0000000 --- a/useradmin/pages/users/users.template +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - ##userstitle##
- ##usershint##
- - - ##onlyactive## - - - ]]>
-
- - - - - - - - - - - - - - - - - - - - - - - - -
- - -
diff --git a/useradmin/static/README.md b/useradmin/static/README.md new file mode 100644 index 0000000..068ac54 --- /dev/null +++ b/useradmin/static/README.md @@ -0,0 +1,3 @@ +Those are static files for Xamboo administration system. + +The public resources static/* are copied to the admin/public environment directory at installation/upgrade of the module diff --git a/useradmin/useradmin.go b/useradmin/useradmin.go deleted file mode 100644 index 6fcbd5d..0000000 --- a/useradmin/useradmin.go +++ /dev/null @@ -1,19 +0,0 @@ -package useradmin - -import ( - "github.com/webability-go/xmodules/base" - - "github.com/webability-go/xdominion" -) - -// GetUsersList to get a list of all the users -func GetOptions(ds *base.Datasource, group string, father int) *xdominion.XRecords { - - user_user := ds.GetTable("user_user") - if user_user == nil { - ds.Log("xmodules::user::GetUsersList: Error, the user_user table is not available on this datasource") - return nil - } - data, _ := user_user.SelectAll() - return data -} diff --git a/useradmin/util.go b/useradmin/util.go index 3108029..25e5f5b 100644 --- a/useradmin/util.go +++ b/useradmin/util.go @@ -1,59 +1,151 @@ package useradmin import ( + "fmt" + "github.com/webability-go/xamboo/applications" // "github.com/webability-go/xcore/v2" "github.com/webability-go/xdominion" "github.com/webability-go/xmodules/adminmenu" + "github.com/webability-go/xmodules/tools" + "github.com/webability-go/xmodules/user" + userassets "github.com/webability-go/xmodules/user/assets" ) func install(ds applications.Datasource) (error, []string) { - // Group, just in case (upsert) - adminmenu.AddGroup(ds, "_admin", "System Administration") + result := []string{} + + // Accesses + err := user.AddAccessGroup(ds, &userassets.AccessGroup{ + Key: "_useradmin", + Name: tools.Message(messages, "accessgroup.name"), + Description: tools.Message(messages, "accessgroup.description"), + }) + if err != nil { + result = append(result, err.Error()) + return err, result + } + + err = user.AddAccess(ds, &userassets.Access{ + Key: "_useradmin", + Name: tools.Message(messages, "mainuser.name"), + Group: "_useradmin", + Description: tools.Message(messages, "mainuser.description"), + }) + if err != nil { + result = append(result, err.Error()) + return err, result + } + + err = user.AddAccess(ds, &userassets.Access{ + Key: "_useradminaccess", + Name: tools.Message(messages, "access.name"), + Group: "_useradmin", + Description: tools.Message(messages, "access.description"), + }) + if err != nil { + result = append(result, err.Error()) + return err, result + } + + err = user.AddAccess(ds, &userassets.Access{ + Key: "_useradminprofile", + Name: tools.Message(messages, "profile.name"), + Group: "_useradmin", + Description: tools.Message(messages, "profile.description"), + }) + if err != nil { + result = append(result, err.Error()) + return err, result + } + + err = user.AddAccess(ds, &userassets.Access{ + Key: "_useradminuser", + Name: tools.Message(messages, "user.name"), + Group: "_useradmin", + Description: tools.Message(messages, "user.description"), + }) + if err != nil { + result = append(result, err.Error()) + return err, result + } mainoption := xdominion.XRecord{ - "key": "user", - "group": "admin", + "key": "_useradmin", + "group": "_admin", "father": nil, - "name": "User administration", - "access": nil, - "icon1": nil, + "name": tools.Message(messages, "userfolder.name"), + "access": "_useradmin", + "icon1": "folder.png", "call1": "openclose", - "description1": "Open or close the option", + "description1": tools.Message(messages, "userfolder.description"), } - err := adminmenu.AddOption(ds, &mainoption) - + err = adminmenu.AddOption(ds, &mainoption) if err != nil { - ds.Log("main", "Error inserting admin adminmenu_option", err) - return err, []string{"xmodules::adminmenu::loadTables: Error upserting the admin adminmenu group"} + result = append(result, err.Error()) + return err, result } option := xdominion.XRecord{ - "key": "userlist", - "group": "admin", - "father": "user", - "name": "User list", - "access": nil, - "icon1": nil, - "call1": "openclose", - "description1": "Open or close the option", + "key": "_useradmin_access", + "group": "_admin", + "father": "_useradmin", + "name": tools.Message(messages, "useraccess.name"), + "access": "_useradminaccess", + "icon1": "option.png", + "call1": "useradmin/access|single", + "description1": tools.Message(messages, "useraccess.description"), + } + err = adminmenu.AddOption(ds, &option) + if err != nil { + result = append(result, err.Error()) + return err, result + } + + option = xdominion.XRecord{ + "key": "_useradmin_profile", + "group": "_admin", + "father": "_useradmin", + "name": tools.Message(messages, "userprofile.name"), + "access": "_useradminprofile", + "icon1": "option.png", + "call1": "useradmin/profile|single", + "description1": tools.Message(messages, "userprofile.description"), } err = adminmenu.AddOption(ds, &option) + if err != nil { + result = append(result, err.Error()) + return err, result + } + option = xdominion.XRecord{ + "key": "_useradmin_user", + "group": "_admin", + "father": "_useradmin", + "name": tools.Message(messages, "useruser.name"), + "access": "_useradminuser", + "icon1": "option.png", + "call1": "useradmin/user|single", + "description1": tools.Message(messages, "useruser.description"), + } + err = adminmenu.AddOption(ds, &option) if err != nil { - ds.Log("main", "Error inserting admin adminmenu_option", err) - return err, []string{"xmodules::adminmenu::loadTables: Error upserting the admin adminmenu group"} + result = append(result, err.Error()) + return err, result } - return nil, []string{"ok"} + return nil, []string{ + fmt.Sprint("useradmin options added"), + } } func upgrade(ds applications.Datasource, oldversion string) (error, []string) { - if oldversion < "0.0.1" { + if oldversion <= "0.0.1" { // do things + return install(ds) } return nil, []string{} diff --git a/xmodules.go b/xmodules.go index 94082bc..616b82a 100644 --- a/xmodules.go +++ b/xmodules.go @@ -1,3 +1,3 @@ package xmodules -const VERSION = "2021.02.16" +const VERSION = "2022.06.09"