123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- package presets
- import (
- "fmt"
- "strings"
- "github.com/google/uuid"
- "github.com/jinzhu/inflection"
- "github.com/qor5/admin/presets/actions"
- . "github.com/qor5/ui/vuetify"
- vx "github.com/qor5/ui/vuetifyx"
- "github.com/qor5/web"
- "github.com/qor5/x/i18n"
- "github.com/qor5/x/perm"
- h "github.com/theplant/htmlgo"
- )
- type EditingBuilder struct {
- mb *ModelBuilder
- Fetcher FetchFunc
- Setter SetterFunc
- Saver SaveFunc
- Deleter DeleteFunc
- Validator ValidateFunc
- tabPanels []ObjectComponentFunc
- hiddenFuncs []ObjectComponentFunc
- sidePanel ComponentFunc
- actionsFunc ObjectComponentFunc
- editingTitleFunc EditingTitleComponentFunc
- FieldsBuilder
- }
- // string / []string / *FieldsSection
- func (mb *ModelBuilder) Editing(vs ...interface{}) (r *EditingBuilder) {
- r = mb.editing
- if len(vs) == 0 {
- return
- }
- r.Only(vs...)
- return r
- }
- // string / []string / *FieldsSection
- func (b *EditingBuilder) Only(vs ...interface{}) (r *EditingBuilder) {
- r = b
- r.FieldsBuilder = *r.FieldsBuilder.Only(vs...)
- return
- }
- func (b *EditingBuilder) Creating(vs ...interface{}) (r *EditingBuilder) {
- if b.mb.creating == nil {
- b.mb.creating = &EditingBuilder{
- mb: b.mb,
- Fetcher: b.Fetcher,
- Setter: b.Setter,
- Saver: b.Saver,
- Deleter: b.Deleter,
- Validator: b.Validator,
- }
- }
- r = b.mb.creating
- r.FieldsBuilder = *b.mb.writeFields.Only(vs...)
- return r
- }
- func (b *EditingBuilder) FetchFunc(v FetchFunc) (r *EditingBuilder) {
- b.Fetcher = v
- return b
- }
- func (b *EditingBuilder) SaveFunc(v SaveFunc) (r *EditingBuilder) {
- b.Saver = v
- return b
- }
- func (b *EditingBuilder) DeleteFunc(v DeleteFunc) (r *EditingBuilder) {
- b.Deleter = v
- return b
- }
- func (b *EditingBuilder) ValidateFunc(v ValidateFunc) (r *EditingBuilder) {
- b.Validator = v
- return b
- }
- func (b *EditingBuilder) SetterFunc(v SetterFunc) (r *EditingBuilder) {
- b.Setter = v
- return b
- }
- func (b *EditingBuilder) AppendTabsPanelFunc(v ObjectComponentFunc) (r *EditingBuilder) {
- b.tabPanels = append(b.tabPanels, v)
- return b
- }
- func (b *EditingBuilder) CleanTabsPanels() (r *EditingBuilder) {
- b.tabPanels = nil
- return b
- }
- func (b *EditingBuilder) SidePanelFunc(v ComponentFunc) (r *EditingBuilder) {
- b.sidePanel = v
- return b
- }
- func (b *EditingBuilder) AppendHiddenFunc(v ObjectComponentFunc) (r *EditingBuilder) {
- b.hiddenFuncs = append(b.hiddenFuncs, v)
- return b
- }
- func (b *EditingBuilder) ActionsFunc(v ObjectComponentFunc) (r *EditingBuilder) {
- b.actionsFunc = v
- return b
- }
- func (b *EditingBuilder) EditingTitleFunc(v EditingTitleComponentFunc) (r *EditingBuilder) {
- b.editingTitleFunc = v
- return b
- }
- func (b *EditingBuilder) formNew(ctx *web.EventContext) (r web.EventResponse, err error) {
- if b.mb.Info().Verifier().Do(PermCreate).WithReq(ctx.R).IsAllowed() != nil {
- ShowMessage(&r, perm.PermissionDenied.Error(), "warning")
- return
- }
- creatingB := b
- if b.mb.creating != nil {
- creatingB = b.mb.creating
- }
- b.mb.p.overlay(ctx.R.FormValue(ParamOverlay), &r, creatingB.editFormFor(nil, ctx), b.mb.rightDrawerWidth)
- return
- }
- func (b *EditingBuilder) formEdit(ctx *web.EventContext) (r web.EventResponse, err error) {
- if b.mb.Info().Verifier().Do(PermGet).WithReq(ctx.R).IsAllowed() != nil {
- ShowMessage(&r, perm.PermissionDenied.Error(), "warning")
- return
- }
- b.mb.p.overlay(ctx.R.FormValue(ParamOverlay), &r, b.editFormFor(nil, ctx), b.mb.rightDrawerWidth)
- return
- }
- func (b *EditingBuilder) singletonPageFunc(ctx *web.EventContext) (r web.PageResponse, err error) {
- if b.mb.Info().Verifier().Do(PermUpdate).WithReq(ctx.R).IsAllowed() != nil {
- err = perm.PermissionDenied
- return
- }
- msgr := MustGetMessages(ctx.R)
- title := msgr.EditingObjectTitle(i18n.T(ctx.R, ModelsI18nModuleKey, inflection.Singular(b.mb.label)), "")
- r.PageTitle = title
- obj, err := b.Fetcher(b.mb.NewModel(), "", ctx)
- if err == ErrRecordNotFound {
- if err = b.Saver(b.mb.NewModel(), "", ctx); err != nil {
- return
- }
- obj, err = b.Fetcher(b.mb.NewModel(), "", ctx)
- }
- if err != nil {
- return
- }
- r.Body = web.Portal(b.editFormFor(obj, ctx)).Name(singletonEditingPortalName)
- return
- }
- func (b *EditingBuilder) editFormFor(obj interface{}, ctx *web.EventContext) h.HTMLComponent {
- msgr := MustGetMessages(ctx.R)
- id := ctx.R.FormValue(ParamID)
- if b.mb.singleton {
- id = vx.ObjectID(obj)
- }
- var buttonLabel = msgr.Create
- var disableUpdateBtn bool
- var title h.HTMLComponent
- title = h.Text(msgr.CreatingObjectTitle(
- i18n.T(ctx.R, ModelsI18nModuleKey, inflection.Singular(b.mb.label)),
- ))
- if len(id) > 0 {
- if obj == nil {
- var err error
- obj, err = b.Fetcher(b.mb.NewModel(), id, ctx)
- if err != nil {
- panic(err)
- }
- }
- disableUpdateBtn = b.mb.Info().Verifier().Do(PermUpdate).ObjectOn(obj).WithReq(ctx.R).IsAllowed() != nil
- buttonLabel = msgr.Update
- editingTitleText := msgr.EditingObjectTitle(
- i18n.T(ctx.R, ModelsI18nModuleKey, inflection.Singular(b.mb.label)),
- getPageTitle(obj, id))
- if b.editingTitleFunc != nil {
- title = b.editingTitleFunc(obj, editingTitleText, ctx)
- } else {
- title = h.Text(editingTitleText)
- }
- }
- if obj == nil {
- obj = b.mb.NewModel()
- }
- var notice h.HTMLComponent
- {
- var text string
- var color string
- if msg, ok := ctx.Flash.(string); ok {
- if len(msg) > 0 {
- text = msg
- color = "success"
- }
- }
- vErr, ok := ctx.Flash.(*web.ValidationErrors)
- if ok {
- gErr := vErr.GetGlobalError()
- if len(gErr) > 0 {
- text = gErr
- color = "error"
- }
- }
- if text != "" {
- showVar := fmt.Sprintf("id%d", uuid.New().ID())
- notice = VSnackbar().Top(true).Timeout(-1).Color(color).
- Attr("v-model", fmt.Sprintf("vars.%s", showVar)).
- Attr(web.InitContextVars, fmt.Sprintf(`{%s: true}`, showVar)).
- Children(
- h.Text(text),
- h.Template().Attr("v-slot:action", "{ attrs }").Children(
- VBtn("").Text(true).
- Attr("v-bind", "attrs").
- Attr("@click", fmt.Sprintf("vars.%s = false", showVar)).
- Children(VIcon("close")),
- ),
- )
- }
- }
- queries := ctx.Queries()
- if b.mb.singleton {
- queries.Add(ParamID, id)
- }
- updateBtn := VBtn(buttonLabel).
- Color("primary").
- Attr("@click", web.Plaid().
- EventFunc(actions.Update).
- Queries(queries).
- URL(b.mb.Info().ListingHref()).
- Go())
- if disableUpdateBtn {
- updateBtn = updateBtn.Disabled(disableUpdateBtn)
- } else {
- updateBtn = updateBtn.Attr(":disabled", "isFetching").
- Attr(":loading", "isFetching")
- }
- var actionButtons h.HTMLComponent = h.Components(
- VSpacer(),
- updateBtn,
- )
- if b.actionsFunc != nil {
- actionButtons = b.actionsFunc(obj, ctx)
- }
- var hiddenComps []h.HTMLComponent
- for _, hf := range b.hiddenFuncs {
- hiddenComps = append(hiddenComps, hf(obj, ctx))
- }
- formContent := h.Components(
- VCardText(
- h.Components(hiddenComps...),
- b.ToComponent(b.mb.Info(), obj, ctx),
- ),
- VCardActions(actionButtons),
- )
- var asideContent h.HTMLComponent = formContent
- if len(b.tabPanels) != 0 {
- var tabs []h.HTMLComponent
- for _, panelFunc := range b.tabPanels {
- value := panelFunc(obj, ctx)
- if value != nil {
- tabs = append(tabs, value)
- }
- }
- if len(tabs) != 0 {
- asideContent = VTabs(
- VTab(h.Text(msgr.FormTitle)),
- VTabItem(web.Scope(formContent).VSlot("{plaidForm}")),
- h.Components(tabs...),
- ).Class("v-tabs--fixed-tabs")
- }
- }
- if b.sidePanel != nil {
- sidePanel := b.sidePanel(ctx)
- if sidePanel != nil {
- asideContent = VContainer(
- VRow(
- VCol(asideContent).Cols(8),
- VCol(sidePanel).Cols(4),
- ),
- )
- }
- }
- overlayType := ctx.R.FormValue(ParamOverlay)
- closeBtnVarScript := closeRightDrawerVarScript
- if overlayType == actions.Dialog {
- closeBtnVarScript = closeDialogVarScript
- }
- return web.Scope(
- notice,
- h.If(!b.mb.singleton,
- VAppBar(
- VToolbarTitle("").Class("pl-2").
- Children(title),
- VSpacer(),
- VBtn("").Icon(true).Children(
- VIcon("close"),
- ).Attr("@click.stop", closeBtnVarScript),
- ).Color("white").Elevation(0).Dense(true),
- ),
- VSheet(
- VCard(asideContent).Flat(true),
- ).Class("pa-2"),
- ).VSlot("{ plaidForm }")
- }
- func (b *EditingBuilder) doDelete(ctx *web.EventContext) (r web.EventResponse, err1 error) {
- if b.mb.Info().Verifier().Do(PermDelete).WithReq(ctx.R).IsAllowed() != nil {
- ShowMessage(&r, perm.PermissionDenied.Error(), "warning")
- return
- }
- id := ctx.R.FormValue(ParamID)
- var obj = b.mb.NewModel()
- if len(id) > 0 {
- err := b.Deleter(obj, id, ctx)
- if err != nil {
- ShowMessage(&r, err.Error(), "warning")
- return
- }
- }
- if event := ctx.Queries().Get(ParamAfterDeleteEvent); event != "" {
- web.AppendVarsScripts(&r,
- "vars.deleteConfirmation = false",
- web.Plaid().
- EventFunc(event).
- Queries(ctx.Queries()).
- Go(),
- )
- } else {
- removeSelectQuery := web.Var(fmt.Sprintf(`{value: %s, add: false, remove: true}`, h.JSONString(id)))
- if isInDialogFromQuery(ctx) {
- u := fmt.Sprintf("%s?%s", b.mb.Info().ListingHref(), ctx.Queries().Get(ParamListingQueries))
- web.AppendVarsScripts(&r,
- "vars.deleteConfirmation = false",
- web.Plaid().
- URL(u).
- EventFunc(actions.UpdateListingDialog).
- MergeQuery(true).
- Queries(ctx.Queries()).
- Query(ParamSelectedIds, removeSelectQuery).
- Go(),
- )
- } else {
- // refresh current page
- // TODO: response location does not support `valueOp`
- // r.PushState = web.Location(nil).
- // MergeQuery(true).
- // Query(ParamSelectedIds, removeSelectQuery)
- web.AppendVarsScripts(&r,
- web.Plaid().
- PushState(true).
- MergeQuery(true).
- Query(ParamSelectedIds, removeSelectQuery).
- Go(),
- )
- }
- }
- return
- }
- func (b *EditingBuilder) FetchAndUnmarshal(id string, removeDeletedAndSort bool, ctx *web.EventContext) (obj interface{}, vErr web.ValidationErrors) {
- obj = b.mb.NewModel()
- if len(id) > 0 {
- var err1 error
- obj, err1 = b.Fetcher(obj, id, ctx)
- if err1 != nil {
- vErr.GlobalError(err1.Error())
- // b.UpdateOverlayContent(ctx, &r, obj, "", err1)
- return
- }
- }
- vErr = b.RunSetterFunc(ctx, removeDeletedAndSort, obj)
- return
- }
- func (b *EditingBuilder) doUpdate(
- ctx *web.EventContext,
- r *web.EventResponse,
- // will not close drawer/dialog
- silent bool,
- ) (err error) {
- id := ctx.R.FormValue(ParamID)
- usingB := b
- if b.mb.creating != nil && id == "" {
- usingB = b.mb.creating
- }
- obj, vErr := usingB.FetchAndUnmarshal(id, true, ctx)
- if vErr.HaveErrors() {
- usingB.UpdateOverlayContent(ctx, r, obj, "", &vErr)
- return &vErr
- }
- if len(id) > 0 {
- if b.mb.Info().Verifier().Do(PermUpdate).ObjectOn(obj).WithReq(ctx.R).IsAllowed() != nil {
- b.UpdateOverlayContent(ctx, r, obj, "", perm.PermissionDenied)
- return perm.PermissionDenied
- }
- } else {
- if b.mb.Info().Verifier().Do(PermCreate).ObjectOn(obj).WithReq(ctx.R).IsAllowed() != nil {
- b.UpdateOverlayContent(ctx, r, obj, "", perm.PermissionDenied)
- return perm.PermissionDenied
- }
- }
- if usingB.Validator != nil {
- if vErr = usingB.Validator(obj, ctx); vErr.HaveErrors() {
- usingB.UpdateOverlayContent(ctx, r, obj, "", &vErr)
- return &vErr
- }
- }
- err1 := usingB.Saver(obj, id, ctx)
- if err1 != nil {
- usingB.UpdateOverlayContent(ctx, r, obj, "", err1)
- return err1
- }
- overlayType := ctx.R.FormValue(ParamOverlay)
- script := closeRightDrawerVarScript
- if overlayType == actions.Dialog {
- script = closeDialogVarScript
- }
- if silent {
- script = ""
- }
- afterUpdateScript := ctx.R.FormValue(ParamOverlayAfterUpdateScript)
- if afterUpdateScript != "" {
- web.AppendVarsScripts(r, script, strings.NewReplacer(".go()",
- fmt.Sprintf(".query(%s, %s).go()",
- h.JSONString(ParamOverlayUpdateID),
- h.JSONString(vx.ObjectID(obj)),
- )).Replace(afterUpdateScript),
- )
- return
- }
- if isInDialogFromQuery(ctx) {
- web.AppendVarsScripts(r,
- web.Plaid().
- URL(ctx.R.RequestURI).
- EventFunc(actions.UpdateListingDialog).
- StringQuery(ctx.R.URL.Query().Get(ParamListingQueries)).
- Go(),
- )
- } else {
- r.PushState = web.Location(nil)
- }
- web.AppendVarsScripts(r, script)
- return
- }
- func (b *EditingBuilder) defaultUpdate(ctx *web.EventContext) (r web.EventResponse, err error) {
- uErr := b.doUpdate(ctx, &r, false)
- if uErr == nil {
- msgr := MustGetMessages(ctx.R)
- ShowMessage(&r, msgr.SuccessfullyUpdated, "")
- }
- return r, nil
- }
- func (b *EditingBuilder) SaveOverlayContent(
- ctx *web.EventContext,
- r *web.EventResponse,
- ) (err error) {
- return b.doUpdate(ctx, r, true)
- }
- func (b *EditingBuilder) RunSetterFunc(ctx *web.EventContext, removeDeletedAndSort bool, toObj interface{}) (vErr web.ValidationErrors) {
- if b.Setter != nil {
- b.Setter(toObj, ctx)
- }
- vErr = b.Unmarshal(toObj, b.mb.Info(), removeDeletedAndSort, ctx)
- return
- }
- func (b *EditingBuilder) UpdateOverlayContent(
- ctx *web.EventContext,
- r *web.EventResponse,
- obj interface{},
- successMessage string,
- err error,
- ) {
- ctx.Flash = err
- if err != nil {
- if _, ok := err.(*web.ValidationErrors); !ok {
- vErr := &web.ValidationErrors{}
- vErr.GlobalError(err.Error())
- ctx.Flash = vErr
- }
- }
- if ctx.Flash == nil {
- ctx.Flash = successMessage
- }
- overlayType := ctx.R.FormValue(ParamOverlay)
- p := rightDrawerContentPortalName
- if overlayType == actions.Dialog {
- p = dialogContentPortalName
- }
- if b.mb.singleton {
- p = singletonEditingPortalName
- }
- r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{
- Name: p,
- Body: b.editFormFor(obj, ctx),
- })
- }
|