updated admin and user upsert forms
This commit is contained in:
		
							parent
							
								
									51d635bc12
								
							
						
					
					
						commit
						956263d1fc
					
				| 
						 | 
					@ -21,7 +21,7 @@ func BindCollectionApi(app core.App, rg *echo.Group) {
 | 
				
			||||||
	subGroup.GET("/:collection", api.view)
 | 
						subGroup.GET("/:collection", api.view)
 | 
				
			||||||
	subGroup.PATCH("/:collection", api.update)
 | 
						subGroup.PATCH("/:collection", api.update)
 | 
				
			||||||
	subGroup.DELETE("/:collection", api.delete)
 | 
						subGroup.DELETE("/:collection", api.delete)
 | 
				
			||||||
	subGroup.POST("/import", api.bulkImport)
 | 
						subGroup.PUT("/import", api.bulkImport)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type collectionApi struct {
 | 
					type collectionApi struct {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,32 +4,58 @@ import (
 | 
				
			||||||
	validation "github.com/go-ozzo/ozzo-validation/v4"
 | 
						validation "github.com/go-ozzo/ozzo-validation/v4"
 | 
				
			||||||
	"github.com/go-ozzo/ozzo-validation/v4/is"
 | 
						"github.com/go-ozzo/ozzo-validation/v4/is"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/core"
 | 
						"github.com/pocketbase/pocketbase/core"
 | 
				
			||||||
 | 
						"github.com/pocketbase/pocketbase/daos"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/forms/validators"
 | 
						"github.com/pocketbase/pocketbase/forms/validators"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/models"
 | 
						"github.com/pocketbase/pocketbase/models"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AdminUpsert defines an admin upsert (create/update) form.
 | 
					// AdminUpsert specifies a [models.Admin] upsert (create/update) form.
 | 
				
			||||||
type AdminUpsert struct {
 | 
					type AdminUpsert struct {
 | 
				
			||||||
	app      core.App
 | 
						config AdminUpsertConfig
 | 
				
			||||||
	admin  *models.Admin
 | 
						admin  *models.Admin
 | 
				
			||||||
	isCreate bool
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Id              string `form:"id" json:"id"`
 | 
				
			||||||
	Avatar          int    `form:"avatar" json:"avatar"`
 | 
						Avatar          int    `form:"avatar" json:"avatar"`
 | 
				
			||||||
	Email           string `form:"email" json:"email"`
 | 
						Email           string `form:"email" json:"email"`
 | 
				
			||||||
	Password        string `form:"password" json:"password"`
 | 
						Password        string `form:"password" json:"password"`
 | 
				
			||||||
	PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
 | 
						PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewAdminUpsert creates new upsert form for the provided admin model
 | 
					// AdminUpsertConfig is the [AdminUpsert] factory initializer config.
 | 
				
			||||||
// (pass an empty admin model instance (`&models.Admin{}`) for create).
 | 
					//
 | 
				
			||||||
 | 
					// NB! Dao is a required struct member.
 | 
				
			||||||
 | 
					type AdminUpsertConfig struct {
 | 
				
			||||||
 | 
						Dao *daos.Dao
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewAdminUpsert creates a new [AdminUpsert] form with initializer
 | 
				
			||||||
 | 
					// config created from the provided [core.App] and [models.Admin] instances
 | 
				
			||||||
 | 
					// (for create you could pass a pointer to an empty Admin - `&models.Admin{}`).
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This factory method is used primarily for convenience (and backward compatibility).
 | 
				
			||||||
 | 
					// If you want to submit the form as part of another transaction, use
 | 
				
			||||||
 | 
					// [NewAdminUpsertWithConfig] with Dao configured to your txDao.
 | 
				
			||||||
func NewAdminUpsert(app core.App, admin *models.Admin) *AdminUpsert {
 | 
					func NewAdminUpsert(app core.App, admin *models.Admin) *AdminUpsert {
 | 
				
			||||||
 | 
						return NewAdminUpsertWithConfig(AdminUpsertConfig{
 | 
				
			||||||
 | 
							Dao: app.Dao(),
 | 
				
			||||||
 | 
						}, admin)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewAdminUpsertWithConfig creates a new [AdminUpsert] form
 | 
				
			||||||
 | 
					// with the provided config and [models.Admin] instance or panics on invalid configuration
 | 
				
			||||||
 | 
					// (for create you could pass a pointer to an empty Admin - `&models.Admin{}`).
 | 
				
			||||||
 | 
					func NewAdminUpsertWithConfig(config AdminUpsertConfig, admin *models.Admin) *AdminUpsert {
 | 
				
			||||||
	form := &AdminUpsert{
 | 
						form := &AdminUpsert{
 | 
				
			||||||
		app:      app,
 | 
							config: config,
 | 
				
			||||||
		admin:  admin,
 | 
							admin:  admin,
 | 
				
			||||||
		isCreate: !admin.HasId(),
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if form.config.Dao == nil || form.admin == nil {
 | 
				
			||||||
 | 
							panic("Invalid initializer config or nil upsert model.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// load defaults
 | 
						// load defaults
 | 
				
			||||||
 | 
						form.Id = admin.Id
 | 
				
			||||||
	form.Avatar = admin.Avatar
 | 
						form.Avatar = admin.Avatar
 | 
				
			||||||
	form.Email = admin.Email
 | 
						form.Email = admin.Email
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,6 +65,13 @@ func NewAdminUpsert(app core.App, admin *models.Admin) *AdminUpsert {
 | 
				
			||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
 | 
					// Validate makes the form validatable by implementing [validation.Validatable] interface.
 | 
				
			||||||
func (form *AdminUpsert) Validate() error {
 | 
					func (form *AdminUpsert) Validate() error {
 | 
				
			||||||
	return validation.ValidateStruct(form,
 | 
						return validation.ValidateStruct(form,
 | 
				
			||||||
 | 
							validation.Field(
 | 
				
			||||||
 | 
								&form.Id,
 | 
				
			||||||
 | 
								validation.When(
 | 
				
			||||||
 | 
									form.admin.IsNew(),
 | 
				
			||||||
 | 
									validation.Length(models.DefaultIdLength, models.DefaultIdLength),
 | 
				
			||||||
 | 
								).Else(validation.In(form.admin.Id)),
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
		validation.Field(
 | 
							validation.Field(
 | 
				
			||||||
			&form.Avatar,
 | 
								&form.Avatar,
 | 
				
			||||||
			validation.Min(0),
 | 
								validation.Min(0),
 | 
				
			||||||
| 
						 | 
					@ -53,7 +86,7 @@ func (form *AdminUpsert) Validate() error {
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		validation.Field(
 | 
							validation.Field(
 | 
				
			||||||
			&form.Password,
 | 
								&form.Password,
 | 
				
			||||||
			validation.When(form.isCreate, validation.Required),
 | 
								validation.When(form.admin.IsNew(), validation.Required),
 | 
				
			||||||
			validation.Length(10, 100),
 | 
								validation.Length(10, 100),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		validation.Field(
 | 
							validation.Field(
 | 
				
			||||||
| 
						 | 
					@ -67,7 +100,7 @@ func (form *AdminUpsert) Validate() error {
 | 
				
			||||||
func (form *AdminUpsert) checkUniqueEmail(value any) error {
 | 
					func (form *AdminUpsert) checkUniqueEmail(value any) error {
 | 
				
			||||||
	v, _ := value.(string)
 | 
						v, _ := value.(string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if form.app.Dao().IsAdminEmailUnique(v, form.admin.Id) {
 | 
						if form.config.Dao.IsAdminEmailUnique(v, form.admin.Id) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,6 +116,12 @@ func (form *AdminUpsert) Submit(interceptors ...InterceptorFunc) error {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// custom insertion id can be set only on create
 | 
				
			||||||
 | 
						if form.admin.IsNew() && form.Id != "" {
 | 
				
			||||||
 | 
							form.admin.MarkAsNew()
 | 
				
			||||||
 | 
							form.admin.SetId(form.Id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	form.admin.Avatar = form.Avatar
 | 
						form.admin.Avatar = form.Avatar
 | 
				
			||||||
	form.admin.Email = form.Email
 | 
						form.admin.Email = form.Email
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,6 +130,6 @@ func (form *AdminUpsert) Submit(interceptors ...InterceptorFunc) error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return runInterceptors(func() error {
 | 
						return runInterceptors(func() error {
 | 
				
			||||||
		return form.app.Dao().SaveAdmin(form.admin)
 | 
							return form.config.Dao.SaveAdmin(form.admin)
 | 
				
			||||||
	}, interceptors...)
 | 
						}, interceptors...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,8 @@ type CollectionUpsertConfig struct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewCollectionUpsert creates a new [CollectionUpsert] form with initializer
 | 
					// NewCollectionUpsert creates a new [CollectionUpsert] form with initializer
 | 
				
			||||||
// config created from the provided [core.App] and [models.Collection] instances.
 | 
					// config created from the provided [core.App] and [models.Collection] instances
 | 
				
			||||||
 | 
					// (for create you could pass a pointer to an empty Collection - `&models.Collection{}`).
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This factory method is used primarily for convenience (and backward compatibility).
 | 
					// This factory method is used primarily for convenience (and backward compatibility).
 | 
				
			||||||
// If you want to submit the form as part of another transaction, use
 | 
					// If you want to submit the form as part of another transaction, use
 | 
				
			||||||
| 
						 | 
					@ -52,7 +53,7 @@ func NewCollectionUpsert(app core.App, collection *models.Collection) *Collectio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewCollectionUpsertWithConfig creates a new [CollectionUpsert] form
 | 
					// NewCollectionUpsertWithConfig creates a new [CollectionUpsert] form
 | 
				
			||||||
// with the provided config and [models.Collection] instance or panics on invalid configuration
 | 
					// with the provided config and [models.Collection] instance or panics on invalid configuration
 | 
				
			||||||
// (for create you could pass a pointer to an empty Collection instance - `&models.Collection{}`).
 | 
					// (for create you could pass a pointer to an empty Collection - `&models.Collection{}`).
 | 
				
			||||||
func NewCollectionUpsertWithConfig(config CollectionUpsertConfig, collection *models.Collection) *CollectionUpsert {
 | 
					func NewCollectionUpsertWithConfig(config CollectionUpsertConfig, collection *models.Collection) *CollectionUpsert {
 | 
				
			||||||
	form := &CollectionUpsert{
 | 
						form := &CollectionUpsert{
 | 
				
			||||||
		config:     config,
 | 
							config:     config,
 | 
				
			||||||
| 
						 | 
					@ -88,7 +89,10 @@ func (form *CollectionUpsert) Validate() error {
 | 
				
			||||||
	return validation.ValidateStruct(form,
 | 
						return validation.ValidateStruct(form,
 | 
				
			||||||
		validation.Field(
 | 
							validation.Field(
 | 
				
			||||||
			&form.Id,
 | 
								&form.Id,
 | 
				
			||||||
 | 
								validation.When(
 | 
				
			||||||
 | 
									form.collection.IsNew(),
 | 
				
			||||||
				validation.Length(models.DefaultIdLength, models.DefaultIdLength),
 | 
									validation.Length(models.DefaultIdLength, models.DefaultIdLength),
 | 
				
			||||||
 | 
								).Else(validation.In(form.collection.Id)),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		validation.Field(
 | 
							validation.Field(
 | 
				
			||||||
			&form.System,
 | 
								&form.System,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,40 +9,74 @@ import (
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						validation "github.com/go-ozzo/ozzo-validation/v4"
 | 
				
			||||||
	"github.com/pocketbase/dbx"
 | 
						"github.com/pocketbase/dbx"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/core"
 | 
						"github.com/pocketbase/pocketbase/core"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/daos"
 | 
						"github.com/pocketbase/pocketbase/daos"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/forms/validators"
 | 
						"github.com/pocketbase/pocketbase/forms/validators"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/models"
 | 
						"github.com/pocketbase/pocketbase/models"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/models/schema"
 | 
						"github.com/pocketbase/pocketbase/models/schema"
 | 
				
			||||||
 | 
						"github.com/pocketbase/pocketbase/tools/filesystem"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/tools/list"
 | 
						"github.com/pocketbase/pocketbase/tools/list"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/tools/rest"
 | 
						"github.com/pocketbase/pocketbase/tools/rest"
 | 
				
			||||||
	"github.com/spf13/cast"
 | 
						"github.com/spf13/cast"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RecordUpsert defines a Record upsert form.
 | 
					// RecordUpsert specifies a [models.Record] upsert (create/update) form.
 | 
				
			||||||
type RecordUpsert struct {
 | 
					type RecordUpsert struct {
 | 
				
			||||||
	app    core.App
 | 
						config RecordUpsertConfig
 | 
				
			||||||
	record *models.Record
 | 
						record *models.Record
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	isCreate      bool
 | 
					 | 
				
			||||||
	filesToDelete []string // names list
 | 
						filesToDelete []string // names list
 | 
				
			||||||
	filesToUpload []*rest.UploadedFile
 | 
						filesToUpload []*rest.UploadedFile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Id   string         `form:"id" json:"id"`
 | 
				
			||||||
	Data map[string]any `json:"data"`
 | 
						Data map[string]any `json:"data"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewRecordUpsert creates a new Record upsert form.
 | 
					// RecordUpsertConfig is the [RecordUpsert] factory initializer config.
 | 
				
			||||||
// (pass a new Record model instance (`models.NewRecord(...)`) for create).
 | 
					//
 | 
				
			||||||
 | 
					// NB! Dao and FilesystemFactory are required struct members.
 | 
				
			||||||
 | 
					type RecordUpsertConfig struct {
 | 
				
			||||||
 | 
						Dao               *daos.Dao
 | 
				
			||||||
 | 
						FilesystemFactory func() (*filesystem.System, error)
 | 
				
			||||||
 | 
						IsDebug           bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewRecordUpsert creates a new [RecordUpsert] form with initializer
 | 
				
			||||||
 | 
					// config created from the provided [core.App] and [models.Record] instances
 | 
				
			||||||
 | 
					// (for create you could pass a pointer to an empty Record - `&models.Record{}`).
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This factory method is used primarily for convenience (and backward compatibility).
 | 
				
			||||||
 | 
					// If you want to submit the form as part of another transaction, use
 | 
				
			||||||
 | 
					// [NewRecordUpsertWithConfig] with Dao configured to your txDao.
 | 
				
			||||||
func NewRecordUpsert(app core.App, record *models.Record) *RecordUpsert {
 | 
					func NewRecordUpsert(app core.App, record *models.Record) *RecordUpsert {
 | 
				
			||||||
 | 
						return NewRecordUpsertWithConfig(RecordUpsertConfig{
 | 
				
			||||||
 | 
							Dao:               app.Dao(),
 | 
				
			||||||
 | 
							FilesystemFactory: app.NewFilesystem,
 | 
				
			||||||
 | 
							IsDebug:           app.IsDebug(),
 | 
				
			||||||
 | 
						}, record)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewRecordUpsertWithConfig creates a new [RecordUpsert] form
 | 
				
			||||||
 | 
					// with the provided config and [models.Record] instance or panics on invalid configuration
 | 
				
			||||||
 | 
					// (for create you could pass a pointer to an empty Record - `&models.Record{}`).
 | 
				
			||||||
 | 
					func NewRecordUpsertWithConfig(config RecordUpsertConfig, record *models.Record) *RecordUpsert {
 | 
				
			||||||
	form := &RecordUpsert{
 | 
						form := &RecordUpsert{
 | 
				
			||||||
		app:           app,
 | 
							config:        config,
 | 
				
			||||||
		record:        record,
 | 
							record:        record,
 | 
				
			||||||
		isCreate:      !record.HasId(),
 | 
					 | 
				
			||||||
		filesToDelete: []string{},
 | 
							filesToDelete: []string{},
 | 
				
			||||||
		filesToUpload: []*rest.UploadedFile{},
 | 
							filesToUpload: []*rest.UploadedFile{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if form.config.Dao == nil ||
 | 
				
			||||||
 | 
							form.config.FilesystemFactory == nil ||
 | 
				
			||||||
 | 
							form.record == nil {
 | 
				
			||||||
 | 
							panic("Invalid initializer config or nil upsert model.")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						form.Id = record.Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	form.Data = map[string]any{}
 | 
						form.Data = map[string]any{}
 | 
				
			||||||
	for _, field := range record.Collection().Schema.Fields() {
 | 
						for _, field := range record.Collection().Schema.Fields() {
 | 
				
			||||||
		form.Data[field.Name] = record.GetDataValue(field.Name)
 | 
							form.Data[field.Name] = record.GetDataValue(field.Name)
 | 
				
			||||||
| 
						 | 
					@ -136,6 +170,10 @@ func (form *RecordUpsert) LoadData(r *http.Request) error {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if id, ok := requestData["id"]; ok {
 | 
				
			||||||
 | 
							form.Id = cast.ToString(id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// extend base data with the extracted one
 | 
						// extend base data with the extracted one
 | 
				
			||||||
	extendedData := form.record.Data()
 | 
						extendedData := form.record.Data()
 | 
				
			||||||
	rawData, err := json.Marshal(requestData)
 | 
						rawData, err := json.Marshal(requestData)
 | 
				
			||||||
| 
						 | 
					@ -202,7 +240,7 @@ func (form *RecordUpsert) LoadData(r *http.Request) error {
 | 
				
			||||||
		// check if there are any new uploaded form files
 | 
							// check if there are any new uploaded form files
 | 
				
			||||||
		files, err := rest.FindUploadedFiles(r, key)
 | 
							files, err := rest.FindUploadedFiles(r, key)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if form.app.IsDebug() {
 | 
								if form.config.IsDebug {
 | 
				
			||||||
				log.Printf("%q uploaded file error: %v\n", key, err)
 | 
									log.Printf("%q uploaded file error: %v\n", key, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -234,8 +272,23 @@ func (form *RecordUpsert) LoadData(r *http.Request) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
 | 
					// Validate makes the form validatable by implementing [validation.Validatable] interface.
 | 
				
			||||||
func (form *RecordUpsert) Validate() error {
 | 
					func (form *RecordUpsert) Validate() error {
 | 
				
			||||||
 | 
						// base form fields validator
 | 
				
			||||||
 | 
						baseFieldsErrors := validation.ValidateStruct(form,
 | 
				
			||||||
 | 
							validation.Field(
 | 
				
			||||||
 | 
								&form.Id,
 | 
				
			||||||
 | 
								validation.When(
 | 
				
			||||||
 | 
									form.record.IsNew(),
 | 
				
			||||||
 | 
									validation.Length(models.DefaultIdLength, models.DefaultIdLength),
 | 
				
			||||||
 | 
								).Else(validation.In(form.record.Id)),
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if baseFieldsErrors != nil {
 | 
				
			||||||
 | 
							return baseFieldsErrors
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// record data validator
 | 
				
			||||||
	dataValidator := validators.NewRecordDataValidator(
 | 
						dataValidator := validators.NewRecordDataValidator(
 | 
				
			||||||
		form.app.Dao(),
 | 
							form.config.Dao,
 | 
				
			||||||
		form.record,
 | 
							form.record,
 | 
				
			||||||
		form.filesToUpload,
 | 
							form.filesToUpload,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
| 
						 | 
					@ -252,12 +305,18 @@ func (form *RecordUpsert) DrySubmit(callback func(txDao *daos.Dao) error) error
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// custom insertion id can be set only on create
 | 
				
			||||||
 | 
						if form.record.IsNew() && form.Id != "" {
 | 
				
			||||||
 | 
							form.record.MarkAsNew()
 | 
				
			||||||
 | 
							form.record.SetId(form.Id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// bulk load form data
 | 
						// bulk load form data
 | 
				
			||||||
	if err := form.record.Load(form.Data); err != nil {
 | 
						if err := form.record.Load(form.Data); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return form.app.Dao().RunInTransaction(func(txDao *daos.Dao) error {
 | 
						return form.config.Dao.RunInTransaction(func(txDao *daos.Dao) error {
 | 
				
			||||||
		tx, ok := txDao.DB().(*dbx.Tx)
 | 
							tx, ok := txDao.DB().(*dbx.Tx)
 | 
				
			||||||
		if !ok {
 | 
							if !ok {
 | 
				
			||||||
			return errors.New("failed to get transaction db")
 | 
								return errors.New("failed to get transaction db")
 | 
				
			||||||
| 
						 | 
					@ -285,13 +344,19 @@ func (form *RecordUpsert) Submit(interceptors ...InterceptorFunc) error {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// custom insertion id can be set only on create
 | 
				
			||||||
 | 
						if form.record.IsNew() && form.Id != "" {
 | 
				
			||||||
 | 
							form.record.MarkAsNew()
 | 
				
			||||||
 | 
							form.record.SetId(form.Id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// bulk load form data
 | 
						// bulk load form data
 | 
				
			||||||
	if err := form.record.Load(form.Data); err != nil {
 | 
						if err := form.record.Load(form.Data); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return runInterceptors(func() error {
 | 
						return runInterceptors(func() error {
 | 
				
			||||||
		return form.app.Dao().RunInTransaction(func(txDao *daos.Dao) error {
 | 
							return form.config.Dao.RunInTransaction(func(txDao *daos.Dao) error {
 | 
				
			||||||
			// persist record model
 | 
								// persist record model
 | 
				
			||||||
			if err := txDao.SaveRecord(form.record); err != nil {
 | 
								if err := txDao.SaveRecord(form.record); err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
| 
						 | 
					@ -322,7 +387,7 @@ func (form *RecordUpsert) processFilesToUpload() error {
 | 
				
			||||||
		return errors.New("The record is not persisted yet.")
 | 
							return errors.New("The record is not persisted yet.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fs, err := form.app.NewFilesystem()
 | 
						fs, err := form.config.FilesystemFactory()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -358,7 +423,7 @@ func (form *RecordUpsert) processFilesToDelete() error {
 | 
				
			||||||
		return errors.New("The record is not persisted yet.")
 | 
							return errors.New("The record is not persisted yet.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fs, err := form.app.NewFilesystem()
 | 
						fs, err := form.config.FilesystemFactory()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,33 +6,63 @@ import (
 | 
				
			||||||
	validation "github.com/go-ozzo/ozzo-validation/v4"
 | 
						validation "github.com/go-ozzo/ozzo-validation/v4"
 | 
				
			||||||
	"github.com/go-ozzo/ozzo-validation/v4/is"
 | 
						"github.com/go-ozzo/ozzo-validation/v4/is"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/core"
 | 
						"github.com/pocketbase/pocketbase/core"
 | 
				
			||||||
 | 
						"github.com/pocketbase/pocketbase/daos"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/forms/validators"
 | 
						"github.com/pocketbase/pocketbase/forms/validators"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/models"
 | 
						"github.com/pocketbase/pocketbase/models"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/tools/list"
 | 
						"github.com/pocketbase/pocketbase/tools/list"
 | 
				
			||||||
	"github.com/pocketbase/pocketbase/tools/types"
 | 
						"github.com/pocketbase/pocketbase/tools/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UserUpsert defines a user upsert (create/update) form.
 | 
					// UserUpsert specifies a [models.User] upsert (create/update) form.
 | 
				
			||||||
type UserUpsert struct {
 | 
					type UserUpsert struct {
 | 
				
			||||||
	app      core.App
 | 
						config UserUpsertConfig
 | 
				
			||||||
	user   *models.User
 | 
						user   *models.User
 | 
				
			||||||
	isCreate bool
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Id              string `form:"id" json:"id"`
 | 
				
			||||||
	Email           string `form:"email" json:"email"`
 | 
						Email           string `form:"email" json:"email"`
 | 
				
			||||||
	Password        string `form:"password" json:"password"`
 | 
						Password        string `form:"password" json:"password"`
 | 
				
			||||||
	PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
 | 
						PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewUserUpsert creates new upsert form for the provided user model
 | 
					// UserUpsertConfig is the [UserUpsert] factory initializer config.
 | 
				
			||||||
// (pass an empty user model instance (`&models.User{}`) for create).
 | 
					//
 | 
				
			||||||
 | 
					// NB! Dao and Settings are required struct members.
 | 
				
			||||||
 | 
					type UserUpsertConfig struct {
 | 
				
			||||||
 | 
						Dao      *daos.Dao
 | 
				
			||||||
 | 
						Settings *core.Settings
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewUserUpsert creates a new [UserUpsert] form with initializer
 | 
				
			||||||
 | 
					// config created from the provided [core.App] and [models.User] instances
 | 
				
			||||||
 | 
					// (for create you could pass a pointer to an empty User - `&models.User{}`).
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This factory method is used primarily for convenience (and backward compatibility).
 | 
				
			||||||
 | 
					// If you want to submit the form as part of another transaction, use
 | 
				
			||||||
 | 
					// [NewUserUpsertWithConfig] with Dao configured to your txDao.
 | 
				
			||||||
func NewUserUpsert(app core.App, user *models.User) *UserUpsert {
 | 
					func NewUserUpsert(app core.App, user *models.User) *UserUpsert {
 | 
				
			||||||
 | 
						return NewUserUpsertWithConfig(UserUpsertConfig{
 | 
				
			||||||
 | 
							Dao:      app.Dao(),
 | 
				
			||||||
 | 
							Settings: app.Settings(),
 | 
				
			||||||
 | 
						}, user)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewUserUpsertWithConfig creates a new [UserUpsert] form
 | 
				
			||||||
 | 
					// with the provided config and [models.User] instance or panics on invalid configuration
 | 
				
			||||||
 | 
					// (for create you could pass a pointer to an empty User - `&models.User{}`).
 | 
				
			||||||
 | 
					func NewUserUpsertWithConfig(config UserUpsertConfig, user *models.User) *UserUpsert {
 | 
				
			||||||
	form := &UserUpsert{
 | 
						form := &UserUpsert{
 | 
				
			||||||
		app:      app,
 | 
							config: config,
 | 
				
			||||||
		user:   user,
 | 
							user:   user,
 | 
				
			||||||
		isCreate: !user.HasId(),
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if form.config.Dao == nil ||
 | 
				
			||||||
 | 
							form.config.Settings == nil ||
 | 
				
			||||||
 | 
							form.user == nil {
 | 
				
			||||||
 | 
							panic("Invalid initializer config or nil upsert model.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// load defaults
 | 
						// load defaults
 | 
				
			||||||
 | 
						form.Id = user.Id
 | 
				
			||||||
	form.Email = user.Email
 | 
						form.Email = user.Email
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return form
 | 
						return form
 | 
				
			||||||
| 
						 | 
					@ -40,9 +70,14 @@ func NewUserUpsert(app core.App, user *models.User) *UserUpsert {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
 | 
					// Validate makes the form validatable by implementing [validation.Validatable] interface.
 | 
				
			||||||
func (form *UserUpsert) Validate() error {
 | 
					func (form *UserUpsert) Validate() error {
 | 
				
			||||||
	config := form.app.Settings()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return validation.ValidateStruct(form,
 | 
						return validation.ValidateStruct(form,
 | 
				
			||||||
 | 
							validation.Field(
 | 
				
			||||||
 | 
								&form.Id,
 | 
				
			||||||
 | 
								validation.When(
 | 
				
			||||||
 | 
									form.user.IsNew(),
 | 
				
			||||||
 | 
									validation.Length(models.DefaultIdLength, models.DefaultIdLength),
 | 
				
			||||||
 | 
								).Else(validation.In(form.user.Id)),
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
		validation.Field(
 | 
							validation.Field(
 | 
				
			||||||
			&form.Email,
 | 
								&form.Email,
 | 
				
			||||||
			validation.Required,
 | 
								validation.Required,
 | 
				
			||||||
| 
						 | 
					@ -53,12 +88,12 @@ func (form *UserUpsert) Validate() error {
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		validation.Field(
 | 
							validation.Field(
 | 
				
			||||||
			&form.Password,
 | 
								&form.Password,
 | 
				
			||||||
			validation.When(form.isCreate, validation.Required),
 | 
								validation.When(form.user.IsNew(), validation.Required),
 | 
				
			||||||
			validation.Length(config.EmailAuth.MinPasswordLength, 100),
 | 
								validation.Length(form.config.Settings.EmailAuth.MinPasswordLength, 100),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		validation.Field(
 | 
							validation.Field(
 | 
				
			||||||
			&form.PasswordConfirm,
 | 
								&form.PasswordConfirm,
 | 
				
			||||||
			validation.When(form.isCreate || form.Password != "", validation.Required),
 | 
								validation.When(form.user.IsNew() || form.Password != "", validation.Required),
 | 
				
			||||||
			validation.By(validators.Compare(form.Password)),
 | 
								validation.By(validators.Compare(form.Password)),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
| 
						 | 
					@ -67,7 +102,7 @@ func (form *UserUpsert) Validate() error {
 | 
				
			||||||
func (form *UserUpsert) checkUniqueEmail(value any) error {
 | 
					func (form *UserUpsert) checkUniqueEmail(value any) error {
 | 
				
			||||||
	v, _ := value.(string)
 | 
						v, _ := value.(string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if v == "" || form.app.Dao().IsUserEmailUnique(v, form.user.Id) {
 | 
						if v == "" || form.config.Dao.IsUserEmailUnique(v, form.user.Id) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -81,8 +116,8 @@ func (form *UserUpsert) checkEmailDomain(value any) error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	domain := val[strings.LastIndex(val, "@")+1:]
 | 
						domain := val[strings.LastIndex(val, "@")+1:]
 | 
				
			||||||
	only := form.app.Settings().EmailAuth.OnlyDomains
 | 
						only := form.config.Settings.EmailAuth.OnlyDomains
 | 
				
			||||||
	except := form.app.Settings().EmailAuth.ExceptDomains
 | 
						except := form.config.Settings.EmailAuth.ExceptDomains
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// only domains check
 | 
						// only domains check
 | 
				
			||||||
	if len(only) > 0 && !list.ExistInSlice(domain, only) {
 | 
						if len(only) > 0 && !list.ExistInSlice(domain, only) {
 | 
				
			||||||
| 
						 | 
					@ -110,7 +145,13 @@ func (form *UserUpsert) Submit(interceptors ...InterceptorFunc) error {
 | 
				
			||||||
		form.user.SetPassword(form.Password)
 | 
							form.user.SetPassword(form.Password)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !form.isCreate && form.Email != form.user.Email {
 | 
						// custom insertion id can be set only on create
 | 
				
			||||||
 | 
						if form.user.IsNew() && form.Id != "" {
 | 
				
			||||||
 | 
							form.user.MarkAsNew()
 | 
				
			||||||
 | 
							form.user.SetId(form.Id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !form.user.IsNew() && form.Email != form.user.Email {
 | 
				
			||||||
		form.user.Verified = false
 | 
							form.user.Verified = false
 | 
				
			||||||
		form.user.LastVerificationSentAt = types.DateTime{} // reset
 | 
							form.user.LastVerificationSentAt = types.DateTime{} // reset
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -118,6 +159,6 @@ func (form *UserUpsert) Submit(interceptors ...InterceptorFunc) error {
 | 
				
			||||||
	form.user.Email = form.Email
 | 
						form.user.Email = form.Email
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return runInterceptors(func() error {
 | 
						return runInterceptors(func() error {
 | 
				
			||||||
		return form.app.Dao().SaveUser(form.user)
 | 
							return form.config.Dao.SaveUser(form.user)
 | 
				
			||||||
	}, interceptors...)
 | 
						}, interceptors...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue