M admin/input.go => admin/input.go +4 -4
@@ 21,9 21,9 @@ func (l *UserLockForm) Validate(c echo.Context) error {
}
type UserForm struct {
- Name string `form:"name" validate:"required"`
- Email string `form:"email" validate:"required,email"`
- IsActive bool `form:"is_active"`
+ Name string `form:"name" validate:"required"`
+ Email string `form:"email" validate:"required,email"`
+ IsVerified bool `form:"is_verified"`
}
func (u *UserForm) Validate(c echo.Context) error {
@@ 31,7 31,7 @@ func (u *UserForm) Validate(c echo.Context) error {
FailFast(false).
String("name", &u.Name).
String("email", &u.Email).
- Bool("is_active", &u.IsActive).
+ Bool("is_verified", &u.IsVerified).
BindErrors()
if errs != nil {
return validate.GetInputErrors(errs)
M admin/routes.go => admin/routes.go +19 -8
@@ 89,6 89,8 @@ func (s *Service) Dashboard(c echo.Context) error {
pd.Data["users"] = lt.Translate("Users")
pd.Data["no_users"] = lt.Translate("No Users")
pd.Data["is_active"] = lt.Translate("Is Active")
+ pd.Data["is_verified"] = lt.Translate("Is Verified")
+ pd.Data["is_locked"] = lt.Translate("Is Locked")
pd.Data["name"] = lt.Translate("Name")
pd.Data["created_on"] = lt.Translate("Created On")
pd.Data["see_more"] = lt.Translate("See more")
@@ 130,7 132,8 @@ func (s *Service) Dashboard(c echo.Context) error {
id
name
email
- isActive
+ isEmailVerified
+ isLocked
createdOn
}
}
@@ 392,6 395,9 @@ func (s *Service) OrgDetail(c echo.Context) error {
op.Var("id", id)
err = links.Execute(c.Request().Context(), op, &result)
if err != nil {
+ if graphError, ok := err.(*gqlclient.Error); ok {
+ return links.ParseInputErrors(c, graphError, gobwebs.Map{})
+ }
return err
}
@@ 1352,6 1358,9 @@ func (s *Service) UserDetail(c echo.Context) error {
pd.Data["email"] = lt.Translate("Email")
pd.Data["created_on"] = lt.Translate("Created On")
pd.Data["is_active"] = lt.Translate("Is Active")
+ pd.Data["is_verified"] = lt.Translate("Is Verified")
+ pd.Data["is_locked"] = lt.Translate("Is Locked")
+ pd.Data["lock_reason"] = lt.Translate("Lock Reason")
pd.Data["name"] = lt.Translate("Name")
pd.Data["yes"] = lt.Translate("Yes")
pd.Data["edit"] = lt.Translate("Edit")
@@ 1382,8 1391,9 @@ func (s *Service) UserDetail(c echo.Context) error {
name
email
createdOn
- isActive
+ isEmailVerified
isLocked
+ lockReason
}
}`)
op.Var("id", id)
@@ 1456,11 1466,11 @@ func (s *Service) UserUpdate(c echo.Context) error {
lt := localizer.GetSessionLocalizer(c)
pd := localizer.NewPageData(lt.Translate("Edit User"))
pd.Data["save"] = lt.Translate("Save")
- pd.Data["is_active"] = lt.Translate("Is Active")
pd.Data["name"] = lt.Translate("Name")
pd.Data["email"] = lt.Translate("Email")
pd.Data["back"] = lt.Translate("Back")
pd.Data["cancel"] = lt.Translate("Cancel")
+ pd.Data["is_verified"] = lt.Translate("Is Verified")
gmap := gobwebs.Map{
"user": user,
"pd": pd,
@@ 1485,8 1495,8 @@ func (s *Service) UserUpdate(c echo.Context) error {
}
var result GraphQLResponse
op := gqlclient.NewOperation(`
- mutation UpdateUser($id: Int!, $isActive: Boolean!, $name: String!, $email: String!) {
- updateAdminUser(input: {id: $id, isActive: $isActive, email: $email, name: $name}) {
+ mutation UpdateUser($id: Int!, $isVerified: Boolean!, $name: String!, $email: String!) {
+ updateAdminUser(input: {id: $id, isVerified: $isVerified, email: $email, name: $name}) {
id
}
}
@@ 1494,7 1504,7 @@ func (s *Service) UserUpdate(c echo.Context) error {
op.Var("id", id)
op.Var("name", form.Name)
op.Var("email", form.Email)
- op.Var("isActive", form.IsActive)
+ op.Var("isVerified", form.IsVerified)
err = links.Execute(c.Request().Context(), op, &result)
if err != nil {
if graphError, ok := err.(*gqlclient.Error); ok {
@@ 1511,9 1521,9 @@ func (s *Service) UserUpdate(c echo.Context) error {
messages.Success(c, lt.Translate("User updated successfully"))
return c.Redirect(http.StatusMovedPermanently, c.Echo().Reverse(s.RouteName("user_detail"), id))
}
- form.IsActive = user.IsActive
form.Name = user.Name
form.Email = user.Email
+ form.IsVerified = user.IsVerified()
return links.Render(c, http.StatusOK, "admin_user_edit.html", gmap)
}
@@ 1571,6 1581,7 @@ func (s *Service) UserList(c echo.Context) error {
pd := localizer.NewPageData(lt.Translate("User List"))
pd.Data["user_list"] = lt.Translate("User List")
pd.Data["is_active"] = lt.Translate("Is Active")
+ pd.Data["is_verified"] = lt.Translate("Is Verified")
pd.Data["name"] = lt.Translate("Name")
pd.Data["created_on"] = lt.Translate("Created On")
pd.Data["next"] = lt.Translate("Next")
@@ 1600,7 1611,7 @@ func (s *Service) UserList(c echo.Context) error {
id
name
email
- isActive
+ isEmailVerified
isLocked
createdOn
}
M api/gqlgen.yml => api/gqlgen.yml +0 -1
@@ 53,7 53,6 @@ models:
Cursor:
model:
- links/api/graph/model.Cursor
-
ID:
model:
- github.com/99designs/gqlgen/graphql.ID
M api/graph/generated.go => api/graph/generated.go +66 -81
@@ 405,13 405,13 @@ type ComplexityRoot struct {
}
User struct {
- CreatedOn func(childComplexity int) int
- Email func(childComplexity int) int
- ID func(childComplexity int) int
- IsActive func(childComplexity int) int
- IsLocked func(childComplexity int) int
- LockReadon func(childComplexity int) int
- Name func(childComplexity int) int
+ CreatedOn func(childComplexity int) int
+ Email func(childComplexity int) int
+ ID func(childComplexity int) int
+ IsEmailVerified func(childComplexity int) int
+ IsLocked func(childComplexity int) int
+ LockReason func(childComplexity int) int
+ Name func(childComplexity int) int
}
UserCursor struct {
@@ 501,8 501,6 @@ type QueryResolver interface {
}
type UserResolver interface {
ID(ctx context.Context, obj *models.User) (int, error)
-
- LockReadon(ctx context.Context, obj *models.User) (string, error)
}
type executableSchema struct {
@@ 2470,12 2468,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.ID(childComplexity), true
- case "User.isActive":
- if e.complexity.User.IsActive == nil {
+ case "User.isEmailVerified":
+ if e.complexity.User.IsEmailVerified == nil {
break
}
- return e.complexity.User.IsActive(childComplexity), true
+ return e.complexity.User.IsEmailVerified(childComplexity), true
case "User.isLocked":
if e.complexity.User.IsLocked == nil {
@@ 2484,12 2482,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.IsLocked(childComplexity), true
- case "User.lockReadon":
- if e.complexity.User.LockReadon == nil {
+ case "User.lockReason":
+ if e.complexity.User.LockReason == nil {
break
}
- return e.complexity.User.LockReadon(childComplexity), true
+ return e.complexity.User.LockReason(childComplexity), true
case "User.name":
if e.complexity.User.Name == nil {
@@ 9558,12 9556,12 @@ func (ec *executionContext) fieldContext_Mutation_register(ctx context.Context,
return ec.fieldContext_User_email(ctx, field)
case "createdOn":
return ec.fieldContext_User_createdOn(ctx, field)
- case "isActive":
- return ec.fieldContext_User_isActive(ctx, field)
+ case "isEmailVerified":
+ return ec.fieldContext_User_isEmailVerified(ctx, field)
case "isLocked":
return ec.fieldContext_User_isLocked(ctx, field)
- case "lockReadon":
- return ec.fieldContext_User_lockReadon(ctx, field)
+ case "lockReason":
+ return ec.fieldContext_User_lockReason(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ 9657,12 9655,12 @@ func (ec *executionContext) fieldContext_Mutation_completeRegister(ctx context.C
return ec.fieldContext_User_email(ctx, field)
case "createdOn":
return ec.fieldContext_User_createdOn(ctx, field)
- case "isActive":
- return ec.fieldContext_User_isActive(ctx, field)
+ case "isEmailVerified":
+ return ec.fieldContext_User_isEmailVerified(ctx, field)
case "isLocked":
return ec.fieldContext_User_isLocked(ctx, field)
- case "lockReadon":
- return ec.fieldContext_User_lockReadon(ctx, field)
+ case "lockReason":
+ return ec.fieldContext_User_lockReason(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ 9756,12 9754,12 @@ func (ec *executionContext) fieldContext_Mutation_updateProfile(ctx context.Cont
return ec.fieldContext_User_email(ctx, field)
case "createdOn":
return ec.fieldContext_User_createdOn(ctx, field)
- case "isActive":
- return ec.fieldContext_User_isActive(ctx, field)
+ case "isEmailVerified":
+ return ec.fieldContext_User_isEmailVerified(ctx, field)
case "isLocked":
return ec.fieldContext_User_isLocked(ctx, field)
- case "lockReadon":
- return ec.fieldContext_User_lockReadon(ctx, field)
+ case "lockReason":
+ return ec.fieldContext_User_lockReason(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ 11710,12 11708,12 @@ func (ec *executionContext) fieldContext_Mutation_updateAdminUser(ctx context.Co
return ec.fieldContext_User_email(ctx, field)
case "createdOn":
return ec.fieldContext_User_createdOn(ctx, field)
- case "isActive":
- return ec.fieldContext_User_isActive(ctx, field)
+ case "isEmailVerified":
+ return ec.fieldContext_User_isEmailVerified(ctx, field)
case "isLocked":
return ec.fieldContext_User_isLocked(ctx, field)
- case "lockReadon":
- return ec.fieldContext_User_lockReadon(ctx, field)
+ case "lockReason":
+ return ec.fieldContext_User_lockReason(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ 15774,12 15772,12 @@ func (ec *executionContext) fieldContext_Query_me(ctx context.Context, field gra
return ec.fieldContext_User_email(ctx, field)
case "createdOn":
return ec.fieldContext_User_createdOn(ctx, field)
- case "isActive":
- return ec.fieldContext_User_isActive(ctx, field)
+ case "isEmailVerified":
+ return ec.fieldContext_User_isEmailVerified(ctx, field)
case "isLocked":
return ec.fieldContext_User_isLocked(ctx, field)
- case "lockReadon":
- return ec.fieldContext_User_lockReadon(ctx, field)
+ case "lockReason":
+ return ec.fieldContext_User_lockReason(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ 15951,12 15949,12 @@ func (ec *executionContext) fieldContext_Query_getUser(ctx context.Context, fiel
return ec.fieldContext_User_email(ctx, field)
case "createdOn":
return ec.fieldContext_User_createdOn(ctx, field)
- case "isActive":
- return ec.fieldContext_User_isActive(ctx, field)
+ case "isEmailVerified":
+ return ec.fieldContext_User_isEmailVerified(ctx, field)
case "isLocked":
return ec.fieldContext_User_isLocked(ctx, field)
- case "lockReadon":
- return ec.fieldContext_User_lockReadon(ctx, field)
+ case "lockReason":
+ return ec.fieldContext_User_lockReason(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ 16657,12 16655,12 @@ func (ec *executionContext) fieldContext_Query_getOrgMembers(ctx context.Context
return ec.fieldContext_User_email(ctx, field)
case "createdOn":
return ec.fieldContext_User_createdOn(ctx, field)
- case "isActive":
- return ec.fieldContext_User_isActive(ctx, field)
+ case "isEmailVerified":
+ return ec.fieldContext_User_isEmailVerified(ctx, field)
case "isLocked":
return ec.fieldContext_User_isLocked(ctx, field)
- case "lockReadon":
- return ec.fieldContext_User_lockReadon(ctx, field)
+ case "lockReason":
+ return ec.fieldContext_User_lockReason(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ 18930,8 18928,8 @@ func (ec *executionContext) fieldContext_User_createdOn(ctx context.Context, fie
return fc, nil
}
-func (ec *executionContext) _User_isActive(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) {
- fc, err := ec.fieldContext_User_isActive(ctx, field)
+func (ec *executionContext) _User_isEmailVerified(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_User_isEmailVerified(ctx, field)
if err != nil {
return graphql.Null
}
@@ 18945,7 18943,7 @@ func (ec *executionContext) _User_isActive(ctx context.Context, field graphql.Co
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.IsActive, nil
+ return obj.IsEmailVerified, nil
}
directive1 := func(ctx context.Context) (interface{}, error) {
scope, err := ec.unmarshalNAccessScope2linksᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "PROFILE")
@@ 18989,7 18987,7 @@ func (ec *executionContext) _User_isActive(ctx context.Context, field graphql.Co
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
-func (ec *executionContext) fieldContext_User_isActive(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+func (ec *executionContext) fieldContext_User_isEmailVerified(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "User",
Field: field,
@@ 19074,8 19072,8 @@ func (ec *executionContext) fieldContext_User_isLocked(ctx context.Context, fiel
return fc, nil
}
-func (ec *executionContext) _User_lockReadon(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) {
- fc, err := ec.fieldContext_User_lockReadon(ctx, field)
+func (ec *executionContext) _User_lockReason(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_User_lockReason(ctx, field)
if err != nil {
return graphql.Null
}
@@ 19089,7 19087,7 @@ func (ec *executionContext) _User_lockReadon(ctx context.Context, field graphql.
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return ec.resolvers.User().LockReadon(rctx, obj)
+ return obj.LockReason, nil
}
directive1 := func(ctx context.Context) (interface{}, error) {
scope, err := ec.unmarshalNAccessScope2linksᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "PROFILE")
@@ 19133,12 19131,12 @@ func (ec *executionContext) _User_lockReadon(ctx context.Context, field graphql.
return ec.marshalNString2string(ctx, field.Selections, res)
}
-func (ec *executionContext) fieldContext_User_lockReadon(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+func (ec *executionContext) fieldContext_User_lockReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "User",
Field: field,
- IsMethod: true,
- IsResolver: true,
+ IsMethod: false,
+ IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
@@ 19193,12 19191,12 @@ func (ec *executionContext) fieldContext_UserCursor_result(ctx context.Context,
return ec.fieldContext_User_email(ctx, field)
case "createdOn":
return ec.fieldContext_User_createdOn(ctx, field)
- case "isActive":
- return ec.fieldContext_User_isActive(ctx, field)
+ case "isEmailVerified":
+ return ec.fieldContext_User_isEmailVerified(ctx, field)
case "isLocked":
return ec.fieldContext_User_isLocked(ctx, field)
- case "lockReadon":
- return ec.fieldContext_User_lockReadon(ctx, field)
+ case "lockReason":
+ return ec.fieldContext_User_lockReason(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ 23388,7 23386,7 @@ func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, o
asMap[k] = v
}
- fieldsInOrder := [...]string{"id", "email", "name", "isActive"}
+ fieldsInOrder := [...]string{"id", "email", "name", "isVerified"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ 23419,11 23417,11 @@ func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, o
if err != nil {
return it, err
}
- case "isActive":
+ case "isVerified":
var err error
- ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("isActive"))
- it.IsActive, err = ec.unmarshalNBoolean2bool(ctx, v)
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("isVerified"))
+ it.IsVerified, err = ec.unmarshalNBoolean2bool(ctx, v)
if err != nil {
return it, err
}
@@ 26432,9 26430,9 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
- case "isActive":
+ case "isEmailVerified":
- out.Values[i] = ec._User_isActive(ctx, field, obj)
+ out.Values[i] = ec._User_isEmailVerified(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&invalids, 1)
@@ 26446,26 26444,13 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
- case "lockReadon":
- field := field
-
- innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
- defer func() {
- if r := recover(); r != nil {
- ec.Error(ctx, ec.Recover(ctx, r))
- }
- }()
- res = ec._User_lockReadon(ctx, field, obj)
- if res == graphql.Null {
- atomic.AddUint32(&invalids, 1)
- }
- return res
- }
+ case "lockReason":
- out.Concurrently(i, func() graphql.Marshaler {
- return innerFunc(ctx)
+ out.Values[i] = ec._User_lockReason(ctx, field, obj)
- })
+ if out.Values[i] == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
default:
panic("unknown field " + strconv.Quote(field.Name))
}
M api/graph/model/models_gen.go => api/graph/model/models_gen.go +4 -4
@@ 430,10 430,10 @@ type UpdateOrganizationInput struct {
}
type UpdateUserInput struct {
- ID int `json:"id"`
- Email string `json:"email"`
- Name string `json:"name"`
- IsActive bool `json:"isActive"`
+ ID int `json:"id"`
+ Email string `json:"email"`
+ Name string `json:"name"`
+ IsVerified bool `json:"isVerified"`
}
type UserCursor struct {
M api/graph/schema.graphqls => api/graph/schema.graphqls +3 -3
@@ 87,9 87,9 @@ type User {
name: String!
email: String!
createdOn: Time! @access(scope: PROFILE, kind: RO)
- isActive: Boolean! @access(scope: PROFILE, kind: RO)
+ isEmailVerified: Boolean! @access(scope: PROFILE, kind: RO)
isLocked: Boolean! @access(scope: PROFILE, kind: RO)
- lockReadon: String! @access(scope: PROFILE, kind: RO)
+ lockReason: String! @access(scope: PROFILE, kind: RO)
}
type BillingSettings {
@@ 576,7 576,7 @@ input UpdateUserInput {
id: Int!
email: String!
name: String!
- isActive: Boolean!
+ isVerified: Boolean!
}
input CompleteRegisterInput {
M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +19 -10
@@ 3994,9 3994,9 @@ func (r *mutationResolver) UpdateAdminUser(ctx context.Context, input *model.Upd
return nil, nil
}
currUser := users[0]
- currUser.IsActive = input.IsActive
currUser.Email = input.Email
currUser.Name = input.Name
+ currUser.SetVerified(input.IsVerified)
err = currUser.Store(ctx)
if err != nil {
return nil, err
@@ 4278,11 4278,15 @@ func (r *queryResolver) GetOrganization(ctx context.Context, id int) (*models.Or
lang := links.GetLangFromRequest(server.EchoForContext(ctx).Request(), user)
lt := localizer.GetLocalizer(lang)
opts := &database.FilterOptions{
- Filter: sq.And{
- sq.Eq{"o.id": id},
+ Filter: sq.Eq{"o.id": id},
+ Limit: 1,
+ }
+ if !user.IsSuperUser() {
+ // XXX Remove this? Not sure why we have this filter in place.
+ opts.Filter = sq.And{
+ opts.Filter,
sq.Eq{"o.is_active": true},
- },
- Limit: 1,
+ }
}
orgs, err := models.GetOrganizations(ctx, opts)
if err != nil {
@@ 6540,11 6544,6 @@ func (r *userResolver) ID(ctx context.Context, obj *models.User) (int, error) {
return int(obj.ID), nil
}
-// LockReadon is the resolver for the lockReadon field.
-func (r *userResolver) LockReadon(ctx context.Context, obj *models.User) (string, error) {
- panic(fmt.Errorf("not implemented: LockReadon - lockReadon"))
-}
-
// Domain returns DomainResolver implementation.
func (r *Resolver) Domain() DomainResolver { return &domainResolver{r} }
@@ 6569,3 6568,13 @@ type orgLinkResolver struct{ *Resolver }
type organizationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type userResolver struct{ *Resolver }
+
+// !!! WARNING !!!
+// The code below was going to be deleted when updating resolvers. It has been copied here so you have
+// one last chance to move it out of harms way if you want. There are two reasons this happens:
+// - When renaming or deleting a resolver the old code will be put in here. You can safely delete
+// it when you're done.
+// - You have helper methods in this file. Move them out to keep these resolver files clean.
+func (r *userResolver) LockReadon(ctx context.Context, obj *models.User) (string, error) {
+ panic(fmt.Errorf("not implemented: LockReadon - lockReadon"))
+}
M migrations/0001_initial.up.sql => migrations/0001_initial.up.sql +0 -1
@@ 17,7 17,6 @@ CREATE TABLE users (
email VARCHAR ( 255 ) UNIQUE NOT NULL,
picture VARCHAR(1024) DEFAULT '',
settings JSONB DEFAULT '{}',
- is_active BOOLEAN DEFAULT TRUE,
is_verified BOOLEAN DEFAULT FALSE,
is_superuser BOOLEAN DEFAULT FALSE,
is_staff BOOLEAN DEFAULT FALSE,
M models/models.go => models/models.go +2 -2
@@ 26,11 26,11 @@ type User struct {
accounts.BaseUser
Name string `db:"full_name" json:"name"`
Settings UserSettings `db:"settings"`
- IsActive bool `db:"is_active"`
IsLocked bool `db:"is_locked" json:"isLocked"`
LockReason string `db:"lock_reason" json:"lockReason"`
- OrgSlug sql.NullString `db:"-" json:"-"` // This field counts like the username for personal organization context
+ OrgSlug sql.NullString `db:"-" json:"-"` // This field counts like the username for personal organization context
+ IsEmailVerified bool `db:"-" json:"isEmailVerified"`
}
// Organization is...
M models/organization.go => models/organization.go +66 -66
@@ 55,15 55,65 @@ func (os *OrganizationSettings) Scan(value interface{}) error {
return json.Unmarshal(b, &os)
}
-// ToLocalTZ convert UTC date to local tz
-func (o *Organization) ToLocalTZ(tz string) error {
- loc, err := time.LoadLocation(tz)
- if err != nil {
- return err
+// GetOrganizations ...
+func GetOrganizations(ctx context.Context, opts *database.FilterOptions) ([]*Organization, error) {
+ if opts == nil {
+ opts = &database.FilterOptions{}
}
- o.CreatedOn = o.CreatedOn.In(loc)
- o.UpdatedOn = o.UpdatedOn.In(loc)
- return nil
+ tz := timezone.ForContext(ctx)
+ orgs := make([]*Organization, 0)
+ if err := database.WithTx(ctx, database.TxOptionsRO, func(tx *sql.Tx) error {
+ q := opts.GetBuilder(nil)
+ rows, err := q.
+ Columns("o.id", "o.owner_id", "o.org_type", "o.name", "o.slug", "o.image", "o.timezone",
+ "o.settings", "o.is_active", "o.created_on", "o.updated_on", "u.full_name", "sc.id",
+ "mc.id").
+ From("organizations o").
+ Join("users u ON o.owner_id = u.id").
+ LeftJoin("organization_users ou ON o.id = ou.org_id").
+ LeftJoin("slack_connections sc ON o.id = sc.org_id").
+ LeftJoin("mattermost_connections mc ON o.id = mc.org_id").
+ LeftJoin("domains d ON o.id = d.org_id").
+ LeftJoin("followers f ON f.org_id = o.id").
+ Distinct().
+ PlaceholderFormat(sq.Dollar).
+ RunWith(tx).
+ QueryContext(ctx)
+ if err != nil {
+ if err == sql.ErrNoRows {
+ return nil
+ }
+ return err
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var org Organization
+ if err = rows.Scan(&org.ID, &org.OwnerID, &org.OrgType, &org.Name, &org.Slug,
+ &org.Image, &org.Timezone, &org.Settings, &org.IsActive, &org.CreatedOn, &org.UpdatedOn,
+ &org.OwnerName, &org.SlackConnID, &org.MattermostConnID,
+ ); err != nil {
+ return err
+ }
+
+ err = org.ToLocalTZ(tz)
+ if err != nil {
+ return err
+ }
+ orgs = append(orgs, &org)
+ }
+ return nil
+ }); err != nil {
+ return nil, err
+ }
+ return orgs, nil
+}
+
+// GetOrganization ...
+func GetOrganization(ctx context.Context, id int) (*Organization, error) {
+ o := &Organization{ID: id}
+ err := o.Load(ctx)
+ return o, err
}
// Load organization
@@ 153,65 203,15 @@ func (o *Organization) Delete(ctx context.Context) error {
return err
}
-// GetOrganization ...
-func GetOrganization(ctx context.Context, id int) (*Organization, error) {
- o := &Organization{ID: id}
- err := o.Load(ctx)
- return o, err
-}
-
-// GetOrganizations ...
-func GetOrganizations(ctx context.Context, opts *database.FilterOptions) ([]*Organization, error) {
- if opts == nil {
- opts = &database.FilterOptions{}
- }
- tz := timezone.ForContext(ctx)
- orgs := make([]*Organization, 0)
- if err := database.WithTx(ctx, database.TxOptionsRO, func(tx *sql.Tx) error {
- q := opts.GetBuilder(nil)
- rows, err := q.
- Columns("o.id", "o.owner_id", "o.org_type", "o.name", "o.slug", "o.image", "o.timezone",
- "o.settings", "o.is_active", "o.created_on", "o.updated_on", "u.full_name", "sc.id",
- "mc.id").
- From("organizations o").
- Join("users u ON o.owner_id = u.id").
- LeftJoin("organization_users ou ON o.id = ou.org_id").
- LeftJoin("slack_connections sc ON o.id = sc.org_id").
- LeftJoin("mattermost_connections mc ON o.id = mc.org_id").
- LeftJoin("domains d ON o.id = d.org_id").
- LeftJoin("followers f ON f.org_id = o.id").
- Distinct().
- PlaceholderFormat(sq.Dollar).
- RunWith(tx).
- QueryContext(ctx)
- if err != nil {
- if err == sql.ErrNoRows {
- return nil
- }
- return err
- }
- defer rows.Close()
-
- for rows.Next() {
- var org Organization
- if err = rows.Scan(&org.ID, &org.OwnerID, &org.OrgType, &org.Name, &org.Slug,
- &org.Image, &org.Timezone, &org.Settings, &org.IsActive, &org.CreatedOn, &org.UpdatedOn,
- &org.OwnerName, &org.SlackConnID, &org.MattermostConnID,
- ); err != nil {
- return err
- }
-
- err = org.ToLocalTZ(tz)
- if err != nil {
- return err
- }
- orgs = append(orgs, &org)
- }
- return nil
- }); err != nil {
- return nil, err
+// ToLocalTZ convert UTC date to local tz
+func (o *Organization) ToLocalTZ(tz string) error {
+ loc, err := time.LoadLocation(tz)
+ if err != nil {
+ return err
}
- return orgs, nil
+ o.CreatedOn = o.CreatedOn.In(loc)
+ o.UpdatedOn = o.UpdatedOn.In(loc)
+ return nil
}
func (o *Organization) permCheck(ctx context.Context, user *User, perm int) bool {
M models/schema.sql => models/schema.sql +0 -1
@@ 17,7 17,6 @@ CREATE TABLE users (
email VARCHAR ( 255 ) UNIQUE NOT NULL,
picture VARCHAR(1024) DEFAULT '',
settings JSONB DEFAULT '{}',
- is_active BOOLEAN DEFAULT TRUE,
is_verified BOOLEAN DEFAULT FALSE,
is_superuser BOOLEAN DEFAULT FALSE,
is_staff BOOLEAN DEFAULT FALSE,
M models/user.go => models/user.go +5 -5
@@ 72,7 72,7 @@ func GetUser(ctx context.Context, uid any, markAuth bool) (*User, error) {
err := sq.
Select().
Columns("u.id", "u.full_name", "u.password", "u.email", "u.settings", "u.is_verified",
- "u.is_superuser", "u.is_staff", "u.created_on", "u.last_login", "u.is_active",
+ "u.is_superuser", "u.is_staff", "u.created_on", "u.last_login",
"u.is_locked", "u.lock_reason", "o.slug").
From("users u").
LeftJoin("organizations o ON o.owner_id = u.id").
@@ 90,7 90,6 @@ func GetUser(ctx context.Context, uid any, markAuth bool) (*User, error) {
&staff,
&user.CreatedOn,
&user.LastLogin,
- &user.IsActive,
&user.IsLocked,
&user.LockReason,
&user.OrgSlug,
@@ 112,6 111,7 @@ func GetUser(ctx context.Context, uid any, markAuth bool) (*User, error) {
user.SetSuperUser(superuser)
user.SetStaff(staff)
user.SetAuthenticated(markAuth)
+ user.IsEmailVerified = user.IsVerified() // hack
return user, nil
}
@@ 127,7 127,7 @@ func GetUsers(ctx context.Context, opts *database.FilterOptions) ([]*User, error
rows, err := q.
Columns("u.id", "u.full_name", "u.password", "u.email", "u.settings",
"u.is_verified", "u.is_superuser", "u.is_staff", "u.created_on",
- "u.last_login", "u.is_active", "u.is_locked", "u.lock_reason", "o.slug").
+ "u.last_login", "u.is_locked", "u.lock_reason", "o.slug").
From("users u").
LeftJoin("organizations o ON o.owner_id = u.id").
LeftJoin("organization_users ou ON u.id = ou.user_id").
@@ 149,7 149,7 @@ func GetUsers(ctx context.Context, opts *database.FilterOptions) ([]*User, error
verified, superuser, staff bool
)
if err = rows.Scan(&u.ID, &u.Name, &u.Password, &u.Email, &u.Settings,
- &verified, &superuser, &staff, &u.CreatedOn, &u.LastLogin, &u.IsActive,
+ &verified, &superuser, &staff, &u.CreatedOn, &u.LastLogin,
&u.IsLocked, &u.LockReason, &u.OrgSlug,
); err != nil {
return err
@@ 157,6 157,7 @@ func GetUsers(ctx context.Context, opts *database.FilterOptions) ([]*User, error
u.SetVerified(verified)
u.SetSuperUser(superuser)
u.SetStaff(staff)
+ u.IsEmailVerified = u.IsVerified() // hack
err = u.ToLocalTZ(tz)
if err != nil {
return err
@@ 198,7 199,6 @@ func (u *User) Store(ctx context.Context) error {
Set("is_verified", u.IsVerified()).
Set("is_superuser", u.IsSuperUser()).
Set("is_staff", u.IsStaff()).
- Set("is_active", u.IsActive).
Set("is_locked", u.IsLocked).
Set("lock_reason", u.LockReason).
Where("id = ?", u.GetID()).
M templates/admin_dashboard.html => templates/admin_dashboard.html +14 -2
@@ 22,7 22,8 @@
<th>{{.pd.Data.name}}</th>
<th>Email</th>
<th>{{.pd.Data.created_on}}</th>
- <th class="text-center">{{.pd.Data.is_active}}</th>
+ <th class="text-center">{{.pd.Data.is_verified}}</th>
+ <th class="text-center">{{.pd.Data.is_locked}}</th>
</tr>
</thead>
<tbody>
@@ 34,7 35,18 @@
<td>{{.Email}}</td>
<td>{{formatDate .CreatedOn}}</td>
<td class="text-center">
- {{if .IsActive}}
+ {{if .IsEmailVerified}}
+ <svg style="width:20px" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-primary">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
+ </svg>
+ {{else}}
+ <svg style="width:20px" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-error">
+ <path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
+ </svg>
+ {{end}}
+ </td>
+ <td class="text-center">
+ {{if .IsLocked}}
<svg style="width:20px" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-primary">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
M templates/admin_user_detail.html => templates/admin_user_detail.html +18 -2
@@ 29,10 29,10 @@
<td>{{formatDate .user.CreatedOn}}</td>
</tr>
<tr>
- <th>{{.pd.Data.is_active}}</th>
+ <th>{{.pd.Data.is_verified}}</th>
<td>
<p class="is-vertical-align">
- {{if .user.IsActive}}
+ {{if .user.IsEmailVerified}}
<svg version="1.1" class="has-solid " viewBox="0 0 36 36" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" focusable="false" role="img" width="22" height="22" fill="#008e00"><path class="clr-i-outline clr-i-outline-path-1" d="M18,6A12,12,0,1,0,30,18,12,12,0,0,0,18,6Zm0,22A10,10,0,1,1,28,18,10,10,0,0,1,18,28Z"/><path class="clr-i-outline clr-i-outline-path-2" d="M16.34,23.74l-5-5a1,1,0,0,1,1.41-1.41l3.59,3.59,6.78-6.78a1,1,0,0,1,1.41,1.41Z"/><path class="clr-i-solid clr-i-solid-path-1" d="M30,18A12,12,0,1,1,18,6,12,12,0,0,1,30,18Zm-4.77-2.16a1.4,1.4,0,0,0-2-2l-6.77,6.77L13,17.16a1.4,1.4,0,0,0-2,2l5.45,5.45Z" style="display:none"/></svg>
{{else}}
<svg version="1.1" class="has-solid " viewBox="0 0 36 36" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" focusable="false" role="img" width="16" height="16" fill="#ff2600"><path class="clr-i-outline clr-i-outline-path-1" d="M19.61,18l4.86-4.86a1,1,0,0,0-1.41-1.41L18.2,16.54l-4.89-4.89a1,1,0,0,0-1.41,1.41L16.78,18,12,22.72a1,1,0,1,0,1.41,1.41l4.77-4.77,4.74,4.74a1,1,0,0,0,1.41-1.41Z"/><path class="clr-i-outline clr-i-outline-path-2" d="M18,34A16,16,0,1,1,34,18,16,16,0,0,1,18,34ZM18,4A14,14,0,1,0,32,18,14,14,0,0,0,18,4Z"/><path class="clr-i-solid clr-i-solid-path-1" d="M18,2A16,16,0,1,0,34,18,16,16,0,0,0,18,2Zm8,22.1a1.4,1.4,0,0,1-2,2l-6-6L12,26.12a1.4,1.4,0,1,1-2-2L16,18.08,9.83,11.86a1.4,1.4,0,1,1,2-2L18,16.1l6.17-6.17a1.4,1.4,0,1,1,2,2L20,18.08Z" style="display:none"/></svg>
@@ 40,6 40,22 @@
</p>
</td>
</tr>
+ <tr>
+ <th>{{.pd.Data.is_locked}}</th>
+ <td>
+ {{if .user.IsLocked}}
+ <svg version="1.1" class="has-solid " viewBox="0 0 36 36" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" focusable="false" role="img" width="22" height="22" fill="#008e00"><path class="clr-i-outline clr-i-outline-path-1" d="M18,6A12,12,0,1,0,30,18,12,12,0,0,0,18,6Zm0,22A10,10,0,1,1,28,18,10,10,0,0,1,18,28Z"/><path class="clr-i-outline clr-i-outline-path-2" d="M16.34,23.74l-5-5a1,1,0,0,1,1.41-1.41l3.59,3.59,6.78-6.78a1,1,0,0,1,1.41,1.41Z"/><path class="clr-i-solid clr-i-solid-path-1" d="M30,18A12,12,0,1,1,18,6,12,12,0,0,1,30,18Zm-4.77-2.16a1.4,1.4,0,0,0-2-2l-6.77,6.77L13,17.16a1.4,1.4,0,0,0-2,2l5.45,5.45Z" style="display:none"/></svg>
+ {{else}}
+ <svg version="1.1" class="has-solid " viewBox="0 0 36 36" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" focusable="false" role="img" width="16" height="16" fill="#ff2600"><path class="clr-i-outline clr-i-outline-path-1" d="M19.61,18l4.86-4.86a1,1,0,0,0-1.41-1.41L18.2,16.54l-4.89-4.89a1,1,0,0,0-1.41,1.41L16.78,18,12,22.72a1,1,0,1,0,1.41,1.41l4.77-4.77,4.74,4.74a1,1,0,0,0,1.41-1.41Z"/><path class="clr-i-outline clr-i-outline-path-2" d="M18,34A16,16,0,1,1,34,18,16,16,0,0,1,18,34ZM18,4A14,14,0,1,0,32,18,14,14,0,0,0,18,4Z"/><path class="clr-i-solid clr-i-solid-path-1" d="M18,2A16,16,0,1,0,34,18,16,16,0,0,0,18,2Zm8,22.1a1.4,1.4,0,0,1-2,2l-6-6L12,26.12a1.4,1.4,0,1,1-2-2L16,18.08,9.83,11.86a1.4,1.4,0,1,1,2-2L18,16.1l6.17-6.17a1.4,1.4,0,1,1,2,2L20,18.08Z" style="display:none"/></svg>
+ {{end}}
+ </td>
+ </tr>
+ {{if .user.IsLocked}}
+ <tr>
+ <th>{{.pd.Data.lock_reason}}</th>
+ <td>{{.user.LockReason}}</td>
+ </tr>
+ {{ end }}
</tbody>
</table>
<h4>{{.pd.Data.organizations}}</h4>
M templates/admin_user_edit.html => templates/admin_user_edit.html +10 -10
@@ 28,16 28,16 @@
{{ end }}
</div>
<div>
- <label for="is_active">{{.pd.Data.is_active}}</label>
- <input type="checkbox" value="true" name="is_active" id="is_active"{{if .form.IsActive}} checked{{end}}>
- {{ with .errors.IsActive }}
- <p class="error">{{ . }}</p>
- {{ end }}
- </div>
+ <label for="is_verified">{{.pd.Data.is_verified}}</label>
+ <input type="checkbox" value="true" name="is_verified" id="is_verified"{{if .form.IsVerified}} checked{{end}}>
+ {{ with .errors.IsVerified }}
+ <p class="error">{{ . }}</p>
+ {{ end }}
+ </div>
+ <footer class="is-right">
+ <button form="user-edit-form" type="submit" class="button dark">{{.pd.Data.save}}</button>
+ <a class="button error" href="{{reverse "admin:user_detail" .user.ID}}">{{.pd.Data.cancel}}</a>
+ </footer>
</form>
- <footer class="is-right">
- <button form="user-edit-form" type="submit" class="button dark">{{.pd.Data.save}}</button>
- <a class="button error" href="{{reverse "admin:user_detail" .user.ID}}">{{.pd.Data.cancel}}</a>
- </footer>
</section>
{{template "base_footer" .}}
M templates/admin_user_list.html => templates/admin_user_list.html +2 -2
@@ 24,7 24,7 @@
<th class="text-center">{{.pd.Data.name}}</th>
<th class="text-center">Email</th>
<th class="text-center">{{.pd.Data.created_on}}</th>
- <th class="text-center">{{.pd.Data.is_active}}</th>
+ <th class="text-center">{{.pd.Data.is_verified}}</th>
</tr>
</thead>
<tbody>
@@ 35,7 35,7 @@
<td class="text-center">{{.Email}}</td>
<td class="text-center">{{formatDate .CreatedOn}}</td>
<td class="text-center">
- {{if .IsActive}}
+ {{if .IsEmailVerified}}
<svg style="width:20px" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-primary">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>