API Reference

Here are all public functions in the uAdmin, their format, and how to use them in the project.

Functions

uadmin.Action

Action is the process of doing something where you can check the status of your activities in the uAdmin project.

Type:

type Action int

There are 11 methods of actions:

  • Added - Saved a new record

  • Custom - For any other action that you would like to log

  • Deleted - Deleted a record

  • LoginDenied - User invalid login

  • LoginSuccessful - User login

  • Logout - User logout

  • Modified - Save an existing record

  • PasswordResetDenied - A password reset attempt was rejected

  • PasswordResetRequest - A password reset was received

  • PasswordResetSuccessful - A password was reset

  • Read - Opened a record

Open “LOGS” in the uAdmin dashboard. You can see the Action field inside it as shown below.

_images/actionhighlighted.png

Now go to the main.go. Let’s add each methods of actions in the log.

func main(){
    // Some codes
    for i := 0; i < 11; i++ {
        // Initialize the log model
        log := uadmin.Log{}

        // Call each methods of action based on the specific loop count
        switch i {
        case 0:
            log.Action = uadmin.Action.Added(0)
        case 1:
            log.Action = uadmin.Action.Custom(0)
        case 2:
            log.Action = uadmin.Action.Deleted(0)
        case 3:
            log.Action = uadmin.Action.LoginDenied(0)
        case 4:
            log.Action = uadmin.Action.LoginSuccessful(0)
        case 5:
            log.Action = uadmin.Action.Logout(0)
        case 6:
            log.Action = uadmin.Action.Modified(0)
        case 7:
            log.Action = uadmin.Action.PasswordResetDenied(0)
        case 8:
            log.Action = uadmin.Action.PasswordResetRequest(0)
        case 9:
            log.Action = uadmin.Action.PasswordResetSuccessful(0)
        default:
            log.Action = uadmin.Action.Read(0)
        }

        // Add the method to the logs
        log.Save()
    }
}

Once you are done, rebuild your application. Check your “LOGS” again to see the result.

_images/actionlist.png

As expected, all types of actions were added in the logs. Good job man!

uadmin.AdminPage

AdminPage fetches records from the database with some standard rules such as sorting data, multiples of, and setting a limit that can be used in pagination.

Function:

func(order string, asc bool, offset int, limit int, a interface{}, query interface{}, args ...interface{}) (err error)

Parameters:

order string: Is the field you want to specify in the database.

asc bool: true in ascending order, false in descending order.

offset int: Is the starting point of your list.

limit int: Is the number of records that you want to display in your application.

a interface{}: Is the variable where the model was initialized

query interface{}: Is an action that you want to perform with in your data list

args …interface{}: Is the series of arguments for query input

See Tutorial Part 8 - Customizing your API Handler for the example.

Create a file named admin_page_list.go inside the api folder with the following codes below:

// AdminPageHandler !
func AdminPageHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/admin_page_list")

    todo := []models.Todo{}

    // "id" - order the todo model by id field
    // false - to sort in descending order
    // 0 - start at index 0
    // 3 - get three records
    // &todo - todo model to execute
    // id > ? - a query where id is greater than the value
    // 0 - a value to be set in ?
    uadmin.AdminPage("id", false, 0, 3, &todo, "id > ?", 0) // <-- place it here

    uadmin.ReturnJSON(w, r, todo)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // AdminPageHandler
    http.HandleFunc("/api/", api.AdminPageHandler)
}

api is the folder name while AdminPageHandler is the name of the function inside admin_page_list.go.

Run your application and see what happens.

_images/adminpagelistapi.png

uadmin.All

All fetches all object in the database.

Function:

func(a interface{}) (err error)

Parameter:

a interface{}: Is the variable where the model was initialized

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Create a file named friend_list.go inside the api folder with the following codes below:

// FriendListHandler !
func FriendListHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/friend_list")

    friend := []models.Friend{}
    uadmin.All(&friend) // <-- place it here

    uadmin.ReturnJSON(w, r, friend)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // FriendListHandler
    http.HandleFunc("/friend_list/", api.FriendListHandler) // <-- place it here
}

api is the folder name while FriendListHandler is the name of the function inside friend_list.go.

Run your application and see what happens.

_images/friendlistapi.png

uadmin.BindIP

BindIP is the IP the application listens to.

Type:

string

Go to the main.go. Connect to the server using a loopback IP address (127.x.x.x). Let’s say 127.0.0.2

func main() {
    // Some codes
    uadmin.BindIP = "127.0.0.2" // <--  place it here
}

If you run your code,

[   OK   ]   Initializing DB: [12/12]
[   OK   ]   Server Started: http://127.0.0.2:8080
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

In the Server Started, it will redirect you to the IP address of 127.0.0.2.

But if you connect to other private IP addresses, it will not work as shown below (User connects to 127.0.0.3).

_images/bindiphighlighted.png

uadmin.Choice

Choice is a struct for the list of choices.

Structure:

type Choice struct {
    V        string
    K        uint
    Selected bool
}

Suppose I have four records in my Category model.

  • Education ID = 4

  • Family ID = 3

  • Work ID = 2

  • Travel ID = 1

_images/categorylist.png

Create a function with a parameter of interface{} and a pointer of User that returns an array of Choice which will be used that later below the main function in main.go.

func GetChoices(m interface{}, user *uadmin.User) []uadmin.Choice {
    // Initialize the Category model
    categorylist := models.Category{}

    // Get the ID of the category
    uadmin.Get(&categorylist, "id = 4")

    // Build choices
    choices := []uadmin.Choice{}

    // Append by getting the ID and string of categorylist
    choices = append(choices, uadmin.Choice{
        V:        uadmin.GetString(categorylist),
        K:        uadmin.GetID(reflect.ValueOf(categorylist)),
        Selected: true,
    })

    return choices
}

Now inside the main function, apply uadmin.Schema function that calls a model name of “todo”, accesses “Choices” as the field name that uses the LimitChoicesTo then assign it to GetChoices which is your function name.

uadmin.Schema["todo"].FieldByName("Choices").LimitChoicesTo = GetChoices

Run your application, go to the Todo model and see what happens in the Choices field.

_images/choicesid4.png

When you notice, the Education is automatically selected. This function has the ability to search whatever you want in the drop down list.

You can also produce multiple choices in the drop down list. In this case, you need to create them manually. Set the Selected value to false.

func GetChoices(m interface{}, user *uadmin.User) []uadmin.Choice {
    // Initialize the Category model
    categorylist := models.Category{}

    // Build choices
    choices := []uadmin.Choice{}

    // Append by getting the ID and string of categorylist
    choices = append(choices, uadmin.Choice{
        V:        uadmin.GetString(categorylist),
        K:        uadmin.GetID(reflect.ValueOf(categorylist)),
        Selected: true,
    })

    // Create the list of choices manually
    choices = append(choices, uadmin.Choice{
        V:        "Travel",
        K:        1,
        Selected: false,
    })
    choices = append(choices, uadmin.Choice{
        V:        "Work",
        K:        2,
        Selected: false,
    })
    choices = append(choices, uadmin.Choice{
        V:        "Family",
        K:        3,
        Selected: false,
    })
    choices = append(choices, uadmin.Choice{
        V:        "Education",
        K:        4,
        Selected: false,
    })

    return choices
}

Now rerun your application to see the result.

_images/manualchoiceslist.png

When you notice, the value of the Category field is empty by default. You can also type whatever you want to search in the choices list above. For this example, let’s choose “Education”.

Once you are done, save the record and see what happens.

_images/choicesid4manualoutput.png

Congrats, now you know how to create a choice by getting the name, ID number, using the Selected field and connecting the GetChoices function to the schema, as well as creating multiple choices manually.

uadmin.ClearDB

ClearDB clears the database object.

Function:

func()

Suppose I have two databases in my project folder.

_images/twodatabases.png

And I set the Name to uadmin.db on Database Settings in main.go.

func main(){
    uadmin.Database = &uadmin.DBSettings{
        Type: "sqlite",
        Name: "uadmin.db",
    }
    // Some codes
}

Let’s create a new file in the models folder named “expression.go” with the following codes below:

package models

import "github.com/uadmin/uadmin"

// ---------------- DROP DOWN LIST ----------------
// Status ...
type Status int

// Keep ...
func (s Status) Keep() Status {
    return 1
}

// ClearDatabase ...
func (s Status) ClearDatabase() Status {
    return 2
}
// -----------------------------------------------

// Expression model ...
type Expression struct {
    uadmin.Model
    Name   string `uadmin:"required"`
    Status Status `uadmin:"required"`
}

// Save ...
func (e *Expression) Save() {
    // If Status is equal to ClearDatabase(), the database
    // will reset and open a new one which is todolist.db.
    if e.Status == e.Status.ClearDatabase() {
        uadmin.ClearDB()        // <-- Place it here

        // Database configurations
        uadmin.Database = &uadmin.DBSettings{
            Type: "sqlite",
            Name: "todolist.db",
        }

        // Returns a pointer to the DB
        uadmin.GetDB()
    }

    // Override save
    uadmin.Save(e)
}

Register your Expression model in the main function.

func main() {

    // Some codes

    uadmin.Register(
        // Some registered models
        models.Expression{}, // <-- place it here
    )

    // Some codes
}

Run the application. Go to the Expressions model and add at least 3 interjections, all Status set to “Keep”.

_images/expressionkeep.png

Now create another data, this time set the Status as “Clear Database” and see what happens.

_images/cleardatabase.png

Your account will automatically logout in the application. Login your account again, go to the Expressions model and see what happens.

_images/cleardatabasesecondmodel.png

As expected, all previous records were gone in the model. It does not mean that they were deleted. It’s just that you have opened a new database called “todolist.db”. Check out the other models that you have. You may notice that something has changed in your database.

uadmin.CookieTimeout

CookieTimeout is the timeout of a login cookie in seconds.

Type:

int

Let’s apply this function in the main.go.

func main() {
    // Some codes
    uadmin.CookieTimeout = 10 // <--  place it here
}

Warning

Use it at your own risk. Once the cookie expires, your account will be permanently deactivated. In this case, you must have an extra admin account in the User database.

Login your account, wait for 10 seconds and see what happens.

_images/loginform.png

It will redirect you to the login form because your cookie has already been expired.

uadmin.Count

Count return the count of records in a table based on a filter.

Function:

func(a interface{}, query interface{}, args ...interface{}) int

Parameters:

a interface{}: Is the variable where the model was initialized

query interface{}: Is an action that you want to perform with in your data list

args …interface{}: Is the series of arguments for query input

Suppose you have ten records in your Todo model.

_images/tendataintodomodel.png

Go to the main.go. Let’s count how many todos do you have with a friend in your model.

func main(){
    // Some codes

    // Initialize the Todo model in the todo variable
    todo := models.Todo{}

    // Initialize the Friend model in the todo variable
    friend := models.Friend{}

    // Fetch the first record from the database
    uadmin.Get(&friend, "id=?", todo.FriendID)

    // Return the count of records in a table based on a Get function to
    // be stored in the total variable
    total := uadmin.Count(&todo, "friend_id = ?", todo.FriendID)

    // Print the result
    uadmin.Trail(uadmin.INFO, "You have %v todos with a friend in your list.", total)
}

Check your terminal to see the result.

[  INFO  ]   You have 5 todos with a friend in your list.

uadmin.CustomTranslation

CustomTranslation allows a user to customize any languages in the uAdmin system.

Type:

[]string

Suppose that English is the only active language in your application. Go to the main.go and apply the following codes below. It should be placed before uadmin.Register.

func main(){
    // Place it here
    uadmin.CustomTranslation = []string{"models/custom", "models/todo_custom"}

    uadmin.Register(
        // Some codes
    )
}

From your project folder, go to static/i18n/models. You will notice that two JSON files are created in the models folder.

_images/customtranslationcreate.png

Every JSON file is per language. In other words, if you have 2 languages available in your application, there will be a total of 4 created JSON files.

uadmin.DashboardMenu

DashboardMenu is a system in uAdmin that is used to add, modify and delete the elements of a model.

Structure:

type DashboardMenu struct {
    Model
    MenuName string `uadmin:"required;list_exclude;multilingual;filter"`
    URL      string `uadmin:"required"`
    ToolTip  string
    Icon     string `uadmin:"image"`
    Cat      string `uadmin:"filter"`
    Hidden   bool   `uadmin:"filter"`
}

There is a function that you can use in DashboardMenu:

  • String() - returns the MenuName

Go to the main.go and apply the following codes below after the RegisterInlines section.

func main(){

    // Some codes

    dashboardmenu := uadmin.DashboardMenu{
        MenuName: "Expressions",
        URL:      "expression",
        ToolTip:  "",
        Icon:     "/media/images/expression.png",
        Cat:      "Yeah!",
        Hidden:   false,
    }

    // This will create a new model based on the information assigned in
    // the dashboardmenu variable.
    uadmin.Save(&dashboardmenu)

    // Returns the MenuName
    uadmin.Trail(uadmin.INFO, "String() returns %s.", dashboardmenu.String())
}

Now run your application and see what happens.

Terminal

[  INFO  ]   String() returns Expressions.
_images/expressionmodelcreated.png

uadmin.Database

Database is the active Database settings.

Structure:

*uadmin.DBSettings

There are 6 fields that you can use in this function:

  • Host - returns a string. It is an IP address where the database was hosted.

  • Name - returns a string. This will generate a database file in your project folder.

  • Password - returns a password string

  • Port - returns an int. It is the port used for http or https server.

  • Type - returns a string. There are 2 types: SQLLite and MySQL.

  • User - returns a user string

Go to the main.go in your Todo list project. Add the codes below above the uadmin.Register.

func main(){
    database := uadmin.Database
    database.Host = "192.168.149.108"
    database.Name = "todolist.db"
    database.Password = "admin"
    database.Port = 8000
    database.Type = "sqlite"
    database.User = "admin"
}

If you run your code,

[   OK   ]   Initializing DB: [12/12]
[   OK   ]   Initializing Languages: [185/185]
[  INFO  ]   Auto generated admin user. Username: admin, Password: admin.
[   OK   ]   Server Started: http://0.0.0.0:8000
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

The todolist.db file is automatically created in your main project folder.

_images/todolistdbhighlighted.png

See uadmin.DBSettings for the process of configuring your database in MySQL.

uadmin.DBSettings

DBSettings is a feature that allows a user to configure the settings of a database.

Structure:

type DBSettings struct {
    Type     string // SQLLite, MySQL
    Name     string // File/DB name
    User     string
    Password string
    Host     string
    Port     int
}

Go to the main.go in your Todo list project. Add the codes below above the uadmin.Register.

func main() {
    uadmin.Database = &uadmin.DBSettings{
        Type:      "sqlite",
        Name:      "todolist.db",
        User:      "admin",
        Password:  "admin",
        Host:      "192.168.149.108",
        Port:      8000,
    }
    // Some codes
}

If you run your code,

[   OK   ]   Initializing DB: [12/12]
[   OK   ]   Initializing Languages: [185/185]
[  INFO  ]   Auto generated admin user. Username: admin, Password: admin.
[   OK   ]   Server Started: http://0.0.0.0:8000
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

The todolist.db file is automatically created in your main project folder.

_images/todolistdbhighlighted.png

You can also migrate your application into the MySQL database server. In order to do that, you must have the MySQL Workbench application installed on your computer. Open your MySQL Workbench and set up your Connection Name (example below is uadmin). Hostname, Port and Username are automatically provided for you but you can change the values there if you wish to. For this example, let’s apply the following information below.

_images/mysqlsetup.png

Click Test Connection to see if the connection is working properly.

_images/mysqlprompt.png

Result

_images/testconnectionresult.png

Once you are done with the connection testing, click OK on the bottom right corner. You will see the interface of the application. Let’s create a new schema by right clicking the area on the bottom left corner highlighted below then select “Create Schema”.

_images/rightclickarea.png

Input the value of the schema name as “todo” then click Apply.

_images/schemasetuptodo.png

You will see the Apply SQL Script to the Database form. Leave it as it is and click Apply.

_images/applysqlscriptform.png

Your todo schema has been created in the MySQL. Congrats!

_images/todocreatedmysql.png

Now go back to your todo list project. Open main.go and apply the following codes below:

uadmin.Database = &uadmin.DBSettings{
    Type:     "mysql",
    Name:     "todo",
    User:     "root",
    Password: "todolist",
    Host:     "127.0.0.1",
    Port:     3306,
}

The information above is well-based on the database configuration settings in MySQL Workbench.

Once you are done, run your application and see what happens.

[   OK   ]   Initializing Languages: [185/185]
[  INFO  ]   Auto generated admin user. Username:admin, Password:admin.
[   OK   ]   Server Started: http://0.0.0.0:8080

Open your browser and type the IP address above. Then login using “admin” as username and password.

_images/loginform.png

You will be greeted by the uAdmin dashboard. System models are built in to uAdmin, and the rest are the ones we created, in this case TODOS model.

_images/uadmindashboard.png

Now open your MySQL Workbench. On todo database in the schema panel, the tables are automatically generated from your uAdmin dashboard.

_images/mysqluadminmodelslist.png

Congrats, now you know how to configure your database settings in both SQLite and MySQL.

uadmin.DEBUG

DEBUG is the display tag under Trail. It is the process of identifying and removing errors.

Type:

untyped int

See uadmin.Trail for the example.

uadmin.DebugDB

DebugDB prints all SQL statements going to DB.

Type:

bool

Go to the main.go. Set this function as true.

func main(){
    uadmin.DebugDB = true
    // Some codes
}

Check your terminal to see the result.

[   OK   ]   Initializing DB: [13/13]

(/home/dev1/go/src/github.com/uadmin/uadmin/db.go:428)
[2018-11-10 12:43:07]  [0.09ms]  SELECT count(*) FROM "languages"  WHERE "languages"."deleted_at" IS NULL
[0 rows affected or returned ]

(/home/dev1/go/src/github.com/uadmin/uadmin/db.go:298)
[2018-11-10 12:43:07]  [0.17ms]  SELECT * FROM "languages"  WHERE "languages"."deleted_at" IS NULL AND ((active = 'true'))
[1 rows affected or returned ]

(/home/dev1/go/src/github.com/uadmin/uadmin/db.go:238)
[2018-11-10 12:43:07]  [0.16ms]  SELECT * FROM "languages"  WHERE "languages"."deleted_at" IS NULL AND ((`default` = 'true')) ORDER BY "languages"."id" ASC LIMIT 1
[1 rows affected or returned ]

(/home/dev1/go/src/github.com/uadmin/uadmin/db.go:162)
[2018-11-10 12:43:07]  [0.32ms]  SELECT * FROM "dashboard_menus"  WHERE "dashboard_menus"."deleted_at" IS NULL
[13 rows affected or returned ]

(/home/dev1/go/src/github.com/uadmin/uadmin/db.go:428)
[2018-11-10 12:43:07]  [0.07ms]  SELECT count(*) FROM "users"  WHERE "users"."deleted_at" IS NULL
[0 rows affected or returned ]

uadmin.Delete

Delete records from database.

Function:

func(a interface{}) (err error)

Parameter:

a interface{}: Is the variable where the model was initialized

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Example #1: By Using API Handler

Suppose you have five records in your Todo model.

_images/fiverecordstodomodel.png

Create a file named delete.go inside the api folder with the following codes below:

// DeleteHandler !
func DeleteHandler(w http.ResponseWriter, r *http.Request) {
    // r.URL.Path creates a new path called /delete
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/delete")

    // Initialize the Todo model
    todo := []models.Todo{}

    // Delete all records in Todo model
    uadmin.Delete(&todo)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // DeleteHandler
    http.HandleFunc("/delete/", api.DeleteHandler) // <-- place it here
}

api is the folder name while DeleteHandler is the name of the function inside delete.go.

Run your application. Add /delete/ path after your access IP and port in the address bar (e.g. http://0.0.0.0:8080/delete/).

Afterwards, go to Todo model and see what happens.

_images/todomodelempty.png

All records are deleted from the database.

Example #2: By Drop Down List Selection

Let’s create a new file in the models folder named “expression.go” with the following codes below:

package models

import "github.com/uadmin/uadmin"

// ---------------- DROP DOWN LIST ----------------
// Status ...
type Status int

// Keep ...
func (s Status) Keep() Status {
    return 1
}

// DeletePrevious ...
func (s Status) DeletePrevious() Status {
    return 2
}
// -----------------------------------------------

// Expression model ...
type Expression struct {
    uadmin.Model
    Name   string `uadmin:"required"`
    Status Status `uadmin:"required"`
}

// Save ...
func (e *Expression) Save() {
    // If Status is equal to DeletePrevious(), it will delete
    // the previous data in the list.
    if e.Status == e.Status.DeletePrevious() {
        uadmin.Delete(e) // <-- place it here
    }

    uadmin.Save(e)
}

Register your Expression model in the main function.

func main() {

    // Some codes

    uadmin.Register(
        // Some registered models
        models.Expression{}, // <-- place it here
    )

    // Some codes
}

Run the application. Go to the Expressions model and add at least 3 interjections, all Status set to “Keep”.

_images/expressionkeep.png

Now create another data, this time set the Status as “Delete Previous” and see what happens.

_images/deleteprevious.png

Result

_images/deletepreviousresult.png

All previous records are deleted from the database.

uadmin.DeleteList

Delete the list of records from database.

Function:

func(a interface{}, query interface{}, args ...interface{}) (err error)

Parameters:

a interface{}: Is the variable where the model was initialized

query interface{}: Is an action that you want to perform with in your data list

args …interface{}: Is the series of arguments for query input

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Example #1: By Using API Handler

Suppose you have five records in your Todo model.

_images/fiverecordstodomodel.png

Create a file named delete_list.go inside the api folder with the following codes below:

// DeleteListHandler !
func DeleteListHandler(w http.ResponseWriter, r *http.Request) {
    // r.URL.Path creates a new path called /delete_list
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/delete_list")

    // Call an array of Todo model
    todo := []models.Todo{}

    // Set the parameter as todo_id that can get multiple values
    todoList := strings.Split(r.FormValue("todo_id"), ",")

    // Delete the list of Todo records based on an assigned ID
    uadmin.DeleteList(&todo, "id IN (?)", todoList)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // DeleteListHandler
    http.HandleFunc("/delete_list/", api.DeleteListHandler) // <-- place it here
}

api is the folder name while DeleteListHandler is the name of the function inside delete_list.go.

Run your application. Let’s assign 1, 2, and 3 in the todo_id parameter. (e.g. http://0.0.0.0:8080/delete_list/?todo_id=1,2,3).

Afterwards, go to Todo model and see what happens.

_images/tworecordstodomodel.png

Based on the result shown above, the first three records are deleted from the database while the last two records remain.

Example #2: By Drop Down List Selection

Let’s create a new file in the models folder named “expression.go” with the following codes below:

package models

import "github.com/uadmin/uadmin"

// ---------------- DROP DOWN LIST ----------------
// Status ...
type Status int

// Keep ...
func (s Status) Keep() Status {
    return 1
}

// Custom ...
func (s Status) Custom() Status {
    return 2
}

// DeleteCustom ...
func (s Status) DeleteCustom() Status {
    return 3
}
// -----------------------------------------------

// Expression model ...
type Expression struct {
    uadmin.Model
    Name   string `uadmin:"required"`
    Status Status `uadmin:"required"`
}

// Save ...
func (e *Expression) Save() {
    // If Status is equal to DeleteCustom(), it will delete the
    // list of data that contains Custom as the status.
    if e.Status == e.Status.DeleteCustom() {
        uadmin.DeleteList(e, "status = ?", 2)
    }

    uadmin.Save(e)
}

Register your Expression model in the main function.

func main() {

    // Some codes

    uadmin.Register(
        // Some registered models
        models.Expression{}, // <-- place it here
    )

    // Some codes
}

Run the application. Go to the Expressions model and add at least 3 interjections, one is set to “Keep” and the other two is set to “Custom”.

_images/expressionkeepcustom.png

Now create another data, this time set the Status as “Delete Custom” and see what happens.

_images/deletecustom.png

Result

_images/deletecustomresult.png

All custom records are deleted from the database.

uadmin.EmailFrom

EmailFrom identifies where the email is coming from.

Type:

string

Go to the main.go and apply the following codes below:

func main(){
    uadmin.EmailFrom = "myemail@integritynet.biz"
    uadmin.EmailUsername = "myemail@integritynet.biz"
    uadmin.EmailPassword = "abc123"
    uadmin.EmailSMTPServer = "smtp.integritynet.biz"
    uadmin.EmailSMTPServerPort = 587
    // Some codes
}

Let’s go back to the uAdmin dashboard, go to Users model, create your own user account and set the email address based on your assigned EmailFrom in the code above.

_images/useremailhighlighted.png

Log out your account. At the moment, you suddenly forgot your password. How can we retrieve our account? Click Forgot Password at the bottom of the login form.

_images/forgotpasswordhighlighted.png

Input your email address based on the user account you wish to retrieve it back.

_images/forgotpasswordinputemail.png

Once you are done, open your email account. You will receive a password reset notification from the Todo List support. To reset your password, click the link highlighted below.

_images/passwordresetnotification.png

You will be greeted by the reset password form. Input the following information in order to create a new password for you.

_images/resetpasswordform.png

Once you are done, you can now access your account using your new password.

uadmin.EmailPassword

EmailPassword assigns the password of an email.

Type:

string

See uadmin.EmailFrom for the example.

uadmin.EmailSMTPServer

EmailSMTPServer assigns the name of the SMTP Server in an email.

Type:

string

See uadmin.EmailFrom for the example.

uadmin.EmailSMTPServerPort

EmailSMTPServerPort assigns the port number of an SMTP Server in an email.

Type:

int

See uadmin.EmailFrom for the example.

uadmin.EmailUsername

EmailUsername assigns the username of an email.

Type:

string

See uadmin.EmailFrom for the example.

uadmin.EncryptKey

EncryptKey is a key for encryption and decryption of data in the DB.

Type:

[]byte

Go to the main.go and set the byte values from 0 to 255. Put it above the uadmin.Register.

func main() {
    uadmin.EncryptKey = []byte{34, 35, 35, 57, 68, 4, 35, 36, 7, 8, 35, 23, 35, 86, 35, 23}
    uadmin.Register(
        // Some codes
    )
}

Run your application to create your key file then exit it.

In your terminal, type cat .key to see the result.

$ cat .key
�!��Q�nt��Z�-���| �9쁌=Y�

uadmin.ERROR

ERROR is a status to notify the user that there is a problem in an application.

Type:

untyped int

See uadmin.Trail for the example.

uadmin.F

F is a field.

Structure:

type F struct {
    Name              string
    DisplayName       string
    Type              string
    TypeName          string
    Value             interface{}
    Help              string
    Max               interface{}
    Min               interface{}
    Format            string
    DefaultValue      string
    Required          bool
    Pattern           string
    PatternMsg        string
    Hidden            bool
    ReadOnly          string
    Searchable        bool
    Filter            bool
    ListDisplay       bool
    FormDisplay       bool
    CategoricalFilter bool
    Translations      []translation
    Choices           []Choice
    IsMethod          bool
    ErrMsg            string
    ProgressBar       map[float64]string
    LimitChoicesTo    func(interface{}, *User) []Choice
    UploadTo          string
    Encrypt           bool
}

There are 2 ways you can do for initialization process using this function: one-by-one and by group.

One-by-one initialization:

func main(){
    // Some codes
    f := uadmin.F{}
    f.Name = "Name"
    f.DisplayName = "DisplayName"
    f.Type = "Type"
    f.Value = "Value"
}

By group initialization:

func main(){
    // Some codes
    f := uadmin.F{
        Name:        "Name",
        DisplayName: "DisplayName",
        Type:        "Type",
        Value:       "Value",
    }
}

In this example, we will use “by group” initialization process.

Go to the main.go and apply the following codes below:

func main(){
    // Some codes
    f1 := uadmin.F{
        Name:        "Name",
        DisplayName: "Reaction",
        Type:        "string",
        Value:       "Wow!",
    }
    f2 := uadmin.F{
        Name:        "Reason",
        DisplayName: "Reason",
        Type:        "string",
        Value:       "My friend's performance is amazing.",
    }
}

The code above shows the two initialized F structs using the Name, DisplayName, Type, and Value fields.

See uadmin.ModelSchema for the continuation of this example.

uadmin.Filter

Filter fetches records from the database.

Function:

func(a interface{}, query interface{}, args ...interface{}) (err error)

Parameters:

a interface{}: Is the variable where the model was initialized

query interface{}: Is an action that you want to perform with in your data list

args …interface{}: Is the series of arguments for query input

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Example #1: Assigning Multiple Values in a Parameter

Suppose you have five records in your Todo model.

_images/fiverecordstodomodel.png

Create a file named filter_list.go inside the api folder with the following codes below:

func FilterListHandler(w http.ResponseWriter, r *http.Request) {
    // r.URL.Path creates a new path called /filter_list
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/filter_list")

    // Call an array of Todo model
    todo := []models.Todo{}

    // Set the parameter as todo_id that can get multiple values
    todoList := strings.Split(r.FormValue("todo_id"), ",")

    // Fetch ID records from DB
    uadmin.Filter(&todo, "id IN (?)", todoList) // <-- place it here

    // Prints the todo in JSON format
    uadmin.ReturnJSON(w, r, todo)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // FilterListHandler
    http.HandleFunc("/filter_list/", api.FilterListHandler) // <-- place it here
}

api is the folder name while FilterListHandler is the name of the function inside filter_list.go.

Run your application. Search for the first and third ID on the todo_id parameter in the address bar and see what happens.

_images/filterlistapiexample1.png

Example #2: Returning the Name

Create a file named filter_list.go inside the api folder with the following codes below:

package api

import (
    "net/http"
    "strings"

    "github.com/username/todo/models"
    "github.com/uadmin/uadmin"
)

// FilterListHandler !
func FilterListHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/filter_list")

    res := map[string]interface{}{}

    filterList := []string{}
    valueList := []interface{}{}
    if r.URL.Query().Get("todo_id") != "" {
        filterList = append(filterList, "todo_id = ?")
        valueList = append(valueList, r.URL.Query().Get("todo_id"))
    }
    filter := strings.Join(filterList, " AND ")

    todo := []models.Todo{}
    results := []map[string]interface{}{}

    uadmin.Filter(&todo, filter, valueList) // <-- place it here

    // This loop returns only the name of your todo list.
    for i := range todo {
        results = append(results, map[string]interface{}{
            "Name": todo[i].Name,
        })
    }

    res["status"] = "ok"
    res["todo"] = results
    uadmin.ReturnJSON(w, r, res)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // FilterListHandler
    http.HandleFunc("/filter_list/", api.FilterListHandler) // <-- place it here
}

api is the folder name while FilterListHandler is the name of the function inside filter_list.go.

Run your application and see what happens.

_images/filterlistapi.png

See uadmin.Preload for more examples of using this function.

uadmin.FilterBuilder

FilterBuilder changes a map filter into a query.

Function:

func(params map[string]interface{}) (query string, args []interface{})

Parameters:

params map[string]interface{}: Stores arbitrary JSON objects and arrays

query string: Returns an AND to concatenate the parameters based on a filter

args []interface{}: Is the variable or container that can be used in execution process.

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Suppose you have ten records in your Todo model.

_images/tendataintodomodel.png

Create a file named filterbuilder.go inside the api folder with the following codes below:

package api

import (
    "net/http"
    "strings"

    "github.com/username/todo/models"
    "github.com/uadmin/uadmin"
)

// FilterBuilderHandler !
func FilterBuilderHandler(w http.ResponseWriter, r *http.Request) {
    // r.URL.Path creates a new path called /filterbuilder
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/filterbuilder")

    res := map[string]interface{}{}

    // Initialize the Todo model
    todo := []models.Todo{}

    // Fetch data from DB
    query, args := uadmin.FilterBuilder(res) // <-- place it here
    uadmin.Filter(&todo, query, args)

    // Accesses and fetches data from another model
    for t := range todo {
        uadmin.Preload(&todo[t])
    }

    // Prints the todo in JSON format
    res["status"] = "ok"
    res["todo"] = todo
    uadmin.ReturnJSON(w, r, res)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // FilterBuilderHandler
    http.HandleFunc("/filterbuilder/", api.FilterBuilderHandler) // <-- place it here
}

api is the folder name while FilterBuilderHandler is the name of the function inside filterbuilder.go.

Run your application and see what happens.

_images/filterbuilderapi.png

uadmin.GenerateBase32

GenerateBase32 generates a base32 string of length.

Function:

func(length int) string

Parameter:

length int: Is how many digits that you want to store with

Go to the friend.go and initialize the Base32 field inside the struct. Set the tag as “read_only”.

// Friend model ...
type Friend struct {
    uadmin.Model
    Name     string `uadmin:"required"`
    Email    string `uadmin:"email"`
    Password string `uadmin:"password;list_exclude"`
    Base32   string `uadmin:"read_only"` // <-- place it here
}

Apply overriding save function. Use this function to the Base32 field and set the integer value as 40.

// Save !
func (f *Friend) Save() {
    f.Base32 = uadmin.GenerateBase32(40) // <-- place it here
    uadmin.Save(f)
}

Now run your application. Go to the Friend model and save any element to see the changes.

_images/friendbase32.png

Result

_images/friendbase32output.png

As you notice, the Base32 value changed automatically.

uadmin.GenerateBase64

GenerateBase64 generates a base64 string of length.

Function:

func(length int) string

Parameter:

length int: Is how many digits that you want to store with

Go to the friend.go and initialize the Base64 field inside the struct. Set the tag as “read_only”.

// Friend model ...
type Friend struct {
    uadmin.Model
    Name     string `uadmin:"required"`
    Email    string `uadmin:"email"`
    Password string `uadmin:"password;list_exclude"`
    Base64   string `uadmin:"read_only"` // <-- place it here
}

Apply overriding save function. Use this function to the Base64 field and set the integer value as 75.

// Save !
func (f *Friend) Save() {
    f.Base64 = uadmin.GenerateBase64(75) // <-- place it here
    uadmin.Save(f)
}

Now run your application. Go to the Friend model and save any element to see the changes.

_images/friendbase64.png

Result

_images/friendbase64output.png

As you notice, the Base64 value changed automatically.

uadmin.Get

Get fetches the first record from the database.

Function:

func(a interface{}, query interface{}, args ...interface{}) (err error)

Parameters:

a interface{}: Is the variable where the model was initialized

query interface{}: Is an action that you want to perform with in your data list

args …interface{}: Is the series of arguments for query input

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Suppose you have five records in your Todo model.

_images/fiverecordstodomodel.png

Create a file named get_list.go inside the api folder with the following codes below:

func GetListHandler(w http.ResponseWriter, r *http.Request) {
    // r.URL.Path creates a new path called /get_list
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/get_list")

    // Set the parameter as todo_id
    todoID := r.FormValue("todo_id")

    // Get a record from DB
    todo := models.Todo{}
    uadmin.Get(&todo, "id=? ", todoID) // <-- place it here

    // Prints the todo in JSON format
    uadmin.ReturnJSON(w, r, todo)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // GetListHandler
    http.HandleFunc("/get_list/", api.GetListHandler) // <-- place it here
}

api is the folder name while GetListHandler is the name of the function inside get_list.go.

Run your application. Search for the third ID on the todo_id parameter in the address bar and see what happens.

_images/getlistapi.png

uadmin.GetDB

GetDB returns a pointer to the DB.

Function:

func() *gorm.DB

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Suppose I have one record in the Todo model.

_images/todomodeloutput.png

Create a file named custom_todo.go inside the api folder with the following codes below:

// CustomTodoHandler !
func CustomTodoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/custom_todo")

    res := map[string]interface{}{}

    // Initialize the Todo model
    todolist := []models.Todo{}

    // Create a query in the sql variable to select all records in todos
    sql := `SELECT * FROM todos`

    // Place it here
    db := uadmin.GetDB()

    // Store the query inside the Raw function in order to scan value to
    // the Todo model
    db.Raw(sql).Scan(&todolist)

    // Print the result in JSON format
    res["status"] = "ok"
    res["todo"] = todolist
    uadmin.ReturnJSON(w, r, res)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // CustomTodoHandler
    http.HandleFunc("/custom_todo/", api.CustomTodoHandler) // <-- place it here
}

api is the folder name while CustomTodoHandler is the name of the function inside custom_todo.go.

Run your application and see what happens.

_images/getdbjson.png

uadmin.GetID

GetID returns an ID number of a field.

Function:

func(m.reflectValue) uint

Parameter:

m.reflectValue: Creates a new instance to read, set, or add values

Suppose I have four records in my Category model.

  • Education ID = 4

  • Family ID = 3

  • Work ID = 2

  • Travel ID = 1

_images/categorylist.png

Go to the main.go and apply the following codes below:

func main(){

    // Some codes

    // Initialize the Category model
    categorylist := models.Category{}

    // Get the value of the name in the categorylist
    uadmin.Get(&categorylist, "name = 'Family'")

    // Get the ID of the name "Family"
    getid := uadmin.GetID(reflect.ValueOf(categorylist))

    // Print the result
    uadmin.Trail(uadmin.INFO, "GetID is %d.", getid)
}

Run your application and check the terminal to see the result.

[  INFO  ]   GetID is 3.

uadmin.GetString

GetString returns string representation on an instance of a model.

Function:

func(a interface{}) string

Parameter:

a interface{}: Is the variable where the model was initialized

Suppose I have four records in my Category model.

  • Education ID = 4

  • Family ID = 3

  • Work ID = 2

  • Travel ID = 1

_images/categorylist.png

Go to the main.go and apply the following codes below:

func main(){

    // Some codes

    // Initialize the Category model
    categorylist := models.Category{}

    // Get the ID in the categorylist
    uadmin.Get(&categorylist, "id = 3")

    // Get the name of the ID 3
    getstring := uadmin.GetString(categorylist)

    // Print the result
    uadmin.Trail(uadmin.INFO, "GetString is %s.", getstring)
}

Run your application and check the terminal to see the result.

[  INFO  ]   GetString is Family.

uadmin.GetUserFromRequest

GetUserFromRequest returns a user from a request.

Function:

func(r *http.Request) *uadmin.User

Parameter:

r http.Request: Is a data structure that represents the client HTTP request

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Suppose that the admin account has logined.

_images/adminhighlighted.png

Create a file named info.go inside the api folder with the following codes below:

// InfoHandler !
func InfoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/info")

    // Place it here
    uadmin.Trail(uadmin.INFO, "GetUserFromRequest: %s", uadmin.GetUserFromRequest(r))
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // InfoHandler
    http.HandleFunc("/info/", api.InfoHandler) // <-- place it here
}

api is the folder name while InfoHandler is the name of the function inside info.go.

Run your application and see what happens.

_images/infoapi.png

Check your terminal for the result.

[  INFO  ]   GetUserFromRequest: System Admin

The result is coming from the user in the dashboard.

_images/getuserfromrequest.png

There is another way of using this function:

// InfoHandler !
func InfoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/info")

    getuser := uadmin.GetUserFromRequest(r)
    getuser.XXXX
}

XXXX contains user fields and functions that you can use. See uadmin.User for the list and examples.

Go to the info.go in API folder containing the following codes below:

// InfoHandler !
func InfoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/info")

    // Get the User that returns the first and last name
    getuser := uadmin.GetUserFromRequest(r)

    // Print the result using Golang fmt
    fmt.Println("GetActiveSession() is", getuser.GetActiveSession())
    fmt.Println("GetDashboardMenu() is", getuser.GetDashboardMenu())

    // Print the result using Trail
    uadmin.Trail(uadmin.INFO, "GetOTP() is %s.", getuser.GetOTP())
    uadmin.Trail(uadmin.INFO, "String() is %s.", getuser.String())
}

Run your application and see what happens.

_images/infoapi.png

Check your terminal for the result.

GetActiveSession() is Pfr7edaO7bBjv9zL9j1Yi01I
GetDashboardMenu() is [Dashboard Menus Users User Groups Sessions User Permissions Group Permissions Languages Logs Todos Categorys Friends Items]
[  INFO  ]   GetOTP() is 363669.
[  INFO  ]   String() is System Admin.

uadmin.GroupPermission

GroupPermission sets the permission of a user group handled by an administrator.

Structure:

type GroupPermission struct {
    Model
    DashboardMenu   DashboardMenu `gorm:"ForeignKey:DashboardMenuID" required:"true" filter:"true"`
    DashboardMenuID uint          `fk:"true" displayName:"DashboardMenu"`
    UserGroup       UserGroup     `gorm:"ForeignKey:UserGroupID" required:"true" filter:"true"`
    UserGroupID     uint          `fk:"true" displayName:"UserGroup"`
    Read            bool
    Add             bool
    Edit            bool
    Delete          bool
}

There are 2 functions that you can use in GroupPermission:

  • HideInDashboard() - Return true and auto hide this from dashboard

  • String() - Returns the GroupPermission ID

There are 2 ways you can do for initialization process using this function: one-by-one and by group.

One-by-one initialization:

func main(){
    // Some codes
    grouppermission := uadmin.GroupPermission{}
    grouppermission.DashboardMenu = dashboardmenu
    grouppermission.DashboardMenuID = 1
    grouppermission.UserGroup = usergroup
    grouppermission.UserGroupID = 1
}

By group initialization:

func main(){
    // Some codes
    grouppermission := uadmin.GroupPermission{
        DashboardMenu: dashboardmenu,
        DashboardMenuID: 1,
        UserGroup: usergroup,
        UserGroupID: 1,
    }
}

In this example, we will use “by group” initialization process.

Suppose that Even Demata is a part of the Front Desk group.

_images/useraccountfrontdesk.png

Go to the main.go and apply the following codes below after the RegisterInlines section.

func main(){

    // Some codes

    grouppermission := uadmin.GroupPermission{
        DashboardMenuID: 9, // Todos
        UserGroupID:     1, // Front Desk
        Read:            true,
        Add:             false,
        Edit:            false,
        Delete:          false,
    }

    // This will create a new group permission based on the information
    // assigned in the grouppermission variable.
    uadmin.Save(&grouppermission)

    // Returns the GroupPermissionID
    uadmin.Trail(uadmin.INFO, "String() returns %s.", grouppermission.String())
}

Now run your application and see what happens.

Terminal

[  INFO  ]   String() returns 1.
_images/grouppermissioncreated.png

Log out your System Admin account. This time login your username and password using the user account that has group permission. Afterwards, you will see that only the Todos model is shown in the dashboard because your user account is not an admin and has no remote access to it. Now click on TODOS model.

_images/userpermissiondashboard.png

As you will see, your user account is restricted to add, edit, or delete a record in the Todo model. You can only read what is inside this model.

_images/useraddeditdeleterestricted.png

If you want to hide the Todo model in your dashboard, first of all, create a HideInDashboard() function in your todo.go inside the models folder and set the return value to “true”.

// HideInDashboard !
func (t Todo) HideInDashboard() bool {
    return true
}

Now you can do something like this in main.go:

func main(){

    // Some codes

    // Initializes the DashboardMenu
    dashboardmenu := uadmin.DashboardMenu{}

    // Assign the grouppermission, call the HideInDashboard() function
    // from todo.go, store it to the Hidden field of the dashboardmenu
    dashboardmenu.Hidden = grouppermission.HideInDashboard()

    // Checks the Dashboard Menu ID number from the grouppermission. If it
    // matches, it will update the value of the Hidden field.
    uadmin.Update(&dashboardmenu, "Hidden", dashboardmenu.Hidden, "id = ?", grouppermission.DashboardMenuID)
}

Now rerun your application using the Even Demata account and see what happens.

_images/dashboardmenuempty.png

The Todo model is now hidden from the dashboard. If you login your System Admin account, you will see in the Dashboard menu that the hidden field of the Todo model is set to true.

_images/todomodelhidden.png

uadmin.HideInDashboarder

HideInDashboarder is used to check if a model should be hidden in the dashboard.

Structure:

type HideInDashboarder interface{
    HideInDashboard() bool
}

Suppose I have five models in my dashboard: Todos, Categorys, Items, Friends, and Expressions. I want Friends and Expressions models to be hidden in the dashboard. In order to do that, go to the friend.go and expression.go inside the models folder and apply the HideInDashboard() function. Set the return value to true inside it.

friend.go

func (f Friend) HideInDashboard() bool {
    return true
}

expression.go

func (e Expression) HideInDashboard() bool {
    return true
}

Now go to the main.go and apply the following codes below inside the main function:

// Initialize the Expression and Friend models inside the modelList with
// the array type of interface
modelList := []interface{}{
    models.Expression{},
    models.Friend{},
}

// Loop the execution process based on the modelList count
for i := range modelList {

    // Returns the reflection type that represents the dynamic type of i
    t := reflect.TypeOf(modelList[i])

    // Calls the HideInDashboarder function to access the HideInDashboard()
    hideItem := modelList[i].(uadmin.HideInDashboarder).HideInDashboard()

    // Initializes the hidethismodel variable to assign the DashboardMenu
    hidethismodel := uadmin.DashboardMenu{

        // Returns the name of the model based on reflection
        MenuName: strings.Join(helper.SplitCamelCase(t.Name()), " "),

        // Returns the boolean value based on the assigned return in the
        // HideInDashboard()
        Hidden:   hideItem,
    }

    // Prints the information of the hidethismodel
    uadmin.Trail(uadmin.INFO, "MenuName: %s,  Hidden: %t", hidethismodel.MenuName, hidethismodel.Hidden)
}

Go back to your application. Open the DashboardMenu then delete the Expressions and Friends model.

_images/deletetwomodels.png

Now rerun your application and see what happens.

[  INFO  ]   MenuName: Expression,  Hidden: true
[  INFO  ]   MenuName: Friend,  Hidden: true
_images/twomodelshidden.png

As expected, Friends and Expressions models are now hidden in the dashboard. If you go to the Dashboard Menus, you will see that they are checked in the Hidden field.

_images/twomodelshiddenchecked.png

uadmin.INFO

INFO is the display tag under Trail. It is a data that is presented within a context that gives it meaning and relevance.

Type:

untyped int

See uadmin.Trail for the example.

uadmin.IsAuthenticated

IsAuthenticated returns the session of the user.

Function:

func(r *http.Request) *uadmin.Session

Parameter:

r http.Request: Is a data structure that represents the client HTTP request

See uadmin.Session for the list of fields and functions that you can use in IsAuthenticated.

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Suppose that the admin account has logined.

_images/adminhighlighted.png

Create a file named custom_todo.go inside the api folder with the following codes below:

// CustomTodoHandler !
func CustomTodoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/custom_todo")

    // Get the session key
    session := uadmin.IsAuthenticated(r)

    // If there is no value in the session, it will return the
    // LoginHandler.
    if session == nil {
        LoginHandler(w, r)
        return
    }

    // Fetch the values from a User model using session IsAuthenticated
    user := session.User
    userid := session.UserID
    username := session.User.Username
    active := session.User.Active

    // Print the result
    uadmin.Trail(uadmin.INFO, "Session / Key: %s", session)
    uadmin.Trail(uadmin.INFO, "User: %s", user)
    uadmin.Trail(uadmin.INFO, "UserID: %d", userid)
    uadmin.Trail(uadmin.INFO, "Username: %s", username)
    uadmin.Trail(uadmin.INFO, "Active: %v", active)

    // Deactivates a session
    session.Logout()
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // CustomTodoHandler
    http.HandleFunc("/custom_todo/", api.CustomTodoHandler) // <-- place it here
}

api is the folder name while CustomTodoHandler is the name of the function inside custom_todo.go.

Run your application and see what happens.

_images/customtodoapi.png

Check your terminal for the result.

[  INFO  ]   Session / Key: Pfr7edaO7bBjv9zL9j1Yi01I
[  INFO  ]   Username: System Admin
[  INFO  ]   UserID: 1
[  INFO  ]   Username: admin
[  INFO  ]   Active: true

The result is coming from the session in the dashboard.

_images/isauthenticated.png

And the values in the User model by calling the User, UserID, Username, and Active fields.

_images/usersession.png

And if you go back to the home page, your account has been logged out automatically that redirects you to the login page.

_images/loginform.png

uadmin.JSONMarshal

JSONMarshal returns the JSON encoding of v.

Function:

func(v interface{}, safeEncoding bool) ([]byte, error)

Parameters:

v interface{}: Is the variable where the model was initialized

safeEncoding bool: Ensures the security of the data

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Create a file named friend_list.go inside the api folder with the following codes below:

// FriendListHandler !
func FriendListHandler(w http.ResponseWriter, r *http.Request) {
    // r.URL.Path creates a new path called /friend_list
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/friend_list")

    // Fetch Data from DB
    friend := []models.Friend{}
    uadmin.All(&friend)

    // Place it here
    output, _ := uadmin.JSONMarshal(&friend, true)

    // Prints the output to the terminal in JSON format
    os.Stdout.Write(output)

    // Unmarshal parses the JSON-encoded data and stores the result in the
    // value pointed to by v.
    json.Unmarshal(output, &friend)

    // Prints the JSON format in the API webpage
    uadmin.ReturnJSON(w, r, friend)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // FriendListHandler
    http.HandleFunc("/friend_list/", api.FriendListHandler) // <-- place it here
}

api is the folder name while FriendListHandler is the name of the function inside friend_list.go.

Run your application and see what happens.

Terminal

[
    {
        "ID": 1,
        "DeletedAt": null,
        "Name": "John Doe",
        "Email": "john.doe@gmail.com",
        "Password": "123456",
        "Nationality": 3,
        "Invite": "https://uadmin.io/"
    }
]

API

_images/friendlistjsonmarshal.png

uadmin.Language

Language is a system in uAdmin that is used to add, modify and delete the elements of a language.

Structure:

type Language struct {
    Model
    EnglishName    string `uadmin:"required;read_only;filter;search"`
    Name           string `uadmin:"required;read_only;filter;search"`
    Flag           string `uadmin:"image;list_exclude"`
    Code           string `uadmin:"filter;read_only;list_exclude"`
    RTL            bool   `uadmin:"list_exclude"`
    Default        bool   `uadmin:"help:Set as the default language;list_exclude"`
    Active         bool   `uadmin:"help:To show this in available languages;filter"`
    AvailableInGui bool   `uadmin:"help:The App is available in this language;read_only"`
}

There are 2 functions that you can use in Language:

  • Save() - Saves the object in the database

  • String() - Returns the Code of the language

There are 2 ways you can do for initialization process using this function: one-by-one and by group.

One-by-one initialization:

func main(){
    // Some codes
    language := uadmin.Language{}
    language.EnglishName = "English Name"
    language.Name = "Name"
}

By group initialization:

func main(){
    // Some codes
    language := uadmin.Language{
        EnglishName: "English Name",
        Name: "Name",
    }
}

In this example, we will use “by group” initialization process.

Suppose the Tagalog language is not active and you want to set this to Active.

_images/tagalognotactive.png

Go to the main.go and apply the following codes below:

func main(){

    // Some codes

    // Language configurations
    language := uadmin.Language{
        EnglishName:    "Tagalog",
        Name:           "Wikang Tagalog",
        Flag:           "",
        Code:           "tl",
        RTL:            false,
        Default:        false,
        Active:         false,
        AvailableInGui: false,
    }

    // Checks the English name from the language. If it matches, it will
    // update the value of the Active field.
    uadmin.Update(&language, "Active", true, "english_name = ?", language.EnglishName)

    // Returns the Code of the language
    uadmin.Trail(uadmin.INFO, "String() returns %s.", language.String())
}

Now run your application, refresh your browser and see what happens.

Terminal

[  INFO  ]   String() returns tl.
_images/tagalogactive.png

As expected, the Tagalog language is now set to active.

uadmin.Log

Log is a system in uAdmin that is used to add, modify, and delete the status of the user activities.

Structure:

type Log struct {
    Model
    Username  string    `uadmin:"filter;read_only"`
    Action    Action    `uadmin:"filter;read_only"`
    TableName string    `uadmin:"filter;read_only"`
    TableID   int       `uadmin:"filter;read_only"`
    Activity  string    `uadmin:"code;read_only" gorm:"type:longtext"`
    RollBack  string    `uadmin:"link;"`
    CreatedAt time.Time `uadmin:"filter;read_only"`
}

There are 5 functions that you can use in Log:

ParseRecord - It means to analyze a record specifically. It uses this format as shown below:

func(a reflect.Value, modelName string, ID uint, user *User, action Action, r *http.Request) (err error)

PasswordReset - It keeps track when the user resets his password. It uses this format as shown below:

func(user string, action Action, r *http.Request) (err error)

Save() - Saves the object in the database

SignIn - It keeps track when the user signs in his account. It uses this format as shown below:

func(user string, action Action, r *http.Request) (err error)

String() - Returns the Log ID

Go to the main.go and apply the following codes below after the RegisterInlines section.

func main(){

    // Some codes

    log := uadmin.Log{
        Username:  "admin",
        Action:    uadmin.Action.Custom(0),
        TableName: "Todo",
        TableID:   1,
        Activity:  "Custom Add from the source code",
        RollBack:  "",
        CreatedAt: time.Now(),
    }

    // This will create a new log based on the information assigned in
    // the log variable.
    log.Save()

    // Returns the Log ID
    uadmin.Trail(uadmin.INFO, "String() returns %s.", log.String())
}

Now run your application and see what happens.

Terminal

[  INFO  ]   String() returns 1.
_images/logcreated.png

uadmin.LogAdd

LogAdd adds a log when a record is added.

Type:

bool

Go to the main.go and apply this function to “true”. Put it above the uadmin.Register.

func main() {
    uadmin.LogAdd = true
    uadmin.Register(
        // Some codes
    )

Run your application and go to “LOGS” model.

_images/logshighlighted.png

Suppose that you have this record in your logs as shown below:

_images/loginitialrecord.png

Go back to uAdmin dashboard then select “LOGS”.

_images/todoshighlightedlog.png

Click “Add New Todo”.

_images/addnewtodo.png

Input the name value in the text box (e.g. Read a book). Click Save button afterwards.

_images/readabook.png

Result

_images/readabookoutput.png

Now go back to the “LOGS” to see the result.

_images/logaddtrueresult.png

Exit your application for a while. Go to the main.go once again. This time, apply this function to “false”.

func main() {
    uadmin.LogAdd = false // <----
    uadmin.Register(
        // Some codes
    )

Rebuild and run your application. Go to “TODOS” model and add another data inside it.

_images/buildarobot.png

Result

_images/buildarobotoutput.png

Now go back to the “LOGS” to see the result.

_images/logaddfalseresult.png

As you can see, the log content remains the same. Well done!

See uadmin.LogRead for the continuation.

uadmin.LogDelete

LogAdd adds a log when a record is deleted.

Type:

bool

Before you proceed to this example, see uadmin.LogEdit.

Go to the main.go and apply the LogDelete function to “true”. Put it above the uadmin.Register.

func main() {
    uadmin.LogAdd = false
    uadmin.LogRead = false
    uadmin.LogEdit = false
    uadmin.LogDelete = true // <----
    uadmin.Register(
        // Some codes
    )

Run your application and go to “LOGS” model.

_images/logshighlighted.png

Suppose that you have this record in your logs as shown below:

_images/logeditfalseresult.png

Go back to uAdmin dashboard then select “LOGS”.

_images/todoshighlightedlog.png

Select any of your existing data that you wish to delete (e.g. Washing the dishes)

_images/washingthedishesdelete.png

Now go back to the “LOGS” to see the result.

_images/logdeletetrueresult.png

Exit your application for a while. Go to the main.go once again. This time, apply the LogDelete function to “false”.

func main() {
    uadmin.LogAdd = false
    uadmin.LogRead = false
    uadmin.LogEdit = false
    uadmin.LogDelete = false // <---
    uadmin.Register(
        // Some codes
    )

Rebuild and run your application. Go to “TODOS” model and delete the remaining data (e.g. Read a book).

_images/readabookdelete.png

Now go back to the “LOGS” to see the result.

_images/logdeletefalseresult.png

As you can see, the log content remains the same. Well done!

uadmin.LogEdit

LogAdd adds a log when a record is edited.

Type:

bool

Before you proceed to this example, see uadmin.LogRead.

Go to the main.go and apply the LogEdit function to “true”. Put it above the uadmin.Register.

func main() {
    uadmin.LogAdd = false
    uadmin.LogRead = false
    uadmin.LogEdit = true // <----
    uadmin.Register(
        // Some codes
    )

Run your application and go to “LOGS” model.

_images/logshighlighted.png

Suppose that you have this record in your logs as shown below:

_images/logreadfalseresult.png

Go back to uAdmin dashboard then select “LOGS”.

_images/todoshighlightedlog.png

Select any of your existing data (e.g. Build a robot)

_images/todoexistingdata.png

Change it to “Assembling the CPU” for instance.

_images/assemblingthecpu.png

Result

_images/assemblingthecpuoutput.png

Now go back to the “LOGS” to see the result.

_images/logedittrueresult.png

Exit your application for a while. Go to the main.go once again. This time, apply the LogEdit function to “false”.

func main() {
    uadmin.LogAdd = false
    uadmin.LogRead = false
    uadmin.LogEdit = false // <----
    uadmin.Register(
        // Some codes
    )

Rebuild and run your application. Go to “TODOS” model and modify any of your existing data (e.g. Assembling the CPU).

_images/buildarobot.png

Change it to “Washing the dishes” for instance.

_images/washingthedishes.png

Result

_images/washingthedishesresult.png

Now go back to the “LOGS” to see the result.

_images/logeditfalseresult.png

As you can see, the log content remains the same. Well done!

See uadmin.LogDelete for the continuation.

uadmin.Login

Login returns the pointer of User and a bool for Is OTP Required.

Function:

func(r *http.Request, username string, password string) (*uadmin.User, bool)

Parameters:

r http.Request: Is a data structure that represents the client HTTP request

username string: Is the account username

password string: Is the password of the user account

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Create a file named info.go inside the api folder with the following codes below:

// InfoHandler !
func InfoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/info")
    fmt.Println(uadmin.Login(r, "admin", "admin")) // <-- place it here
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // InfoHandler
    http.HandleFunc("/info/", api.InfoHandler) // <-- place it here
}

api is the folder name while InfoHandler is the name of the function inside info.go.

Run your application and see what happens.

_images/infoapi.png

Check your terminal for the result.

System Admin false

The result is coming from the user in the dashboard.

_images/systemadminotphighlighted.png

uadmin.Login2FA

Login2FA returns the pointer of User with a two-factor authentication.

Function:

func(r *http.Request, username string, password string, otpPass string) *uadmin.User

Parameters:

r http.Request: Is a data structure that represents the client HTTP request

username string: Is the account username

password string: Is the password of the user account

otpPass string: Is the OTP code assigned by your terminal

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

First of all, activate the OTP Required in your System Admin account.

_images/otprequired.png

Afterwards, logout your account then login again to get the OTP verification code in your terminal.

_images/loginformwithotp.png
[  INFO  ]   User: admin OTP: 445215

Now create a file named info.go inside the api folder with the following codes below:

package api

import (
    "fmt"
    "net/http"
    "strings"

    "github.com/uadmin/uadmin"
)

// InfoHandler !
func InfoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/info")

    // Place it here
    fmt.Println(uadmin.Login2FA(r, "admin", "admin", "445215"))
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // InfoHandler
    http.HandleFunc("/info/", api.InfoHandler) // <-- place it here
}

api is the folder name while InfoHandler is the name of the function inside info.go.

Run your application and see what happens.

_images/infoapi.png

Check your terminal for the result.

System Admin

uadmin.Logout

Logout deactivates the session.

Function:

func(r *http.Request)

Parameter:

r http.Request: Is a data structure that represents the client HTTP request

Suppose that the admin account has logined.

_images/adminhighlighted.png

Create a file named logout.go inside the api folder with the following codes below:

// LogoutHandler !
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/logout")
    uadmin.Logout(r) // <-- place it here
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // LogoutHandler
    http.HandleFunc("/logout/", api.LogoutHandler)) // <-- place it here
}

api is the folder name while LogoutHandler is the name of the function inside logout.go.

Run your application and see what happens.

_images/logoutapi.png

Refresh your browser and see what happens.

_images/loginform.png

Your account has been logged out automatically that redirects you to the login page.

uadmin.LogRead

LogRead adds a log when a record is read.

Type:

bool

Before you proceed to this example, see uadmin.LogAdd.

Go to the main.go and apply the LogRead function to “true”. Put it above the uadmin.Register.

func main() {
    uadmin.LogAdd = false
    uadmin.LogRead = true // <----
    uadmin.Register(
        // Some codes
    )

Run your application and go to “LOGS” model.

_images/logshighlighted.png

Suppose that you have this record in your logs as shown below:

_images/logaddfalseresult.png

Go back to uAdmin dashboard then select “LOGS”.

_images/todoshighlightedlog.png

Select any of your existing data.

_images/todoexistingdata.png

Result

_images/readabook.png

Now go back to the “LOGS” to see the result.

_images/logreadtrueresult.png

Exit your application for a while. Go to the main.go once again. This time, apply the LogRead function to “false”.

func main() {
    uadmin.LogAdd = false
    uadmin.LogRead = false // <----
    uadmin.Register(
        // Some codes
    )

Rebuild and run your application. Go to “TODOS” model and add select any of your existing data.

_images/todoexistingdata.png

Result

_images/readabook.png

Now go back to the “LOGS” to see the result.

_images/logreadfalseresult.png

As you can see, the log content remains the same. Well done!

See uadmin.LogEdit for the continuation.

uadmin.MaxImageHeight

MaxImageHeight sets the maximum height of an image.

Type:

int

See uadmin.MaxImageWidth for the example.

uadmin.MaxImageWidth

MaxImageWidth sets the maximum width of an image.

Type:

int

Let’s set the MaxImageWidth to 360 pixels and the MaxImageHeight to 240 pixels.

func main() {
    // Some codes
    uadmin.MaxImageWidth = 360      // <--  place it here
    uadmin.MaxImageHeight = 240     // <--  place it here
}

uAdmin has a feature that allows you to customize your own profile. In order to do that, click the profile icon on the top right corner then select admin as highlighted below.

_images/adminhighlighted.png

By default, there is no profile photo inserted on the top left corner. If you want to add it in your profile, click the Choose File button to browse the image on your computer.

_images/choosefilephotohighlighted.png

Let’s pick a photo that surpasses the MaxImageWidth and MaxImageHeight values.

_images/widthheightbackground.png

Once you are done, click Save Changes on the left corner and refresh the webpage to see the output.

_images/profilepicadded.png

As expected, the profile pic will be uploaded to the user profile that automatically resizes to 360x240 pixels.

uadmin.MaxUploadFileSize

MaxUploadFileSize is the maximum upload file size in kilobytes.

Type:

int64

Go to the main.go. Let’s set the MaxUploadFileSize value to 1024. 1024 is equivalent to 1 MB.

func main() {
    // Some codes
    uadmin.MaxUploadFileSize = 1024     // <--  place it here
}

Run the application, go to your profile and upload an image that exceeds the MaxUploadFileSize limit. If you click Save changes…

_images/noprofilepic.png

The profile picture has failed to upload in the user profile because the file size is larger than the limit.

uadmin.Model

Model is the standard struct to be embedded in any other struct to make it a model for uAdmin.

Structure:

type Model struct {
    ID        uint       `gorm:"primary_key"`
    DeletedAt *time.Time `sql:"index"`
}

In every struct, uadmin.Model must always come first before creating a field.

type (struct_name) struct{
    uadmin.Model // <-- place it here
    // Some codes here
}

uadmin.ModelSchema

ModelSchema is a representation of a plan or theory in the form of an outline or model.

Structure:

type ModelSchema struct {
    Name          string // Name of the Model
    DisplayName   string // Display Name of the model
    ModelName     string // URL
    ModelID       uint
    Inlines       []*ModelSchema
    InlinesData   []listData
    Fields        []F
    IncludeFormJS []string
    IncludeListJS []string
    FormModifier  func(*uadmin.ModelSchema, interface{}, *uadmin.User)
    ListModifier  func(*uadmin.ModelSchema, *uadmin.User) (string, []interface{})
}

Here are the following fields and their definitions:

  • Name - The name of the Model

  • DisplayName - A human readable version of the name of the Model

  • ModelName - The same as the Name but in small letters.

  • ModelID - (Data) A place holder to store the primary key of a single row for form processing

  • Inlines - A list of associated inlines to this model

  • InlinesData - (Data) A place holder to store the data of the inlines

  • Fields - A list of uadmin.F type representing the fields of the model

  • IncludeFormJS - A list of string where you could add URLs to javascript files that uAdmin will run when a form view of this model is rendered

  • IncludeListJS - A list of string where you could add URLs to javascript files that uAdmin will run when a list view of this model is rendered

  • FormModifier - A function that you could pass that will allow you to modify the schema when rendering a form. It will pass to you the a pointer to the schema so you could modify it and a copy of the Model that is being rendered and the user access it to be able to customize per user (or per user group).

  • ListModifier - A function that you could pass that will allow you to modify the schema when rendering a list. It will pass to you the a pointer to the schema so you could modify it and the user access it to be able to customize per user (or per user group).

There is a function that you can use in ModelSchema:

  • FieldByName - Calls the name of the field inside the function. It uses this format as shown below:

func(a string) *uadmin.F

Structure:

modelschema.FieldByName("Name").XXXX = Value

XXXX has many things: See uadmin.F for the list. It is an alternative way of changing the feature of the field rather than using Tags. For more information, see Tag Reference.

There are 2 ways you can do for initialization process using this function: one-by-one and by group.

One-by-one initialization:

func main(){
    // Some codes
    modelschema := uadmin.ModelSchema{}
    modelschema.Name = "Name"
    modelschema.DisplayName = "Display Name"
}

By group initialization:

func main(){
    // Some codes
    modelschema := uadmin.ModelSchema{
        Name: "Name",
        DisplayName: "Display Name",
    }
}

In this example, we will use “by group” initialization process.

Before you proceed to this example, see uadmin.F.

Example #1: Initializing names and fields

Go to the main.go and apply the following codes below:

func main(){
    // Some codes
    // uadmin.F codes here
    modelschema := uadmin.ModelSchema{
        Name:        "Expressions",
        DisplayName: "What's on your mind?",
        ModelName:   "expression",
        ModelID:     13,

        // f1 and f2 are initialized variables in uadmin.F
        Fields:      []uadmin.F{f1, f2},
    }
}

The code above shows an initialized modelschema struct using the Name, DisplayName, ModelName, ModelID, and Fields.

See uadmin.Schema for the continuation of this example.

Example #2: Applying FormModifier and ListModifier

Functions:

// FormModifier
func(*uadmin.ModelSchema, interface{}, *uadmin.User)

// ListModifier
func(*uadmin.ModelSchema, *uadmin.User) (string, []interface{})

uadmin.ModelSchema has the following fields and their definitions:

  • Name - The name of the Model

  • DisplayName - A human readable version of the name of the Model

  • ModelName - The same as the Name but in small letters.

  • ModelID - (Data) A place holder to store the primary key of a single row for form processing

  • Inlines - A list of associated inlines to this model

  • InlinesData - (Data) A place holder to store the data of the inlines

  • Fields - A list of uadmin.F type representing the fields of the model

  • IncludeFormJS - A list of string where you could add URLs to javascript files that uAdmin will run when a form view of this model is rendered

  • IncludeListJS - A list of string where you could add URLs to javascript files that uAdmin will run when a list view of this model is rendered

  • FormModifier - A function that you could pass that will allow you to modify the schema when rendering a form. It will pass to you the a pointer to the schema so you could modify it and a copy of the Model that is being rendered and the user access it to be able to customize per user (or per user group).

  • ListModifier - A function that you could pass that will allow you to modify the schema when rendering a list. It will pass to you the a pointer to the schema so you could modify it and the user access it to be able to customize per user (or per user group).

interface{} is the parameter used to cast or access the model to modify the fields.

uadmin.User has the following fields and their definitions:

  • Username - The username that you can use in login process and CreatedBy which is a reserved word in uAdmin

  • FirstName - The given name of the user

  • LastName - The surname of the user

  • Password - A secret word or phrase that must be used to gain admission to something. This field is automatically hashed for security protection.

  • Email - A method of exchanging messages between people using electronic devices.

  • Active - Checks whether the user is logged in

  • Admin - Checks whether the user is authorized to access all features in the system

  • RemoteAccess - Checks whether the user has access to remote devices

  • UserGroup - Returns the GroupName

  • UserGroupID - An ID to access the UserGroup

  • Photo - Profile picture of the user

  • LastLogin - The date when the user last logged in his account

  • ExpiresOn - The date when the user account expires

  • OTPRequired - Checks whether the OTP is Active

  • OTPSeed - Private field for OTP

_images/userfields.png

First of all, make sure that your non-admin account has Read and Add access user permission to the Todo model.

_images/userpermissionjohndoe.png

Now go to todo.go in the models folder. Create a RequiredFormFilter function that holds s as the pointer of uadmin.ModelSchema and u as the pointer of uadmin.User. This function implementation is the structure of a FormModifier in ModelSchema.

// Todo model ...
type Todo struct {
    uadmin.Model
    Name        string
    Description string `uadmin:"html"`
    TargetDate  time.Time
    Progress    int `uadmin:"progress_bar"`
}

// RequiredFormFilter makes Name and Description required if the user is not
// an admin and the Name and Description fields are empty strings.
func RequiredFormFilter(s *uadmin.ModelSchema, m interface{}, u *uadmin.User) {
    // Casts an interface to the Todo model
    t, _ := m.(*Todo)

    // Check whether the user is not an admin and the Name and Description
    // fields are empty strings
    if !u.Admin && t.Name == "" && t.Description == "" {
        // Set the Name and Description required fields
        s.FieldByName("Name").Required = true
        s.FieldByName("Description").Required = true
    }
}

Inside the main function, create a Schema Form Modifier that calls the Todo model. Place it after the Register functions.

func main(){
    // Initialize docS variable that calls the Todo model in the schema
    docS := uadmin.Schema["todo"]

    // Assigns RequiredFormFilter to the FormModifier
    docS.FormModifier = models.RequiredFormFilter

    // Pass back to the schema of Todo model
    uadmin.Schema["todo"] = docS
}

Use any of your existing accounts that is not an admin. Here’s the result if you are adding a new record:

_images/namedescriptionrequired.png

Now let’s apply the ListModifier in todo.go. As an admin, you want your non-admin user to limit the records that they can see in the Todo model. In order to do that, let’s add another field called “AssignedTo” with the type uadmin.User.

// Todo model ...
type Todo struct {
    uadmin.Model
    Name         string
    Description  string `uadmin:"html"`
    TargetDate   time.Time
    Progress     int `uadmin:"progress_bar"`
    AssignedTo   uadmin.User
    AssignedToID uint
}

// AssignedToListFilter is a function that assigns the user ID to the query.
// If they match, the user can see the record assigned to him.
func AssignedToListFilter(m *uadmin.ModelSchema, u *uadmin.User) (string, []interface{}) {
    // Check whether the user is not an admin
    if !u.Admin {
        // Returns the AssignedToID with the value of UserID
        return "assigned_to_id = ?", []interface{}{u.ID}
    }
    // Returns nothing
    return "", []interface{}{}
}

Inside the main function, create a Schema List Modifier that calls the Todo model. Place it after the docs.FormModifier declaration.

func main(){
    // Some codes

    // Assigns AssignedToListFilter to the ListModifier
    docS.ListModifier = models.AssignedToListFilter
}

Login your admin account and create at least five records with the AssignedTo value.

_images/todofiverecordsassignedto.png

Now login any of your non-admin account and see what happens.

_images/assignedtovisible.png

Congrats! Now you know how to use the FormModifier and ListModifier functions in ModelSchema.

uadmin.NewModel

NewModel creates a new model from a model name.

Function:

func(modelName string, pointer bool) (reflect.Value, bool)

Parameters:

modelName string: Is the model you want to call in the function

pointer bool Points to the interface

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Suppose I have four records in my Category model.

  • Education ID = 4

  • Family ID = 3

  • Work ID = 2

  • Travel ID = 1

_images/categorylist.png

Create a file named custom_todo.go inside the api folder with the following codes below:

// CustomTodoHandler !
func CustomTodoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/custom_todo")

    res := map[string]interface{}{}

    // Call the category model and set the pointer to true
    m, _ := uadmin.NewModel("category", true)

    // Fetch the records of the category model
    uadmin.Get(m.Interface(), "id = ?", 3)

    // Assign the m.Interface() to the newmodel
    newmodel := m.Interface()

    // Print the result in JSON format
    res["status"] = "ok"
    res["category"] = newmodel
    uadmin.ReturnJSON(w, r, res)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // CustomTodoHandler
    http.HandleFunc("/custom_todo/", api.CustomTodoHandler) // <-- place it here
}

api is the folder name while CustomTodoHandler is the name of the function inside custom_todo.go.

Run your application and see what happens.

_images/newmodeljson.png

uadmin.NewModelArray

NewModelArray creates a new model array from a model name.

Function:

func(modelName string, pointer bool) (reflect.Value, bool)

Parameters:

modelName string: Is the model you want to call in the function

pointer bool Points to the interface

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Suppose I have four records in my Category model.

_images/categorylist.png

Create a file named custom_todo.go inside the api folder with the following codes below:

// CustomTodoHandler !
func CustomTodoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/custom_todo")

    res := map[string]interface{}{}

    // Call the category model and set the pointer to true
    m, _ := uadmin.NewModelArray("category", true)

    // Fetch the records of the category model
    uadmin.Filter(m.Interface(), "id >= ?", 1)

    // Assign the m.Interface() to the newmodelarray
    newmodelarray := m.Interface()

    // Print the result in JSON format
    res["status"] = "ok"
    res["category"] = newmodelarray
    uadmin.ReturnJSON(w, r, res)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // CustomTodoHandler
    http.HandleFunc("/custom_todo/", api.CustomTodoHandler) // <-- place it here
}

api is the folder name while CustomTodoHandler is the name of the function inside custom_todo.go.

Run your application and see what happens.

_images/newmodelarrayjson.png

uadmin.OK

OK is the display tag under Trail. It is a status to show that the application is doing well.

Type:

untyped int

See uadmin.Trail for the example.

uadmin.OTPAlgorithm

OTPAlgorithm is the hashing algorithm of OTP.

Type:

string

There are 3 different algorithms:

  • sha1 (default)

  • sha256

  • sha512

You can apply any of these in main.go.

func main(){
    uadmin.OTPAlgorithm = "sha256"
    // OR
    uadmin.OTPAlgorithm = "sha512"
}

uadmin.OTPDigits

OTPDigits is the number of digits for the OTP.

Type:

int

Go to the main.go and set the OTPDigits to 8.

func main() {
    // Some codes
    uadmin.OTPDigits = 8 // <--  place it here
}

Run your application, login your account, and check your terminal afterwards to see the OTP verification code assigned by your system.

[  INFO  ]   User: admin OTP: 90401068

As shown above, it has 8 OTP digits.

uadmin.OTPPeriod

OTPPeriod is the number of seconds for the OTP to change.

Type:

uint

Go to the main.go and set the OTPPeriod to 10 seconds.

func main() {
    // Some codes
    uadmin.OTPPeriod = uint(10) // <--  place it here
}

Run your application, login your account, and check your terminal afterwards to see how the OTP code changes every 10 seconds by refreshing your browser.

// Before refreshing your browser
[  INFO  ]   User: admin OTP: 433452

// After refreshing your browser in more than 10 seconds
[  INFO  ]   User: admin OTP: 185157

uadmin.OTPSkew

OTPSkew is the number of minutes to search around the OTP.

Type:

uint

Go to the main.go and set the OTPSkew to 2 minutes.

func main() {
    // Some codes
    uadmin.OTPSkew = uint(2) // <--  place it here
}

Run your application, login your account, and check your terminal afterwards to see the OTP verification code assigned by your system. Wait for more than two minutes and check if the OTP code is still valid.

After waiting for more than two minutes,

_images/loginformwithotp.png

It redirects to the same webpage which means your OTP code is no longer valid.

uadmin.PageLength

PageLength is the list view max number of records.

Type:

int

Go to the main.go and apply the PageLength function.

func main() {
    // Some codes
    uadmin.PageLength = 4  // <--  place it here
}

Run your application, go to the Item model, inside it you have 6 total elements. The elements in the item model will display 4 elements per page.

_images/pagelength.png

uadmin.Port

Port is the port used for http or https server.

Type:

int

Go to the main.go in your Todo list project and apply 8000 as a port number.

func main() {
    // Some codes
    uadmin.Port = 8000
}

If you run your code,

[   OK   ]   Initializing DB: [12/12]
[   OK   ]   Server Started: http://0.0.0.0:8000
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

In the Server Started, it will redirect you to port number 8000.

uadmin.Preload

Preload accesses the information of the fields in another model.

Function:

func(a interface{}, preload ...string) (err error)

Parameters:

a interface{}: Is the variable where the model was initialized

preload …string Is the field that you want to access with

Go to the friend.go and add the Points field inside the struct.

// Friend model ...
type Friend struct {
    uadmin.Model
    Name     string `uadmin:"required"`
    Email    string `uadmin:"email"`
    Password string `uadmin:"password;list_exclude"`
    TotalPoints int // <-- place it here
}

Now go to the todo.go and apply some business logic that will get the total points of each friend in the todo list. Let’s apply overriding save function and put it below the Todo struct.

// Save ...
func (t *Todo) Save() {
    // Save the model to DB
    uadmin.Save(t)

    // Get a list of other todo items that share the same
    // FriendID. Notice that in the filter we use friend_id which
    // is the way this is created in the DB
    todoList := []Todo{}
    uadmin.Filter(&todoList, "friend_id = ?", t.FriendID)
    progressSum := 0

    // Sum up the progress of all todos
    for _, todo := range todoList {
        progressSum += todo.Progress
    }

    // Preload the todo model to get the related points
    uadmin.Preload(t) // <-- place it here

    // Calculate the total progress
    t.Friend.TotalPoints = progressSum

    // Finally save the Friend
    uadmin.Save(&t.Friend)
}

Suppose you have ten records in your Todo model.

_images/tendataintodomodel.png

Now go to the Friend model and see what happens.

_images/friendpoints.png

In my list, Willie Revillame wins 85 points and Even Demata wins 130 points.

uadmin.PublicMedia

PublicMedia allows public access to media handler without authentication.

Type:

bool

For instance, my account was not signed in.

_images/loginform.png

And you want to access travel.png inside your media folder.

_images/mediapath.png

Go to the main.go and apply this function as “true”. Put it above the uadmin.Register.

func main() {
    uadmin.PublicMedia = true // <-- place it here
    uadmin.Register(
        // Some codes
    )
}

Result

_images/publicmediaimage.png

uadmin.Register

Register is used to register models to uAdmin.

Function:

func(m ...interface{})

Parameter:

m …interface{}: Is the model that you want to add in the dashboard

Create an internal Todo model inside the main.go. Afterwards, call the Todo{} inside the uadmin.Register so that the application will identify the Todo model to be added in the dashboard.

// Todo model ...
type Todo struct {
    uadmin.Model
    Name        string
    Description string `uadmin:"html"`
    TargetDate  time.Time
    Progress    int `uadmin:"progress_bar"`
}

func main() {
    uadmin.Register(Todo{}) // <-- place it here
}

Output

_images/uadmindashboard.png

If you click the Todos model, it will display this result as shown below.

_images/todomodel.png

uadmin.RegisterInlines

RegisterInlines is a function to register a model as an inline for another model

Function:

func RegisterInlines(model interface{}, fk map[string]string)

Parameters:

model (struct instance): Is the model that you want to add inlines to.

fk (map[interface{}]string): This is a map of the inlines to be added to the model. The map’s key is the name of the model of the inline and the value of the map is the foreign key field’s name.

Example:

type Person struct {
    uadmin.Model
    Name string
}

type Card struct {
    uadmin.Model
    PersonID uint
    Person   Person
}

func main() {
    // ...
    uadmin.RegisterInlines(Person{}, map[string]string{
        "Card": "PersonID",
    })
    // ...
}

uadmin.ReportingLevel

ReportingLevel is the standard reporting level.

Type:

int

There are 6 different levels:

  • DEBUG

  • WORKING

  • INFO

  • OK

  • WARNING

  • ERROR

Let’s set the ReportingLevel to 1 to show that the debugging process is working.

func main() {
    // Some codes
    uadmin.ReportingLevel = 1 // <--  place it here
}

Result

[   OK   ]   Initializing DB: [12/12]
[   OK   ]   Server Started: http://0.0.0.0:8080
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

What if I set the value to 5?

func main() {
    // Some codes
    uadmin.ReportingLevel = 5 // <--  place it here
}

Result

[   OK   ]   Initializing DB: [12/12]
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

The database was initialized. The server has started. However the error message did not show up because the reporting level is assigned to 5 which is ERROR.

uadmin.ReportTimeStamp

ReportTimeStamp set this to true to have a time stamp in your logs.

Type:

bool

Go to the main.go and set the ReportTimeStamp value as true.

func main() {
    // Some codes
    uadmin.ReportTimeStamp = true // <--  place it here
}

If you run your code,

[   OK   ]   Initializing DB: [12/12]
2018/11/07 08:52:14 [   OK   ]   Server Started: http://0.0.0.0:8080
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

uadmin.ReturnJSON

ReturnJSON returns JSON to the client.

Function:

func(w http.ResponseWriter, r *http.Request, v interface{})

Parameters:

w http.ResponseWriter: Assembles the HTTP server’s response; by writing to it, we send data to the HTTP client

r http.Request Is a data structure that represents the client HTTP request

v interface{} Is the arbitrary JSON objects and arrays that you want to return with

See Tutorial Part 7 - Introduction to API for the example.

uadmin.RootURL

RootURL is where the listener is mapped to.

Type:

string

Go to the main.go and apply this function as “/admin/”. Put it above the uadmin.Register.

func main() {
    uadmin.RootURL = "/admin/" // <-- place it here
    uadmin.Register(
        // Some codes
    )
}

Result

_images/rooturladmin.png

uadmin.Salt

Salt is extra salt added to password hashing.

Type:

string

Go to the friend.go and apply the following codes below:

// This function hashes a password with a salt.
func hashPass(pass string) string {
    // Generates a random string
    uadmin.Salt = uadmin.GenerateBase64(20)

    // Combine salt and password
    password := []byte(pass + uadmin.Salt)

    // Returns the bcrypt hash of the password at the given cost
    hash, err := bcrypt.GenerateFromPassword(password, 12)
    if err != nil {
        log.Fatal(err)
    }

    // Returns the string of hash value
    return string(hash)
}

// Save !
func (f *Friend) Save() {

    // Calls the function of hashPass to store the value in the password
    // field.
    f.Password = hashPass(f.Password)

    // Override save
    uadmin.Save(f)
}

Now go to the Friend model and put the password as 123456. Save it and check the result.

_images/passwordwithsalt.png

uadmin.Save

Save saves the object in the database.

Function:

func(a interface{}) (err error)

Parameter:

a interface{}: Is the model that you want to save with

Let’s add an Invite field in the friend.go that will direct you to his website. In order to do that, set the field name as “Invite” with the tag “link”.

// Friend model ...
type Friend struct {
    uadmin.Model
    Name        string
    Email       string
    Password    string
    Nationality string
    Invite      string `uadmin:"link"`
}

To make it functional, add the overriding save function after the Friend struct.

// Save !
func (f *Friend) Save() {
    f.Invite = "https://www.google.com/"
    uadmin.Save(f) // <-- place it here
}

Run your application, go to the Friends model and update the elements inside. Afterwards, click the Invite button on the output structure and see what happens.

_images/invitebuttonhighlighted.png

Result

_images/googlewebsitescreen.png

uadmin.Schema

Schema is the global schema of the system.

Structure:

map[string]uadmin.ModelSchema

Before you proceed to this example, see uadmin.ModelSchema.

Go to the main.go and apply the following codes below:

func main(){
    // Some codes
    // uadmin.F codes here
    // uadmin.ModelSchema codes here

    // Sets the actual name in the field from a modelschema
    uadmin.Schema[modelschema.ModelName].FieldByName("Name").DisplayName = modelschema.DisplayName

    // Generates the converted string value of two fields combined
    uadmin.Schema[modelschema.ModelName].FieldByName("Name").DefaultValue = modelschema.Fields[0].Value.(string) + " " + modelschema.Fields[1].Value.(string)

    // Set the Name field of an Expression model as required
    uadmin.Schema[modelschema.ModelName].FieldByName("Name").Required = true
}

Alternative/shortcut way:

func main(){
    // Sets the actual name in the field from a modelschema
    modelschema.FieldByName("Name").DisplayName = modelschema.DisplayName

    // Generates the converted string value of two fields combined
    modelschema.FieldByName("Name").DefaultValue = modelschema.Fields[0].Value.(string) + " " + modelschema.Fields[1].Value.(string)

    // Set the Name field of an Expression model as required
    modelschema.FieldByName("Name").Required = true
}

Now run your application, go to the Expression model and see what happens.

The name of the field has changed to “What’s on your mind?”

_images/expressiondisplayname.png

Click Add New Expression button at the top right corner and see what happens.

_images/expressionrequireddefault.png

Well done! The Name field is now set to required and the value has automatically generated using the Schema function.

uadmin.SendEmail

SendEmail sends email using system configured variables.

Function:

func(to, cc, bcc []string, subject, body string) (err error)

Parameters:

to []string: This is who you are primarily writing the email to, it’s clear to both the writer and the recipient who is writing the email and to whom it intended.

cc []string: This means carbon copy and it includes people who might be interested in knowing that there was an email between the sender and the primary TO, typically CC’s are not meant to respond, only the primary sender. Everyone can see who was included in the To and CC.

bcc []string: This means blind carbon copy. The sender has added people that the receiving TO and CC are not able to see as a part of the email, someone on BCC is not to respond and they will not be included in the response from the TO or CC. BCC is often used to include a stakeholder like a boss to make sure they are aware of a situation but they can’t respond. 3

subject string: This means what your email content is all about.

body string: This means the content of your email. It would be either a job application, the letter of your friend, notifications from your subscribed website, etc.

Go to the main.go and apply the following codes below:

func main(){

    // Some codes

    // Email configurations
    uadmin.EmailFrom = "myemail@integritynet.biz"
    uadmin.EmailUsername = "myemail@integritynet.biz"
    uadmin.EmailPassword = "abc123"
    uadmin.EmailSMTPServer = "smtp.integritynet.biz"
    uadmin.EmailSMTPServerPort = 587

    // Place it here
    uadmin.SendEmail([]string{"myemail@integritynet.biz"}, []string{}, []string{}, "Todo List", "Here are the tasks that I should have done today.")
}

Once you are done, open your email account. You will receive an email from a sender.

_images/sendemailnotification.png

uadmin.Session

Session is an activity that a user with a unique IP address spends on a Web site during a specified period of time. 2

Structure:

type Session struct {
    Model
    Key        string
    User       User `gorm:"ForeignKey:UserID" uadmin:"filter"`
    UserID     uint `fk:"true" displayName:"User"`
    LoginTime  time.Time
    LastLogin  time.Time
    Active     bool   `uadmin:"filter"`
    IP         string `uadmin:"filter"`
    PendingOTP bool   `uadmin:"filter"`
    ExpiresOn  *time.Time
}

There are 5 functions that you can use in Session:

  • GenerateKey() - Automatically generates a random string of characters for you

  • HideInDashboard() - Return true and auto hide this from dashboard

  • Logout() - Deactivates a session

  • Save() - Saves the object in the database

  • String() - Returns the value of the Key

There are 2 ways you can do for initialization process using this function: one-by-one and by group.

One-by-one initialization:

func main(){
    // Some codes
    session := uadmin.Session{}
    session.Key = "Key"
    session.UserID = 1
}

By group initialization:

func main(){
    // Some codes
    session := uadmin.Session{
        Key:    "Key",
        UserID: 1,
    }
}

In this example, we will use “by group” initialization process.

Go to the main.go and apply the following codes below after the RegisterInlines section.

func main(){

    // Some codes

    now := time.Now()
    then := now.AddDate(0, 0, 1)
    session := uadmin.Session{

        // Generates a random string dynamically
        Key: uadmin.GenerateBase64(20),

        // UserID of System Admin account
        UserID: 1,

        LoginTime:  now,
        LastLogin:  now,
        Active:     true,
        IP:         "",
        PendingOTP: false,
        ExpiresOn:  &then,
    }

    // This will create a new session based on the information assigned in
    // the session variable.
    session.Save()

    // Returns the value of the key
    uadmin.Trail(uadmin.INFO, "String() returns %s", session.String())
}

Now run your application and see what happens.

Terminal

[  INFO  ]   String() returns 0G81O_LecZLru3CTm_Qz
_images/sessioncreated.png

The other way around is you can use GenerateKey() function instead of initializing the Key field inside the uadmin.Session. Omit the session.Save() as well because session.Logout() has the ability to save it.

func main(){
    now := time.Now()
    then := now.AddDate(0, 0, 1)
    session := uadmin.Session{

        // ------------ KEY FIELD REMOVED ------------

        // UserID of System Admin account
        UserID: 1,

        LoginTime:  now,
        LastLogin:  now,
        Active:     true,
        IP:         "",
        PendingOTP: false,
        ExpiresOn:  &then,
    }

    // Automatically generates a random string of characters for you
    session.GenerateKey()

    // Deactivates a session
    session.Logout()

    // ------------ SESSION.SAVE() REMOVED ------------

    // Returns the value of the key
    uadmin.Trail(uadmin.INFO, "String() returns %s", session.String())
}

Now run your application and see what happens.

Terminal

[  INFO  ]   String() returns 8dDjMOvX8onCVuRUJstZ1Jrl
_images/sessioncreated2.png

Suppose that “SESSIONS” model is visible in the dashboard.

_images/sessionshighlighteddashboard.png

In order to hide it, you can use HideInDashboard() built-in function from uadmin.Session. Go to the main.go and apply the following codes below:

func main(){
    // Initialize the session and dashboardmenu
    session := uadmin.Session{}
    dashboardmenu := uadmin.DashboardMenu{}

    // Checks the url from the dashboardmenu. If it matches, it will
    // update the value of the Hidden field.
    uadmin.Update(&dashboardmenu, "Hidden", session.HideInDashboard(), "url = ?", "session")
}

Now run your application, go to “DASHBOARD MENUS” and you will notice that Sessions is now hidden.

_images/sessionshidden.png

uadmin.SiteName

SiteName is the name of the website that shows on title and dashboard.

Type:

string

Go to the main.go and assign the SiteName value as Todo List.

func main() {
    // Some codes
    uadmin.SiteName = "Todo List"
}

Run your application and see the changes above the web browser.

_images/todolisttitle.png

uadmin.StartSecureServer

StartSecureServer is the process of activating a uAdmin server using a localhost IP or an apache with SSL security.

Function:

func(certFile, keyFile string)

Parameters:

certFile string: Is your public key

keyFile string: Is your private key

To enable SSL for your project, you need an SSL certificate. This is a two parts system with a public key and a private key. The public key is used for encryption and the private key is used for decryption. To get an SSL certificate, you can generate one using openssl which is a tool for generating self-signed SSL certificate.

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout priv.pem -out pub.pem

It will ask you for several certificate parameters but you can just press “Enter” and skip filling them for development.

You can change the key size by changing 2048 to a higher value like 4096. For production, you would want to get a certificate that is not self-signed to avoid the SSL error message on the browser. For that, you can buy one from any SSL vendor or you can get a free one from letsencrypt.org or follow the instructions here.

Once installed, move the pub.pem and priv.pem to your project folder.

_images/sslcertificate.png

Afterwards, go to the main.go and apply this function on the last section.

func main(){
    // Some codes
    uadmin.StartSecureServer("pub.pem", "priv.pem")
}

Once you start your app, you will notice that your terminal logs are showing a message that says https instead of http:

$ ~/go/src/github.com/username/todo$ go build; ./todo
[   OK   ]   Initializing DB: [12/12]
[   OK   ]   Server Started: https://0.0.0.0:8000
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

Let’s use this website as an example of a secure server. Click the padlock icon at the top left section then click Certificate (Valid).

_images/uadminiosecure.png

You will see the following information in the certificate viewer.

_images/certificateinfo.png

uadmin.StartServer

StartServer is the process of activating a uAdmin server using a localhost IP or an apache.

Function:

func()

Go to the main.go and put uadmin.StartServer() inside the main function.

func main() {
    // Some codes
    uadmin.StartServer() // <-- place it here
}

Now to run your code:

$ go build; ./todo
[   OK   ]   Initializing DB: [9/9]
[   OK   ]   Initializing Languages: [185/185]
[  INFO  ]   Auto generated admin user. Username: admin, Password: admin.
[   OK   ]   Server Started: http://0.0.0.0:8080
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

uadmin.Tf

Tf is a function for translating strings into any given language.

Function:

func(path string, lang string, term string, args ...interface{}) string

Parameters:

path (string): This is where to get the translation from. It is in the format of “GROUPNAME/FILENAME” for example: “models/Todo”

lang (string): Is the language code. If empty string is passed we will use the default language.

term (string): The term to translate

args (…interface{}): Is a list of arguments to fill the term with place holders


First of all, create a back-end validation function inside the todo.go.

// Validate !
func (t Todo) Validate() (errMsg map[string]string) {
    // Initialize the error messages
    errMsg = map[string]string{}

    // Get any records from the database that matches the name of
    // this record and make sure the record is not the record we are
    // editing right now
    todo := Todo{}
    system := "system"
    if uadmin.Count(&todo, "name = ? AND id <> ?", t.Name, t.ID) != 0 {
        errMsg["Name"] = uadmin.Tf("models/Todo/Name/errMsg", "", fmt.Sprintf("This todo name is already in the %s", system))
    }
    return
}

Run your application and login using “admin” as username and password.

_images/loginformadmin.png

Open “LANGUAGES” model.

_images/languageshighlighted.png

Search whatever languages you want to be available in your application. For this example, let’s choose Tagalog and set it to Active.

_images/tagalogactive.png

Open “TODOS” model and create at least one record inside it.

_images/todomodeloutput.png

Logout your account and login again. Set your language to Wikang Tagalog (Tagalog).

_images/loginformtagalog.png

Open “TODOS” model, create a duplicate record, save it and let’s see what happens.

_images/duplicaterecord.png

The error message appears. Now rebuild your application and see what happens.

[   OK   ]   Initializing DB: [9/9]
[ WARNING]   Translation of tl at 0% [0/134]

It says tl is 0% which means we have not translated yet.

From your project folder, go to static/i18n/models/todo.tl.json. Inside it, you will see a bunch of data in JSON format that says Translate Me. This is where you put your translated text. For this example, let’s translate the err_msg value in Tagalog language then save it.

_images/errmsgtagalog.png

Once you are done, go back to your application, refresh your browser and see what happens.

_images/todotagalogtranslatedtf.png

And if you rebuild your application, you will notice that uAdmin has found 1 word we have translated and is telling us we are at 1% translation for the Tagalog language.

[   OK   ]   Initializing DB: [13/13]
[ WARNING]   Translation of tl at 1% [1/134]

Congrats, now you know how to translate your sentence using uadmin.Tf.

uadmin.Theme

Theme is the name of the theme used in uAdmin.

Type:

string

uadmin.Trail

Trail prints to the log.

Function:

func(level int, msg interface{}, i ...interface{})

Parameters:

level int: This is where we apply Trail tags.

msg interface{}: Is the string of characters used for output.

i …interface{}: A variable or container that can be used to store a value in the msg interface{}.

Trail has 6 different tags:

  • DEBUG

  • WORKING

  • INFO

  • OK

  • WARNING

  • ERROR

Let’s apply them in the overriding save function under the friend.go.

// Save !
func (f *Friend) Save() {
    f.Invite = "https://uadmin.io/"
    temp := "saved"                                                  // declare temp variable
    uadmin.Trail(uadmin.DEBUG, "Your friend has been %s.", temp)     // used DEBUG tag
    uadmin.Trail(uadmin.WORKING, "Your friend has been %s.", temp)   // used WORKING tag
    uadmin.Trail(uadmin.INFO, "Your friend has been %s.", temp)      // used INFO tag
    uadmin.Trail(uadmin.OK, "Your friend has been %s.", temp)        // used OK tag
    uadmin.Trail(uadmin.WARNING, "Someone %s your friend.", temp)    // used WARNING tag
    uadmin.Trail(uadmin.ERROR, "Your friend has not been %s.", temp) // used ERROR tag
    uadmin.Save(f)
}

Run your application, go to the Friend model and save any of the elements inside it. Check your terminal afterwards to see the result.

_images/trailtagsoutput.png

The output shows the different colors per tag.

uadmin.Translate

Translate is used to get a translation from a multilingual fields.

Function:

func(raw string, lang string, args ...bool) string

Parameters:

raw string: Is the field of the model that you want to access to

lang string: Is the code of the language

args …bool: Series of arguments that returns a boolean value

Before we proceed to the example, read Tutorial Part 7 - Introduction to API to familiarize how API works in uAdmin.

Suppose I have two multilingual fields in my Item record.

_images/itementl.png

Create a file named custom_todo.go inside the api folder with the following codes below:

// CustomTodoHandler !
func CustomTodoHandler(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.TrimPrefix(r.URL.Path, "/custom_todo")

    res := map[string]interface{}{}

    item := models.Item{}

    results := []map[string]interface{}{}

    uadmin.Get(&item, "id = 1")

    results = append(results, map[string]interface{}{
        "Description (en)": uadmin.Translate(item.Description, "en"),
        "Description (tl)": uadmin.Translate(item.Description, "tl"),
    })

    res["status"] = "ok"
    res["item"] = results
    uadmin.ReturnJSON(w, r, res)
}

Establish a connection in the main.go to the API by using http.HandleFunc. It should be placed after the uadmin.Register and before the StartServer.

func main() {
    // Some codes

    // CustomTodoHandler
    http.HandleFunc("/custom_todo/", api.CustomTodoHandler) // <-- place it here
}

api is the folder name while CustomTodoHandler is the name of the function inside custom_todo.go.

Run your application and see what happens.

_images/translatejson.png

uadmin.Update

Update updates the field name and value of an interface.

Function:

func(a interface{}, fieldName string, value interface{}, query string, args ...interface{}) (err error)

Parameters:

a interface{}: Is the variable where the model was initialized

fieldName string: Is the field name that you want to access to

value interface{}: Is the value that you want to update in the field

query string: Is the command you want to execute in the database

args …interface{}: Is the series of arguments that you want to update in the query

Suppose you have one record in your Todo model.

_images/todoreadabook.png

Go to the main.go and apply the following codes below:

func main(){
    // Some codes

    // Initialize todo and id
    todo := models.Todo{}
    id := 1

    // Updates the Todo name
    uadmin.Update(&todo, "Name", "Read a magazine", "id = ?", id)
}

Now run your application, go to the Todo model and see what happens.

_images/todoreadamagazine.png

The Todo name has updated from “Read a book” to “Read a magazine”.

uadmin.User

User is a system in uAdmin that is used to add, modify and delete the elements of the user.

Structure:

type User struct {
    Model
    Username     string    `uadmin:"required;filter"`
    FirstName    string    `uadmin:"filter"`
    LastName     string    `uadmin:"filter"`
    Password     string    `uadmin:"required;password;help:To reset password, clear the field and type a new password.;list_exclude"`
    Email        string    `uadmin:"email"`
    Active       bool      `uadmin:"filter"`
    Admin        bool      `uadmin:"filter"`
    RemoteAccess bool      `uadmin:"filter"`
    UserGroup    UserGroup `uadmin:"filter"`
    UserGroupID  uint
    Photo        string `uadmin:"image"`
    LastLogin   *time.Time `uadmin:"read_only"`
    ExpiresOn   *time.Time
    OTPRequired bool
    OTPSeed     string `uadmin:"list_exclude;hidden;read_only"`
}

Here are the following fields and their definitions:

  • Username - The username that you can use in login process and CreatedBy which is a reserved word in uAdmin

  • FirstName - The given name of the user

  • LastName - The surname of the user

  • Password - A secret word or phrase that must be used to gain admission to something. This field is automatically hashed for security protection.

  • Email - A method of exchanging messages between people using electronic devices.

  • Active - Checks whether the user is logged in

  • Admin - Checks whether the user is authorized to access all features in the system

  • RemoteAccess - Checks whether the user has access to remote devices

  • UserGroup - Returns the GroupName

  • UserGroupID - An ID to access the UserGroup

  • Photo - Profile picture of the user

  • LastLogin - The date when the user last logged in his account

  • ExpiresOn - The date when the user account expires

  • OTPRequired - Checks whether the OTP is Active

  • OTPSeed - Private field for OTP

There are 9 functions that you can use in User:

  • GetActiveSession() - returns an active session key

  • GetDashboardMenu() - returns the list of models in the dashboard menu

  • GetOTP() - returns a string of OTP code

  • HasAccess - searches for the url in the modelName. Uses this format as shown below:

func(modelName string) UserPermission

Login - Returns the pointer of User and a bool for Is OTP Required. It uses this format as shown below:

func(pass string, otp string) *uadmin.Session
  • Save() - Saves the object in the database

  • String() - Returns the first name and the last name

  • Validate() - Validate user when saving from uadmin. It returns (ret map[string]string).

  • VerifyOTP - Verifies the OTP of the user. It uses this format as shown below:

func(pass string) bool

There are 2 ways you can do for initialization process using this function: one-by-one and by group.

One-by-one initialization:

func main(){
    // Some codes
    user := uadmin.User{}
    user.Username = "Username"
    user.FirstName = "First Name"
    user.LastName = "Last Name"
}

By group initialization:

func main(){
    // Some codes
    user := uadmin.User{
        Username: "Username",
        FirstName: "First Name",
        LastName: "Last Name",
    }
}

In this example, we will use “by group” initialization process.

Go to the main.go and apply the following codes below after the RegisterInlines section.

func main(){

    // Some codes

    now := time.Now()
    user := uadmin.User{
        Username:     "even",
        FirstName:    "Even",
        LastName:     "Demata",
        Password:     "123456",
        Email:        "evendemata@gmail.com",
        Active:       true,
        Admin:        false,
        RemoteAccess: false,
        UserGroupID:  1, // Front Desk
        Photo:        "/media/images/users.png",
        LastLogin:    &now,
        OTPRequired:  false,
    }

    // This will create a new user based on the information assigned in
    // the user variable.
    user.Save()

    // Returns the first name and the last name
    uadmin.Trail(uadmin.INFO, "String() returns %s.", user.String())
}

Now run your application and see what happens.

Terminal

[  INFO  ]   String() returns Even Demata.
_images/usercreated.png

Select “Even Demata” account in the list.

_images/evendematahighlighted.png

Go to the User Permission tab. Afterwards, click Add New User Permission button at the right side.

_images/addnewuserpermission.png

Set the Dashboard Menu to “Todos” model, User linked to “Even Demata”, and activate the “Read” only. It means Even Demata user account has restricted access to adding, editing and deleting a record in the Todos model.

_images/userpermissionevendemata.png

Result

_images/userpermissionevendemataoutput.png

Log out your System Admin account. This time login your username and password using the user account that has user permission. Afterwards, you will see that only the Todos model is shown in the dashboard because your user account is not an admin and has no remote access to it.

_images/userpermissiondashboard.png

Now go back to the main.go and apply the following codes below:

func main(){
    // Initialize the User function
    user := uadmin.User{}

    // Fetch the username record as "even" from the user
    uadmin.Get(&user, "username = ?", "even")

    // Print the results
    fmt.Println("GetActiveSession() is", user.GetActiveSession())
    fmt.Println("GetDashboardMenu() is", user.GetDashboardMenu())
    fmt.Println("GetOTP() is", user.GetOTP())
    fmt.Println("HasAccess is", user.HasAccess("todo"))
}

Run your application and check your terminal to see the results.

GetActiveSession() is GOzo21lIBCIaj3YkXJsCZXnj
GetDashboardMenu() is [Todos]
GetOTP() is 251553
HasAccess is 1

Take note the value of the GetOTP(). Go to the main.go again and apply the following codes below:

func main(){
    user := uadmin.User{}
    uadmin.Get(&user, "username = ?", "even")

    // First parameter is password and second parameter is the value from
    // GetOTP()
    fmt.Println("Login is", user.Login("123456", "251553"))

    // The parameter is the value from GetOTP()
    fmt.Println("VerifyOTP is", user.VerifyOTP("251553"))
}

Run your application and check your terminal to see the results.

Login is GOzo21lIBCIaj3YkXJsCZXnj
VerifyOTP is true

If your Login does not return anything and VerifyOTP is false, it means your OTP is no longer valid. You need to use GetOTP() again to get a new one. OTP usually takes 5 minutes of validity by default.

Validate() function allows you to search if the user already exists. For instance, the username is “even” and all of the contents about him are there which was already included in the User model. Go to the main.go and apply the following codes below:

func main(){
    // Some codes
    now := time.Now()
    user := uadmin.User{
        Username:     "even",
        FirstName:    "Even",
        LastName:     "Demata",
        Password:     "123456",
        Email:        "evendemata@gmail.com",
        Active:       true,
        Admin:        false,
        RemoteAccess: false,
        UserGroupID:  1, // Front Desk
        Photo:        "/media/images/users.png",
        LastLogin:    &now,
        OTPRequired:  false,
    }

    fmt.Println("Validate is", user.Validate())
}

Run your application and check your terminal to see the results.

Validate is map[Username:Username is already Taken.]

Congrats, now you know how to configure the User fields, fetching the username record and applying the functions of the User.

uadmin.UserGroup

UserGroup is a system in uAdmin used to add, modify, and delete the group name.

Structure:

type UserGroup struct {
    Model
    GroupName string `uadmin:"filter"`
}

There are 2 functions that you can use in UserGroup:

HasAccess() - Returns the Group Permission ID. It uses this format as shown below:

func(modelName string) uadmin.GroupPermission

String() - Returns the GroupName

There are 2 ways you can do for initialization process using this function: one-by-one and by group.

One-by-one initialization:

func main(){
    // Some codes
    usergroup := uadmin.UserGroup{}
    user.GroupName = "Group Name"
}

By group initialization:

func main(){
    // Some codes
    usergroup := uadmin.UserGroup{
        GroupName: "Group Name",
    }
}

In this example, we will use “by group” initialization process.

Go to the main.go and apply the following codes below after the RegisterInlines section.

func main(){

    // Some codes

    usergroup := uadmin.UserGroup{
        GroupName: "Front Desk",
    }

    // This will create a new user group based on the information assigned
    // in the usergroup variable.
    uadmin.Save(&usergroup)

    // Returns the GroupName
    uadmin.Trail(uadmin.INFO, "String() returns %s.", usergroup.String())
}

Now run your application and see what happens.

Terminal

[  INFO  ]   String() returns Front Desk.
_images/usergroupcreated.png

Link your created user group to any of your existing accounts (example below is Even Demata).

_images/useraccountfrontdesklinked.png

Afterwards, click the Front Desk highlighted below.

_images/frontdeskhighlighted.png

Go to the Group Permission tab. Afterwards, click Add New Group Permission button at the right side.

_images/addnewgrouppermission.png

Set the Dashboard Menu to “Todos” model, User linked to “Even Demata”, and activate the “Read” only. It means Front Desk User Group has restricted access to adding, editing and deleting a record in the Todos model.

_images/grouppermissionadd.png

Result

_images/grouppermissionaddoutput.png

Log out your System Admin account. This time login your username and password using the user account that has group permission.

_images/userpermissiondashboard.png

Now go back to the main.go and apply the following codes below:

func main(){
    // Initializes the UserGroup function
    usergroup := uadmin.UserGroup{}

    // Fetches the Group Permission ID from the user
    uadmin.Get(&usergroup, "id = ?", 1)

    // Prints the HasAccess result
    fmt.Println("HasAccess is", usergroup.HasAccess("todo"))
}

Run your application and check your terminal to see the result.

HasAccess is 1

Congrats, now you know how to add the UserGroup from code, fetching the record from ID and applying the functions of the UserGroup.

uadmin.UserPermission

UserPermission sets the permission of a user handled by an administrator.

Structure:

type UserPermission struct {
    Model
    DashboardMenu   DashboardMenu `gorm:"ForeignKey:DashboardMenuID" required:"true" filter:"true" uadmin:"filter"`
    DashboardMenuID uint          `fk:"true" displayName:"DashboardMenu"`
    User            User          `gorm:"ForeignKey:UserID" required:"true" filter:"true" uadmin:"filter"`
    UserID          uint          `fk:"true" displayName:"User"`
    Read            bool          `uadmin:"filter"`
    Add             bool          `uadmin:"filter"`
    Edit            bool          `uadmin:"filter"`
    Delete          bool          `uadmin:"filter"`
}

There are 2 functions that you can use in GroupPermission:

  • HideInDashboard() - Return true and auto hide this from dashboard

  • String() - Returns the User Permission ID

There are 2 ways you can do for initialization process using this function: one-by-one and by group.

One-by-one initialization:

func main(){
    // Some codes
    userpermission := uadmin.UserPermission{}
    userpermission.DashboardMenu = dashboardmenu
    userpermission.DashboardMenuID = 1
    userpermission.User = user
    userpermission.UserID = 1
}

By group initialization:

func main(){
    // Some codes
    userpermission := uadmin.UserPermission{
        DashboardMenu: dashboardmenu,
        DashboardMenuID: 1,
        User: user,
        UserID: 1,
    }
}

In this example, we will use “by group” initialization process.

Go to the main.go and apply the following codes below after the RegisterInlines section.

func main(){

    // Some codes

    userpermission := uadmin.UserPermission{
        DashboardMenuID: 9,     // Todos
        UserID:          2,     // Even Demata
        Read:            true,
        Add:             false,
        Edit:            false,
        Delete:          false,
    }

    // This will create a new user permission based on the information
    // assigned in the userpermission variable.
    uadmin.Save(&userpermission)
}

Now run your application and see what happens.

_images/userpermissioncreated.png

Log out your System Admin account. This time login your username and password using the user account that has user permission. Afterwards, you will see that only the Todos model is shown in the dashboard because your user account is not an admin and has no remote access to it. Now click on TODOS model.

_images/userpermissiondashboard.png

As you will see, your user account is restricted to add, edit, or delete a record in the Todo model. You can only read what is inside this model.

_images/useraddeditdeleterestricted.png

If you want to hide the Todo model in your dashboard, first of all, create a HideInDashboard() function in your todo.go inside the models folder and set the return value to “true”.

// HideInDashboard !
func (t Todo) HideInDashboard() bool {
    return true
}

Now you can do something like this in main.go:

func main(){

    // Some codes

    // Initializes the DashboardMenu
    dashboardmenu := uadmin.DashboardMenu{}

    // Assign the userpermission, call the HideInDashboard() function
    // from todo.go, store it to the Hidden field of the dashboardmenu
    dashboardmenu.Hidden = userpermission.HideInDashboard()

    // Checks the Dashboard Menu ID number from the userpermission. If it
    // matches, it will update the value of the Hidden field.
    uadmin.Update(&dashboardmenu, "Hidden", dashboardmenu.Hidden, "id = ?", userpermission.DashboardMenuID)
}

Now rerun your application using the Even Demata account and see what happens.

_images/dashboardmenuempty.png

The Todo model is now hidden from the dashboard. If you login your System Admin account, you will see in the Dashboard menu that the hidden field of the Todo model is set to true.

_images/todomodelhidden.png

uadmin.Version

Version number as per Semantic Versioning 2.0.0 (semver.org)

Type:

untyped string

Let’s check what version of uAdmin are we using.

func main() {
    // Some codes
    uadmin.Trail(uadmin.INFO, uadmin.Version)
}

Result

[   OK   ]   Initializing DB: [9/9]
[  INFO  ]   0.1.0-rc.1
[   OK   ]   Server Started: http://0.0.0.0:8080
         ___       __          _
  __  __/   | ____/ /___ ___  (_)___
 / / / / /| |/ __  / __  __ \/ / __ \
/ /_/ / ___ / /_/ / / / / / / / / / /
\__,_/_/  |_\__,_/_/ /_/ /_/_/_/ /_/

You can also directly check it by typing uadmin version in your terminal.

$ uadmin version
[  INFO  ]   0.1.0-rc.1

uadmin.WARNING

WARNING is the display tag under Trail. It is the statement or event that indicates a possible problems occurring in an application.

Type:

untyped int

See uadmin.Trail for the example.

uadmin.WORKING

OK is the display tag under Trail. It is a status to show that the application is working.

Type:

untyped int

See uadmin.Trail for the example.

Reference

1

Rouse, Margaret (2018). MongoDB. Retrieved from https://searchdatamanagement.techtarget.com/definition/MongoDB

2

QuinStreet Inc. (2018). User Session. Retrieved from https://www.webopedia.com/TERM/U/user_session.html

3

Corbin, Anke (2017, Feb 27). What is the meaning of TO, CC and BCC in e-mail? Retrieved from https://www.quora.com/What-is-the-meaning-of-TO-CC-and-BCC-in-e-mail

4

(2018, September 4). Debugging. Retrieved from https://en.wikipedia.org/wiki/Debugging