model.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. package presets
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "reflect"
  7. "strings"
  8. "github.com/iancoleman/strcase"
  9. "github.com/jinzhu/inflection"
  10. "github.com/qor5/admin/presets/actions"
  11. "github.com/qor5/web"
  12. "github.com/qor5/x/perm"
  13. )
  14. type ModelBuilder struct {
  15. p *Builder
  16. model interface{}
  17. primaryField string
  18. modelType reflect.Type
  19. menuGroupName string
  20. notInMenu bool
  21. menuIcon string
  22. uriName string
  23. defaultURLQueryFunc func(*http.Request) url.Values
  24. label string
  25. fieldLabels []string
  26. placeholders []string
  27. listing *ListingBuilder
  28. detailing *DetailingBuilder
  29. editing *EditingBuilder
  30. creating *EditingBuilder
  31. writeFields *FieldsBuilder
  32. hasDetailing bool
  33. rightDrawerWidth string
  34. link string
  35. layoutConfig *LayoutConfig
  36. modelInfo *ModelInfo
  37. singleton bool
  38. web.EventsHub
  39. }
  40. func NewModelBuilder(p *Builder, model interface{}) (mb *ModelBuilder) {
  41. mb = &ModelBuilder{p: p, model: model, primaryField: "ID"}
  42. mb.modelType = reflect.TypeOf(model)
  43. if mb.modelType.Kind() != reflect.Ptr {
  44. panic(fmt.Sprintf("model %#+v must be pointer", model))
  45. }
  46. modelstr := mb.modelType.String()
  47. modelName := modelstr[strings.LastIndex(modelstr, ".")+1:]
  48. mb.label = strcase.ToCamel(inflection.Plural(modelName))
  49. mb.uriName = inflection.Plural(strcase.ToKebab(modelName))
  50. mb.modelInfo = &ModelInfo{mb: mb}
  51. // Be aware the uriName here is still the original struct
  52. mb.newListing()
  53. mb.newDetailing()
  54. mb.newEditing()
  55. return
  56. }
  57. func (mb *ModelBuilder) RightDrawerWidth(v string) *ModelBuilder {
  58. mb.rightDrawerWidth = v
  59. return mb
  60. }
  61. func (mb *ModelBuilder) Link(v string) *ModelBuilder {
  62. mb.link = v
  63. return mb
  64. }
  65. func (mb *ModelBuilder) registerDefaultEventFuncs() {
  66. mb.RegisterEventFunc(actions.New, mb.editing.formNew)
  67. mb.RegisterEventFunc(actions.Edit, mb.editing.formEdit)
  68. mb.RegisterEventFunc(actions.DeleteConfirmation, mb.listing.deleteConfirmation)
  69. mb.RegisterEventFunc(actions.Update, mb.editing.defaultUpdate)
  70. mb.RegisterEventFunc(actions.DoDelete, mb.editing.doDelete)
  71. mb.RegisterEventFunc(actions.DoBulkAction, mb.listing.doBulkAction)
  72. mb.RegisterEventFunc(actions.DoListingAction, mb.listing.doListingAction)
  73. mb.RegisterEventFunc(actions.OpenBulkActionDialog, mb.listing.openBulkActionDialog)
  74. mb.RegisterEventFunc(actions.OpenActionDialog, mb.listing.openActionDialog)
  75. mb.RegisterEventFunc(actions.Action, mb.detailing.formDrawerAction)
  76. mb.RegisterEventFunc(actions.DoAction, mb.detailing.doAction)
  77. mb.RegisterEventFunc(actions.DetailingDrawer, mb.detailing.showInDrawer)
  78. mb.RegisterEventFunc(actions.ReloadList, mb.listing.reloadList)
  79. mb.RegisterEventFunc(actions.OpenListingDialog, mb.listing.openListingDialog)
  80. mb.RegisterEventFunc(actions.UpdateListingDialog, mb.listing.updateListingDialog)
  81. // list editor
  82. mb.RegisterEventFunc(actions.AddRowEvent, addListItemRow(mb))
  83. mb.RegisterEventFunc(actions.RemoveRowEvent, removeListItemRow(mb))
  84. mb.RegisterEventFunc(actions.SortEvent, sortListItems(mb))
  85. }
  86. func (mb *ModelBuilder) NewModel() (r interface{}) {
  87. return reflect.New(mb.modelType.Elem()).Interface()
  88. }
  89. func (mb *ModelBuilder) NewModelSlice() (r interface{}) {
  90. return reflect.New(reflect.SliceOf(mb.modelType)).Interface()
  91. }
  92. func (mb *ModelBuilder) newListing() (lb *ListingBuilder) {
  93. mb.listing = &ListingBuilder{
  94. mb: mb,
  95. FieldsBuilder: *mb.p.listFieldDefaults.InspectFields(mb.model),
  96. dialogWidth: "1200px",
  97. }
  98. if mb.p.dataOperator != nil {
  99. mb.listing.SearchFunc(mb.p.dataOperator.Search)
  100. }
  101. rmb := mb.listing.RowMenu()
  102. rmb.RowMenuItem("Edit").ComponentFunc(editRowMenuItemFunc(mb.Info(), "", url.Values{}))
  103. rmb.RowMenuItem("Delete").ComponentFunc(deleteRowMenuItemFunc(mb.Info(), "", url.Values{}))
  104. return
  105. }
  106. func (mb *ModelBuilder) newEditing() (r *EditingBuilder) {
  107. mb.writeFields, mb.listing.searchColumns = mb.p.writeFieldDefaults.inspectFieldsAndCollectName(mb.model, reflect.TypeOf(""))
  108. mb.editing = &EditingBuilder{mb: mb, FieldsBuilder: *mb.writeFields}
  109. if mb.p.dataOperator != nil {
  110. mb.editing.FetchFunc(mb.p.dataOperator.Fetch)
  111. mb.editing.SaveFunc(mb.p.dataOperator.Save)
  112. mb.editing.DeleteFunc(mb.p.dataOperator.Delete)
  113. }
  114. return
  115. }
  116. func (mb *ModelBuilder) newDetailing() (r *DetailingBuilder) {
  117. mb.detailing = &DetailingBuilder{mb: mb, FieldsBuilder: *mb.p.detailFieldDefaults.InspectFields(mb.model)}
  118. if mb.p.dataOperator != nil {
  119. mb.detailing.FetchFunc(mb.p.dataOperator.Fetch)
  120. }
  121. return
  122. }
  123. func (mb *ModelBuilder) Info() (r *ModelInfo) {
  124. return mb.modelInfo
  125. }
  126. type ModelInfo struct {
  127. mb *ModelBuilder
  128. }
  129. func (b ModelInfo) ListingHref() string {
  130. return fmt.Sprintf("%s/%s", b.mb.p.prefix, b.mb.uriName)
  131. }
  132. func (b ModelInfo) EditingHref(id string) string {
  133. return fmt.Sprintf("%s/%s/%s/edit", b.mb.p.prefix, b.mb.uriName, id)
  134. }
  135. func (b ModelInfo) DetailingHref(id string) string {
  136. return fmt.Sprintf("%s/%s/%s", b.mb.p.prefix, b.mb.uriName, id)
  137. }
  138. func (b ModelInfo) HasDetailing() bool {
  139. return b.mb.hasDetailing
  140. }
  141. func (b ModelInfo) DetailingInDrawer() bool {
  142. return b.mb.detailing.drawer
  143. }
  144. func (b ModelInfo) PresetsPrefix() string {
  145. return b.mb.p.prefix
  146. }
  147. func (b ModelInfo) URIName() string {
  148. return b.mb.uriName
  149. }
  150. func (b ModelInfo) Label() string {
  151. return b.mb.label
  152. }
  153. func (b ModelInfo) Verifier() *perm.Verifier {
  154. v := b.mb.p.verifier.Spawn()
  155. if b.mb.menuGroupName != "" {
  156. v.SnakeOn("mg_" + b.mb.menuGroupName)
  157. }
  158. return v.SnakeOn(b.mb.uriName)
  159. }
  160. func (mb *ModelBuilder) URIName(v string) (r *ModelBuilder) {
  161. mb.uriName = v
  162. return mb
  163. }
  164. func (mb *ModelBuilder) DefaultURLQueryFunc(v func(*http.Request) url.Values) (r *ModelBuilder) {
  165. mb.defaultURLQueryFunc = v
  166. return mb
  167. }
  168. func (mb *ModelBuilder) PrimaryField(v string) (r *ModelBuilder) {
  169. mb.primaryField = v
  170. return mb
  171. }
  172. func (mb *ModelBuilder) InMenu(v bool) (r *ModelBuilder) {
  173. mb.notInMenu = !v
  174. return mb
  175. }
  176. func (mb *ModelBuilder) MenuIcon(v string) (r *ModelBuilder) {
  177. mb.menuIcon = v
  178. return mb
  179. }
  180. func (mb *ModelBuilder) Label(v string) (r *ModelBuilder) {
  181. mb.label = v
  182. return mb
  183. }
  184. func (mb *ModelBuilder) Labels(vs ...string) (r *ModelBuilder) {
  185. mb.fieldLabels = append(mb.fieldLabels, vs...)
  186. return mb
  187. }
  188. func (mb *ModelBuilder) LayoutConfig(v *LayoutConfig) (r *ModelBuilder) {
  189. mb.layoutConfig = v
  190. return mb
  191. }
  192. func (mb *ModelBuilder) Placeholders(vs ...string) (r *ModelBuilder) {
  193. mb.placeholders = append(mb.placeholders, vs...)
  194. return mb
  195. }
  196. func (mb *ModelBuilder) Singleton(v bool) (r *ModelBuilder) {
  197. mb.singleton = v
  198. return mb
  199. }
  200. func (mb *ModelBuilder) getComponentFuncField(field *FieldBuilder) (r *FieldContext) {
  201. r = &FieldContext{
  202. ModelInfo: mb.Info(),
  203. Name: field.name,
  204. Label: mb.getLabel(field.NameLabel),
  205. }
  206. return
  207. }
  208. func (mb *ModelBuilder) getLabel(field NameLabel) (r string) {
  209. if len(field.label) > 0 {
  210. return field.label
  211. }
  212. for i := 0; i < len(mb.fieldLabels)-1; i = i + 2 {
  213. if mb.fieldLabels[i] == field.name {
  214. return mb.fieldLabels[i+1]
  215. }
  216. }
  217. return humanizeString(field.name)
  218. }