123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- package views
- import (
- "context"
- "encoding/json"
- "fmt"
- "path"
- "sort"
- "time"
- "github.com/qor5/admin/media"
- "github.com/qor5/admin/media/media_library"
- "github.com/qor5/admin/presets"
- "github.com/qor5/ui/cropper"
- "github.com/qor5/ui/fileicons"
- . "github.com/qor5/ui/vuetify"
- "github.com/qor5/web"
- "github.com/qor5/x/i18n"
- "github.com/qor5/x/perm"
- "github.com/sunfmin/reflectutils"
- h "github.com/theplant/htmlgo"
- "golang.org/x/text/language"
- "gorm.io/gorm"
- )
- type MediaBoxConfigKey int
- var MediaLibraryPerPage int = 39
- const MediaBoxConfig MediaBoxConfigKey = iota
- const I18nMediaLibraryKey i18n.ModuleKey = "I18nMediaLibraryKey"
- var permVerifier *perm.Verifier
- func Configure(b *presets.Builder, db *gorm.DB) {
- err := db.AutoMigrate(&media_library.MediaLibrary{})
- if err != nil {
- panic(err)
- }
- b.ExtraAsset("/cropper.js", "text/javascript", cropper.JSComponentsPack())
- b.ExtraAsset("/cropper.css", "text/css", cropper.CSSComponentsPack())
- permVerifier = perm.NewVerifier("media_library", b.GetPermission())
- b.FieldDefaults(presets.WRITE).
- FieldType(media_library.MediaBox{}).
- ComponentFunc(MediaBoxComponentFunc(db)).
- SetterFunc(MediaBoxSetterFunc(db))
- b.FieldDefaults(presets.LIST).
- FieldType(media_library.MediaBox{}).
- ComponentFunc(MediaBoxListFunc())
- registerEventFuncs(b.GetWebBuilder(), db)
- b.I18n().
- RegisterForModule(language.English, I18nMediaLibraryKey, Messages_en_US).
- RegisterForModule(language.SimplifiedChinese, I18nMediaLibraryKey, Messages_zh_CN).
- RegisterForModule(language.Japanese, I18nMediaLibraryKey, Messages_ja_JP)
- configList(b, db)
- }
- func MediaBoxComponentFunc(db *gorm.DB) presets.FieldComponentFunc {
- return func(obj interface{}, field *presets.FieldContext, ctx *web.EventContext) h.HTMLComponent {
- cfg, ok := field.ContextValue(MediaBoxConfig).(*media_library.MediaBoxConfig)
- if !ok {
- cfg = &media_library.MediaBoxConfig{}
- }
- mediaBox := field.Value(obj).(media_library.MediaBox)
- return QMediaBox(db).
- FieldName(field.FormKey).
- Value(&mediaBox).
- Label(field.Label).
- Config(cfg).Disabled(field.Disabled)
- }
- }
- func MediaBoxSetterFunc(db *gorm.DB) presets.FieldSetterFunc {
- return func(obj interface{}, field *presets.FieldContext, ctx *web.EventContext) (err error) {
- jsonValuesField := fmt.Sprintf("%s.Values", field.FormKey)
- mediaBox := media_library.MediaBox{}
- err = mediaBox.Scan(ctx.R.FormValue(jsonValuesField))
- if err != nil {
- return
- }
- descriptionField := fmt.Sprintf("%s.Description", field.FormKey)
- mediaBox.Description = ctx.R.FormValue(descriptionField)
- err = reflectutils.Set(obj, field.Name, mediaBox)
- if err != nil {
- return
- }
- return
- }
- }
- type QMediaBoxBuilder struct {
- fieldName string
- label string
- value *media_library.MediaBox
- config *media_library.MediaBoxConfig
- db *gorm.DB
- disabled bool
- }
- func QMediaBox(db *gorm.DB) (r *QMediaBoxBuilder) {
- r = &QMediaBoxBuilder{
- db: db,
- }
- return
- }
- func (b *QMediaBoxBuilder) FieldName(v string) (r *QMediaBoxBuilder) {
- b.fieldName = v
- return b
- }
- func (b *QMediaBoxBuilder) Value(v *media_library.MediaBox) (r *QMediaBoxBuilder) {
- b.value = v
- return b
- }
- func (b *QMediaBoxBuilder) Disabled(v bool) (r *QMediaBoxBuilder) {
- b.disabled = v
- return b
- }
- func (b *QMediaBoxBuilder) Label(v string) (r *QMediaBoxBuilder) {
- b.label = v
- return b
- }
- func (b *QMediaBoxBuilder) Config(v *media_library.MediaBoxConfig) (r *QMediaBoxBuilder) {
- b.config = v
- return b
- }
- func (b *QMediaBoxBuilder) MarshalHTML(c context.Context) (r []byte, err error) {
- if len(b.fieldName) == 0 {
- panic("FieldName required")
- }
- if b.value == nil {
- panic("Value required")
- }
- ctx := web.MustGetEventContext(c)
- portalName := mainPortalName(b.fieldName)
- return h.Components(
- VSheet(
- h.If(len(b.label) > 0,
- h.Label(b.label).Class("v-label theme--light"),
- ),
- web.Portal(
- mediaBoxThumbnails(ctx, b.value, b.fieldName, b.config, b.disabled),
- ).Name(mediaBoxThumbnailsPortalName(b.fieldName)),
- web.Portal().Name(portalName),
- ).Class("pb-4").
- Rounded(true).
- Attr(web.InitContextVars, `{showFileChooser: false}`),
- ).MarshalHTML(c)
- }
- func mediaBoxThumb(msgr *Messages, cfg *media_library.MediaBoxConfig,
- f *media_library.MediaBox, field string, thumb string, disabled bool) h.HTMLComponent {
- size := cfg.Sizes[thumb]
- fileSize := f.FileSizes[thumb]
- url := f.URL(thumb)
- if thumb == media.DefaultSizeKey {
- url = f.URL()
- }
- return VCard(
- h.If(media.IsImageFormat(f.FileName),
- VImg().Src(fmt.Sprintf("%s?%d", url, time.Now().UnixNano())).Height(150),
- ).Else(
- h.Div(
- fileThumb(f.FileName),
- h.A().Text(f.FileName).Href(f.Url).Target("_blank"),
- ).Style("text-align:center"),
- ),
- h.If(media.IsImageFormat(f.FileName) && (size != nil || thumb == media.DefaultSizeKey),
- VCardActions(
- VChip(
- thumbName(thumb, size, fileSize, f),
- ).Small(true).Disabled(disabled).Attr("@click", web.Plaid().
- EventFunc(loadImageCropperEvent).
- Query("field", field).
- Query("id", fmt.Sprint(f.ID)).
- Query("thumb", thumb).
- FieldValue("cfg", h.JSONString(cfg)).
- Go()),
- ),
- ),
- )
- }
- func fileThumb(filename string) h.HTMLComponent {
- return h.Div(
- fileicons.Icon(path.Ext(filename)[1:]).Attr("height", "150").Class("pt-4"),
- ).Class("d-flex align-center justify-center")
- }
- func deleteConfirmation(db *gorm.DB) web.EventFunc {
- return func(ctx *web.EventContext) (r web.EventResponse, err error) {
- msgr := i18n.MustGetModuleMessages(ctx.R, presets.CoreI18nModuleKey, Messages_en_US).(*presets.Messages)
- field := ctx.R.FormValue("field")
- id := ctx.R.FormValue("id")
- cfg := ctx.R.FormValue("cfg")
- r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{
- Name: deleteConfirmPortalName(field),
- Body: VDialog(
- VCard(
- VCardTitle(h.Text(msgr.DeleteConfirmationText(id))),
- VCardActions(
- VSpacer(),
- VBtn(msgr.Cancel).
- Depressed(true).
- Class("ml-2").
- On("click", "vars.mediaLibrary_deleteConfirmation = false"),
- VBtn(msgr.Delete).
- Color("primary").
- Depressed(true).
- Dark(true).
- Attr("@click", web.Plaid().
- EventFunc(doDeleteEvent).
- Query("field", field).
- Query("id", id).
- FieldValue("cfg", cfg).
- Go()),
- ),
- ),
- ).MaxWidth("600px").
- Attr("v-model", "vars.mediaLibrary_deleteConfirmation").
- Attr(web.InitContextVars, `{mediaLibrary_deleteConfirmation: false}`),
- })
- r.VarsScript = "setTimeout(function(){ vars.mediaLibrary_deleteConfirmation = true }, 100)"
- return
- }
- }
- func doDelete(db *gorm.DB) web.EventFunc {
- return func(ctx *web.EventContext) (r web.EventResponse, err error) {
- field := ctx.R.FormValue("field")
- id := ctx.R.FormValue("id")
- cfg := ctx.R.FormValue("cfg")
- var obj media_library.MediaLibrary
- err = db.Where("id = ?", id).First(&obj).Error
- if err != nil {
- if err == gorm.ErrRecordNotFound {
- renderFileChooserDialogContent(
- ctx,
- &r,
- field,
- db,
- stringToCfg(cfg),
- )
- r.VarsScript = "vars.mediaLibrary_deleteConfirmation = false"
- return r, nil
- }
- panic(err)
- }
- if err = deleteIsAllowed(ctx.R, &obj); err != nil {
- return
- }
- err = db.Delete(&media_library.MediaLibrary{}, "id = ?", id).Error
- if err != nil {
- panic(err)
- }
- renderFileChooserDialogContent(
- ctx,
- &r,
- field,
- db,
- stringToCfg(cfg),
- )
- r.VarsScript = "vars.mediaLibrary_deleteConfirmation = false"
- return
- }
- }
- func mediaBoxThumbnails(ctx *web.EventContext, mediaBox *media_library.MediaBox, field string, cfg *media_library.MediaBoxConfig, disabled bool) h.HTMLComponent {
- msgr := i18n.MustGetModuleMessages(ctx.R, I18nMediaLibraryKey, Messages_en_US).(*Messages)
- c := VContainer().Fluid(true)
- if mediaBox.ID.String() != "" && mediaBox.ID.String() != "0" {
- row := VRow()
- if len(cfg.Sizes) == 0 {
- row.AppendChildren(
- VCol(
- mediaBoxThumb(msgr, cfg, mediaBox, field, media.DefaultSizeKey, disabled),
- ).Cols(6).Sm(4).Class("pl-0"),
- )
- } else {
- var keys []string
- for k, _ := range cfg.Sizes {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
- row.AppendChildren(
- VCol(
- mediaBoxThumb(msgr, cfg, mediaBox, field, k, disabled),
- ).Cols(6).Sm(4).Class("pl-0"),
- )
- }
- }
- c.AppendChildren(row)
- if media.IsImageFormat(mediaBox.FileName) {
- fieldName := fmt.Sprintf("%s.Description", field)
- value := ctx.R.FormValue(fieldName)
- if len(value) == 0 {
- value = mediaBox.Description
- }
- c.AppendChildren(
- VRow(
- VCol(
- VTextField().
- Value(value).
- Attr(web.VFieldName(fieldName)...).
- Label(msgr.DescriptionForAccessibility).
- Dense(true).
- HideDetails(true).
- Outlined(true).
- Disabled(disabled),
- ).Cols(12).Class("pl-0 pt-0"),
- ),
- )
- }
- }
- mediaBoxValue := ""
- if mediaBox.ID.String() != "" && mediaBox.ID.String() != "0" {
- mediaBoxValue = h.JSONString(mediaBox)
- }
- return h.Components(
- c,
- web.Portal().Name(cropperPortalName(field)),
- h.Input("").Type("hidden").
- Value(mediaBoxValue).
- Attr(web.VFieldName(fmt.Sprintf("%s.Values", field))...),
- VBtn(msgr.ChooseFile).
- Depressed(true).
- Attr("@click", web.Plaid().EventFunc(openFileChooserEvent).
- Query("field", field).
- FieldValue("cfg", h.JSONString(cfg)).
- Go(),
- ).Disabled(disabled),
- h.If(mediaBox != nil && mediaBox.ID.String() != "" && mediaBox.ID.String() != "0",
- VBtn(msgr.Delete).
- Depressed(true).
- Attr("@click", web.Plaid().EventFunc(deleteFileEvent).
- Query("field", field).
- FieldValue("cfg", h.JSONString(cfg)).
- Go(),
- ).Disabled(disabled),
- ),
- )
- }
- func MediaBoxListFunc() presets.FieldComponentFunc {
- return func(obj interface{}, field *presets.FieldContext, ctx *web.EventContext) h.HTMLComponent {
- mediaBox := field.Value(obj).(media_library.MediaBox)
- return h.Td(h.Img("").Src(mediaBox.URL(media_library.QorPreviewSizeName)).Style("height: 48px;"))
- }
- }
- func deleteFileField() web.EventFunc {
- return func(ctx *web.EventContext) (r web.EventResponse, err error) {
- field := ctx.R.FormValue("field")
- cfg := stringToCfg(ctx.R.FormValue("cfg"))
- r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{
- Name: mediaBoxThumbnailsPortalName(field),
- Body: mediaBoxThumbnails(ctx, &media_library.MediaBox{}, field, cfg, false),
- })
- return
- }
- }
- func stringToCfg(v string) *media_library.MediaBoxConfig {
- var cfg media_library.MediaBoxConfig
- if len(v) == 0 {
- return &cfg
- }
- err := json.Unmarshal([]byte(v), &cfg)
- if err != nil {
- panic(err)
- }
- return &cfg
- }
- func thumbName(name string, size *media.Size, fileSize int, f *media_library.MediaBox) h.HTMLComponent {
- text := name
- if size != nil {
- text = fmt.Sprintf("%s(%dx%d)", text, size.Width, size.Height)
- }
- if name == media.DefaultSizeKey {
- text = fmt.Sprintf("%s(%dx%d)", text, f.Width, f.Height)
- }
- if fileSize != 0 {
- text = fmt.Sprintf("%s %s", text, media.ByteCountSI(fileSize))
- }
- return h.Text(text)
- }
- func updateDescription(db *gorm.DB) web.EventFunc {
- return func(ctx *web.EventContext) (r web.EventResponse, err error) {
- field := ctx.R.FormValue("field")
- id := ctx.R.FormValue("id")
- cfg := ctx.R.FormValue("cfg")
- var obj media_library.MediaLibrary
- err = db.Where("id = ?", id).First(&obj).Error
- if err != nil {
- if err == gorm.ErrRecordNotFound {
- renderFileChooserDialogContent(
- ctx,
- &r,
- field,
- db,
- stringToCfg(cfg),
- )
- // TODO: prompt that the record has been deleted?
- return r, nil
- }
- panic(err)
- }
- if err = updateDescIsAllowed(ctx.R, &obj); err != nil {
- return
- }
- var media media_library.MediaLibrary
- if err = db.Find(&media, id).Error; err != nil {
- return
- }
- media.File.Description = ctx.R.FormValue("CurrentDescription")
- if err = db.Save(&media).Error; err != nil {
- return
- }
- r.VarsScript = `vars.snackbarShow = true;`
- return
- }
- }
|