package models import ( "time" "gorm.io/gorm" ) // User is the persisted account the JWT guard and user API load. type User struct { ID uint `gorm:"column:id;primaryKey"` Email string `gorm:"column:email"` Password string `gorm:"column:password" json:"-"` MustChangePassword bool `gorm:"column:must_change_password"` Name *string `gorm:"column:name"` Surname *string `gorm:"column:surname"` IsActivated bool `gorm:"column:is_activated"` ActivatedAt *time.Time `gorm:"column:activated_at"` ActivationCode *string `gorm:"column:activation_code" json:"-"` ActivationCodeIssuedAt *time.Time `gorm:"column:activation_code_issued_at"` ResetPasswordCode *string `gorm:"column:reset_password_code" json:"-"` ResetPasswordCodeIssuedAt *time.Time `gorm:"column:reset_password_code_issued_at"` HasSelfSetPassword bool `gorm:"column:has_self_set_password"` MarketingConsent bool `gorm:"column:marketing_consent"` IsOnboarded bool `gorm:"column:is_onboarded"` OrganisationID *uint `gorm:"column:organisation_id"` OrganisationRole *string `gorm:"column:organisation_role"` PreferredLocale *string `gorm:"column:preferred_locale"` TokensValidAfter *time.Time `gorm:"column:tokens_valid_after"` CreatedIPAddress *string `gorm:"column:created_ip_address"` LastIPAddress *string `gorm:"column:last_ip_address"` // LastSeen is stamped by login and token refresh, at most once per five // minutes (classes.TouchLastSeen). It is shown in the admin list and is // never part of a user API payload. LastSeen *time.Time `gorm:"column:last_seen" json:"-"` // CreatedAt and UpdatedAt are read-only: the database defaults fill them // and GORM never writes or stamps them through this model. CreatedAt time.Time `gorm:"column:created_at;->" json:"-"` UpdatedAt time.Time `gorm:"column:updated_at;->" json:"-"` DeletedAt gorm.DeletedAt `gorm:"column:deleted_at"` // Groups is the PHP belongsToMany groups relation through users_groups. // It is loaded only on request, written only by a caller that // associates groups, and never serialized: the user API answers // "groups":[] from its own payload builder. Groups []UserGroup `gorm:"many2many:users_groups;joinForeignKey:user_id;joinReferences:user_group_id" json:"-"` } func (User) TableName() string { return "users" } // MorphName is the PHP class string stored in system_files.attachment_type. func (User) MorphName() string { return `Golem15\User\Models\User` } // Fillable is the register/update field list. Username, PIN, and GDPR columns // stay out of Phase 7 because no ported handler reads them. func (User) Fillable() []string { return []string{ "name", "surname", "email", "password", "created_ip_address", "last_ip_address", "is_onboarded", "preferred_locale", "marketing_consent", "organisation_id", "organisation_role", "must_change_password", } } // Hidden mirrors PHP User::$hidden for the secrets this model stores. func (User) Hidden() []string { return []string{"password", "reset_password_code", "activation_code"} } // Rules is the register-time rule set. confirmed covers password_confirmation. func (User) Rules() map[string]string { return map[string]string{ "email": "required|between:6,255|email|unique:users", "password": "required|between:8,255|confirmed", "password_confirmation": "between:8,255", } } // FilterByGroup is the admin list scope that keeps the members of one group, // the PHP User::scopeFilterByGroup. const FilterByGroup = "filterByGroup" // FilterScopes lists the scope names a config_filter.yaml may call. func (User) FilterScopes() []string { return []string{FilterByGroup} } // FilterScope narrows db to the members of the group whose id is value. Any // other scope name matches nothing. func (User) FilterScope(name string, db *gorm.DB, value any) *gorm.DB { if db == nil { return db } if name != FilterByGroup { return db.Where("1 = 0") } return db.Where("users.id IN (SELECT user_id FROM users_groups WHERE user_group_id = ?)", value) } func init() { Register(User{}) }