Skip to content

Commit 42096c6

Browse files
committed
Added topics validation, fixed repo topics duplication (go-gitea#4031)
Signed-off-by: Alexey Terentyev <[email protected]>
1 parent 85414d8 commit 42096c6

File tree

4 files changed

+54
-2
lines changed

4 files changed

+54
-2
lines changed

models/topic.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"code.gitea.io/gitea/modules/util"
1212

1313
"github.com/go-xorm/builder"
14+
"regexp"
1415
)
1516

1617
func init() {
@@ -20,6 +21,8 @@ func init() {
2021
)
2122
}
2223

24+
var topicPattern = regexp.MustCompile(`^[a-z0-9+#_.-]+$`)
25+
2326
// Topic represents a topic of repositories
2427
type Topic struct {
2528
ID int64
@@ -51,6 +54,26 @@ func (err ErrTopicNotExist) Error() string {
5154
return fmt.Sprintf("topic is not exist [name: %s]", err.Name)
5255
}
5356

57+
func TopicValidator(topic string) bool {
58+
return len(topic) <= 35 && topicPattern.MatchString(topic)
59+
}
60+
61+
// Remove duplicates from topics slice
62+
func RemoveDuplicateTopics(topics []string) []string {
63+
// Map to record duplicates
64+
saved := make(map[string]struct{}, len(topics))
65+
i := 0
66+
for _, v := range topics {
67+
v = strings.TrimSpace(strings.ToLower(v))
68+
if _, ok := saved[v]; !ok {
69+
saved[v] = struct{}{}
70+
topics[i] = v
71+
i++
72+
}
73+
}
74+
return topics[:i]
75+
}
76+
5477
// GetTopicByName retrieves topic by name
5578
func GetTopicByName(name string) (*Topic, error) {
5679
var topic Topic

options/locale/locale_en-US.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,8 @@ branch.protected_deletion_failed = Branch '%s' is protected. It cannot be delete
11671167

11681168
topic.manage_topics = Manage Topics
11691169
topic.done = Done
1170+
topic.count_prompt = You can't select more than 25 topics
1171+
topic.format_prompt = Topics must use letter or number and can include hyphen(-), underscore(_), plus(+), hash(#), dot(.) with max length of 35
11701172
11711173
[org]
11721174
org_name_holder = Organization Name

routers/repo/topic.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
)
1414

1515
// TopicPost response for creating repository
16-
func TopicPost(ctx *context.Context) {
16+
func TopicsPost(ctx *context.Context) {
1717
if ctx.User == nil {
1818
ctx.JSON(403, map[string]interface{}{
1919
"message": "Only owners could change the topics.",
@@ -27,6 +27,33 @@ func TopicPost(ctx *context.Context) {
2727
topics = strings.Split(topicsStr, ",")
2828
}
2929

30+
topics = models.RemoveDuplicateTopics(topics)
31+
32+
if len(topics) > 25 {
33+
log.Error(2, "Incorrect number of topics(max 25): %v", )
34+
ctx.JSON(422, map[string]interface{}{
35+
"invalidTopics": topics[:0],
36+
"message": ctx.Tr("repo.topic.count_error"),
37+
})
38+
return
39+
}
40+
41+
var invalidTopics = make([]string, 0)
42+
for _, topic := range topics {
43+
if !models.TopicValidator(topic) {
44+
invalidTopics = append(invalidTopics, topic)
45+
}
46+
}
47+
48+
if len(invalidTopics) > 0 {
49+
log.Error(2, "Invalid topics: %v", invalidTopics)
50+
ctx.JSON(422, map[string]interface{}{
51+
"invalidTopics": invalidTopics,
52+
"message": ctx.Tr("repo.topic.pattern_error"),
53+
})
54+
return
55+
}
56+
3057
err := models.SaveTopics(ctx.Repo.Repository.ID, topics...)
3158
if err != nil {
3259
log.Error(2, "SaveTopics failed: %v", err)

routers/routes/routes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ func RegisterRoutes(m *macaron.Macaron) {
626626
}, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeReleases))
627627

628628
m.Group("/:username/:reponame", func() {
629-
m.Post("/topics", repo.TopicPost)
629+
m.Post("/topics", repo.TopicsPost)
630630
}, context.RepoAssignment(), reqRepoAdmin)
631631

632632
m.Group("/:username/:reponame", func() {

0 commit comments

Comments
 (0)