From e6efde0bcb842a0e2ea875effede53741cc929de Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Sat, 8 Oct 2022 15:32:43 +0800 Subject: [PATCH 01/16] #2908 1. add medal category api 2. add medal api(list,operate) --- models/medal.go | 103 ++++++++++++++++++++++ models/medal_category.go | 79 +++++++++++++++++ models/models.go | 2 + models/user_medal.go | 10 +++ routers/medal/category.go | 46 ++++++++++ routers/medal/medal.go | 46 ++++++++++ routers/routes/routes.go | 14 ++- services/admin/operate_log/operate_log.go | 38 ++++++++ services/medal/category.go | 64 ++++++++++++++ services/medal/medal.go | 64 ++++++++++++++ 10 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 models/medal.go create mode 100644 models/medal_category.go create mode 100644 models/user_medal.go create mode 100644 routers/medal/category.go create mode 100644 routers/medal/medal.go create mode 100644 services/medal/category.go create mode 100644 services/medal/medal.go diff --git a/models/medal.go b/models/medal.go new file mode 100644 index 000000000..d2508b3b1 --- /dev/null +++ b/models/medal.go @@ -0,0 +1,103 @@ +package models + +import ( + "code.gitea.io/gitea/modules/timeutil" + "xorm.io/builder" +) + +type Medal struct { + ID int64 `xorm:"pk autoincr"` + Name string + LightedIcon string `xorm:"varchar(2048)"` + GreyedIcon string `xorm:"varchar(2048)"` + Url string `xorm:"varchar(2048)"` + CategoryId int64 + CreatedUnix timeutil.TimeStamp `xorm:"created"` + UpdatedUnix timeutil.TimeStamp `xorm:"updated"` + DeletedAt timeutil.TimeStamp `xorm:"deleted"` +} + +type GetMedalOpts struct { + MedalType MedalType +} + +type MedalAndCategory struct { + Medal Medal `xorm:"extends"` + Category MedalCategory `xorm:"extends"` +} + +func (*MedalAndCategory) TableName() string { + return "medal" +} + +func (m *MedalAndCategory) ToShow() *Medal4Show { + return &Medal4Show{ + ID: m.Medal.ID, + Name: m.Medal.Name, + LightedIcon: m.Medal.LightedIcon, + GreyedIcon: m.Medal.GreyedIcon, + Url: m.Medal.Url, + CategoryName: m.Category.Name, + CategoryId: m.Category.ID, + CreatedUnix: m.Medal.CreatedUnix, + UpdatedUnix: m.Medal.UpdatedUnix, + } +} + +type Medal4Show struct { + ID int64 + Name string + LightedIcon string + GreyedIcon string + Url string + CategoryName string + CategoryId int64 + CreatedUnix timeutil.TimeStamp + UpdatedUnix timeutil.TimeStamp +} + +func (m Medal4Show) ToDTO() Medal { + return Medal{ + Name: m.Name, + LightedIcon: m.LightedIcon, + GreyedIcon: m.GreyedIcon, + Url: m.Url, + CategoryId: m.CategoryId, + } +} + +func GetMedalList(opts GetMedalOpts) ([]*MedalAndCategory, error) { + var cond = builder.NewCond() + if opts.MedalType > 0 { + cond = cond.And(builder.Eq{"medal_category.type": opts.MedalType}) + } + + r := make([]*MedalAndCategory, 0) + if err := x.Join("INNER", "medal_category", "medal_category.ID = medal.category_id").Where(cond).OrderBy("medal.created_unix desc").Find(&r); err != nil { + return nil, err + } + return r, nil +} + +func AddMedal(m Medal) (int64, error) { + return x.Insert(&m) +} + +func UpdateMedalById(id int64, param Medal) (int64, error) { + return x.ID(id).Update(¶m) +} + +func DelMedal(id int64) (int64, error) { + return x.ID(id).Delete(&Medal{}) +} + +func GetMedalById(id int64) (*Medal, error) { + m := &Medal{} + has, err := x.ID(id).Get(m) + if err != nil { + return nil, err + } else if !has { + return nil, &ErrRecordNotExist{} + } + return m, nil +} diff --git a/models/medal_category.go b/models/medal_category.go new file mode 100644 index 000000000..3c8769650 --- /dev/null +++ b/models/medal_category.go @@ -0,0 +1,79 @@ +package models + +import "code.gitea.io/gitea/modules/timeutil" + +type MedalType int + +const ( + CustomizeMedal = iota + 1 + SystemMedal +) + +type MedalCategory struct { + ID int64 `xorm:"pk autoincr"` + Name string + Position int64 + Type MedalType + CreatedUnix timeutil.TimeStamp `xorm:"created"` + UpdatedUnix timeutil.TimeStamp `xorm:"updated"` + DeletedAt timeutil.TimeStamp `xorm:"deleted"` +} + +func (m *MedalCategory) ToShow() *MedalCategory4Show { + return &MedalCategory4Show{ + ID: m.ID, + Name: m.Name, + Position: m.Position, + Type: m.Type, + CreatedUnix: m.CreatedUnix, + } +} + +type MedalCategory4Show struct { + ID int64 `xorm:"pk autoincr"` + Name string + Position int64 + Type MedalType + CreatedUnix timeutil.TimeStamp `xorm:"created"` +} + +func (m MedalCategory4Show) ToDTO() MedalCategory { + return MedalCategory{ + ID: m.ID, + Name: m.Name, + Position: m.Position, + Type: m.Type, + CreatedUnix: m.CreatedUnix, + } +} + +func GetMedalCategoryList() ([]*MedalCategory, error) { + r := make([]*MedalCategory, 0) + if err := x.OrderBy("position asc,created_unix desc").Find(&r); err != nil { + return nil, err + } + return r, nil +} + +func AddMedalCategory(m MedalCategory) (int64, error) { + return x.Insert(&m) +} + +func UpdateMedalCategoryById(id int64, param MedalCategory) (int64, error) { + return x.ID(id).Update(¶m) +} + +func DelMedalCategory(id int64) (int64, error) { + return x.ID(id).Delete(&MedalCategory{}) +} + +func GetMedalCategoryById(id int64) (*MedalCategory, error) { + m := &MedalCategory{} + has, err := x.ID(id).Get(m) + if err != nil { + return nil, err + } else if !has { + return nil, &ErrRecordNotExist{} + } + return m, nil +} diff --git a/models/models.go b/models/models.go index 4c2079cd8..910bfde65 100755 --- a/models/models.go +++ b/models/models.go @@ -161,6 +161,8 @@ func init() { new(CloudbrainSpec), new(CloudbrainTemp), new(DatasetReference), + new(MedalCategory), + new(Medal), ) tablesStatistic = append(tablesStatistic, diff --git a/models/user_medal.go b/models/user_medal.go new file mode 100644 index 000000000..a4421df13 --- /dev/null +++ b/models/user_medal.go @@ -0,0 +1,10 @@ +package models + +import "code.gitea.io/gitea/modules/timeutil" + +type UserMedal struct { + ID int64 `xorm:"pk autoincr"` + UserId int64 `xorm:"index"` + MedalId int64 `xorm:"index"` + CreatedUnix timeutil.TimeStamp `xorm:"created"` +} diff --git a/routers/medal/category.go b/routers/medal/category.go new file mode 100644 index 000000000..b010af586 --- /dev/null +++ b/routers/medal/category.go @@ -0,0 +1,46 @@ +package medal + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/routers/response" + "code.gitea.io/gitea/services/medal" + "errors" + "net/http" +) + +func GetCategoryList(ctx *context.Context) { + r, err := medal.GetMedalCategoryList() + if err != nil { + log.Error("GetCategoryList error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + m := make(map[string]interface{}) + m["List"] = r + ctx.JSON(http.StatusOK, response.SuccessWithData(m)) +} + +func OperateMedalCategory(ctx *context.Context, category models.MedalCategory4Show) { + action := ctx.Params(":action") + + var err error + switch action { + case "edit": + err = medal.EditMedalCategory(category, ctx.User) + case "new": + err = medal.AddMedalCategory(category, ctx.User) + case "del": + err = medal.DelMedalCategory(category.ID, ctx.User) + default: + err = errors.New("action type error") + } + + if err != nil { + log.Error("OperateMedalCategory error ,%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} diff --git a/routers/medal/medal.go b/routers/medal/medal.go new file mode 100644 index 000000000..d0625a2b4 --- /dev/null +++ b/routers/medal/medal.go @@ -0,0 +1,46 @@ +package medal + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/routers/response" + "code.gitea.io/gitea/services/medal" + "errors" + "net/http" +) + +func GetCustomizeMedalList(ctx *context.Context) { + r, err := medal.GetMedalList(models.GetMedalOpts{MedalType: models.CustomizeMedal}) + if err != nil { + log.Error("GetCategoryList error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + m := make(map[string]interface{}) + m["List"] = r + ctx.JSON(http.StatusOK, response.SuccessWithData(m)) +} + +func OperateMedal(ctx *context.Context, category models.Medal4Show) { + action := ctx.Params(":action") + + var err error + switch action { + case "edit": + err = medal.EditMedal(category, ctx.User) + case "new": + err = medal.AddMedal(category, ctx.User) + case "del": + err = medal.DelMedal(category.ID, ctx.User) + default: + err = errors.New("action type error") + } + + if err != nil { + log.Error("OperateCustomizeMedal error ,%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 66a357c79..5830edfdc 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -6,6 +6,7 @@ package routes import ( "bytes" + "code.gitea.io/gitea/routers/medal" "code.gitea.io/gitea/routers/reward/point" "code.gitea.io/gitea/routers/task" "code.gitea.io/gitea/services/reward" @@ -659,9 +660,20 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/task/config", func() { m.Get("/list", task.GetTaskConfigList) - m.Post("/add/batch", bindIgnErr(models.BatchLimitConfigVO{}), task.BatchAddTaskConfig) m.Post("/^:action(new|edit|del)$", bindIgnErr(models.TaskConfigWithLimit{}), task.OperateTaskConfig) }) + + m.Group("/medal", func() { + m.Group("/category", func() { + m.Get("/list", medal.GetCategoryList) + m.Post("/^:action(new|edit|del)$", bindIgnErr(models.MedalCategory4Show{}), medal.OperateMedalCategory) + }) + m.Group("/customize", func() { + m.Get("/list", medal.GetCustomizeMedalList) + }) + m.Post("/^:action(new|edit|del)$", bindIgnErr(models.Medal4Show{}), medal.OperateMedal) + }) + }, operationReq) // ***** END: Operation ***** diff --git a/services/admin/operate_log/operate_log.go b/services/admin/operate_log/operate_log.go index 7b72ec2e2..7f966db0c 100644 --- a/services/admin/operate_log/operate_log.go +++ b/services/admin/operate_log/operate_log.go @@ -4,6 +4,13 @@ import ( "code.gitea.io/gitea/models" ) +type LogBizType string + +const ( + MedalCategoryOperate LogBizType = "MedalCategoryOperate" + MedalOperate LogBizType = "MedalOperate" +) + func Log(log models.AdminOperateLog) error { _, err := models.InsertAdminOperateLog(log) return err @@ -12,3 +19,34 @@ func Log(log models.AdminOperateLog) error { func NewLogValues() *models.LogValues { return &models.LogValues{Params: make([]models.LogValue, 0)} } + +func Log4Add(bizType LogBizType, newValue interface{}, doerId int64, comment string) { + Log(models.AdminOperateLog{ + BizType: string(bizType), + OperateType: "add", + NewValue: NewLogValues().Add("new", newValue).JsonString(), + CreatedBy: doerId, + Comment: comment, + }) +} + +func Log4Edit(bizType LogBizType, oldValue interface{}, newValue interface{}, doerId int64, comment string) { + Log(models.AdminOperateLog{ + BizType: string(bizType), + OperateType: "edit", + NewValue: NewLogValues().Add("new", newValue).JsonString(), + OldValue: NewLogValues().Add("old", oldValue).JsonString(), + CreatedBy: doerId, + Comment: comment, + }) +} + +func Log4Del(bizType LogBizType, oldValue interface{}, doerId int64, comment string) { + Log(models.AdminOperateLog{ + BizType: string(bizType), + OperateType: "del", + OldValue: NewLogValues().Add("old", oldValue).JsonString(), + CreatedBy: doerId, + Comment: comment, + }) +} diff --git a/services/medal/category.go b/services/medal/category.go new file mode 100644 index 000000000..50f1b804e --- /dev/null +++ b/services/medal/category.go @@ -0,0 +1,64 @@ +package medal + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/admin/operate_log" + "errors" +) + +func GetMedalCategoryList() ([]*models.MedalCategory4Show, error) { + list, err := models.GetMedalCategoryList() + if err != nil { + return nil, err + } + if len(list) == 0 { + return nil, nil + } + r := make([]*models.MedalCategory4Show, len(list)) + for i := 0; i < len(list); i++ { + r[i] = list[i].ToShow() + } + + return r, nil +} + +func AddMedalCategory(m models.MedalCategory4Show, doer *models.User) error { + _, err := models.AddMedalCategory(m.ToDTO()) + if err != nil { + return err + } + operate_log.Log4Add(operate_log.MedalCategoryOperate, m, doer.ID, "新增了勋章分类") + return nil +} + +func EditMedalCategory(m models.MedalCategory4Show, doer *models.User) error { + if m.ID == 0 { + log.Error(" EditMedalCategory param error") + return errors.New("param error") + } + old, err := models.GetMedalCategoryById(m.ID) + if err != nil { + return err + } + _, err = models.UpdateMedalCategoryById(m.ID, m.ToDTO()) + if err != nil { + return err + } + operate_log.Log4Edit(operate_log.MedalCategoryOperate, old, m.ToDTO(), doer.ID, "修改了勋章分类") + return err +} + +func DelMedalCategory(id int64, doer *models.User) error { + if id == 0 { + log.Error(" DelMedalCategory param error") + return errors.New("param error") + } + old, err := models.GetMedalCategoryById(id) + if err != nil { + return err + } + _, err = models.DelMedalCategory(id) + operate_log.Log4Del(operate_log.MedalCategoryOperate, old, doer.ID, "删除了勋章分类") + return err +} diff --git a/services/medal/medal.go b/services/medal/medal.go new file mode 100644 index 000000000..967d7011f --- /dev/null +++ b/services/medal/medal.go @@ -0,0 +1,64 @@ +package medal + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/admin/operate_log" + "errors" +) + +func GetMedalList(opts models.GetMedalOpts) ([]*models.Medal4Show, error) { + list, err := models.GetMedalList(opts) + if err != nil { + return nil, err + } + if len(list) == 0 { + return nil, nil + } + r := make([]*models.Medal4Show, len(list)) + for i := 0; i < len(list); i++ { + r[i] = list[i].ToShow() + } + + return r, nil +} + +func AddMedal(m models.Medal4Show, doer *models.User) error { + _, err := models.AddMedal(m.ToDTO()) + if err != nil { + return err + } + operate_log.Log4Add(operate_log.MedalOperate, m, doer.ID, "新增了勋章") + return nil +} + +func EditMedal(m models.Medal4Show, doer *models.User) error { + if m.ID == 0 { + log.Error(" EditMedal param error") + return errors.New("param error") + } + old, err := models.GetMedalById(m.ID) + if err != nil { + return err + } + _, err = models.UpdateMedalById(m.ID, m.ToDTO()) + if err != nil { + return err + } + operate_log.Log4Edit(operate_log.MedalOperate, old, m.ToDTO(), doer.ID, "修改了勋章") + return err +} + +func DelMedal(id int64, doer *models.User) error { + if id == 0 { + log.Error(" DelMedal param error") + return errors.New("param error") + } + old, err := models.GetMedalById(id) + if err != nil { + return err + } + _, err = models.DelMedal(id) + operate_log.Log4Del(operate_log.MedalOperate, old, doer.ID, "删除了勋章") + return err +} From def6c11dbfaac24301ba1c1b8aa268cf55550d22 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Sun, 9 Oct 2022 18:07:57 +0800 Subject: [PATCH 02/16] #2908 1.medal-> badge 2.add badge user management 3.add badge in user profile --- models/badge.go | 137 +++++++++++++++++ .../{medal_category.go => badge_category.go} | 38 ++--- models/badge_user.go | 140 ++++++++++++++++++ models/medal.go | 103 ------------- models/models.go | 6 +- models/user.go | 21 +++ models/user_medal.go | 10 -- routers/badge/badge.go | 98 ++++++++++++ routers/{medal => badge}/category.go | 18 +-- routers/medal/medal.go | 46 ------ routers/routes/routes.go | 18 ++- routers/user/profile.go | 16 ++ services/admin/operate_log/operate_log.go | 4 +- services/badge/badge.go | 64 ++++++++ services/badge/category.go | 64 ++++++++ services/badge/user.go | 104 +++++++++++++ services/medal/category.go | 64 -------- services/medal/medal.go | 64 -------- 18 files changed, 690 insertions(+), 325 deletions(-) create mode 100644 models/badge.go rename models/{medal_category.go => badge_category.go} (59%) create mode 100644 models/badge_user.go delete mode 100644 models/medal.go delete mode 100644 models/user_medal.go create mode 100644 routers/badge/badge.go rename routers/{medal => badge}/category.go (62%) delete mode 100644 routers/medal/medal.go create mode 100644 services/badge/badge.go create mode 100644 services/badge/category.go create mode 100644 services/badge/user.go delete mode 100644 services/medal/category.go delete mode 100644 services/medal/medal.go diff --git a/models/badge.go b/models/badge.go new file mode 100644 index 000000000..20e3ea66a --- /dev/null +++ b/models/badge.go @@ -0,0 +1,137 @@ +package models + +import ( + "code.gitea.io/gitea/modules/timeutil" + "xorm.io/builder" +) + +type Badge struct { + ID int64 `xorm:"pk autoincr"` + Name string + LightedIcon string `xorm:"varchar(2048)"` + GreyedIcon string `xorm:"varchar(2048)"` + Url string `xorm:"varchar(2048)"` + CategoryId int64 + CreatedUnix timeutil.TimeStamp `xorm:"created"` + UpdatedUnix timeutil.TimeStamp `xorm:"updated"` + DeletedAt timeutil.TimeStamp `xorm:"deleted"` +} + +func (m *Badge) ToUserShow() *Badge4UserShow { + return &Badge4UserShow{ + Name: m.Name, + LightedIcon: m.LightedIcon, + GreyedIcon: m.GreyedIcon, + Url: m.Url, + } +} + +type GetBadgeOpts struct { + BadgeType BadgeType +} + +type BadgeAndCategory struct { + Badge Badge `xorm:"extends"` + Category BadgeCategory `xorm:"extends"` +} + +func (*BadgeAndCategory) TableName() string { + return "badge" +} + +func (m *BadgeAndCategory) ToShow() *Badge4AdminShow { + return &Badge4AdminShow{ + ID: m.Badge.ID, + Name: m.Badge.Name, + LightedIcon: m.Badge.LightedIcon, + GreyedIcon: m.Badge.GreyedIcon, + Url: m.Badge.Url, + CategoryName: m.Category.Name, + CategoryId: m.Category.ID, + CreatedUnix: m.Badge.CreatedUnix, + UpdatedUnix: m.Badge.UpdatedUnix, + } +} + +type Badge4AdminShow struct { + ID int64 + Name string + LightedIcon string + GreyedIcon string + Url string + CategoryName string + CategoryId int64 + CreatedUnix timeutil.TimeStamp + UpdatedUnix timeutil.TimeStamp +} + +func (m Badge4AdminShow) ToDTO() Badge { + return Badge{ + Name: m.Name, + LightedIcon: m.LightedIcon, + GreyedIcon: m.GreyedIcon, + Url: m.Url, + CategoryId: m.CategoryId, + } +} + +type Badge4UserShow struct { + Name string + LightedIcon string + GreyedIcon string + Url string +} + +type BadgeShowWithStatus struct { + Badge *Badge4UserShow + IsLighted bool +} + +type UserAllBadgeInCategory struct { + CategoryName string + CategoryId int64 + LightedNum int + Badges []*BadgeShowWithStatus +} + +func GetBadgeList(opts GetBadgeOpts) ([]*BadgeAndCategory, error) { + var cond = builder.NewCond() + if opts.BadgeType > 0 { + cond = cond.And(builder.Eq{"badge_category.type": opts.BadgeType}) + } + + r := make([]*BadgeAndCategory, 0) + if err := x.Join("INNER", "badge_category", "badge_category.ID = badge.category_id").Where(cond).OrderBy("badge.created_unix desc").Find(&r); err != nil { + return nil, err + } + return r, nil +} + +func AddBadge(m Badge) (int64, error) { + return x.Insert(&m) +} + +func UpdateBadgeById(id int64, param Badge) (int64, error) { + return x.ID(id).Update(¶m) +} + +func DelBadge(id int64) (int64, error) { + return x.ID(id).Delete(&Badge{}) +} + +func GetBadgeById(id int64) (*Badge, error) { + m := &Badge{} + has, err := x.ID(id).Get(m) + if err != nil { + return nil, err + } else if !has { + return nil, &ErrRecordNotExist{} + } + return m, nil +} + +func GetBadgeByCategoryId(categoryId int64) ([]*Badge, error) { + r := make([]*Badge, 0) + err := x.Where("category_id = ?", categoryId).Find(&r) + return r, err +} diff --git a/models/medal_category.go b/models/badge_category.go similarity index 59% rename from models/medal_category.go rename to models/badge_category.go index 3c8769650..e8f90f6c1 100644 --- a/models/medal_category.go +++ b/models/badge_category.go @@ -2,25 +2,25 @@ package models import "code.gitea.io/gitea/modules/timeutil" -type MedalType int +type BadgeType int const ( - CustomizeMedal = iota + 1 - SystemMedal + CustomizeBadge = iota + 1 + SystemBadge ) -type MedalCategory struct { +type BadgeCategory struct { ID int64 `xorm:"pk autoincr"` Name string Position int64 - Type MedalType + Type BadgeType CreatedUnix timeutil.TimeStamp `xorm:"created"` UpdatedUnix timeutil.TimeStamp `xorm:"updated"` DeletedAt timeutil.TimeStamp `xorm:"deleted"` } -func (m *MedalCategory) ToShow() *MedalCategory4Show { - return &MedalCategory4Show{ +func (m *BadgeCategory) ToShow() *BadgeCategory4Show { + return &BadgeCategory4Show{ ID: m.ID, Name: m.Name, Position: m.Position, @@ -29,16 +29,16 @@ func (m *MedalCategory) ToShow() *MedalCategory4Show { } } -type MedalCategory4Show struct { +type BadgeCategory4Show struct { ID int64 `xorm:"pk autoincr"` Name string Position int64 - Type MedalType + Type BadgeType CreatedUnix timeutil.TimeStamp `xorm:"created"` } -func (m MedalCategory4Show) ToDTO() MedalCategory { - return MedalCategory{ +func (m BadgeCategory4Show) ToDTO() BadgeCategory { + return BadgeCategory{ ID: m.ID, Name: m.Name, Position: m.Position, @@ -47,28 +47,28 @@ func (m MedalCategory4Show) ToDTO() MedalCategory { } } -func GetMedalCategoryList() ([]*MedalCategory, error) { - r := make([]*MedalCategory, 0) +func GetBadgeCategoryList() ([]*BadgeCategory, error) { + r := make([]*BadgeCategory, 0) if err := x.OrderBy("position asc,created_unix desc").Find(&r); err != nil { return nil, err } return r, nil } -func AddMedalCategory(m MedalCategory) (int64, error) { +func AddBadgeCategory(m BadgeCategory) (int64, error) { return x.Insert(&m) } -func UpdateMedalCategoryById(id int64, param MedalCategory) (int64, error) { +func UpdateBadgeCategoryById(id int64, param BadgeCategory) (int64, error) { return x.ID(id).Update(¶m) } -func DelMedalCategory(id int64) (int64, error) { - return x.ID(id).Delete(&MedalCategory{}) +func DelBadgeCategory(id int64) (int64, error) { + return x.ID(id).Delete(&BadgeCategory{}) } -func GetMedalCategoryById(id int64) (*MedalCategory, error) { - m := &MedalCategory{} +func GetBadgeCategoryById(id int64) (*BadgeCategory, error) { + m := &BadgeCategory{} has, err := x.ID(id).Get(m) if err != nil { return nil, err diff --git a/models/badge_user.go b/models/badge_user.go new file mode 100644 index 000000000..b2428ab1a --- /dev/null +++ b/models/badge_user.go @@ -0,0 +1,140 @@ +package models + +import ( + "code.gitea.io/gitea/modules/timeutil" + "xorm.io/builder" +) + +const ( + ActionAddBadgeUser = 1 + ActionDelBadgeUser = 2 +) + +type BadgeUser struct { + ID int64 `xorm:"pk autoincr"` + UserId int64 `xorm:"unique(user_id,badge_id)"` + BadgeId int64 `xorm:"index"` + CreatedUnix timeutil.TimeStamp `xorm:"created index"` +} + +type BadgeUserLog struct { + ID int64 `xorm:"pk autoincr"` + UserId int64 `xorm:"index"` + BadgeId int64 `xorm:"index"` + Action int + CreatedUnix timeutil.TimeStamp `xorm:"created index"` +} + +type BadgeUserDetail struct { + BadgeUser BadgeUser `xorm:"extends"` + User User `xorm:"extends"` +} + +func (*BadgeUserDetail) TableName() string { + return "badge_user" +} + +func (m *BadgeUserDetail) ToShow() *BadgeUser4SHow { + return &BadgeUser4SHow{ + ID: m.BadgeUser.ID, + UserId: m.BadgeUser.UserId, + Name: m.User.Name, + Avatar: m.User.RelAvatarLink(), + Email: m.User.Email, + CreatedUnix: m.BadgeUser.CreatedUnix, + } +} + +type BadgeUser4SHow struct { + ID int64 + UserId int64 + Name string + Avatar string + Email string + CreatedUnix timeutil.TimeStamp +} + +type AddBadgeUsersReq struct { + BadgeId int64 + Users string +} +type DelBadgeUserReq struct { + ID int64 +} + +type GetUserBadgesOpts struct { + CategoryId int64 + ListOptions +} + +func AddBadgeUser(m BadgeUser) (int64, error) { + sess := x.NewSession() + defer sess.Close() + sess.Begin() + n, err := sess.Insert(&m) + if err != nil || n == 0 { + return 0, err + } + _, err = sess.Insert(&BadgeUserLog{ + UserId: m.UserId, + BadgeId: m.BadgeId, + Action: ActionAddBadgeUser, + }) + if err != nil { + sess.Rollback() + return 0, err + } + return n, sess.Commit() +} + +func DelBadgeUser(id int64) (int64, error) { + m := BadgeUser{} + has, err := x.ID(id).Get(&m) + if err != nil { + return 0, err + } + if !has { + return 0, ErrRecordNotExist{} + } + sess := x.NewSession() + defer sess.Close() + sess.Begin() + n, err := x.ID(m.ID).Delete(&BadgeUser{}) + if err != nil || n == 0 { + return 0, err + } + _, err = sess.Insert(&BadgeUserLog{ + UserId: m.UserId, + BadgeId: m.BadgeId, + Action: ActionDelBadgeUser, + }) + if err != nil { + sess.Rollback() + return 0, err + } + return n, sess.Commit() +} + +func GetBadgeUsers(badgeId int64, opts ListOptions) ([]BadgeUserDetail, error) { + if opts.Page <= 0 { + opts.Page = 1 + } + m := make([]BadgeUserDetail, 0) + err := x.Join("LEFT", "public.user", "public.user.ID = badge_user.user_id").Where("badge_user.badge_id = ?", badgeId).OrderBy("badge_user.created_unix desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&m) + if err != nil { + return nil, err + } + return m, nil +} + +func GetUserBadges(userId int64, opts GetUserBadgesOpts) ([]*Badge, error) { + cond := builder.NewCond() + cond = cond.And(builder.Eq{"badge_user.user_id": userId}) + if opts.CategoryId > 0 { + cond = cond.And(builder.Eq{"badge.category_id": opts.CategoryId}) + } + + r := make([]*Badge, 0) + err := x.Join("INNER", "badge_user", "badge_user.badge_id = badge.id").Where(cond).OrderBy("badge_user.created_unix desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&r) + return r, err +} diff --git a/models/medal.go b/models/medal.go deleted file mode 100644 index d2508b3b1..000000000 --- a/models/medal.go +++ /dev/null @@ -1,103 +0,0 @@ -package models - -import ( - "code.gitea.io/gitea/modules/timeutil" - "xorm.io/builder" -) - -type Medal struct { - ID int64 `xorm:"pk autoincr"` - Name string - LightedIcon string `xorm:"varchar(2048)"` - GreyedIcon string `xorm:"varchar(2048)"` - Url string `xorm:"varchar(2048)"` - CategoryId int64 - CreatedUnix timeutil.TimeStamp `xorm:"created"` - UpdatedUnix timeutil.TimeStamp `xorm:"updated"` - DeletedAt timeutil.TimeStamp `xorm:"deleted"` -} - -type GetMedalOpts struct { - MedalType MedalType -} - -type MedalAndCategory struct { - Medal Medal `xorm:"extends"` - Category MedalCategory `xorm:"extends"` -} - -func (*MedalAndCategory) TableName() string { - return "medal" -} - -func (m *MedalAndCategory) ToShow() *Medal4Show { - return &Medal4Show{ - ID: m.Medal.ID, - Name: m.Medal.Name, - LightedIcon: m.Medal.LightedIcon, - GreyedIcon: m.Medal.GreyedIcon, - Url: m.Medal.Url, - CategoryName: m.Category.Name, - CategoryId: m.Category.ID, - CreatedUnix: m.Medal.CreatedUnix, - UpdatedUnix: m.Medal.UpdatedUnix, - } -} - -type Medal4Show struct { - ID int64 - Name string - LightedIcon string - GreyedIcon string - Url string - CategoryName string - CategoryId int64 - CreatedUnix timeutil.TimeStamp - UpdatedUnix timeutil.TimeStamp -} - -func (m Medal4Show) ToDTO() Medal { - return Medal{ - Name: m.Name, - LightedIcon: m.LightedIcon, - GreyedIcon: m.GreyedIcon, - Url: m.Url, - CategoryId: m.CategoryId, - } -} - -func GetMedalList(opts GetMedalOpts) ([]*MedalAndCategory, error) { - var cond = builder.NewCond() - if opts.MedalType > 0 { - cond = cond.And(builder.Eq{"medal_category.type": opts.MedalType}) - } - - r := make([]*MedalAndCategory, 0) - if err := x.Join("INNER", "medal_category", "medal_category.ID = medal.category_id").Where(cond).OrderBy("medal.created_unix desc").Find(&r); err != nil { - return nil, err - } - return r, nil -} - -func AddMedal(m Medal) (int64, error) { - return x.Insert(&m) -} - -func UpdateMedalById(id int64, param Medal) (int64, error) { - return x.ID(id).Update(¶m) -} - -func DelMedal(id int64) (int64, error) { - return x.ID(id).Delete(&Medal{}) -} - -func GetMedalById(id int64) (*Medal, error) { - m := &Medal{} - has, err := x.ID(id).Get(m) - if err != nil { - return nil, err - } else if !has { - return nil, &ErrRecordNotExist{} - } - return m, nil -} diff --git a/models/models.go b/models/models.go index 910bfde65..ff64bfad2 100755 --- a/models/models.go +++ b/models/models.go @@ -161,8 +161,10 @@ func init() { new(CloudbrainSpec), new(CloudbrainTemp), new(DatasetReference), - new(MedalCategory), - new(Medal), + new(BadgeCategory), + new(Badge), + new(BadgeUser), + new(BadgeUserLog), ) tablesStatistic = append(tablesStatistic, diff --git a/models/user.go b/models/user.go index f40eb699f..b21858e37 100755 --- a/models/user.go +++ b/models/user.go @@ -2184,3 +2184,24 @@ func GetBlockChainUnSuccessUsers() ([]*User, error) { Find(&users) return users, err } + +//GetUserIdsByUserNames Get userIDs in batches through username paging, this method will ignore errors +func GetUserIdsByUserNames(names []string) []int64 { + pageSize := 200 + length := len(names) + r := make([]int64, 0, length) + for i := 0; i < length; i = i + pageSize { + if length-i < 200 { + pageSize = length - i + } + userNameTemp := names[i : i+pageSize] + t := make([]int64, 0, length) + err := x.Table("public.user").Cols("id").In("name", userNameTemp).Find(&t) + if err != nil { + continue + } + r = append(r, t...) + + } + return r +} diff --git a/models/user_medal.go b/models/user_medal.go deleted file mode 100644 index a4421df13..000000000 --- a/models/user_medal.go +++ /dev/null @@ -1,10 +0,0 @@ -package models - -import "code.gitea.io/gitea/modules/timeutil" - -type UserMedal struct { - ID int64 `xorm:"pk autoincr"` - UserId int64 `xorm:"index"` - MedalId int64 `xorm:"index"` - CreatedUnix timeutil.TimeStamp `xorm:"created"` -} diff --git a/routers/badge/badge.go b/routers/badge/badge.go new file mode 100644 index 000000000..c62c1ee74 --- /dev/null +++ b/routers/badge/badge.go @@ -0,0 +1,98 @@ +package badge + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/routers/response" + "code.gitea.io/gitea/services/badge" + "errors" + "net/http" + "strings" +) + +func GetCustomizeBadgeList(ctx *context.Context) { + r, err := badge.GetBadgeList(models.GetBadgeOpts{BadgeType: models.CustomizeBadge}) + if err != nil { + log.Error("GetCustomizeBadgeList error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + m := make(map[string]interface{}) + m["List"] = r + ctx.JSON(http.StatusOK, response.SuccessWithData(m)) +} + +func OperateBadge(ctx *context.Context, category models.Badge4AdminShow) { + action := ctx.Params(":action") + + var err error + switch action { + case "edit": + err = badge.EditBadge(category, ctx.User) + case "new": + err = badge.AddBadge(category, ctx.User) + case "del": + err = badge.DelBadge(category.ID, ctx.User) + default: + err = errors.New("action type error") + } + + if err != nil { + log.Error("OperateBadge error ,%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func GetBadgeUsers(ctx *context.Context) { + page := ctx.QueryInt("page") + badgeId := ctx.QueryInt64("badge") + r, err := badge.GetBadgeUsers(badgeId, models.ListOptions{PageSize: 20, Page: page}) + if err != nil { + log.Error("GetBadgeUsers error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + m := make(map[string]interface{}) + m["List"] = r + ctx.JSON(http.StatusOK, response.SuccessWithData(m)) +} + +func AddOperateBadgeUsers(ctx *context.Context, req models.AddBadgeUsersReq) { + userStr := req.Users + if userStr == "" { + ctx.JSON(http.StatusOK, response.Success()) + return + } + userStr = strings.ReplaceAll(userStr, " ", "") + userStr = strings.ReplaceAll(userStr, "\r", "") + userNames := strings.Split(userStr, "\n") + n, err := badge.AddBadgeUsers(req.BadgeId, userNames) + if err != nil { + log.Error("AddOperateBadgeUsers error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + m := make(map[string]interface{}) + m["Total"] = len(userNames) + m["Success"] = n + ctx.JSON(http.StatusOK, response.SuccessWithData(m)) +} + +func DelBadgeUsers(ctx *context.Context, req models.DelBadgeUserReq) { + id := req.ID + if id <= 0 { + ctx.JSON(http.StatusOK, response.Success()) + return + } + + err := badge.DelBadgeUser(id) + if err != nil { + log.Error("DelBadgeUsers error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} diff --git a/routers/medal/category.go b/routers/badge/category.go similarity index 62% rename from routers/medal/category.go rename to routers/badge/category.go index b010af586..a386f8fc5 100644 --- a/routers/medal/category.go +++ b/routers/badge/category.go @@ -1,17 +1,17 @@ -package medal +package badge import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/routers/response" - "code.gitea.io/gitea/services/medal" + "code.gitea.io/gitea/services/badge" "errors" "net/http" ) -func GetCategoryList(ctx *context.Context) { - r, err := medal.GetMedalCategoryList() +func GetBadgeCategoryList(ctx *context.Context) { + r, err := badge.GetBadgeCategoryList() if err != nil { log.Error("GetCategoryList error.%v", err) ctx.JSON(http.StatusOK, response.ServerError(err.Error())) @@ -22,23 +22,23 @@ func GetCategoryList(ctx *context.Context) { ctx.JSON(http.StatusOK, response.SuccessWithData(m)) } -func OperateMedalCategory(ctx *context.Context, category models.MedalCategory4Show) { +func OperateBadgeCategory(ctx *context.Context, category models.BadgeCategory4Show) { action := ctx.Params(":action") var err error switch action { case "edit": - err = medal.EditMedalCategory(category, ctx.User) + err = badge.EditBadgeCategory(category, ctx.User) case "new": - err = medal.AddMedalCategory(category, ctx.User) + err = badge.AddBadgeCategory(category, ctx.User) case "del": - err = medal.DelMedalCategory(category.ID, ctx.User) + err = badge.DelBadgeCategory(category.ID, ctx.User) default: err = errors.New("action type error") } if err != nil { - log.Error("OperateMedalCategory error ,%v", err) + log.Error("OperateBadgeCategory error ,%v", err) ctx.JSON(http.StatusOK, response.ServerError(err.Error())) return } diff --git a/routers/medal/medal.go b/routers/medal/medal.go deleted file mode 100644 index d0625a2b4..000000000 --- a/routers/medal/medal.go +++ /dev/null @@ -1,46 +0,0 @@ -package medal - -import ( - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/routers/response" - "code.gitea.io/gitea/services/medal" - "errors" - "net/http" -) - -func GetCustomizeMedalList(ctx *context.Context) { - r, err := medal.GetMedalList(models.GetMedalOpts{MedalType: models.CustomizeMedal}) - if err != nil { - log.Error("GetCategoryList error.%v", err) - ctx.JSON(http.StatusOK, response.ServerError(err.Error())) - return - } - m := make(map[string]interface{}) - m["List"] = r - ctx.JSON(http.StatusOK, response.SuccessWithData(m)) -} - -func OperateMedal(ctx *context.Context, category models.Medal4Show) { - action := ctx.Params(":action") - - var err error - switch action { - case "edit": - err = medal.EditMedal(category, ctx.User) - case "new": - err = medal.AddMedal(category, ctx.User) - case "del": - err = medal.DelMedal(category.ID, ctx.User) - default: - err = errors.New("action type error") - } - - if err != nil { - log.Error("OperateCustomizeMedal error ,%v", err) - ctx.JSON(http.StatusOK, response.ServerError(err.Error())) - return - } - ctx.JSON(http.StatusOK, response.Success()) -} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 5830edfdc..14f2d0f74 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -6,7 +6,7 @@ package routes import ( "bytes" - "code.gitea.io/gitea/routers/medal" + "code.gitea.io/gitea/routers/badge" "code.gitea.io/gitea/routers/reward/point" "code.gitea.io/gitea/routers/task" "code.gitea.io/gitea/services/reward" @@ -663,15 +663,21 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/^:action(new|edit|del)$", bindIgnErr(models.TaskConfigWithLimit{}), task.OperateTaskConfig) }) - m.Group("/medal", func() { + m.Group("/badge", func() { m.Group("/category", func() { - m.Get("/list", medal.GetCategoryList) - m.Post("/^:action(new|edit|del)$", bindIgnErr(models.MedalCategory4Show{}), medal.OperateMedalCategory) + m.Get("/list", badge.GetBadgeCategoryList) + m.Post("/^:action(new|edit|del)$", bindIgnErr(models.BadgeCategory4Show{}), badge.OperateBadgeCategory) }) m.Group("/customize", func() { - m.Get("/list", medal.GetCustomizeMedalList) + m.Get("/list", badge.GetCustomizeBadgeList) + + }) + m.Group("/users", func() { + m.Get("", badge.GetBadgeUsers) + m.Post("/add", bindIgnErr(models.AddBadgeUsersReq{}), badge.AddOperateBadgeUsers) + m.Post("/del", bindIgnErr(models.DelBadgeUserReq{}), badge.DelBadgeUsers) }) - m.Post("/^:action(new|edit|del)$", bindIgnErr(models.Medal4Show{}), medal.OperateMedal) + m.Post("/^:action(new|edit|del)$", bindIgnErr(models.Badge4AdminShow{}), badge.OperateBadge) }) }, operationReq) diff --git a/routers/user/profile.go b/routers/user/profile.go index 42cdfd1a8..1d275c191 100755 --- a/routers/user/profile.go +++ b/routers/user/profile.go @@ -6,6 +6,7 @@ package user import ( + "code.gitea.io/gitea/services/badge" "errors" "fmt" "path" @@ -90,10 +91,18 @@ func Profile(ctx *context.Context) { return } + // Show user badges + badges, err := badge.GetUserBadges(ctxUser.ID, models.ListOptions{Page: 1, PageSize: 5}) + if err != nil { + ctx.ServerError("GetUserBadges", err) + return + } + ctx.Data["Title"] = ctxUser.DisplayName() ctx.Data["PageIsUserProfile"] = true ctx.Data["Owner"] = ctxUser ctx.Data["OpenIDs"] = openIDs + ctx.Data["RecentBadges"] = badges ctx.Data["EnableHeatmap"] = setting.Service.EnableUserHeatmap ctx.Data["HeatmapUser"] = ctxUser.Name showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == ctxUser.ID) @@ -297,6 +306,13 @@ func Profile(ctx *context.Context) { } total = int(count) + case "badge": + allBadges, err := badge.GetUserAllBadges(ctxUser.ID) + if err != nil { + ctx.ServerError("GetUserAllBadges", err) + return + } + ctx.Data["AllBadges"] = allBadges default: ctx.ServerError("tab error", errors.New("tab error")) return diff --git a/services/admin/operate_log/operate_log.go b/services/admin/operate_log/operate_log.go index 7f966db0c..f52950351 100644 --- a/services/admin/operate_log/operate_log.go +++ b/services/admin/operate_log/operate_log.go @@ -7,8 +7,8 @@ import ( type LogBizType string const ( - MedalCategoryOperate LogBizType = "MedalCategoryOperate" - MedalOperate LogBizType = "MedalOperate" + BadgeCategoryOperate LogBizType = "BadgeCategoryOperate" + BadgeOperate LogBizType = "BadgeOperate" ) func Log(log models.AdminOperateLog) error { diff --git a/services/badge/badge.go b/services/badge/badge.go new file mode 100644 index 000000000..83c3581b5 --- /dev/null +++ b/services/badge/badge.go @@ -0,0 +1,64 @@ +package badge + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/admin/operate_log" + "errors" +) + +func GetBadgeList(opts models.GetBadgeOpts) ([]*models.Badge4AdminShow, error) { + list, err := models.GetBadgeList(opts) + if err != nil { + return nil, err + } + if len(list) == 0 { + return nil, nil + } + r := make([]*models.Badge4AdminShow, len(list)) + for i := 0; i < len(list); i++ { + r[i] = list[i].ToShow() + } + + return r, nil +} + +func AddBadge(m models.Badge4AdminShow, doer *models.User) error { + _, err := models.AddBadge(m.ToDTO()) + if err != nil { + return err + } + operate_log.Log4Add(operate_log.BadgeOperate, m, doer.ID, "新增了勋章") + return nil +} + +func EditBadge(m models.Badge4AdminShow, doer *models.User) error { + if m.ID == 0 { + log.Error(" EditBadge param error") + return errors.New("param error") + } + old, err := models.GetBadgeById(m.ID) + if err != nil { + return err + } + _, err = models.UpdateBadgeById(m.ID, m.ToDTO()) + if err != nil { + return err + } + operate_log.Log4Edit(operate_log.BadgeOperate, old, m.ToDTO(), doer.ID, "修改了勋章") + return err +} + +func DelBadge(id int64, doer *models.User) error { + if id == 0 { + log.Error(" DelBadge param error") + return errors.New("param error") + } + old, err := models.GetBadgeById(id) + if err != nil { + return err + } + _, err = models.DelBadge(id) + operate_log.Log4Del(operate_log.BadgeOperate, old, doer.ID, "删除了勋章") + return err +} diff --git a/services/badge/category.go b/services/badge/category.go new file mode 100644 index 000000000..316e654a7 --- /dev/null +++ b/services/badge/category.go @@ -0,0 +1,64 @@ +package badge + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/admin/operate_log" + "errors" +) + +func GetBadgeCategoryList() ([]*models.BadgeCategory4Show, error) { + list, err := models.GetBadgeCategoryList() + if err != nil { + return nil, err + } + if len(list) == 0 { + return nil, nil + } + r := make([]*models.BadgeCategory4Show, len(list)) + for i := 0; i < len(list); i++ { + r[i] = list[i].ToShow() + } + + return r, nil +} + +func AddBadgeCategory(m models.BadgeCategory4Show, doer *models.User) error { + _, err := models.AddBadgeCategory(m.ToDTO()) + if err != nil { + return err + } + operate_log.Log4Add(operate_log.BadgeCategoryOperate, m, doer.ID, "新增了勋章分类") + return nil +} + +func EditBadgeCategory(m models.BadgeCategory4Show, doer *models.User) error { + if m.ID == 0 { + log.Error(" EditBadgeCategory param error") + return errors.New("param error") + } + old, err := models.GetBadgeCategoryById(m.ID) + if err != nil { + return err + } + _, err = models.UpdateBadgeCategoryById(m.ID, m.ToDTO()) + if err != nil { + return err + } + operate_log.Log4Edit(operate_log.BadgeCategoryOperate, old, m.ToDTO(), doer.ID, "修改了勋章分类") + return err +} + +func DelBadgeCategory(id int64, doer *models.User) error { + if id == 0 { + log.Error(" DelBadgeCategory param error") + return errors.New("param error") + } + old, err := models.GetBadgeCategoryById(id) + if err != nil { + return err + } + _, err = models.DelBadgeCategory(id) + operate_log.Log4Del(operate_log.BadgeCategoryOperate, old, doer.ID, "删除了勋章分类") + return err +} diff --git a/services/badge/user.go b/services/badge/user.go new file mode 100644 index 000000000..ccc112f01 --- /dev/null +++ b/services/badge/user.go @@ -0,0 +1,104 @@ +package badge + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" +) + +func GetBadgeUsers(badgeId int64, opts models.ListOptions) ([]*models.BadgeUser4SHow, error) { + list, err := models.GetBadgeUsers(badgeId, opts) + if err != nil { + return nil, err + } + if len(list) == 0 { + return nil, nil + } + r := make([]*models.BadgeUser4SHow, len(list)) + for i := 0; i < len(list); i++ { + r[i] = list[i].ToShow() + } + + return r, nil +} + +func AddBadgeUsers(badgeId int64, userNames []string) (int, error) { + userIds := models.GetUserIdsByUserNames(userNames) + if len(userIds) == 0 { + return 0, nil + } + successCount := 0 + for _, v := range userIds { + m := models.BadgeUser{ + UserId: v, + BadgeId: badgeId, + } + _, err := models.AddBadgeUser(m) + if err != nil { + log.Error("AddBadgeUser err in loop, m=%+v. e=%v", m, err) + continue + } + successCount++ + } + return successCount, nil +} + +func DelBadgeUser(id int64) error { + _, err := models.DelBadgeUser(id) + return err +} + +//GetUserBadges Only Returns badges the user has earned +func GetUserBadges(userId int64, opts models.ListOptions) ([]*models.Badge4UserShow, error) { + badges, err := models.GetUserBadges(userId, models.GetUserBadgesOpts{ListOptions: opts}) + if err != nil { + return nil, err + } + r := make([]*models.Badge4UserShow, len(badges)) + for i, v := range badges { + r[i] = v.ToUserShow() + } + return r, nil +} + +func GetUserAllBadges(userId int64) ([]models.UserAllBadgeInCategory, error) { + categoryList, err := models.GetBadgeCategoryList() + if err != nil { + return nil, err + } + r := make([]models.UserAllBadgeInCategory, len(categoryList)) + for i, v := range categoryList { + badges, err := models.GetBadgeByCategoryId(v.ID) + userBadgeMap, err := getUserBadgesMap(userId, v.ID, 100, 1) + if err != nil { + return nil, err + } + t := models.UserAllBadgeInCategory{ + CategoryName: v.Name, + CategoryId: v.ID, + LightedNum: len(userBadgeMap), + } + bArray := make([]*models.BadgeShowWithStatus, len(badges)) + for j, v := range badges { + b := &models.BadgeShowWithStatus{Badge: v.ToUserShow()} + if _, has := userBadgeMap[v.ID]; has { + b.IsLighted = true + } + bArray[j] = b + } + t.Badges = bArray + r[i] = t + } + return r, nil +} + +func getUserBadgesMap(userId, categoryId int64, pageSize, page int) (map[int64]*models.Badge, error) { + userBadges, err := models.GetUserBadges(userId, models.GetUserBadgesOpts{ListOptions: models.ListOptions{PageSize: pageSize, Page: page}, CategoryId: categoryId}) + if err != nil { + return nil, err + } + m := make(map[int64]*models.Badge, 0) + for _, v := range userBadges { + m[v.ID] = v + } + return m, nil +} diff --git a/services/medal/category.go b/services/medal/category.go deleted file mode 100644 index 50f1b804e..000000000 --- a/services/medal/category.go +++ /dev/null @@ -1,64 +0,0 @@ -package medal - -import ( - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/services/admin/operate_log" - "errors" -) - -func GetMedalCategoryList() ([]*models.MedalCategory4Show, error) { - list, err := models.GetMedalCategoryList() - if err != nil { - return nil, err - } - if len(list) == 0 { - return nil, nil - } - r := make([]*models.MedalCategory4Show, len(list)) - for i := 0; i < len(list); i++ { - r[i] = list[i].ToShow() - } - - return r, nil -} - -func AddMedalCategory(m models.MedalCategory4Show, doer *models.User) error { - _, err := models.AddMedalCategory(m.ToDTO()) - if err != nil { - return err - } - operate_log.Log4Add(operate_log.MedalCategoryOperate, m, doer.ID, "新增了勋章分类") - return nil -} - -func EditMedalCategory(m models.MedalCategory4Show, doer *models.User) error { - if m.ID == 0 { - log.Error(" EditMedalCategory param error") - return errors.New("param error") - } - old, err := models.GetMedalCategoryById(m.ID) - if err != nil { - return err - } - _, err = models.UpdateMedalCategoryById(m.ID, m.ToDTO()) - if err != nil { - return err - } - operate_log.Log4Edit(operate_log.MedalCategoryOperate, old, m.ToDTO(), doer.ID, "修改了勋章分类") - return err -} - -func DelMedalCategory(id int64, doer *models.User) error { - if id == 0 { - log.Error(" DelMedalCategory param error") - return errors.New("param error") - } - old, err := models.GetMedalCategoryById(id) - if err != nil { - return err - } - _, err = models.DelMedalCategory(id) - operate_log.Log4Del(operate_log.MedalCategoryOperate, old, doer.ID, "删除了勋章分类") - return err -} diff --git a/services/medal/medal.go b/services/medal/medal.go deleted file mode 100644 index 967d7011f..000000000 --- a/services/medal/medal.go +++ /dev/null @@ -1,64 +0,0 @@ -package medal - -import ( - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/services/admin/operate_log" - "errors" -) - -func GetMedalList(opts models.GetMedalOpts) ([]*models.Medal4Show, error) { - list, err := models.GetMedalList(opts) - if err != nil { - return nil, err - } - if len(list) == 0 { - return nil, nil - } - r := make([]*models.Medal4Show, len(list)) - for i := 0; i < len(list); i++ { - r[i] = list[i].ToShow() - } - - return r, nil -} - -func AddMedal(m models.Medal4Show, doer *models.User) error { - _, err := models.AddMedal(m.ToDTO()) - if err != nil { - return err - } - operate_log.Log4Add(operate_log.MedalOperate, m, doer.ID, "新增了勋章") - return nil -} - -func EditMedal(m models.Medal4Show, doer *models.User) error { - if m.ID == 0 { - log.Error(" EditMedal param error") - return errors.New("param error") - } - old, err := models.GetMedalById(m.ID) - if err != nil { - return err - } - _, err = models.UpdateMedalById(m.ID, m.ToDTO()) - if err != nil { - return err - } - operate_log.Log4Edit(operate_log.MedalOperate, old, m.ToDTO(), doer.ID, "修改了勋章") - return err -} - -func DelMedal(id int64, doer *models.User) error { - if id == 0 { - log.Error(" DelMedal param error") - return errors.New("param error") - } - old, err := models.GetMedalById(id) - if err != nil { - return err - } - _, err = models.DelMedal(id) - operate_log.Log4Del(operate_log.MedalOperate, old, doer.ID, "删除了勋章") - return err -} From 9300604644d4b8f90ac9433959d1d3c3c961e985 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Mon, 10 Oct 2022 17:00:21 +0800 Subject: [PATCH 03/16] #2908 1.add icon upload api 2.fix bug --- models/badge.go | 58 +++++++++++++++++--- models/badge_category.go | 17 +++++- models/badge_user.go | 32 ++++++++--- modules/setting/setting.go | 15 +++++ routers/badge/badge.go | 48 ++++++++++++++-- routers/badge/category.go | 6 +- routers/routes/routes.go | 16 +++++- routers/user/profile.go | 6 ++ services/badge/badge.go | 24 +++++--- services/badge/category.go | 10 ++-- services/badge/icon.go | 109 +++++++++++++++++++++++++++++++++++++ services/badge/user.go | 21 ++++--- 12 files changed, 312 insertions(+), 50 deletions(-) create mode 100644 services/badge/icon.go diff --git a/models/badge.go b/models/badge.go index 20e3ea66a..7e20ab2d4 100644 --- a/models/badge.go +++ b/models/badge.go @@ -1,7 +1,10 @@ package models import ( + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "path/filepath" + "strings" "xorm.io/builder" ) @@ -20,14 +23,15 @@ type Badge struct { func (m *Badge) ToUserShow() *Badge4UserShow { return &Badge4UserShow{ Name: m.Name, - LightedIcon: m.LightedIcon, - GreyedIcon: m.GreyedIcon, + LightedIcon: GetIconOuterLink(m.LightedIcon), + GreyedIcon: GetIconOuterLink(m.GreyedIcon), Url: m.Url, } } type GetBadgeOpts struct { BadgeType BadgeType + LO ListOptions } type BadgeAndCategory struct { @@ -43,8 +47,8 @@ func (m *BadgeAndCategory) ToShow() *Badge4AdminShow { return &Badge4AdminShow{ ID: m.Badge.ID, Name: m.Badge.Name, - LightedIcon: m.Badge.LightedIcon, - GreyedIcon: m.Badge.GreyedIcon, + LightedIcon: GetIconOuterLink(m.Badge.LightedIcon), + GreyedIcon: GetIconOuterLink(m.Badge.GreyedIcon), Url: m.Badge.Url, CategoryName: m.Category.Name, CategoryId: m.Category.ID, @@ -75,6 +79,25 @@ func (m Badge4AdminShow) ToDTO() Badge { } } +type BadgeOperateReq struct { + ID int64 + Name string + LightedIcon string + GreyedIcon string + Url string + CategoryId int64 +} + +func (m BadgeOperateReq) ToDTO() Badge { + return Badge{ + Name: m.Name, + LightedIcon: m.LightedIcon, + GreyedIcon: m.GreyedIcon, + Url: m.Url, + CategoryId: m.CategoryId, + } +} + type Badge4UserShow struct { Name string LightedIcon string @@ -94,17 +117,23 @@ type UserAllBadgeInCategory struct { Badges []*BadgeShowWithStatus } -func GetBadgeList(opts GetBadgeOpts) ([]*BadgeAndCategory, error) { +func GetBadgeList(opts GetBadgeOpts) (int64, []*BadgeAndCategory, error) { + if opts.LO.Page <= 0 { + opts.LO.Page = 1 + } var cond = builder.NewCond() if opts.BadgeType > 0 { cond = cond.And(builder.Eq{"badge_category.type": opts.BadgeType}) } - + n, err := x.Join("INNER", "badge_category", "badge_category.ID = badge.category_id").Where(cond).Count(&BadgeAndCategory{}) + if err != nil { + return 0, nil, err + } r := make([]*BadgeAndCategory, 0) - if err := x.Join("INNER", "badge_category", "badge_category.ID = badge.category_id").Where(cond).OrderBy("badge.created_unix desc").Find(&r); err != nil { - return nil, err + if err = x.Join("INNER", "badge_category", "badge_category.ID = badge.category_id").Where(cond).OrderBy("badge.created_unix desc").Limit(opts.LO.PageSize, (opts.LO.Page-1)*opts.LO.PageSize).Find(&r); err != nil { + return 0, nil, err } - return r, nil + return n, r, nil } func AddBadge(m Badge) (int64, error) { @@ -135,3 +164,14 @@ func GetBadgeByCategoryId(categoryId int64) ([]*Badge, error) { err := x.Where("category_id = ?", categoryId).Find(&r) return r, err } + +func GetCustomIconByHash(hash string) string { + if len(hash) == 0 { + return "" + } + return filepath.Join(setting.IconUploadPath, hash) +} + +func GetIconOuterLink(hash string) string { + return strings.TrimRight(setting.AppSubURL, "/") + "/show/icon/" + hash +} diff --git a/models/badge_category.go b/models/badge_category.go index e8f90f6c1..069fb6b10 100644 --- a/models/badge_category.go +++ b/models/badge_category.go @@ -47,6 +47,21 @@ func (m BadgeCategory4Show) ToDTO() BadgeCategory { } } +func GetBadgeCategoryListPaging(opts ListOptions) (int64, []*BadgeCategory, error) { + n, err := x.Count(&BadgeCategory{}) + if err != nil { + return 0, nil, err + } + if opts.Page <= 0 { + opts.Page = 1 + } + r := make([]*BadgeCategory, 0) + if err := x.OrderBy("position asc,created_unix desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&r); err != nil { + return 0, nil, err + } + return n, r, nil +} + func GetBadgeCategoryList() ([]*BadgeCategory, error) { r := make([]*BadgeCategory, 0) if err := x.OrderBy("position asc,created_unix desc").Find(&r); err != nil { @@ -73,7 +88,7 @@ func GetBadgeCategoryById(id int64) (*BadgeCategory, error) { if err != nil { return nil, err } else if !has { - return nil, &ErrRecordNotExist{} + return nil, ErrRecordNotExist{} } return m, nil } diff --git a/models/badge_user.go b/models/badge_user.go index b2428ab1a..32861248e 100644 --- a/models/badge_user.go +++ b/models/badge_user.go @@ -12,8 +12,8 @@ const ( type BadgeUser struct { ID int64 `xorm:"pk autoincr"` - UserId int64 `xorm:"unique(user_id,badge_id)"` - BadgeId int64 `xorm:"index"` + UserId int64 `xorm:"unique(user_badge)"` + BadgeId int64 `xorm:"unique(user_badge) index"` CreatedUnix timeutil.TimeStamp `xorm:"created index"` } @@ -115,19 +115,23 @@ func DelBadgeUser(id int64) (int64, error) { return n, sess.Commit() } -func GetBadgeUsers(badgeId int64, opts ListOptions) ([]BadgeUserDetail, error) { +func GetBadgeUsers(badgeId int64, opts ListOptions) (int64, []BadgeUserDetail, error) { + n, err := x.Join("LEFT", "public.user", "public.user.ID = badge_user.user_id").Where("badge_user.badge_id = ?", badgeId).Count(&BadgeUserDetail{}) + if err != nil { + return 0, nil, err + } if opts.Page <= 0 { opts.Page = 1 } m := make([]BadgeUserDetail, 0) - err := x.Join("LEFT", "public.user", "public.user.ID = badge_user.user_id").Where("badge_user.badge_id = ?", badgeId).OrderBy("badge_user.created_unix desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&m) + err = x.Join("LEFT", "public.user", "public.user.ID = badge_user.user_id").Where("badge_user.badge_id = ?", badgeId).OrderBy("badge_user.id desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&m) if err != nil { - return nil, err + return 0, nil, err } - return m, nil + return n, m, nil } -func GetUserBadges(userId int64, opts GetUserBadgesOpts) ([]*Badge, error) { +func GetUserBadgesPaging(userId int64, opts GetUserBadgesOpts) ([]*Badge, error) { cond := builder.NewCond() cond = cond.And(builder.Eq{"badge_user.user_id": userId}) if opts.CategoryId > 0 { @@ -135,6 +139,18 @@ func GetUserBadges(userId int64, opts GetUserBadgesOpts) ([]*Badge, error) { } r := make([]*Badge, 0) - err := x.Join("INNER", "badge_user", "badge_user.badge_id = badge.id").Where(cond).OrderBy("badge_user.created_unix desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&r) + err := x.Join("INNER", "badge_user", "badge_user.badge_id = badge.id").Where(cond).OrderBy("badge_user.id desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&r) + return r, err +} + +func GetUserBadges(userId, categoryId int64) ([]*Badge, error) { + cond := builder.NewCond() + cond = cond.And(builder.Eq{"badge_user.user_id": userId}) + if categoryId > 0 { + cond = cond.And(builder.Eq{"badge.category_id": categoryId}) + } + + r := make([]*Badge, 0) + err := x.Join("INNER", "badge_user", "badge_user.badge_id = badge.id").Where(cond).OrderBy("badge_user.created_unix desc").Find(&r) return r, err } diff --git a/modules/setting/setting.go b/modules/setting/setting.go index d6e4824ef..00b0245f6 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -622,6 +622,13 @@ var ( DeductTaskRange time.Duration DeductTaskRangeForFirst time.Duration + //badge config + BadgeIconMaxFileSize int64 + BadgeIconMaxWidth int + BadgeIconMaxHeight int + BadgeIconDefaultSize uint + IconUploadPath string + //wechat auto reply config UserNameOfWechatReply string RepoNameOfWechatReply string @@ -1515,6 +1522,14 @@ func NewContext() { CloudBrainPayInterval = sec.Key("CLOUDBRAIN_PAY_INTERVAL").MustDuration(60 * time.Minute) DeductTaskRange = sec.Key("DEDUCT_TASK_RANGE").MustDuration(30 * time.Minute) DeductTaskRangeForFirst = sec.Key("DEDUCT_TASK_RANGE_FOR_FIRST").MustDuration(3 * time.Hour) + + sec = Cfg.Section("icons") + BadgeIconMaxFileSize = sec.Key("BADGE_ICON_MAX_FILE_SIZE").MustInt64(1048576) + BadgeIconMaxWidth = sec.Key("BADGE_ICON_MAX_WIDTH").MustInt(4096) + BadgeIconMaxHeight = sec.Key("BADGE_ICON_MAX_HEIGHT").MustInt(3072) + BadgeIconDefaultSize = sec.Key("BADGE_ICON_DEFAULT_SIZE").MustUint(200) + IconUploadPath = sec.Key("ICON_UPLOAD_PATH").MustString(path.Join(AppDataPath, "icons")) + SetRadarMapConfig() sec = Cfg.Section("warn_mail") diff --git a/routers/badge/badge.go b/routers/badge/badge.go index c62c1ee74..5344b4e39 100644 --- a/routers/badge/badge.go +++ b/routers/badge/badge.go @@ -4,15 +4,19 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/response" "code.gitea.io/gitea/services/badge" "errors" + "github.com/unknwon/com" "net/http" "strings" ) func GetCustomizeBadgeList(ctx *context.Context) { - r, err := badge.GetBadgeList(models.GetBadgeOpts{BadgeType: models.CustomizeBadge}) + page := ctx.QueryInt("page") + pageSize := 50 + n, r, err := badge.GetBadgeList(models.GetBadgeOpts{BadgeType: models.CustomizeBadge, LO: models.ListOptions{PageSize: pageSize, Page: page}}) if err != nil { log.Error("GetCustomizeBadgeList error.%v", err) ctx.JSON(http.StatusOK, response.ServerError(err.Error())) @@ -20,20 +24,22 @@ func GetCustomizeBadgeList(ctx *context.Context) { } m := make(map[string]interface{}) m["List"] = r + m["Total"] = n + m["PageSize"] = pageSize ctx.JSON(http.StatusOK, response.SuccessWithData(m)) } -func OperateBadge(ctx *context.Context, category models.Badge4AdminShow) { +func OperateBadge(ctx *context.Context, req models.BadgeOperateReq) { action := ctx.Params(":action") var err error switch action { case "edit": - err = badge.EditBadge(category, ctx.User) + err = badge.EditBadge(req, ctx.User) case "new": - err = badge.AddBadge(category, ctx.User) + err = badge.AddBadge(req, ctx.User) case "del": - err = badge.DelBadge(category.ID, ctx.User) + err = badge.DelBadge(req.ID, ctx.User) default: err = errors.New("action type error") } @@ -49,7 +55,8 @@ func OperateBadge(ctx *context.Context, category models.Badge4AdminShow) { func GetBadgeUsers(ctx *context.Context) { page := ctx.QueryInt("page") badgeId := ctx.QueryInt64("badge") - r, err := badge.GetBadgeUsers(badgeId, models.ListOptions{PageSize: 20, Page: page}) + pageSize := 50 + n, r, err := badge.GetBadgeUsers(badgeId, models.ListOptions{PageSize: pageSize, Page: page}) if err != nil { log.Error("GetBadgeUsers error.%v", err) ctx.JSON(http.StatusOK, response.ServerError(err.Error())) @@ -57,6 +64,8 @@ func GetBadgeUsers(ctx *context.Context) { } m := make(map[string]interface{}) m["List"] = r + m["Total"] = n + m["PageSize"] = pageSize ctx.JSON(http.StatusOK, response.SuccessWithData(m)) } @@ -96,3 +105,30 @@ func DelBadgeUsers(ctx *context.Context, req models.DelBadgeUserReq) { } ctx.JSON(http.StatusOK, response.Success()) } + +func UploadIcon(ctx *context.Context, form badge.IconUploadForm) { + + uploader := badge.NewIconUploader(badge.IconUploadConfig{ + FileMaxSize: setting.BadgeIconMaxFileSize, + FileMaxWidth: setting.BadgeIconMaxWidth, + FileMaxHeight: setting.BadgeIconMaxHeight, + }) + iconName, err := uploader.Upload(form, ctx.User) + if err != nil { + log.Error("UploadIcon error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + m := make(map[string]string, 0) + m["IconName"] = iconName + ctx.JSON(http.StatusOK, response.SuccessWithData(m)) +} + +func GetIcon(ctx *context.Context) { + hash := ctx.Params(":hash") + if !com.IsFile(models.GetCustomIconByHash(hash)) { + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) + return + } + ctx.Redirect(setting.AppSubURL + "/icons/" + hash) +} diff --git a/routers/badge/category.go b/routers/badge/category.go index a386f8fc5..4ac85df4a 100644 --- a/routers/badge/category.go +++ b/routers/badge/category.go @@ -11,7 +11,9 @@ import ( ) func GetBadgeCategoryList(ctx *context.Context) { - r, err := badge.GetBadgeCategoryList() + page := ctx.QueryInt("page") + pageSize := 50 + n, r, err := badge.GetBadgeCategoryList(models.ListOptions{Page: page, PageSize: pageSize}) if err != nil { log.Error("GetCategoryList error.%v", err) ctx.JSON(http.StatusOK, response.ServerError(err.Error())) @@ -19,6 +21,8 @@ func GetBadgeCategoryList(ctx *context.Context) { } m := make(map[string]interface{}) m["List"] = r + m["Total"] = n + m["PageSize"] = pageSize ctx.JSON(http.StatusOK, response.SuccessWithData(m)) } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 14f2d0f74..91b5690ad 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/routers/badge" "code.gitea.io/gitea/routers/reward/point" "code.gitea.io/gitea/routers/task" + badge_service "code.gitea.io/gitea/services/badge" "code.gitea.io/gitea/services/reward" "encoding/gob" "net/http" @@ -194,6 +195,14 @@ func NewMacaron() *macaron.Macaron { ExpiresAfter: setting.StaticCacheTime, }, )) + m.Use(public.StaticHandler( + setting.IconUploadPath, + &public.Options{ + Prefix: "icons", + SkipLogging: setting.DisableRouterLog, + ExpiresAfter: setting.StaticCacheTime, + }, + )) m.Use(public.StaticHandler( setting.RepositoryAvatarUploadPath, &public.Options{ @@ -518,6 +527,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/avatar/:hash", user.AvatarByEmailHash) + m.Get("/show/icon/:hash", badge.GetIcon) + adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true}) // ***** START: Admin ***** @@ -670,16 +681,15 @@ func RegisterRoutes(m *macaron.Macaron) { }) m.Group("/customize", func() { m.Get("/list", badge.GetCustomizeBadgeList) - }) m.Group("/users", func() { m.Get("", badge.GetBadgeUsers) m.Post("/add", bindIgnErr(models.AddBadgeUsersReq{}), badge.AddOperateBadgeUsers) m.Post("/del", bindIgnErr(models.DelBadgeUserReq{}), badge.DelBadgeUsers) }) - m.Post("/^:action(new|edit|del)$", bindIgnErr(models.Badge4AdminShow{}), badge.OperateBadge) + m.Post("/^:action(new|edit|del)$", bindIgnErr(models.BadgeOperateReq{}), badge.OperateBadge) }) - + m.Post("/icon/upload", bindIgnErr(badge_service.IconUploadForm{}), badge.UploadIcon) }, operationReq) // ***** END: Operation ***** diff --git a/routers/user/profile.go b/routers/user/profile.go index 1d275c191..8685cd734 100755 --- a/routers/user/profile.go +++ b/routers/user/profile.go @@ -6,7 +6,9 @@ package user import ( + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/services/badge" + "encoding/json" "errors" "fmt" "path" @@ -103,6 +105,8 @@ func Profile(ctx *context.Context) { ctx.Data["Owner"] = ctxUser ctx.Data["OpenIDs"] = openIDs ctx.Data["RecentBadges"] = badges + b, _ := json.Marshal(badges) + log.Info(string(b)) ctx.Data["EnableHeatmap"] = setting.Service.EnableUserHeatmap ctx.Data["HeatmapUser"] = ctxUser.Name showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == ctxUser.ID) @@ -312,6 +316,8 @@ func Profile(ctx *context.Context) { ctx.ServerError("GetUserAllBadges", err) return } + ab, _ := json.Marshal(allBadges) + log.Info(string(ab)) ctx.Data["AllBadges"] = allBadges default: ctx.ServerError("tab error", errors.New("tab error")) diff --git a/services/badge/badge.go b/services/badge/badge.go index 83c3581b5..0aefeca6e 100644 --- a/services/badge/badge.go +++ b/services/badge/badge.go @@ -7,24 +7,32 @@ import ( "errors" ) -func GetBadgeList(opts models.GetBadgeOpts) ([]*models.Badge4AdminShow, error) { - list, err := models.GetBadgeList(opts) +func GetBadgeList(opts models.GetBadgeOpts) (int64, []*models.Badge4AdminShow, error) { + total, list, err := models.GetBadgeList(opts) if err != nil { - return nil, err + return 0, nil, err } if len(list) == 0 { - return nil, nil + return 0, nil, nil } r := make([]*models.Badge4AdminShow, len(list)) for i := 0; i < len(list); i++ { r[i] = list[i].ToShow() } - return r, nil + return total, r, nil } -func AddBadge(m models.Badge4AdminShow, doer *models.User) error { - _, err := models.AddBadge(m.ToDTO()) +func AddBadge(m models.BadgeOperateReq, doer *models.User) error { + _, err := models.GetBadgeCategoryById(m.CategoryId) + + if err != nil { + if models.IsErrRecordNotExist(err) { + return errors.New("badge category is not available") + } + return err + } + _, err = models.AddBadge(m.ToDTO()) if err != nil { return err } @@ -32,7 +40,7 @@ func AddBadge(m models.Badge4AdminShow, doer *models.User) error { return nil } -func EditBadge(m models.Badge4AdminShow, doer *models.User) error { +func EditBadge(m models.BadgeOperateReq, doer *models.User) error { if m.ID == 0 { log.Error(" EditBadge param error") return errors.New("param error") diff --git a/services/badge/category.go b/services/badge/category.go index 316e654a7..14d06620a 100644 --- a/services/badge/category.go +++ b/services/badge/category.go @@ -7,20 +7,20 @@ import ( "errors" ) -func GetBadgeCategoryList() ([]*models.BadgeCategory4Show, error) { - list, err := models.GetBadgeCategoryList() +func GetBadgeCategoryList(opts models.ListOptions) (int64, []*models.BadgeCategory4Show, error) { + total, list, err := models.GetBadgeCategoryListPaging(opts) if err != nil { - return nil, err + return 0, nil, err } if len(list) == 0 { - return nil, nil + return 0, nil, nil } r := make([]*models.BadgeCategory4Show, len(list)) for i := 0; i < len(list); i++ { r[i] = list[i].ToShow() } - return r, nil + return total, r, nil } func AddBadgeCategory(m models.BadgeCategory4Show, doer *models.User) error { diff --git a/services/badge/icon.go b/services/badge/icon.go new file mode 100644 index 000000000..ae7c25241 --- /dev/null +++ b/services/badge/icon.go @@ -0,0 +1,109 @@ +package badge + +import ( + "bytes" + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/setting" + "crypto/md5" + "errors" + "fmt" + "image" + "image/png" + "io/ioutil" + "mime/multipart" + "os" +) + +type IconUploader struct { + Config IconUploadConfig +} + +type IconUploadForm struct { + Icon *multipart.FileHeader +} + +type IconUploadConfig struct { + FileMaxSize int64 + FileMaxWidth int + FileMaxHeight int +} + +func NewIconUploader(config IconUploadConfig) IconUploader { + return IconUploader{Config: config} +} + +func (u IconUploader) Upload(form IconUploadForm, user *models.User) (string, error) { + if form.Icon == nil || form.Icon.Filename == "" { + return "", errors.New("File or fileName is empty") + } + + fr, err := form.Icon.Open() + if err != nil { + return "", fmt.Errorf("Icon.Open: %v", err) + } + defer fr.Close() + + if form.Icon.Size > u.Config.FileMaxSize { + return "", errors.New("File is too large") + } + + data, err := ioutil.ReadAll(fr) + if err != nil { + return "", fmt.Errorf("ioutil.ReadAll: %v", err) + } + if !base.IsImageFile(data) { + return "", errors.New("File is not a image") + } + iconName, err := u.uploadIcon(data, user.ID) + if err != nil { + return "", fmt.Errorf("uploadIcon: %v", err) + } + return iconName, nil + +} + +func (u IconUploader) uploadIcon(data []byte, userId int64) (string, error) { + m, err := u.prepare(data) + if err != nil { + return "", err + } + + iconName := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", userId, md5.Sum(data))))) + + if err := os.MkdirAll(setting.IconUploadPath, os.ModePerm); err != nil { + return "", fmt.Errorf("uploadIcon. Failed to create dir %s: %v", setting.AvatarUploadPath, err) + } + + fw, err := os.Create(models.GetCustomIconByHash(iconName)) + if err != nil { + return "", fmt.Errorf("Create: %v", err) + } + defer fw.Close() + + if err = png.Encode(fw, *m); err != nil { + return "", fmt.Errorf("Encode: %v", err) + } + + return iconName, nil +} + +func (u IconUploader) prepare(data []byte) (*image.Image, error) { + imgCfg, _, err := image.DecodeConfig(bytes.NewReader(data)) + if err != nil { + return nil, fmt.Errorf("DecodeConfig: %v", err) + } + if imgCfg.Width > u.Config.FileMaxWidth { + return nil, fmt.Errorf("Image width is too large: %d > %d", imgCfg.Width, setting.AvatarMaxWidth) + } + if imgCfg.Height > u.Config.FileMaxHeight { + return nil, fmt.Errorf("Image height is too large: %d > %d", imgCfg.Height, setting.AvatarMaxHeight) + } + + img, _, err := image.Decode(bytes.NewReader(data)) + if err != nil { + return nil, fmt.Errorf("Decode: %v", err) + } + + return &img, nil +} diff --git a/services/badge/user.go b/services/badge/user.go index ccc112f01..70fc7bba7 100644 --- a/services/badge/user.go +++ b/services/badge/user.go @@ -5,20 +5,20 @@ import ( "code.gitea.io/gitea/modules/log" ) -func GetBadgeUsers(badgeId int64, opts models.ListOptions) ([]*models.BadgeUser4SHow, error) { - list, err := models.GetBadgeUsers(badgeId, opts) +func GetBadgeUsers(badgeId int64, opts models.ListOptions) (int64, []*models.BadgeUser4SHow, error) { + total, list, err := models.GetBadgeUsers(badgeId, opts) if err != nil { - return nil, err + return 0, nil, err } if len(list) == 0 { - return nil, nil + return 0, nil, nil } r := make([]*models.BadgeUser4SHow, len(list)) for i := 0; i < len(list); i++ { r[i] = list[i].ToShow() } - return r, nil + return total, r, nil } func AddBadgeUsers(badgeId int64, userNames []string) (int, error) { @@ -49,7 +49,7 @@ func DelBadgeUser(id int64) error { //GetUserBadges Only Returns badges the user has earned func GetUserBadges(userId int64, opts models.ListOptions) ([]*models.Badge4UserShow, error) { - badges, err := models.GetUserBadges(userId, models.GetUserBadgesOpts{ListOptions: opts}) + badges, err := models.GetUserBadgesPaging(userId, models.GetUserBadgesOpts{ListOptions: opts}) if err != nil { return nil, err } @@ -68,7 +68,10 @@ func GetUserAllBadges(userId int64) ([]models.UserAllBadgeInCategory, error) { r := make([]models.UserAllBadgeInCategory, len(categoryList)) for i, v := range categoryList { badges, err := models.GetBadgeByCategoryId(v.ID) - userBadgeMap, err := getUserBadgesMap(userId, v.ID, 100, 1) + if badges == nil || len(badges) == 0 { + continue + } + userBadgeMap, err := getUserBadgesMap(userId, v.ID) if err != nil { return nil, err } @@ -91,8 +94,8 @@ func GetUserAllBadges(userId int64) ([]models.UserAllBadgeInCategory, error) { return r, nil } -func getUserBadgesMap(userId, categoryId int64, pageSize, page int) (map[int64]*models.Badge, error) { - userBadges, err := models.GetUserBadges(userId, models.GetUserBadgesOpts{ListOptions: models.ListOptions{PageSize: pageSize, Page: page}, CategoryId: categoryId}) +func getUserBadgesMap(userId, categoryId int64) (map[int64]*models.Badge, error) { + userBadges, err := models.GetUserBadges(userId, categoryId) if err != nil { return nil, err } From 3940775144dbdb19edb220de74d65e8d5d0c01b0 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Tue, 11 Oct 2022 10:13:47 +0800 Subject: [PATCH 04/16] #2908 update --- routers/badge/badge.go | 1 + services/badge/icon.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/routers/badge/badge.go b/routers/badge/badge.go index 5344b4e39..5e4efcdbf 100644 --- a/routers/badge/badge.go +++ b/routers/badge/badge.go @@ -112,6 +112,7 @@ func UploadIcon(ctx *context.Context, form badge.IconUploadForm) { FileMaxSize: setting.BadgeIconMaxFileSize, FileMaxWidth: setting.BadgeIconMaxWidth, FileMaxHeight: setting.BadgeIconMaxHeight, + NeedSquare: true, }) iconName, err := uploader.Upload(form, ctx.User) if err != nil { diff --git a/services/badge/icon.go b/services/badge/icon.go index ae7c25241..fd731b586 100644 --- a/services/badge/icon.go +++ b/services/badge/icon.go @@ -8,6 +8,8 @@ import ( "crypto/md5" "errors" "fmt" + "github.com/nfnt/resize" + "github.com/oliamb/cutter" "image" "image/png" "io/ioutil" @@ -27,6 +29,9 @@ type IconUploadConfig struct { FileMaxSize int64 FileMaxWidth int FileMaxHeight int + DefaultSize uint + NeedResize bool + NeedSquare bool } func NewIconUploader(config IconUploadConfig) IconUploader { @@ -105,5 +110,31 @@ func (u IconUploader) prepare(data []byte) (*image.Image, error) { return nil, fmt.Errorf("Decode: %v", err) } + if u.Config.NeedSquare { + if imgCfg.Width != imgCfg.Height { + var newSize, ax, ay int + if imgCfg.Width > imgCfg.Height { + newSize = imgCfg.Height + ax = (imgCfg.Width - imgCfg.Height) / 2 + } else { + newSize = imgCfg.Width + ay = (imgCfg.Height - imgCfg.Width) / 2 + } + + img, err = cutter.Crop(img, cutter.Config{ + Width: newSize, + Height: newSize, + Anchor: image.Point{ax, ay}, + }) + if err != nil { + return nil, err + } + } + } + + if u.Config.NeedResize && u.Config.DefaultSize > 0 { + img = resize.Resize(u.Config.DefaultSize, u.Config.DefaultSize, img, resize.NearestNeighbor) + } + return &img, nil } From a3819e4fe03340fedd23caf2c9e081875ec858b4 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Tue, 11 Oct 2022 10:27:20 +0800 Subject: [PATCH 05/16] #2908 remove useless code --- routers/user/profile.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/routers/user/profile.go b/routers/user/profile.go index 8685cd734..1d275c191 100755 --- a/routers/user/profile.go +++ b/routers/user/profile.go @@ -6,9 +6,7 @@ package user import ( - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/services/badge" - "encoding/json" "errors" "fmt" "path" @@ -105,8 +103,6 @@ func Profile(ctx *context.Context) { ctx.Data["Owner"] = ctxUser ctx.Data["OpenIDs"] = openIDs ctx.Data["RecentBadges"] = badges - b, _ := json.Marshal(badges) - log.Info(string(b)) ctx.Data["EnableHeatmap"] = setting.Service.EnableUserHeatmap ctx.Data["HeatmapUser"] = ctxUser.Name showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == ctxUser.ID) @@ -316,8 +312,6 @@ func Profile(ctx *context.Context) { ctx.ServerError("GetUserAllBadges", err) return } - ab, _ := json.Marshal(allBadges) - log.Info(string(ab)) ctx.Data["AllBadges"] = allBadges default: ctx.ServerError("tab error", errors.New("tab error")) From e4db58a70afe725652e2e6175b1abed588c8868f Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Tue, 11 Oct 2022 14:14:49 +0800 Subject: [PATCH 06/16] #2910 update --- routers/badge/badge.go | 9 +++++++++ routers/badge/category.go | 9 +++++++++ routers/routes/routes.go | 2 ++ 3 files changed, 20 insertions(+) diff --git a/routers/badge/badge.go b/routers/badge/badge.go index 5e4efcdbf..4367f1841 100644 --- a/routers/badge/badge.go +++ b/routers/badge/badge.go @@ -2,6 +2,7 @@ package badge import ( "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -13,6 +14,14 @@ import ( "strings" ) +const ( + tplBadgeCustomize base.TplName = "admin/badges/customize" +) + +func GetBadgeCustomizePage(ctx *context.Context) { + ctx.HTML(200, tplBadgeCustomize) +} + func GetCustomizeBadgeList(ctx *context.Context) { page := ctx.QueryInt("page") pageSize := 50 diff --git a/routers/badge/category.go b/routers/badge/category.go index 4ac85df4a..b5457c857 100644 --- a/routers/badge/category.go +++ b/routers/badge/category.go @@ -2,6 +2,7 @@ package badge import ( "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/routers/response" @@ -10,6 +11,14 @@ import ( "net/http" ) +const ( + tplBadgeCategory base.TplName = "admin/badges/category" +) + +func GetBadgeCategoryPage(ctx *context.Context) { + ctx.HTML(200, tplBadgeCategory) +} + func GetBadgeCategoryList(ctx *context.Context) { page := ctx.QueryInt("page") pageSize := 50 diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 91b5690ad..648164fbe 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -676,10 +676,12 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/badge", func() { m.Group("/category", func() { + m.Get("", badge.GetBadgeCategoryPage) m.Get("/list", badge.GetBadgeCategoryList) m.Post("/^:action(new|edit|del)$", bindIgnErr(models.BadgeCategory4Show{}), badge.OperateBadgeCategory) }) m.Group("/customize", func() { + m.Get("", badge.GetBadgeCustomizePage) m.Get("/list", badge.GetCustomizeBadgeList) }) m.Group("/users", func() { From 326946e36625e07ef561264e1d1374bb5097da41 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Tue, 11 Oct 2022 14:52:54 +0800 Subject: [PATCH 07/16] #2910 update --- routers/badge/badge.go | 9 --------- routers/badge/category.go | 9 --------- routers/routes/routes.go | 2 -- 3 files changed, 20 deletions(-) diff --git a/routers/badge/badge.go b/routers/badge/badge.go index 4367f1841..5e4efcdbf 100644 --- a/routers/badge/badge.go +++ b/routers/badge/badge.go @@ -2,7 +2,6 @@ package badge import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -14,14 +13,6 @@ import ( "strings" ) -const ( - tplBadgeCustomize base.TplName = "admin/badges/customize" -) - -func GetBadgeCustomizePage(ctx *context.Context) { - ctx.HTML(200, tplBadgeCustomize) -} - func GetCustomizeBadgeList(ctx *context.Context) { page := ctx.QueryInt("page") pageSize := 50 diff --git a/routers/badge/category.go b/routers/badge/category.go index b5457c857..4ac85df4a 100644 --- a/routers/badge/category.go +++ b/routers/badge/category.go @@ -2,7 +2,6 @@ package badge import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/routers/response" @@ -11,14 +10,6 @@ import ( "net/http" ) -const ( - tplBadgeCategory base.TplName = "admin/badges/category" -) - -func GetBadgeCategoryPage(ctx *context.Context) { - ctx.HTML(200, tplBadgeCategory) -} - func GetBadgeCategoryList(ctx *context.Context) { page := ctx.QueryInt("page") pageSize := 50 diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 648164fbe..91b5690ad 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -676,12 +676,10 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/badge", func() { m.Group("/category", func() { - m.Get("", badge.GetBadgeCategoryPage) m.Get("/list", badge.GetBadgeCategoryList) m.Post("/^:action(new|edit|del)$", bindIgnErr(models.BadgeCategory4Show{}), badge.OperateBadgeCategory) }) m.Group("/customize", func() { - m.Get("", badge.GetBadgeCustomizePage) m.Get("/list", badge.GetCustomizeBadgeList) }) m.Group("/users", func() { From 4217e44a7e037aeeeaf13255c31464cbd2205631 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Tue, 11 Oct 2022 17:32:09 +0800 Subject: [PATCH 08/16] #2188 fix bug --- routers/repo/editor.go | 6 +++--- routers/repo/setting_protected_branch.go | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/routers/repo/editor.go b/routers/repo/editor.go index 40edc4767..b350343db 100644 --- a/routers/repo/editor.go +++ b/routers/repo/editor.go @@ -303,7 +303,7 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo } if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(models.UnitTypePullRequests) { - ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + ctx.Repo.BranchName + "..." + form.NewBranchName) + ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName)) } else { ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath)) } @@ -475,7 +475,7 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) { ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath)) if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(models.UnitTypePullRequests) { - ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + ctx.Repo.BranchName + "..." + form.NewBranchName) + ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName)) } else { treePath := filepath.Dir(ctx.Repo.TreePath) if treePath == "." { @@ -686,7 +686,7 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) { } if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(models.UnitTypePullRequests) { - ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + ctx.Repo.BranchName + "..." + form.NewBranchName) + ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName)) } else { ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath)) } diff --git a/routers/repo/setting_protected_branch.go b/routers/repo/setting_protected_branch.go index ab0fd77ee..f1ea17528 100644 --- a/routers/repo/setting_protected_branch.go +++ b/routers/repo/setting_protected_branch.go @@ -5,6 +5,7 @@ package repo import ( + "code.gitea.io/gitea/modules/util" "fmt" "strings" "time" @@ -192,7 +193,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm) } if f.RequiredApprovals < 0 { ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min")) - ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch)) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch))) } var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64 @@ -263,7 +264,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm) return } ctx.Flash.Success(ctx.Tr("repo.settings.update_protect_branch_success", branch)) - ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch)) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch))) } else { if protectBranch != nil { if err := ctx.Repo.Repository.DeleteProtectedBranch(protectBranch.ID); err != nil { From a83afede2f9ce44af13a37234c0f9fa433c9dfa6 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Fri, 14 Oct 2022 10:47:04 +0800 Subject: [PATCH 09/16] #2908 update --- models/badge.go | 14 ++++++++----- routers/badge/badge.go | 9 ++++---- routers/badge/category.go | 6 +++--- routers/response/response_list.go | 3 +++ services/badge/badge.go | 34 +++++++++++++++++++------------ services/badge/category.go | 30 +++++++++++++++++---------- 6 files changed, 60 insertions(+), 36 deletions(-) diff --git a/models/badge.go b/models/badge.go index 7e20ab2d4..fcfbdc27f 100644 --- a/models/badge.go +++ b/models/badge.go @@ -30,8 +30,9 @@ func (m *Badge) ToUserShow() *Badge4UserShow { } type GetBadgeOpts struct { - BadgeType BadgeType - LO ListOptions + BadgeType BadgeType + CategoryId int64 + ListOpts ListOptions } type BadgeAndCategory struct { @@ -118,19 +119,22 @@ type UserAllBadgeInCategory struct { } func GetBadgeList(opts GetBadgeOpts) (int64, []*BadgeAndCategory, error) { - if opts.LO.Page <= 0 { - opts.LO.Page = 1 + if opts.ListOpts.Page <= 0 { + opts.ListOpts.Page = 1 } var cond = builder.NewCond() if opts.BadgeType > 0 { cond = cond.And(builder.Eq{"badge_category.type": opts.BadgeType}) } + if opts.CategoryId > 0 { + cond = cond.And(builder.Eq{"badge_category.id": opts.CategoryId}) + } n, err := x.Join("INNER", "badge_category", "badge_category.ID = badge.category_id").Where(cond).Count(&BadgeAndCategory{}) if err != nil { return 0, nil, err } r := make([]*BadgeAndCategory, 0) - if err = x.Join("INNER", "badge_category", "badge_category.ID = badge.category_id").Where(cond).OrderBy("badge.created_unix desc").Limit(opts.LO.PageSize, (opts.LO.Page-1)*opts.LO.PageSize).Find(&r); err != nil { + if err = x.Join("INNER", "badge_category", "badge_category.ID = badge.category_id").Where(cond).OrderBy("badge.created_unix desc").Limit(opts.ListOpts.PageSize, (opts.ListOpts.Page-1)*opts.ListOpts.PageSize).Find(&r); err != nil { return 0, nil, err } return n, r, nil diff --git a/routers/badge/badge.go b/routers/badge/badge.go index 5e4efcdbf..6d8725b12 100644 --- a/routers/badge/badge.go +++ b/routers/badge/badge.go @@ -15,8 +15,9 @@ import ( func GetCustomizeBadgeList(ctx *context.Context) { page := ctx.QueryInt("page") + category := ctx.QueryInt64("category") pageSize := 50 - n, r, err := badge.GetBadgeList(models.GetBadgeOpts{BadgeType: models.CustomizeBadge, LO: models.ListOptions{PageSize: pageSize, Page: page}}) + n, r, err := badge.GetBadgeList(models.GetBadgeOpts{CategoryId: category, BadgeType: models.CustomizeBadge, ListOpts: models.ListOptions{PageSize: pageSize, Page: page}}) if err != nil { log.Error("GetCustomizeBadgeList error.%v", err) ctx.JSON(http.StatusOK, response.ServerError(err.Error())) @@ -32,7 +33,7 @@ func GetCustomizeBadgeList(ctx *context.Context) { func OperateBadge(ctx *context.Context, req models.BadgeOperateReq) { action := ctx.Params(":action") - var err error + var err *response.BizError switch action { case "edit": err = badge.EditBadge(req, ctx.User) @@ -41,12 +42,12 @@ func OperateBadge(ctx *context.Context, req models.BadgeOperateReq) { case "del": err = badge.DelBadge(req.ID, ctx.User) default: - err = errors.New("action type error") + err = response.NewBizError(errors.New("action type error")) } if err != nil { log.Error("OperateBadge error ,%v", err) - ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + ctx.JSON(http.StatusOK, response.ResponseError(err)) return } ctx.JSON(http.StatusOK, response.Success()) diff --git a/routers/badge/category.go b/routers/badge/category.go index 4ac85df4a..71c34e1ba 100644 --- a/routers/badge/category.go +++ b/routers/badge/category.go @@ -29,7 +29,7 @@ func GetBadgeCategoryList(ctx *context.Context) { func OperateBadgeCategory(ctx *context.Context, category models.BadgeCategory4Show) { action := ctx.Params(":action") - var err error + var err *response.BizError switch action { case "edit": err = badge.EditBadgeCategory(category, ctx.User) @@ -38,12 +38,12 @@ func OperateBadgeCategory(ctx *context.Context, category models.BadgeCategory4Sh case "del": err = badge.DelBadgeCategory(category.ID, ctx.User) default: - err = errors.New("action type error") + err = response.NewBizError(errors.New("action type error")) } if err != nil { log.Error("OperateBadgeCategory error ,%v", err) - ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + ctx.JSON(http.StatusOK, response.ResponseError(err)) return } ctx.JSON(http.StatusOK, response.Success()) diff --git a/routers/response/response_list.go b/routers/response/response_list.go index 6514f3edd..8bdbf375c 100644 --- a/routers/response/response_list.go +++ b/routers/response/response_list.go @@ -3,3 +3,6 @@ package response var RESOURCE_QUEUE_NOT_AVAILABLE = &BizError{Code: 1001, Err: "resource queue not available"} var SPECIFICATION_NOT_EXIST = &BizError{Code: 1002, Err: "specification not exist"} var SPECIFICATION_NOT_AVAILABLE = &BizError{Code: 1003, Err: "specification not available"} + +var CATEGORY_STILL_HAS_BADGES = &BizError{Code: 1004, Err: "Please delete badges in the category first"} +var BADGES_STILL_HAS_USERS = &BizError{Code: 1005, Err: "Please delete users of badge first"} diff --git a/services/badge/badge.go b/services/badge/badge.go index 0aefeca6e..c6f833f65 100644 --- a/services/badge/badge.go +++ b/services/badge/badge.go @@ -3,6 +3,7 @@ package badge import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/routers/response" "code.gitea.io/gitea/services/admin/operate_log" "errors" ) @@ -23,50 +24,57 @@ func GetBadgeList(opts models.GetBadgeOpts) (int64, []*models.Badge4AdminShow, e return total, r, nil } -func AddBadge(m models.BadgeOperateReq, doer *models.User) error { +func AddBadge(m models.BadgeOperateReq, doer *models.User) *response.BizError { _, err := models.GetBadgeCategoryById(m.CategoryId) if err != nil { if models.IsErrRecordNotExist(err) { - return errors.New("badge category is not available") + return response.NewBizError(errors.New("badge category is not available")) } - return err + return response.NewBizError(err) } _, err = models.AddBadge(m.ToDTO()) if err != nil { - return err + return response.NewBizError(err) } operate_log.Log4Add(operate_log.BadgeOperate, m, doer.ID, "新增了勋章") return nil } -func EditBadge(m models.BadgeOperateReq, doer *models.User) error { +func EditBadge(m models.BadgeOperateReq, doer *models.User) *response.BizError { if m.ID == 0 { log.Error(" EditBadge param error") - return errors.New("param error") + return response.NewBizError(errors.New("param error")) } old, err := models.GetBadgeById(m.ID) if err != nil { - return err + return response.NewBizError(err) } _, err = models.UpdateBadgeById(m.ID, m.ToDTO()) if err != nil { - return err + return response.NewBizError(err) } operate_log.Log4Edit(operate_log.BadgeOperate, old, m.ToDTO(), doer.ID, "修改了勋章") - return err + return nil } -func DelBadge(id int64, doer *models.User) error { +func DelBadge(id int64, doer *models.User) *response.BizError { if id == 0 { log.Error(" DelBadge param error") - return errors.New("param error") + return response.NewBizError(errors.New("param error")) } old, err := models.GetBadgeById(id) if err != nil { - return err + return response.NewBizError(err) + } + n, _, err := models.GetBadgeUsers(id, models.ListOptions{PageSize: 1, Page: 1}) + if err != nil { + return response.NewBizError(err) + } + if n > 0 { + return response.BADGES_STILL_HAS_USERS } _, err = models.DelBadge(id) operate_log.Log4Del(operate_log.BadgeOperate, old, doer.ID, "删除了勋章") - return err + return nil } diff --git a/services/badge/category.go b/services/badge/category.go index 14d06620a..445dedcad 100644 --- a/services/badge/category.go +++ b/services/badge/category.go @@ -3,6 +3,7 @@ package badge import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/routers/response" "code.gitea.io/gitea/services/admin/operate_log" "errors" ) @@ -23,42 +24,49 @@ func GetBadgeCategoryList(opts models.ListOptions) (int64, []*models.BadgeCatego return total, r, nil } -func AddBadgeCategory(m models.BadgeCategory4Show, doer *models.User) error { +func AddBadgeCategory(m models.BadgeCategory4Show, doer *models.User) *response.BizError { _, err := models.AddBadgeCategory(m.ToDTO()) if err != nil { - return err + return response.NewBizError(err) } operate_log.Log4Add(operate_log.BadgeCategoryOperate, m, doer.ID, "新增了勋章分类") return nil } -func EditBadgeCategory(m models.BadgeCategory4Show, doer *models.User) error { +func EditBadgeCategory(m models.BadgeCategory4Show, doer *models.User) *response.BizError { if m.ID == 0 { log.Error(" EditBadgeCategory param error") - return errors.New("param error") + return response.NewBizError(errors.New("param error")) } old, err := models.GetBadgeCategoryById(m.ID) if err != nil { - return err + return response.NewBizError(err) } _, err = models.UpdateBadgeCategoryById(m.ID, m.ToDTO()) if err != nil { - return err + return response.NewBizError(err) } operate_log.Log4Edit(operate_log.BadgeCategoryOperate, old, m.ToDTO(), doer.ID, "修改了勋章分类") - return err + return nil } -func DelBadgeCategory(id int64, doer *models.User) error { +func DelBadgeCategory(id int64, doer *models.User) *response.BizError { if id == 0 { log.Error(" DelBadgeCategory param error") - return errors.New("param error") + return response.NewBizError(errors.New("param error")) } old, err := models.GetBadgeCategoryById(id) if err != nil { - return err + return response.NewBizError(err) + } + badges, err := models.GetBadgeByCategoryId(id) + if err != nil { + return response.NewBizError(err) + } + if len(badges) > 0 { + return response.CATEGORY_STILL_HAS_BADGES } _, err = models.DelBadgeCategory(id) operate_log.Log4Del(operate_log.BadgeCategoryOperate, old, doer.ID, "删除了勋章分类") - return err + return nil } From d38a58b57b3c92f0b8ca333ce8a5cf110561930e Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Mon, 17 Oct 2022 14:38:42 +0800 Subject: [PATCH 10/16] bug fix --- services/badge/user.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/badge/user.go b/services/badge/user.go index 70fc7bba7..b4273d8d2 100644 --- a/services/badge/user.go +++ b/services/badge/user.go @@ -65,8 +65,8 @@ func GetUserAllBadges(userId int64) ([]models.UserAllBadgeInCategory, error) { if err != nil { return nil, err } - r := make([]models.UserAllBadgeInCategory, len(categoryList)) - for i, v := range categoryList { + r := make([]models.UserAllBadgeInCategory, 0) + for _, v := range categoryList { badges, err := models.GetBadgeByCategoryId(v.ID) if badges == nil || len(badges) == 0 { continue @@ -89,7 +89,7 @@ func GetUserAllBadges(userId int64) ([]models.UserAllBadgeInCategory, error) { bArray[j] = b } t.Badges = bArray - r[i] = t + r = append(r, t) } return r, nil } From 4e2ec5ac738610556857ce00b8d706c79f9f62f4 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Mon, 17 Oct 2022 14:55:11 +0800 Subject: [PATCH 11/16] add total badges --- models/badge_user.go | 3 +++ routers/user/profile.go | 7 +++++++ services/badge/user.go | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/models/badge_user.go b/models/badge_user.go index 32861248e..9b556bc0e 100644 --- a/models/badge_user.go +++ b/models/badge_user.go @@ -142,6 +142,9 @@ func GetUserBadgesPaging(userId int64, opts GetUserBadgesOpts) ([]*Badge, error) err := x.Join("INNER", "badge_user", "badge_user.badge_id = badge.id").Where(cond).OrderBy("badge_user.id desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&r) return r, err } +func CountUserBadges(userId int64) (int64, error) { + return x.Where("user_id = ?", userId).Count(&BadgeUser{}) +} func GetUserBadges(userId, categoryId int64) ([]*Badge, error) { cond := builder.NewCond() diff --git a/routers/user/profile.go b/routers/user/profile.go index 1d275c191..66a480b7f 100755 --- a/routers/user/profile.go +++ b/routers/user/profile.go @@ -97,12 +97,19 @@ func Profile(ctx *context.Context) { ctx.ServerError("GetUserBadges", err) return } + // Count user badges + cnt, err := badge.CountUserBadges(ctxUser.ID) + if err != nil { + ctx.ServerError("CountUserBadges", err) + return + } ctx.Data["Title"] = ctxUser.DisplayName() ctx.Data["PageIsUserProfile"] = true ctx.Data["Owner"] = ctxUser ctx.Data["OpenIDs"] = openIDs ctx.Data["RecentBadges"] = badges + ctx.Data["TotalBadges"] = cnt ctx.Data["EnableHeatmap"] = setting.Service.EnableUserHeatmap ctx.Data["HeatmapUser"] = ctxUser.Name showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == ctxUser.ID) diff --git a/services/badge/user.go b/services/badge/user.go index b4273d8d2..025b10f77 100644 --- a/services/badge/user.go +++ b/services/badge/user.go @@ -60,6 +60,10 @@ func GetUserBadges(userId int64, opts models.ListOptions) ([]*models.Badge4UserS return r, nil } +func CountUserBadges(userId int64) (int64, error) { + return models.CountUserBadges(userId) +} + func GetUserAllBadges(userId int64) ([]models.UserAllBadgeInCategory, error) { categoryList, err := models.GetBadgeCategoryList() if err != nil { From 46a28cbd6ce5063f1c7ab9ccf2f9eaa64a7e2e5a Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Mon, 17 Oct 2022 14:58:04 +0800 Subject: [PATCH 12/16] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E5=BE=BD=E7=AB=A0?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=98=BE=E7=A4=BA=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- options/locale/locale_en-US.ini | 1 + options/locale/locale_zh-CN.ini | 1 + templates/repo/badge.tmpl | 25 ++++++++++ templates/user/profile.tmpl | 18 +++++++ web_src/less/_user.less | 85 ++++++++++++++++++++++++++++++++- 5 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 templates/repo/badge.tmpl diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index dbd3f81f8..c3e2426fb 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -525,6 +525,7 @@ datasets = Datasets activity = Public Activity followers = Followers starred = Starred Repositories +badge = Achievement Badge following = Following follow = Follow unfollow = Unfollow diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 21c4f45bd..bf7549918 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -530,6 +530,7 @@ datasets=数据集 activity=公开活动 followers=关注者 starred=已点赞 +badge=成就徽章 following=关注中 follow=关注 unfollow=取消关注 diff --git a/templates/repo/badge.tmpl b/templates/repo/badge.tmpl new file mode 100644 index 000000000..61b542be8 --- /dev/null +++ b/templates/repo/badge.tmpl @@ -0,0 +1,25 @@ +
+ {{range .AllBadges }} +
+
{{.CategoryName}}   (已点亮{{.LightedNum}}个)
+ +
+ {{ end }} + {{ template "base/paginate" . }} +
+ diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index be6ecbaa0..f1328b626 100755 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -17,6 +17,17 @@ {{if .Owner.FullName}}{{.Owner.FullName}}{{end}} {{.Owner.Name}} + +
+ {{range $k,$v :=.RecentBadges}} + {{if le $k 3}} +
+ {{else}} + + {{end}} + {{end}} + +
{{if eq .TabName "activity"}} @@ -201,6 +217,8 @@ {{template "explore/dataset_search" .}} {{template "explore/dataset_list" .}} {{template "base/paginate" .}} + {{else if eq .TabName "badge"}} + {{template "repo/badge" .}} {{else}} {{template "explore/repo_search" .}} {{template "explore/repo_list" .}} diff --git a/web_src/less/_user.less b/web_src/less/_user.less index 6acbb35ee..c2381820c 100644 --- a/web_src/less/_user.less +++ b/web_src/less/_user.less @@ -9,11 +9,30 @@ .username { display: block; } - + .badge-wrap { + display: flex; + justify-content: center; + align-items: center; + .badge-img-avatar { + width: 32px; + height: 32px; + margin-right: 5px; + } + .badge-more-icon { + width: 32px; + height: 32px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + border: 1px #f8f9fa solid; + background: #f8f9fa; + } + } .header { font-weight: 700; font-size: 1.3rem; - margin-top: -.2rem; + margin-top: -0.2rem; line-height: 1.3rem; } @@ -158,3 +177,65 @@ max-width: 60px; } } +.badge-achive { + .bagde-section { + color: #000; + margin-top: 28px; + border-bottom: 1px solid #dededf; + } + .bagde-section:last-child { + color: #000; + margin-top: 28px; + border-bottom: none; + } + .badge-section-title { + position: relative; + font-size: 16px; + line-height: 24px; + padding-left: 8px; + margin-bottom: 20px; + display: flex; + justify-content: space-between; + } + .badge-section-children { + width: 100%; + } + .badge-honor-badge { + margin-bottom: 25px; + } + .badge-honor-badge-basic { + display: flex; + align-items: flex-start; + flex-wrap: wrap; + } + .badge-honor-badge-basic-item { + text-align: center; + font-size: 12px; + margin-right: 30px; + color: #101010; + } + .is-not-pointer { + cursor: pointer; + pointer-events: none; + } + .badge-honor-badge-basic-img { + width: 56px; + height: 56px; + margin-bottom: 5px; + } + .badge-honor-badge-basic-txt { + line-height: 20px; + width: 65px; + word-break: break-all; + } + .badge-section-title:before { + content: ""; + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + width: 3px; + height: 1em; + background-color: #000; + } +} From c5feced4f1c877e5885f23b97d69f66debc8b4a6 Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Mon, 17 Oct 2022 15:48:08 +0800 Subject: [PATCH 13/16] fix issue --- templates/user/profile.tmpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index f1328b626..d4e97a961 100755 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -184,6 +184,7 @@ {{.i18n.Tr "user.badge"}} +
{{.TotalBadges}}
@@ -246,5 +247,8 @@ .user.profile .ui.card .extra.content ul { padding: 5px 0; } + .ui.secondary.pointing.menu .item{ + padding: 0.78571429em 0.92857143em; + } \ No newline at end of file From f80265742ca9e0b357c859576d5883cd8cc6bc32 Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Tue, 18 Oct 2022 11:38:33 +0800 Subject: [PATCH 14/16] =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E9=A1=B5=E5=BE=BD?= =?UTF-8?q?=E7=AB=A0=E5=A4=A7=E5=B0=8F=E6=A0=B7=E5=BC=8F=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web_src/less/_user.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web_src/less/_user.less b/web_src/less/_user.less index c2381820c..e07831c25 100644 --- a/web_src/less/_user.less +++ b/web_src/less/_user.less @@ -219,13 +219,13 @@ pointer-events: none; } .badge-honor-badge-basic-img { - width: 56px; - height: 56px; - margin-bottom: 5px; + width: 100px; + height: 100px; + margin-bottom: 10px; } .badge-honor-badge-basic-txt { line-height: 20px; - width: 65px; + width: 100px; word-break: break-all; } .badge-section-title:before { From 2e25fc2d079ea36663cc182496a4cdd5c5964eb9 Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Fri, 21 Oct 2022 15:53:46 +0800 Subject: [PATCH 15/16] fix style css --- web_src/less/_user.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web_src/less/_user.less b/web_src/less/_user.less index e07831c25..29ca96255 100644 --- a/web_src/less/_user.less +++ b/web_src/less/_user.less @@ -227,6 +227,11 @@ line-height: 20px; width: 100px; word-break: break-all; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; } .badge-section-title:before { content: ""; From 363116b80fa2e12c7eeb51e499568e1d204cd991 Mon Sep 17 00:00:00 2001 From: zhoupzh Date: Fri, 21 Oct 2022 16:54:21 +0800 Subject: [PATCH 16/16] fix issue --- templates/org/member/members.tmpl | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 templates/org/member/members.tmpl diff --git a/templates/org/member/members.tmpl b/templates/org/member/members.tmpl new file mode 100644 index 000000000..cd8a691a7 --- /dev/null +++ b/templates/org/member/members.tmpl @@ -0,0 +1,80 @@ +{{template "base/head" .}} +
+ {{template "org/header" .}} +
+ {{template "base/alert" .}} + {{template "org/navber" .}} +
+ +
+ {{ range .Members}} +
+
+ +
+
+ +
{{.FullName}}
+
+
+
+ {{$.i18n.Tr "org.members.membership_visibility"}} +
+
+ {{ $isPublic := index $.MembersIsPublicMember .ID}} + {{if $isPublic}} + {{$.i18n.Tr "org.members.public"}} + {{if or (eq $.SignedUser.ID .ID) $.IsOrganizationOwner}}({{$.i18n.Tr "org.members.public_helper"}}){{end}} + {{else}} + {{$.i18n.Tr "org.members.private"}} + {{if or (eq $.SignedUser.ID .ID) $.IsOrganizationOwner}}({{$.i18n.Tr "org.members.private_helper"}}){{end}} + {{end}} +
+
+
+
+ {{$.i18n.Tr "org.members.member_role"}} +
+
+ {{if index $.MembersIsUserOrgOwner .ID}}{{svg "octicon-shield-lock" 16}} {{$.i18n.Tr "org.members.owner"}}{{else}}{{$.i18n.Tr "org.members.member"}}{{end}} +
+
+
+
+ 2FA +
+
+ + {{if index $.MembersTwoFaStatus .ID}} + {{svg "octicon-check" 16}} + {{else}} + {{svg "octicon-x" 16}} + {{end}} + +
+
+
+
+ {{if eq $.SignedUser.ID .ID}} +
+ {{$.CsrfTokenHtml}} + +
+ {{else if $.IsOrganizationOwner}} +
+ {{$.CsrfTokenHtml}} + +
+ {{end}} +
+
+
+ {{end}} +
+ + {{template "base/paginate" .}} +
+ +
+
+{{template "base/footer" .}} \ No newline at end of file