admincore

package module
v0.0.0-...-9c2da45 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2017 License: MIT Imports: 39 Imported by: 0

README

QOR Admin

Instantly create a beautiful, cross platform, configurable Admin Interface and API for managing your data in minutes.

GoDoc

For security issues, please send us an email to security@getqor.com and give us time to respond BEFORE posting as an issue or reporting on public forums.

Documentation

https://doc.getqor.com/chapter2/setup.html

Features

  • Admin Interface for managing data
  • JSON API
  • Association handling
  • Search and filtering
  • Actions/Batch Actions
  • Authentication and Authorization (based on Permissions)
  • Extendability

Quick Start

package main

import (
    "fmt"
    "net/http"

    "github.com/jinzhu/gorm"
    _ "github.com/mattn/go-sqlite3"
    "github.com/qor/qor"
    "github.com/qor/admin"
)

// Create a GORM-backend model
type User struct {
  gorm.Model
  Name string
}

// Create another GORM-backend model
type Product struct {
  gorm.Model
  Name        string
  Description string
}

func main() {
  DB, _ := gorm.Open("sqlite3", "demo.db")
  DB.AutoMigrate(&User{}, &Product{})

  // Initalize
  Admin := admin.New(&qor.Config{DB: DB})

  // Create resources from GORM-backend model
  Admin.AddResource(&User{})
  Admin.AddResource(&Product{})

  // Register route
  mux := http.NewServeMux()
  // amount to /admin, so visit `/admin` to view the admin interface
  Admin.MountTo("/admin", mux)

  fmt.Println("Listening on: 9000")
  http.ListenAndServe(":9000", mux)
}

go run main.go and visit localhost:9000/admin to see the result !

General Setting

Site Name

Use SetSiteName to set QOR Admin's HTML title, the name will also be used to auto-load javascripts and stylesheet files that you can provide for customizing the admin interface.

For example, say you set the Site Name as QOR Demo, admin will look up qor_demo.js, qor_demo.css in QOR view paths, and load them if present.

Admin.SetSiteName("QOR DEMO")

Dashboard

QOR Admin provides a default dashboard page with some dummy text. If you want to customize the dashboard, you can create a file dashboard.tmpl in QOR view paths, QOR Admin will load it as golang templates when rendering the dashboard.

If you want to disable the dashboard, you can redirect it to some other page, for example:

Admin.GetRouter().Get("/", func(c *admin.Context) {
  http.Redirect(c.Writer, c.Request, "/admin/clients", http.StatusSeeOther)
})

Authentication

QOR Admin provides a flexible authorization solution. With it, you could integrate with your current authorization method.

What you need to do is implement an Auth interface like below, and set it in the Admin value.

type Auth interface {
	GetCurrentUser(*Context) qor.CurrentUser // get current user, if don't have permission, then return nil
	LoginURL(*Context) string // get login url, if don't have permission, will redirect to this url
	LogoutURL(*Context) string // get logout url, if click logout link from admin interface, will visit this page
}

Here is an example:

type Auth struct{}

func (Auth) LoginURL(*admin.Context) string {
  return "/login"
}

func (Auth) LogoutURL(*Context) string {
  return "/logout"
}

func (Auth) GetCurrentUser(c *admin.Context) qor.CurrentUser {
  if userid, err := c.Request.Cookie("userid"); err == nil {
    var user User
    if !DB.First(&user, "id = ?", userid.Value).RecordNotFound() {
      return &user
    }
  }
  return nil
}

func (u User) DisplayName() string {
  return u.Name
}

// Register Auth for QOR Admin
Admin.SetAuth(&Auth{})

Menu

Register a Menu

It is possible to define a nested menu structure for the admin interface.

Admin.AddMenu(&admin.Menu{Name: "Dashboard", Link: "/admin"})

// Register nested menu
Admin.AddMenu(&admin.Menu{Name: "menu", Link: "/link", Ancestors: []string{"Dashboard"}})

// Register menu with permission
Admin.AddMenu(&admin.Menu{Name: "Report", Link: "/admin", Permission: roles.Allow(roles.Read, "admin")})
Add Resources to a menu
Admin.AddResource(&User{})

Admin.AddResource(&Product{}, &admin.Config{Menu: []string{"Product Management"}})
Admin.AddResource(&Color{}, &admin.Config{Menu: []string{"Product Management"}})
Admin.AddResource(&Size{}, &admin.Config{Menu: []string{"Product Management"}})

Admin.AddResource(&Order{}, &admin.Config{Menu: []string{"Order Management"}})

If you don't want a resource to be displayed in the menu, pass the Invisible option:

Admin.AddResource(&User{}, &admin.Config{Invisible: true})

Internationalization

To translate admin interface to a new language, you could use i18n https://github.com/qor/i18n

Working with a Resource

Every QOR Admin Resource needs a GORM-backend model. Once you have defined the model you can create a QOR Admin resource: Admin.AddResource(&Product{})

Once a resource has been added, QOR Admin will generate the admin interface to manage it, including a RESTFul JSON API.

So for above example, you could visit localhost:9000/admin/products to manage Product in the HTML admin interface, or use the RESTFul JSON api localhost:9000/admin/products.json to perform CRUD activities.

Customizing CRUD pages

// Set attributes will be shown in the index page
// show given attributes
order.IndexAttrs("User", "PaymentAmount", "ShippedAt", "CancelledAt", "State", "ShippingAddress")
// show all attributes except `State`
order.IndexAttrs("-State")

// Set attributes will be shown in the new page
order.NewAttrs("User", "PaymentAmount", "ShippedAt", "CancelledAt", "State", "ShippingAddress")
// show all attributes except `State`
order.NewAttrs("-State")
// Structure the new form to make it tidy and clean with `Section`
product.NewAttrs(
  &admin.Section{
		Title: "Basic Information",
		Rows: [][]string{
			{"Name"},
			{"Code", "Price"},
		}
  },
  &admin.Section{
		Title: "Organization",
		Rows: [][]string{
			{"Category", "Collections", "MadeCountry"},
    }
  },
  "Description",
  "ColorVariations",
}

// Set attributes will be shown for the edit page, similiar with new page
order.EditAttrs("User", "PaymentAmount", "ShippedAt", "CancelledAt", "State", "ShippingAddress")

// Set attributes will be shown for the show page, similiar with new page
// If ShowAttrs haven't been configured, there will be no show page generated, by will show the edit from instead
order.ShowAttrs("User", "PaymentAmount", "ShippedAt", "CancelledAt", "State", "ShippingAddress")

It is possible to specify database table columns as search attributes, using SearchAttrs, the columns will be used to perform any search queries. It is also possible to specify nested relations.

// Search products with its name, code, category's name, brand's name
product.SearchAttrs("Name", "Code", "Category.Name", "Brand.Name")

If you want to fully customize the search function, you could set the SearchHandler:

order.SearchHandler = func(keyword string, context *qor.Context) *gorm.DB {
  // search orders
}
Search Center

You might want to search a broad range of resources from a single web page, in this case Search Center is for you! Simply add resources that you want to be searchable to the Admin value's search center:

// add resource `product`, `user`, `order` to search resources
Admin.AddSearchResource(product, user, order)

Search Center Online Demo

Scopes

You can define scopes to filter data with given conditions, for example:

// Only show actived users
user.Scope(&admin.Scope{Name: "Active", Handle: func(db *gorm.DB, context *qor.Context) *gorm.DB {
  return db.Where("active = ?", true)
}})
Group Scopes
order.Scope(&admin.Scope{Name: "Paid", Group: "State", Handle: func(db *gorm.DB, context *qor.Context) *gorm.DB {
  return db.Where("state = ?", "paid")
}})

order.Scope(&admin.Scope{Name: "Shipped", Group: "State", Handle: func(db *gorm.DB, context *qor.Context) *gorm.DB {
  return db.Where("state = ?", "shipped")
}})

Scopes Online Demo

Actions

QOR Admin has the notion of four action modes:

  • Bulk actions (will be shown in index page as bulk actions)
  • Edit form action (will be shown in edit page)
  • Show page action (will be shown in show page)
  • Menu item action (will be shown in table's menu)

You can register an Action of any mode using the Action method, along with Modes values to contol where to show them:

product.Action(&admin.Action{
	Name: "enable",
	Handle: func(actionArgument *admin.ActionArgument) error {
    // `FindSelectedRecords` => return selected record in bulk action mode, return current record in other mode
		for _, record := range actionArgument.FindSelectedRecords() {
			actionArgument.Context.DB.Model(record.(*models.Product)).Update("disabled", false)
		}
		return nil
	},
	Modes: []string{"index", "edit", "show", "menu_item"},
})

// Register Actions need user's input
order.Action(&admin.Action{
  Name: "Ship",
  Handle: func(argument *admin.ActionArgument) error {
    trackingNumberArgument := argument.Argument.(*trackingNumberArgument)
    for _, record := range argument.FindSelectedRecords() {
      argument.Context.GetDB().Model(record).UpdateColumn("tracking_number", trackingNumberArgument.TrackingNumber)
    }
    return nil
  },
  Resource: Admin.NewResource(&trackingNumberArgument{}),
  Modes: []string{"show", "menu_item"},
})

// the ship action's argument
type trackingNumberArgument struct {
  TrackingNumber string
}

// Use `Visible` to hide registered Action in some case
order.Action(&admin.Action{
  Name: "Cancel",
  Handle: func(argument *admin.ActionArgument) error {
    // cancel the order
  },
  Visible: func(record interface{}) bool {
    if order, ok := record.(*models.Order); ok {
      for _, state := range []string{"draft", "checkout", "paid", "processing"} {
        if order.State == state {
          return true
        }
      }
    }
    return false
  },
  Modes: []string{"show", "menu_item"},
})

Customizing the Form

By default, management pages in QOR Admin are rendered based on your resource's fields' data types and relations. The default should satisfy most use cases, however should you need to you can customize the rendering by overwritting the Meta definition.

There are some Meta types that have been predefined, including string, password, date, datetime, rich_editor, select_one, select_many and so on (see full list here: qor admin form templates). QOR Admin will auto select a type for Meta based on a field's data type. For example, if a field's type is time.Time, QOR Admin will determine datetime as the type.

// Change the Meta type of `Password` field in User resource from `string` (default value) to `password`
user.Meta(&admin.Meta{Name: "Password", Type: "password"})

// Change the Meta type of `Gender` field in User resource from `string` (default value) to `select_one`, with options `M` | `F`
user.Meta(&admin.Meta{Name: "Gender", Type: "select_one", Collection: []string{"M", "F"}})

Authorization and Permissions

Authorization in QOR Admin is based on setting Permissions per Role. QOR Admin uses https://github.com/qor/roles for Permission management, please refer to it's documentation for information on how to define Roles and Permissions.

// CRUD permission for admin users, deny create permission for manager
user := Admin.AddResource(&User{}, &admin.Config{Permission: roles.Allow(roles.CRUD, "admin").Deny(roles.Create, "manager")})

// For user's Email field, allow CRUD for admin users, deny update for manager
user.Meta(&admin.Meta{Name: "Email", Permission: roles.Allow(roles.CRUD, "admin").Deny(roles.Create, "manager")})

An automagic RESTFul API

The RESTFul API shares the same configuration as your admin interface, including actions and permissions - so after you have configured your admin interface, you will get an API for free!

Extendability

Configuring QOR Admin Resources Automatically

If your model has the following two methods defined, they will be called when registering:

func ConfigureQorResourceBeforeInitialize(resource) {
  // resource.(*admin.Resource)
}

func ConfigureQorResource(resource) {
  // resource.(*admin.Resource)
}
Configuring QOR Admin Meta Automatically

If your field's type has the following two methods defined, they will be called when registering:

func ConfigureQorMetaBeforeInitialize(meta) {
  // resource.(*admin.Meta)
}

func ConfigureMetaInterface(meta) {
  // resource.(*admin.Meta)
}
Using a Theme

A custom theme can be applied using a custom javascript and css file, for example to make a product page look super fancy. To apply a custom theme, provide the theme name using the UseTheme method, this will load assets/javascripts/fancy.js and assets/stylesheets/fancy.css from QOR view paths

product.UseTheme("fancy")
Customizing Views

QOR Admin will look up templates in QOR Admin view paths and use them to render any admin page. By placing your own templates in {current path}/app/views/qor you can extend your application by customizing it's views. If you want to customize your views from other places, you could register any new paths with admin.RegisterViewPath.

Customize Views Rules:

  • To overwrite a template, create a file with the same name under {current path}/app/views/qor.
  • To overwrite templates for a specific resource, put templates with the same name in {qor view paths}/{resource param}, for example {current path}/app/views/qor/products/index.tmpl.
  • To overwrite templates for resources using a theme, put templates with the same name in {qor view paths}/themes/{theme name}.
Registering HTTP routes

Qor admin uses Qor's Router.

router := Admin.GetRouter()

router.Get("/path", func(context *admin.Context) {
    // do something here
})

router.Post("/path", func(context *admin.Context) {
    // do something here
})

router.Put("/path", func(context *admin.Context) {
    // do something here
})

router.Delete("/path", func(context *admin.Context) {
    // do something here
})

// naming route
router.Get("/path/:name", func(context *admin.Context) {
    context.Request.URL.Query().Get(":name")
})

// regexp support
router.Get("/path/:name[world]", func(context *admin.Context) { // "/hello/world"
    context.Request.URL.Query().Get(":name")
})

router.Get("/path/:name[\\d+]", func(context *admin.Context) { // "/hello/123"
    context.Request.URL.Query().Get(":name")
})
Plugins

There are a few plugins created for QOR already, you can find some of them at https://github.com/qor, visit them to learn more about how to extend QOR.

Live DEMO

Q & A

  • How to integrate with beego
mux := http.NewServeMux()
Admin.MountTo("/admin", mux)

beego.Handler("/admin/*", mux)
beego.Run()
  • How to integrate with Gin
mux := http.NewServeMux()
Admin.MountTo("/admin", mux)

r := gin.Default()
r.Any("/admin/*w", gin.WrapH(mux))
r.Run()

License

Released under the MIT License.

Documentation

Index

Constants

View Source
const HTTPUnprocessableEntity = 422

HTTPUnprocessableEntity error status code

Variables

View Source
var (
	// ErrUnsupportedEncoder unsupported encoder error
	ErrUnsupportedEncoder = errors.New("unsupported encoder")
	// ErrUnsupportedDecoder unsupported decoder error
	ErrUnsupportedDecoder = errors.New("unsupported decoder")
)
View Source
var DefaultTransformer = &Transformer{
	Encoders: map[string][]EncoderInterface{},
	Decoders: map[string][]DecoderInterface{},
}

DefaultTransformer registered encoders, decoders for admin

View Source
var DisableCompositePrimaryKeyMode = "composite_primary_key:query:disable"
View Source
var PaginationPageCount = 20

PaginationPageCount default pagination page count

View Source
var XMLMarshalDefaultHandler = func(xmlStruct XMLStruct, e *xml.Encoder, start xml.StartElement) error {
	defaultStartElement := xml.StartElement{Name: xml.Name{Local: "XMLStruct"}}
	reflectValue := reflect.Indirect(reflect.ValueOf(xmlStruct.Result))
	res := xmlStruct.Resource
	context := xmlStruct.Context

	switch reflectValue.Kind() {
	case reflect.Map:

		if start.Name.Local == defaultStartElement.Name.Local {
			start.Name.Local = "response"
		}

		if err := e.EncodeToken(start); err != nil {
			return err
		}

		mapKeys := reflectValue.MapKeys()
		for _, mapKey := range mapKeys {
			var (
				err       error
				mapValue  = reflectValue.MapIndex(mapKey)
				startElem = xml.StartElement{
					Name: xml.Name{Space: "", Local: fmt.Sprint(mapKey.Interface())},
					Attr: []xml.Attr{},
				}
			)

			mapValue = reflect.Indirect(reflect.ValueOf(mapValue.Interface()))
			if mapValue.Kind() == reflect.Map {
				err = e.EncodeElement(xmlStruct.Initialize(mapValue.Interface(), xmlStruct.Resource), startElem)
			} else {
				err = e.EncodeElement(fmt.Sprint(reflectValue.MapIndex(mapKey).Interface()), startElem)
			}

			if err != nil {
				return err
			}
		}
	case reflect.Slice:

		if start.Name.Local == defaultStartElement.Name.Local {
			modelType := utils.ModelType(xmlStruct.Result)
			if xmlStruct.Resource != nil && modelType == utils.ModelType(xmlStruct.Resource.Value) {
				start.Name.Local = inflection.Plural(strings.Replace(xmlStruct.Resource.Name, " ", "", -1))
			} else {
				start.Name.Local = "responses"
			}
		}

		if err := e.EncodeToken(start); err != nil {
			return err
		}

		for i := 0; i < reflectValue.Len(); i++ {
			if err := e.EncodeElement(xmlStruct.Initialize(reflect.Indirect(reflectValue.Index(i)).Interface(), xmlStruct.Resource), defaultStartElement); err != nil {
				return err
			}
		}
	case reflect.Struct:

		if xmlStruct.Resource == nil || utils.ModelType(xmlStruct.Result) != utils.ModelType(xmlStruct.Resource.Value) {
			if err := e.EncodeElement(fmt.Sprint(xmlStruct.Result), start); err != nil {
				return err
			}
		} else {
			if start.Name.Local == defaultStartElement.Name.Local {
				start.Name.Local = strings.Replace(xmlStruct.Resource.Name, " ", "", -1)
			}

			if err := e.EncodeToken(start); err != nil {
				return err
			}

			metas := []*Meta{}
			switch xmlStruct.Action {
			case "index":
				metas = res.ConvertSectionToMetas(res.allowedSections(res.IndexAttrs(), context, roles.Update))
			case "edit":
				metas = res.ConvertSectionToMetas(res.allowedSections(res.EditAttrs(), context, roles.Update))
			case "show":
				metas = res.ConvertSectionToMetas(res.allowedSections(res.ShowAttrs(), context, roles.Read))
			}

			for _, meta := range metas {
				if meta.HasPermission(roles.Read, context.Context) {
					metaStart := xml.StartElement{
						Name: xml.Name{
							Space: "",
							Local: strings.Replace(meta.Label, " ", "", -1),
						},
					}

					if meta.Resource != nil && (meta.FieldStruct != nil && meta.FieldStruct.Relationship != nil && (meta.FieldStruct.Relationship.Kind == "has_one" || meta.FieldStruct.Relationship.Kind == "has_many" || meta.Type == "single_edit" || meta.Type == "collection_edit")) {
						if err := e.EncodeElement(xmlStruct.Initialize(context.RawValueOf(xmlStruct.Result, meta), meta.Resource), metaStart); err != nil {
							return err
						}
					} else {
						if err := e.EncodeElement(context.FormattedValueOf(xmlStruct.Result, meta), metaStart); err != nil {
							return err
						}
					}
				}
			}
		}
	default:
		if reflectValue.IsValid() {
			if err := e.EncodeElement(fmt.Sprint(reflectValue.Interface()), start); err != nil {
				return err
			}
		} else {
			return nil
		}
	}

	if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
		return err
	}
	return nil
}

XMLMarshalDefaultHandler default xml marshal handler, allow developers overwrite it

Functions

func RegisterViewPath

func RegisterViewPath(pth string)

RegisterViewPath register view path for all assetfs

Types

type Action

type Action struct {
	Name        string
	Label       string
	Method      string
	URL         func(record interface{}, context *Context) string
	URLOpenType string
	Visible     func(record interface{}, context *Context) bool
	Handler     func(argument *ActionArgument) error
	Modes       []string
	Resource    *Resource
	Permission  *roles.Permission
}

Action action definiation

func (Action) HasPermission

func (action Action) HasPermission(mode roles.PermissionMode, context *qor.Context) bool

HasPermission check if current user has permission for the action

func (Action) IsAllowed

func (action Action) IsAllowed(mode roles.PermissionMode, context *Context, records ...interface{}) bool

IsAllowed check if current user has permission to view the action

func (Action) ToParam

func (action Action) ToParam() string

ToParam used to register routes for actions

type ActionArgument

type ActionArgument struct {
	PrimaryValues       []string
	Context             *Context
	Argument            interface{}
	SkipDefaultResponse bool
}

ActionArgument action argument that used in handle

func (*ActionArgument) FindSelectedRecords

func (actionArgument *ActionArgument) FindSelectedRecords() []interface{}

FindSelectedRecords find selected records when run bulk actions

type Admin

type Admin struct {
	SiteName       string
	Config         *qor.Config
	I18n           I18n
	Auth           Auth
	SessionManager session.ManagerInterface
	*Transformer

	AssetFS assetfs.Interface

	Enliven *enliven.Enliven
	// contains filtered or unexported fields
}

Admin is a struct that used to generate admin/api interface

func New

func New(config *qor.Config) *Admin

New new admin with configuration

func (*Admin) AddMenu

func (admin *Admin) AddMenu(menu *Menu) *Menu

AddMenu add a menu to admin sidebar

func (*Admin) AddResource

func (admin *Admin) AddResource(value interface{}, config ...*Config) *Resource

AddResource make a model manageable from admin interface

func (*Admin) AddSearchResource

func (admin *Admin) AddSearchResource(resources ...*Resource)

AddSearchResource make a resource searchable from search center

func (Admin) GetMenu

func (admin Admin) GetMenu(name string) *Menu

GetMenu get sidebar menu with name

func (Admin) GetMenus

func (admin Admin) GetMenus() []*Menu

GetMenus get all sidebar menus for admin

func (*Admin) GetResource

func (admin *Admin) GetResource(name string) (resource *Resource)

GetResource get resource with name

func (*Admin) GetResources

func (admin *Admin) GetResources() []*Resource

GetResources get defined resources from admin

func (*Admin) GetRouter

func (admin *Admin) GetRouter() *Router

GetRouter get router from admin

func (*Admin) MountTo

func (admin *Admin) MountTo(mountTo string)

MountTo mount the service into mux (HTTP request multiplexer) with given path

func (*Admin) NewContext

func (admin *Admin) NewContext(w http.ResponseWriter, r *http.Request) *Context

NewContext new admin context

func (*Admin) NewResource

func (admin *Admin) NewResource(value interface{}, config ...*Config) *Resource

NewResource initialize a new qor resource, won't add it to admin, just initialize it

func (*Admin) NewServeMux

func (admin *Admin) NewServeMux(prefix string) *ServeMux

NewServeMux generate http.Handler for admin

func (*Admin) RegisterFuncMap

func (admin *Admin) RegisterFuncMap(name string, fc interface{})

RegisterFuncMap register view funcs, it could be used in view templates

func (*Admin) RegisterMetaConfigor

func (admin *Admin) RegisterMetaConfigor(kind string, fc func(*Meta))

RegisterMetaConfigor register configor for a kind, it will be called when register those kind of metas

func (*Admin) RegisterResourceRouters

func (admin *Admin) RegisterResourceRouters(res *Resource, actions ...string)

RegisterResourceRouters register resource to router

func (*Admin) RegisterViewPath

func (admin *Admin) RegisterViewPath(pth string)

RegisterViewPath register view path for admin

func (*Admin) SetAssetFS

func (admin *Admin) SetAssetFS(assetFS assetfs.Interface)

SetAssetFS set AssetFS for admin

func (*Admin) SetAuth

func (admin *Admin) SetAuth(auth Auth)

SetAuth set admin's authorization gateway

func (*Admin) SetSiteName

func (admin *Admin) SetSiteName(siteName string)

SetSiteName set site's name, the name will be used as admin HTML title and admin interface will auto load javascripts, stylesheets files based on its value For example, if you named it as `Qor Demo`, admin will look up `qor_demo.js`, `qor_demo.css` in QOR view paths, and load them if found

func (*Admin) T

func (admin *Admin) T(context *qor.Context, key string, value string, values ...interface{}) template.HTML

T call i18n backend to translate

type Auth

type Auth interface {
	GetCurrentUser(*Context) qor.CurrentUser
	LoginURL(*Context) string
	LogoutURL(*Context) string
}

Auth is an auth interface that used to qor admin If you want to implement an authorization gateway for admin interface, you could implement this interface, and set it to the admin with `admin.SetAuth(auth)`

type CollectionEditConfig

type CollectionEditConfig struct {
	Template string
	Max      uint
	// contains filtered or unexported fields
}

CollectionEditConfig meta configuration used for collection edit

func (*CollectionEditConfig) ConfigureQorMeta

func (collectionEditConfig *CollectionEditConfig) ConfigureQorMeta(metaor resource.Metaor)

ConfigureQorMeta configure collection edit meta

func (CollectionEditConfig) GetTemplate

func (collectionEditConfig CollectionEditConfig) GetTemplate(context *Context, metaType string) ([]byte, error)

GetTemplate get template for collection edit

type Config

type Config struct {
	Name       string
	Menu       []string
	Invisible  bool
	Priority   int
	PageCount  int
	Singleton  bool
	Permission *roles.Permission
	Themes     []ThemeInterface
}

Config admin config struct

type Context

type Context struct {
	*qor.Context
	*Searcher
	Resource     *Resource
	Admin        *Admin
	Content      template.HTML
	Action       string
	Settings     map[string]interface{}
	Result       interface{}
	RouteHandler *routeHandler
	// contains filtered or unexported fields
}

Context admin context, which is used for admin controller

func (*Context) AllowedActions

func (context *Context) AllowedActions(actions []*Action, mode string, records ...interface{}) []*Action

AllowedActions return allowed actions based on context

func (*Context) Asset

func (context *Context) Asset(layouts ...string) ([]byte, error)

func (*Context) Encode

func (context *Context) Encode(action string, result interface{}) error

func (*Context) Execute

func (context *Context) Execute(name string, result interface{})

Execute execute template with layout

func (*Context) Flash

func (context *Context) Flash(message string, typ string)

Flash set flash message

func (*Context) FormattedValueOf

func (context *Context) FormattedValueOf(value interface{}, meta *Meta) interface{}

FormattedValueOf return formatted value of a meta for current resource

func (*Context) FuncMap

func (context *Context) FuncMap() template.FuncMap

FuncMap return funcs map

func (*Context) Funcs

func (context *Context) Funcs(funcMaps template.FuncMap) *Context

Funcs set FuncMap for templates

func (*Context) Get

func (context *Context) Get(key string) interface{}

Get get context's Settings

func (*Context) GetScopes

func (context *Context) GetScopes() (menus []*scopeMenu)

GetScopes get scopes from current context

func (*Context) GetSearchableResources

func (context *Context) GetSearchableResources() (resources []*Resource)

GetSearchableResources get defined searchable resources has performance

func (*Context) JSON

func (context *Context) JSON(action string, result interface{})

JSON generate json outputs for action

func (*Context) NewResourceContext

func (context *Context) NewResourceContext(name ...interface{}) *Context

NewResourceContext new resource context

func (*Context) Pagination

func (context *Context) Pagination() *PaginationResult

Pagination return pagination information Keep visiblePageCount's pages visible, exclude prev and next link Assume there are 12 pages in total. When current page is 1 [current, 2, 3, 4, 5, 6, 7, 8, next] When current page is 6 [prev, 2, 3, 4, 5, current, 7, 8, 9, 10, next] When current page is 10 [prev, 5, 6, 7, 8, 9, current, 11, 12] If total page count less than VISIBLE_PAGE_COUNT, always show all pages

func (*Context) RawValueOf

func (context *Context) RawValueOf(value interface{}, meta *Meta) interface{}

RawValueOf return raw value of a meta for current resource

func (*Context) Render

func (context *Context) Render(name string, results ...interface{}) template.HTML

Render render template based on context

func (*Context) Set

func (context *Context) Set(key string, value interface{})

Set set context's Settings

func (*Context) URLFor

func (context *Context) URLFor(value interface{}, resources ...*Resource) string

URLFor generate url for resource value

context.URLFor(&Product{})
context.URLFor(&Product{ID: 111})
context.URLFor(productResource)

type Controller

type Controller struct {
	*Admin
	// contains filtered or unexported fields
}

Controller admin controller

func (*Controller) Action

func (ac *Controller) Action(context *Context)

Action handle action related requests

func (*Controller) Asset

func (ac *Controller) Asset(context *Context)

Asset handle asset requests

func (*Controller) Create

func (ac *Controller) Create(context *Context)

Create create data

func (*Controller) Dashboard

func (ac *Controller) Dashboard(context *Context)

Dashboard render dashboard page

func (*Controller) Delete

func (ac *Controller) Delete(context *Context)

Delete delete data

func (*Controller) Edit

func (ac *Controller) Edit(context *Context)

Edit render edit page

func (*Controller) Index

func (ac *Controller) Index(context *Context)

Index render index page

func (*Controller) New

func (ac *Controller) New(context *Context)

New render new page

func (*Controller) SearchCenter

func (ac *Controller) SearchCenter(context *Context)

SearchCenter render search center page

func (*Controller) Show

func (ac *Controller) Show(context *Context)

Show render show page

func (*Controller) Update

func (ac *Controller) Update(context *Context)

Update update data

type Decoder

type Decoder struct {
	Action   string
	Resource *Resource
	Context  *Context
	Result   interface{}
}

Decoder decoder struct used for decode

type DecoderInterface

type DecoderInterface interface {
	CouldDecode(Decoder) bool
	Decode(writer io.Writer, decoder Decoder) error
}

DecoderInterface decoder interface

type Encoder

type Encoder struct {
	Action   string
	Resource *Resource
	Context  *Context
	Result   interface{}
}

Encoder encoder struct used for encode

type EncoderInterface

type EncoderInterface interface {
	CouldEncode(Encoder) bool
	Encode(writer io.Writer, encoder Encoder) error
}

EncoderInterface encoder interface

type Filter

type Filter struct {
	Name       string
	Label      string
	Type       string
	Operations []string // eq, cont, gt, gteq, lt, lteq
	Resource   *Resource
	Handler    func(*gorm.DB, *FilterArgument) *gorm.DB
	Config     FilterConfigInterface
}

Filter filter definiation

type FilterArgument

type FilterArgument struct {
	Value    *resource.MetaValues
	Resource *Resource
	Context  *qor.Context
}

FilterArgument filter argument that used in handler

type FilterConfigInterface

type FilterConfigInterface interface {
	ConfigureQORAdminFilter(*Filter)
}

FilterConfigInterface filter config interface

type HasPermissioner

type HasPermissioner interface {
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

HasPermissioner has permission interface

type I18n

type I18n interface {
	Scope(scope string) I18n
	Default(value string) I18n
	T(locale string, key string, args ...interface{}) template.HTML
}

I18n define admin's i18n interface

type JSONTransformer

type JSONTransformer struct{}

JSONTransformer json transformer

func (JSONTransformer) CouldEncode

func (JSONTransformer) CouldEncode(encoder Encoder) bool

CouldEncode check if encodable

func (JSONTransformer) Encode

func (JSONTransformer) Encode(writer io.Writer, encoder Encoder) error

Encode encode encoder to writer as JSON

type Menu struct {
	Name         string
	Link         string
	RelativePath string
	Priority     int
	Ancestors    []string
	Permissioner HasPermissioner
	Permission   *roles.Permission
	// contains filtered or unexported fields
}

Menu admin sidebar menu definiation

func (menu *Menu) GetSubMenus() []*Menu

GetSubMenus get submenus for a menu

func (menu Menu) HasPermission(mode roles.PermissionMode, context *qor.Context) bool

HasPermission check menu has permission or not

func (menu Menu) URL() string

URL return menu's URL

type Meta

type Meta struct {
	Name            string
	Type            string
	Label           string
	FieldName       string
	Setter          func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Resource        *Resource
	Permission      *roles.Permission
	Config          MetaConfigInterface

	Metas      []resource.Metaor
	Collection interface{}
	*resource.Meta
	// contains filtered or unexported fields
}

Meta meta struct definition

func (*Meta) DBName

func (meta *Meta) DBName() string

DBName get meta's db name

func (*Meta) GetMetas

func (meta *Meta) GetMetas() []resource.Metaor

GetMetas get sub metas

func (*Meta) GetResource

func (meta *Meta) GetResource() resource.Resourcer

GetResource get resource from meta

func (Meta) HasPermission

func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool

HasPermission check has permission or not

func (*Meta) SetPermission

func (meta *Meta) SetPermission(permission *roles.Permission)

SetPermission set meta's permission

type MetaConfigInterface

type MetaConfigInterface interface {
	resource.MetaConfigInterface
}

MetaConfigInterface meta config interface

type Middleware

type Middleware struct {
	Name    string
	Handler func(*Context, *Middleware)
	// contains filtered or unexported fields
}

Middleware is a way to filter a request and response coming into your application

Register new middleware with `admin.GetRouter().Use(Middleware{
  Name: "middleware name", // use middleware with same name will overwrite old one
  Handler: func(*Context, *Middleware) {
    // do something
    // run next middleware
    middleware.Next(context)
  },
})`

It will be called in order, it need to be registered before `admin.MountTo`

func (Middleware) Next

func (middleware Middleware) Next(context *Context)

Next will call the next middleware

type Page

type Page struct {
	Page       int
	Current    bool
	IsPrevious bool
	IsNext     bool
	IsFirst    bool
	IsLast     bool
}

Page contain pagination information

type Pagination

type Pagination struct {
	Total       int
	Pages       int
	CurrentPage int
	PerPage     int
}

Pagination is used to hold pagination related information when rendering tables

type PaginationResult

type PaginationResult struct {
	Pagination Pagination
	Pages      []Page
}

type RedactorPlugin

type RedactorPlugin struct {
	Name   string
	Source string
}

type Resource

type Resource struct {
	*resource.Resource
	Config         *Config
	Metas          []*Meta
	Actions        []*Action
	SearchHandler  func(keyword string, context *qor.Context) *gorm.DB
	ParentResource *Resource
	// contains filtered or unexported fields
}

Resource is the most important thing for qor admin, every model is defined as a resource, qor admin will genetate management interface based on its definition

func (*Resource) Action

func (res *Resource) Action(action *Action) *Action

Action register action for qor resource

func (*Resource) ConvertSectionToMetas

func (res *Resource) ConvertSectionToMetas(sections []*Section) []*Meta

ConvertSectionToMetas convert section to metas

func (*Resource) ConvertSectionToStrings

func (res *Resource) ConvertSectionToStrings(sections []*Section) []string

ConvertSectionToStrings convert section to strings

func (*Resource) Decode

func (res *Resource) Decode(context *qor.Context, value interface{}) error

Decode decode context into a value

func (*Resource) EditAttrs

func (res *Resource) EditAttrs(values ...interface{}) []*Section

EditAttrs set attributes will be shown in the edit page

   // show given attributes in the new page
   order.EditAttrs("User", "PaymentAmount", "ShippedAt", "CancelledAt", "State", "ShippingAddress")
   // show all attributes except `State` in the edit page
   order.EditAttrs("-State")
You could also use `Section` to structure form to make it tidy and clean
   product.EditAttrs(
     &admin.Section{
     	Title: "Basic Information",
     	Rows: [][]string{
     		{"Name"},
     		{"Code", "Price"},
     	}},
     &admin.Section{
     	Title: "Organization",
     	Rows: [][]string{
     		{"Category", "Collections", "MadeCountry"},
     	}},
     "Description",
     "ColorVariations",
   }

func (*Resource) Filter

func (res *Resource) Filter(filter *Filter)

Filter register filter for qor resource

func (*Resource) GetAction

func (res *Resource) GetAction(name string) *Action

GetAction get defined action

func (Resource) GetAdmin

func (res Resource) GetAdmin() *Admin

GetAdmin get admin from resource

func (*Resource) GetFilters

func (res *Resource) GetFilters() []*Filter

func (*Resource) GetMeta

func (res *Resource) GetMeta(name string) *Meta

GetMeta get meta with name

func (*Resource) GetMetaOrNew

func (res *Resource) GetMetaOrNew(name string) *Meta

GetMetaOrNew get meta or initalize a new one

func (*Resource) GetMetas

func (res *Resource) GetMetas(attrs []string) []resource.Metaor

GetMetas get metas with give attrs

func (Resource) GetPrimaryValue

func (res Resource) GetPrimaryValue(request *http.Request) string

GetPrimaryValue get priamry value from request

func (*Resource) GetTheme

func (res *Resource) GetTheme(name string) ThemeInterface

GetTheme get registered theme with name

func (*Resource) IndexAttrs

func (res *Resource) IndexAttrs(values ...interface{}) []*Section

IndexAttrs set attributes will be shown in the index page

// show given attributes in the index page
order.IndexAttrs("User", "PaymentAmount", "ShippedAt", "CancelledAt", "State", "ShippingAddress")
// show all attributes except `State` in the index page
order.IndexAttrs("-State")

func (*Resource) Meta

func (res *Resource) Meta(meta *Meta) *Meta

Meta register meta for admin resource

func (*Resource) NewAttrs

func (res *Resource) NewAttrs(values ...interface{}) []*Section

NewAttrs set attributes will be shown in the new page

   // show given attributes in the new page
   order.NewAttrs("User", "PaymentAmount", "ShippedAt", "CancelledAt", "State", "ShippingAddress")
   // show all attributes except `State` in the new page
   order.NewAttrs("-State")
You could also use `Section` to structure form to make it tidy and clean
   product.NewAttrs(
     &admin.Section{
     	Title: "Basic Information",
     	Rows: [][]string{
     		{"Name"},
     		{"Code", "Price"},
     	}},
     &admin.Section{
     	Title: "Organization",
     	Rows: [][]string{
     		{"Category", "Collections", "MadeCountry"},
     	}},
     "Description",
     "ColorVariations",
   }

func (*Resource) NewResource

func (res *Resource) NewResource(value interface{}, config ...*Config) *Resource

NewResource initialize a new qor resource, won't add it to admin, just initialize it

func (Resource) ParamIDName

func (res Resource) ParamIDName() string

ParamIDName return param name for primary key like :product_id

func (*Resource) RegisterRoute

func (res *Resource) RegisterRoute(method string, relativePath string, handler requestHandler, config *RouteConfig)

RegisterRoute register route

func (*Resource) RoutePrefix

func (res *Resource) RoutePrefix() string

RoutePrefix return route prefix of resource

func (*Resource) Scope

func (res *Resource) Scope(scope *Scope)

Scope register scope for qor resource

func (*Resource) SearchAttrs

func (res *Resource) SearchAttrs(columns ...string) []string

SearchAttrs set search attributes, when search resources, will use those columns to search

    // Search products with its name, code, category's name, brand's name
	   product.SearchAttrs("Name", "Code", "Category.Name", "Brand.Name")

func (*Resource) ShowAttrs

func (res *Resource) ShowAttrs(values ...interface{}) []*Section

ShowAttrs set attributes will be shown in the show page

   // show given attributes in the show page
   order.ShowAttrs("User", "PaymentAmount", "ShippedAt", "CancelledAt", "State", "ShippingAddress")
   // show all attributes except `State` in the show page
   order.ShowAttrs("-State")
You could also use `Section` to structure form to make it tidy and clean
   product.ShowAttrs(
     &admin.Section{
     	Title: "Basic Information",
     	Rows: [][]string{
     		{"Name"},
     		{"Code", "Price"},
     	}},
     &admin.Section{
     	Title: "Organization",
     	Rows: [][]string{
     		{"Category", "Collections", "MadeCountry"},
     	}},
     "Description",
     "ColorVariations",
   }

func (*Resource) SortableAttrs

func (res *Resource) SortableAttrs(columns ...string) []string

SortableAttrs set sortable attributes, sortable attributes could be click to order in qor table

func (*Resource) ToParam

func (res *Resource) ToParam() string

ToParam used as urls to register routes for resource

func (*Resource) UseTheme

func (res *Resource) UseTheme(theme interface{}) []ThemeInterface

UseTheme use them for resource, will auto load the theme's javascripts, stylesheets for this resource

type ResourceNamer

type ResourceNamer interface {
	ResourceName() string
}

ResourceNamer is an interface for models that defined method `ResourceName`

type RichEditorConfig

type RichEditorConfig struct {
	AssetManager *Resource
	Plugins      []RedactorPlugin
	Settings     map[string]interface{}
	// contains filtered or unexported fields
}

func (*RichEditorConfig) ConfigureQorMeta

func (richEditorConfig *RichEditorConfig) ConfigureQorMeta(metaor resource.Metaor)

ConfigureQorMeta configure rich editor meta

func (RichEditorConfig) GetTemplate

func (RichEditorConfig) GetTemplate(context *Context, metaType string) ([]byte, error)

GetTemplate get customized template for meta

type RouteConfig

type RouteConfig struct {
	Resource       *Resource
	Permissioner   HasPermissioner
	PermissionMode roles.PermissionMode
	Values         map[interface{}]interface{}
}

RouteConfig config for admin routes

type Router

type Router struct {
	Prefix string
	// contains filtered or unexported fields
}

Router contains registered routers

func (*Router) Delete

func (r *Router) Delete(path string, handle requestHandler, config ...*RouteConfig)

Delete register a DELETE request handle with the given path

func (*Router) Get

func (r *Router) Get(path string, handle requestHandler, config ...*RouteConfig)

Get register a GET request handle with the given path

func (*Router) GetMiddleware

func (r *Router) GetMiddleware(name string) *Middleware

GetMiddleware get registered middleware

func (*Router) Post

func (r *Router) Post(path string, handle requestHandler, config ...*RouteConfig)

Post register a POST request handle with the given path

func (*Router) Put

func (r *Router) Put(path string, handle requestHandler, config ...*RouteConfig)

Put register a PUT request handle with the given path

func (*Router) Use

func (r *Router) Use(middleware *Middleware)

Use reigster a middleware to the router

type Scope

type Scope struct {
	Name    string
	Label   string
	Group   string
	Handler func(*gorm.DB, *qor.Context) *gorm.DB
	Default bool
}

Scope scope definiation

type Searcher

type Searcher struct {
	*Context

	Pagination Pagination
	// contains filtered or unexported fields
}

Searcher is used to search results

func (*Searcher) Filter

func (s *Searcher) Filter(filter *Filter, values *resource.MetaValues) *Searcher

Filter filter with defined filters, filter with columns value

func (*Searcher) FindMany

func (s *Searcher) FindMany() (interface{}, error)

FindMany find many records based on current conditions

func (*Searcher) FindOne

func (s *Searcher) FindOne() (interface{}, error)

FindOne find one record based on current conditions

func (*Searcher) Page

func (s *Searcher) Page(num int) *Searcher

Page set current page, if current page equal -1, then show all records

func (*Searcher) PerPage

func (s *Searcher) PerPage(num int) *Searcher

PerPage set pre page count

func (*Searcher) Scope

func (s *Searcher) Scope(names ...string) *Searcher

Scope filter with defined scopes

type Section

type Section struct {
	Resource *Resource
	Title    string
	Rows     [][]string
}

Section is used to structure forms, it could group your fields into sections, to make your form clean & tidy

product.EditAttrs(
  &admin.Section{
  	Title: "Basic Information",
  	Rows: [][]string{
  		{"Name"},
  		{"Code", "Price"},
  	}},
  &admin.Section{
  	Title: "Organization",
  	Rows: [][]string{
  		{"Category", "Collections", "MadeCountry"},
  	}},
  "Description",
  "ColorVariations",
}

func (*Section) String

func (section *Section) String() string

String stringify section

type SelectManyConfig

type SelectManyConfig struct {
	Collection               interface{} // []string, [][]string, func(interface{}, *qor.Context) [][]string, func(interface{}, *admin.Context) [][]string
	DefaultCreating          bool
	Placeholder              string
	SelectionTemplate        string
	SelectMode               string // select, select_async, bottom_sheet
	Select2ResultTemplate    template.JS
	Select2SelectionTemplate template.JS
	RemoteDataResource       *Resource
	SelectOneConfig
}

SelectManyConfig meta configuration used for select many

func (*SelectManyConfig) ConfigureQorMeta

func (selectManyConfig *SelectManyConfig) ConfigureQorMeta(metaor resource.Metaor)

ConfigureQorMeta configure select many meta

func (SelectManyConfig) GetTemplate

func (selectManyConfig SelectManyConfig) GetTemplate(context *Context, metaType string) ([]byte, error)

GetTemplate get template for selection template

type SelectOneConfig

type SelectOneConfig struct {
	Collection               interface{} // []string, [][]string, func(interface{}, *qor.Context) [][]string, func(interface{}, *admin.Context) [][]string
	Placeholder              string
	AllowBlank               bool
	DefaultCreating          bool
	SelectionTemplate        string
	SelectMode               string // select, select_async, bottom_sheet
	PrimaryField             string
	Select2ResultTemplate    template.JS
	Select2SelectionTemplate template.JS
	RemoteDataResource       *Resource
	// contains filtered or unexported fields
}

SelectOneConfig meta configuration used for select one

func (*SelectOneConfig) ConfigureQORAdminFilter

func (selectOneConfig *SelectOneConfig) ConfigureQORAdminFilter(filter *Filter)

func (*SelectOneConfig) ConfigureQorMeta

func (selectOneConfig *SelectOneConfig) ConfigureQorMeta(metaor resource.Metaor)

ConfigureQorMeta configure select one meta

func (*SelectOneConfig) FilterValue

func (selectOneConfig *SelectOneConfig) FilterValue(filter *Filter, context *Context) interface{}

func (*SelectOneConfig) GetCollection

func (selectOneConfig *SelectOneConfig) GetCollection(value interface{}, context *Context) [][]string

GetCollection get collections from select one meta

func (SelectOneConfig) GetPlaceholder

func (selectOneConfig SelectOneConfig) GetPlaceholder(*Context) (template.HTML, bool)

GetPlaceholder get placeholder

func (SelectOneConfig) GetTemplate

func (selectOneConfig SelectOneConfig) GetTemplate(context *Context, metaType string) ([]byte, error)

GetTemplate get template for selection template

type ServeMux

type ServeMux struct {
	// contains filtered or unexported fields
}

ServeMux is used to serve the admin pages

func (*ServeMux) ServeHTTP

func (serveMux *ServeMux) ServeHTTP(ctx *enliven.Context)

ServeHTTP dispatches the handler registered in the matched route

type SingleEditConfig

type SingleEditConfig struct {
	Template string
	// contains filtered or unexported fields
}

SingleEditConfig meta configuration used for single edit

func (*SingleEditConfig) ConfigureQorMeta

func (singleEditConfig *SingleEditConfig) ConfigureQorMeta(metaor resource.Metaor)

ConfigureQorMeta configure single edit meta

func (SingleEditConfig) GetTemplate

func (singleEditConfig SingleEditConfig) GetTemplate(context *Context, metaType string) ([]byte, error)

GetTemplate get template for single edit

type Theme

type Theme struct {
	Name string
}

Theme base theme config struct

func (Theme) ConfigAdminTheme

func (Theme) ConfigAdminTheme(*Resource)

ConfigAdminTheme config theme for admin resource

func (Theme) GetName

func (theme Theme) GetName() string

GetName get name from theme

func (Theme) GetViewPaths

func (Theme) GetViewPaths() []string

GetViewPaths get view paths from theme

type ThemeInterface

type ThemeInterface interface {
	GetName() string
	GetViewPaths() []string
	ConfigAdminTheme(*Resource)
}

ThemeInterface theme interface

type Transformer

type Transformer struct {
	Encoders map[string][]EncoderInterface
	Decoders map[string][]DecoderInterface
}

Transformer encoder & decoder transformer

func (*Transformer) Decode

func (transformer *Transformer) Decode(writer io.Writer, decoder Decoder) error

Decode decode data based on request content type #FIXME

func (*Transformer) Encode

func (transformer *Transformer) Encode(writer io.Writer, encoder Encoder) error

Encode encode data based on request accept type

func (*Transformer) RegisterTransformer

func (transformer *Transformer) RegisterTransformer(format string, transformers ...interface{}) error

RegisterTransformer register transformers for encode, decode

type XMLStruct

type XMLStruct struct {
	Action   string
	Resource *Resource
	Context  *Context
	Result   interface{}
}

XMLStruct used to decode resource to xml

func (XMLStruct) Initialize

func (xmlStruct XMLStruct) Initialize(value interface{}, res *Resource) XMLStruct

Initialize initialize a resource to XML Transformer

func (XMLStruct) MarshalXML

func (xmlStruct XMLStruct) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML implement MarshalXMLInterface

type XMLTransformer

type XMLTransformer struct{}

XMLTransformer xml transformer

func (XMLTransformer) CouldEncode

func (XMLTransformer) CouldEncode(encoder Encoder) bool

CouldEncode check if encodable

func (XMLTransformer) Encode

func (XMLTransformer) Encode(writer io.Writer, encoder Encoder) error

Encode encode encoder to writer as XML

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL