aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/web.go6
-rw-r--r--gogs.go2
-rw-r--r--models/issue.go176
-rw-r--r--routers/dashboard.go11
-rw-r--r--routers/install.go1
-rw-r--r--routers/repo/issue.go116
-rw-r--r--templates/help.tmpl11
-rw-r--r--templates/issue/list.tmpl4
-rw-r--r--templates/issue/view.tmpl35
9 files changed, 266 insertions, 96 deletions
diff --git a/cmd/web.go b/cmd/web.go
index 60bef9d035..0e8fc09b9e 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -76,7 +76,6 @@ func runWeb(*cli.Context) {
m.Get("/issues", reqSignIn, user.Issues)
m.Get("/pulls", reqSignIn, user.Pulls)
m.Get("/stars", reqSignIn, user.Stars)
- m.Get("/help", routers.Help)
m.Group("/api", func(r martini.Router) {
m.Group("/v1", func(r martini.Router) {
@@ -191,9 +190,12 @@ func runWeb(*cli.Context) {
r.Get("/new", repo.CreateIssue)
r.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost)
r.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
- r.Post("/:index/assignee", repo.UpdateAssignee)
+ r.Post("/:index/label", repo.UpdateIssueLabel)
r.Post("/:index/milestone", repo.UpdateIssueMilestone)
+ r.Post("/:index/assignee", repo.UpdateAssignee)
r.Post("/labels/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
+ r.Post("/labels/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
+ r.Post("/labels/delete", repo.DeleteLabel)
r.Get("/milestones", repo.Milestones)
r.Get("/milestones/new", repo.NewMilestone)
r.Post("/milestones/new", bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
diff --git a/gogs.go b/gogs.go
index 16fd5ec768..45aa9f5931 100644
--- a/gogs.go
+++ b/gogs.go
@@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/base"
)
-const APP_VER = "0.3.5.0521 Alpha"
+const APP_VER = "0.3.5.0523 Alpha"
func init() {
base.AppVer = APP_VER
diff --git a/models/issue.go b/models/issue.go
index 62cc9363b8..3651c46116 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -17,6 +17,7 @@ import (
var (
ErrIssueNotExist = errors.New("Issue does not exist")
+ ErrLabelNotExist = errors.New("Label does not exist")
ErrMilestoneNotExist = errors.New("Milestone does not exist")
)
@@ -28,14 +29,15 @@ type Issue struct {
Name string
Repo *Repository `xorm:"-"`
PosterId int64
- Poster *User `xorm:"-"`
+ Poster *User `xorm:"-"`
+ LabelIds string `xorm:"TEXT"`
+ Labels []*Label `xorm:"-"`
MilestoneId int64
AssigneeId int64
Assignee *User `xorm:"-"`
IsRead bool `xorm:"-"`
IsPull bool // Indicates whether is a pull request or not.
IsClosed bool
- Labels string `xorm:"TEXT"`
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
Priority int
@@ -54,11 +56,37 @@ func (i *Issue) GetPoster() (err error) {
return err
}
+func (i *Issue) GetLabels() error {
+ if len(i.LabelIds) < 3 {
+ return nil
+ }
+
+ strIds := strings.Split(strings.TrimSuffix(i.LabelIds[1:], "|"), "|$")
+ i.Labels = make([]*Label, 0, len(strIds))
+ for _, strId := range strIds {
+ id, _ := base.StrTo(strId).Int64()
+ if id > 0 {
+ l, err := GetLabelById(id)
+ if err != nil {
+ if err == ErrLabelNotExist {
+ continue
+ }
+ return err
+ }
+ i.Labels = append(i.Labels, l)
+ }
+ }
+ return nil
+}
+
func (i *Issue) GetAssignee() (err error) {
if i.AssigneeId == 0 {
return nil
}
i.Assignee, err = GetUserById(i.AssigneeId)
+ if err == ErrUserNotExist {
+ return nil
+ }
return err
}
@@ -108,7 +136,7 @@ func GetIssueById(id int64) (*Issue, error) {
}
// GetIssues returns a list of issues by given conditions.
-func GetIssues(uid, rid, pid, mid int64, page int, isClosed bool, labels, sortType string) ([]Issue, error) {
+func GetIssues(uid, rid, pid, mid int64, page int, isClosed bool, labelIds, sortType string) ([]Issue, error) {
sess := orm.Limit(20, (page-1)*20)
if rid > 0 {
@@ -127,9 +155,9 @@ func GetIssues(uid, rid, pid, mid int64, page int, isClosed bool, labels, sortTy
sess.And("milestone_id=?", mid)
}
- if len(labels) > 0 {
- for _, label := range strings.Split(labels, ",") {
- sess.And("labels like '%$" + label + "|%'")
+ if len(labelIds) > 0 {
+ for _, label := range strings.Split(labelIds, ",") {
+ sess.And("label_ids like '%$" + label + "|%'")
}
}
@@ -155,6 +183,13 @@ func GetIssues(uid, rid, pid, mid int64, page int, isClosed bool, labels, sortTy
return issues, err
}
+// GetIssuesByLabel returns a list of issues by given label and repository.
+func GetIssuesByLabel(repoId int64, label string) ([]*Issue, error) {
+ issues := make([]*Issue, 0, 10)
+ err := orm.Where("repo_id=?", repoId).And("label_ids like '%$" + label + "|%'").Find(&issues)
+ return issues, err
+}
+
// GetIssueCountByPoster returns number of issues of repository by poster.
func GetIssueCountByPoster(uid, rid int64, isClosed bool) int64 {
count, _ := orm.Where("repo_id=?", rid).And("poster_id=?", uid).And("is_closed=?", isClosed).Count(new(Issue))
@@ -175,7 +210,6 @@ type IssueUser struct {
IssueId int64
RepoId int64
MilestoneId int64
- Labels string `xorm:"TEXT"`
IsRead bool
IsAssigned bool
IsMentioned bool
@@ -400,6 +434,98 @@ func UpdateIssueUserPairsByMentions(uids []int64, iid int64) error {
return nil
}
+// .____ ___. .__
+// | | _____ \_ |__ ____ | |
+// | | \__ \ | __ \_/ __ \| |
+// | |___ / __ \| \_\ \ ___/| |__
+// |_______ (____ /___ /\___ >____/
+// \/ \/ \/ \/
+
+// Label represents a label of repository for issues.
+type Label struct {
+ Id int64
+ RepoId int64 `xorm:"INDEX"`
+ Name string
+ Color string `xorm:"VARCHAR(7)"`
+ NumIssues int
+ NumClosedIssues int
+ NumOpenIssues int `xorm:"-"`
+ IsChecked bool `xorm:"-"`
+}
+
+// CalOpenIssues calculates the open issues of label.
+func (m *Label) CalOpenIssues() {
+ m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
+}
+
+// NewLabel creates new label of repository.
+func NewLabel(l *Label) error {
+ _, err := orm.Insert(l)
+ return err
+}
+
+// GetLabelById returns a label by given ID.
+func GetLabelById(id int64) (*Label, error) {
+ l := &Label{Id: id}
+ has, err := orm.Get(l)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, ErrLabelNotExist
+ }
+ return l, nil
+}
+
+// GetLabels returns a list of labels of given repository ID.
+func GetLabels(repoId int64) ([]*Label, error) {
+ labels := make([]*Label, 0, 10)
+ err := orm.Where("repo_id=?", repoId).Find(&labels)
+ return labels, err
+}
+
+// UpdateLabel updates label information.
+func UpdateLabel(l *Label) error {
+ _, err := orm.Id(l.Id).Update(l)
+ return err
+}
+
+// DeleteLabel delete a label of given repository.
+func DeleteLabel(repoId int64, strId string) error {
+ id, _ := base.StrTo(strId).Int64()
+ l, err := GetLabelById(id)
+ if err != nil {
+ if err == ErrLabelNotExist {
+ return nil
+ }
+ return err
+ }
+
+ issues, err := GetIssuesByLabel(repoId, strId)
+ if err != nil {
+ return err
+ }
+
+ sess := orm.NewSession()
+ defer sess.Close()
+ if err = sess.Begin(); err != nil {
+ return err
+ }
+
+ for _, issue := range issues {
+ issue.LabelIds = strings.Replace(issue.LabelIds, "$"+strId+"|", "", -1)
+ if _, err = sess.Id(issue.Id).AllCols().Update(issue); err != nil {
+ sess.Rollback()
+ return err
+ }
+ }
+
+ if _, err = sess.Delete(l); err != nil {
+ sess.Rollback()
+ return err
+ }
+ return sess.Commit()
+}
+
// _____ .__.__ __
// / \ |__| | ____ _______/ |_ ____ ____ ____
// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
@@ -611,42 +737,6 @@ func DeleteMilestone(m *Milestone) (err error) {
return sess.Commit()
}
-// .____ ___. .__
-// | | _____ \_ |__ ____ | |
-// | | \__ \ | __ \_/ __ \| |
-// | |___ / __ \| \_\ \ ___/| |__
-// |_______ (____ /___ /\___ >____/
-// \/ \/ \/ \/
-
-// Label represents a label of repository for issues.
-type Label struct {
- Id int64
- RepoId int64 `xorm:"INDEX"`
- Name string
- Color string `xorm:"VARCHAR(7)"`
- NumIssues int
- NumClosedIssues int
- NumOpenIssues int `xorm:"-"`
-}
-
-// CalOpenIssues calculates the open issues of label.
-func (m *Label) CalOpenIssues() {
- m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
-}
-
-// NewLabel creates new label of repository.
-func NewLabel(l *Label) error {
- _, err := orm.Insert(l)
- return err
-}
-
-// GetLabels returns a list of labels of given repository ID.
-func GetLabels(repoId int64) ([]*Label, error) {
- labels := make([]*Label, 0, 10)
- err := orm.Where("repo_id=?", repoId).Find(&labels)
- return labels, err
-}
-
// _________ __
// \_ ___ \ ____ _____ _____ ____ _____/ |_
// / \ \/ / _ \ / \ / \_/ __ \ / \ __\
diff --git a/routers/dashboard.go b/routers/dashboard.go
index 78533127fd..6387089e54 100644
--- a/routers/dashboard.go
+++ b/routers/dashboard.go
@@ -32,9 +32,8 @@ func Home(ctx *middleware.Context) {
}
for _, repo := range repos {
- repo.Owner, err = models.GetUserById(repo.OwnerId)
- if err != nil {
- ctx.Handle(500, "dashboard.Home(GetUserById)", err)
+ if err = repo.GetOwner(); err != nil {
+ ctx.Handle(500, "dashboard.Home(GetOwner)", err)
return
}
}
@@ -43,12 +42,6 @@ func Home(ctx *middleware.Context) {
ctx.HTML(200, "home")
}
-func Help(ctx *middleware.Context) {
- ctx.Data["PageIsHelp"] = true
- ctx.Data["Title"] = "Help"
- ctx.HTML(200, "help")
-}
-
func NotFound(ctx *middleware.Context) {
ctx.Data["PageIsNotFound"] = true
ctx.Data["Title"] = "Page Not Found"
diff --git a/routers/install.go b/routers/install.go
index 53ce90d5b6..da00b90e37 100644
--- a/routers/install.go
+++ b/routers/install.go
@@ -25,7 +25,6 @@ import (
"github.com/gogits/gogs/modules/social"
)
-// Check run mode(Default of martini is Dev).
func checkRunMode() {
switch base.Cfg.MustValue("", "RUN_MODE") {
case "prod":
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index 8852dd08f9..0c0b7348f4 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -197,7 +197,7 @@ func CreateIssuePost(ctx *middleware.Context, params martini.Params, form auth.C
PosterId: ctx.User.Id,
MilestoneId: form.MilestoneId,
AssigneeId: form.AssigneeId,
- Labels: form.Labels,
+ LabelIds: form.Labels,
Content: form.Content,
}
if err := models.NewIssue(issue); err != nil {
@@ -269,6 +269,17 @@ func CreateIssuePost(ctx *middleware.Context, params martini.Params, form auth.C
ctx.Redirect(fmt.Sprintf("/%s/%s/issues/%d", params["username"], params["reponame"], issue.Index))
}
+func checkLabels(labels, allLabels []*models.Label) {
+ for _, l := range labels {
+ for _, l2 := range allLabels {
+ if l.Id == l2.Id {
+ l2.IsChecked = true
+ break
+ }
+ }
+ }
+}
+
func ViewIssue(ctx *middleware.Context, params martini.Params) {
idx, _ := base.StrTo(params["index"]).Int64()
if idx == 0 {
@@ -286,6 +297,19 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
return
}
+ // Get labels.
+ if err = issue.GetLabels(); err != nil {
+ ctx.Handle(500, "issue.ViewIssue(GetLabels)", err)
+ return
+ }
+ labels, err := models.GetLabels(ctx.Repo.Repository.Id)
+ if err != nil {
+ ctx.Handle(500, "issue.ViewIssue(GetLabels.2)", err)
+ return
+ }
+ checkLabels(issue.Labels, labels)
+ ctx.Data["Labels"] = labels
+
// Get assigned milestone.
if issue.MilestoneId > 0 {
ctx.Data["Milestone"], err = models.GetMilestoneById(issue.MilestoneId)
@@ -364,13 +388,13 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
}
func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) {
- idx, err := base.StrTo(params["index"]).Int()
- if err != nil {
+ idx, _ := base.StrTo(params["index"]).Int64()
+ if idx <= 0 {
ctx.Error(404)
return
}
- issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, int64(idx))
+ issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, idx)
if err != nil {
if err == models.ErrIssueNotExist {
ctx.Handle(404, "issue.UpdateIssue", err)
@@ -381,14 +405,14 @@ func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat
}
if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner {
- ctx.Handle(404, "issue.UpdateIssue", nil)
+ ctx.Error(403)
return
}
issue.Name = form.IssueName
issue.MilestoneId = form.MilestoneId
issue.AssigneeId = form.AssigneeId
- issue.Labels = form.Labels
+ issue.LabelIds = form.Labels
issue.Content = form.Content
// try get content from text, ignore conflict with preview ajax
if form.Content == "" {
@@ -406,6 +430,55 @@ func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat
})
}
+func UpdateIssueLabel(ctx *middleware.Context, params martini.Params) {
+ if !ctx.Repo.IsOwner {
+ ctx.Error(403)
+ return
+ }
+
+ idx, _ := base.StrTo(params["index"]).Int64()
+ if idx <= 0 {
+ ctx.Error(404)
+ return
+ }
+
+ issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, idx)
+ if err != nil {
+ if err == models.ErrIssueNotExist {
+ ctx.Handle(404, "issue.UpdateIssueLabel", err)
+ } else {
+ ctx.Handle(500, "issue.UpdateIssueLabel(GetIssueByIndex)", err)
+ }
+ return
+ }
+
+ isAttach := ctx.Query("action") == "attach"
+ labelStrId := ctx.Query("id")
+ isHad := strings.Contains(issue.LabelIds, "$"+labelStrId+"|")
+ isNeedUpdate := false
+ if isAttach {
+ if !isHad {
+ issue.LabelIds += "$" + labelStrId + "|"
+ isNeedUpdate = true
+ }
+ } else {
+ if isHad {
+ issue.LabelIds = strings.Replace(issue.LabelIds, "$"+labelStrId+"|", "", -1)
+ isNeedUpdate = true
+ }
+ }
+
+ if isNeedUpdate {
+ if err = models.UpdateIssue(issue); err != nil {
+ ctx.Handle(500, "issue.UpdateIssueLabel(UpdateIssue)", err)
+ return
+ }
+ }
+ ctx.JSON(200, map[string]interface{}{
+ "ok": true,
+ })
+}
+
func UpdateIssueMilestone(ctx *middleware.Context) {
if !ctx.Repo.IsOwner {
ctx.Error(403)
@@ -622,8 +695,37 @@ func NewLabel(ctx *middleware.Context, form auth.CreateLabelForm) {
ctx.Redirect(ctx.Repo.RepoLink + "/issues")
}
-func UpdateLabel(ctx *middleware.Context, params martini.Params) {
+func UpdateLabel(ctx *middleware.Context, params martini.Params, form auth.CreateLabelForm) {
+ id, _ := base.StrTo(ctx.Query("id")).Int64()
+ if id == 0 {
+ ctx.Error(404)
+ return
+ }
+
+ l := &models.Label{
+ Id: id,
+ Name: form.Title,
+ Color: form.Color,
+ }
+ if err := models.UpdateLabel(l); err != nil {
+ ctx.Handle(500, "issue.UpdateLabel(UpdateLabel)", err)
+ return
+ }
+ ctx.Redirect(ctx.Repo.RepoLink + "/issues")
+}
+
+func DeleteLabel(ctx *middleware.Context) {
+ strIds := strings.Split(ctx.Query("remove"), ",")
+ for _, strId := range strIds {
+ if err := models.DeleteLabel(ctx.Repo.Repository.Id, strId); err != nil {
+ ctx.Handle(500, "issue.DeleteLabel(DeleteLabel)", err)
+ return
+ }
+ }
+ ctx.JSON(200, map[string]interface{}{
+ "ok": true,
+ })
}
func Milestones(ctx *middleware.Context) {
diff --git a/templates/help.tmpl b/templates/help.tmpl
deleted file mode 100644
index e835c2981e..0000000000
--- a/templates/help.tmpl
+++ /dev/null
@@ -1,11 +0,0 @@
-{{template "base/head" .}}
-{{template "base/navbar" .}}
-<div id="body-nav">
- <div class="container">
- <h3>Help</h3>
- </div>
-</div>
-<div id="body" class="container" data-page="user">
- {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}}
-</div>
-{{template "base/footer" .}} \ No newline at end of file
diff --git a/templates/issue/list.tmpl b/templates/issue/list.tmpl
index edf855932d..2853312394 100644
--- a/templates/issue/list.tmpl
+++ b/templates/issue/list.tmpl
@@ -15,7 +15,7 @@
</div>
<div class="label-filter">
<h4>Label</h4>
- <ul class="list-unstyled" id="label-list" data-ajax="/{url}">
+ <ul class="list-unstyled" id="label-list" data-ajax="{{$.RepoLink}}/issues/labels/delete">
{{range .Labels}}
<li class="label-item" id="label-{{.Id}}" data-id="{{.Id}}"><a href="#">
<span class="pull-right count">{{if $.IsShowClosed}}{{.NumClosedIssues}}{{else}}{{.NumOpenIssues}}{{end}}</span>
@@ -60,7 +60,7 @@
{{template "base/alert" .}}
<div class="filter-option">
<div class="btn-group">
- <a class="btn btn-default issue-open{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/issues?type={{.ViewType}}">{{..IssueStats.OpenCount}} Open</a>
+ <a class="btn btn-default issue-open{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/issues?type={{.ViewType}}">{{.IssueStats.OpenCount}} Open</a>
<a class="btn btn-default issue-close{{if .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/issues?type={{.ViewType}}&state=closed">{{.IssueStats.ClosedCount}} Closed</a>
</div>
</div>
diff --git a/templates/issue/view.tmpl b/templates/issue/view.tmpl
index ea5777cb15..ba1fe16b79 100644
--- a/templates/issue/view.tmpl
+++ b/templates/issue/view.tmpl
@@ -118,7 +118,7 @@
</div>
<div class="issue-bar col-md-2">
- <div class="labels" data-ajax="{url}">
+ <div class="labels" data-ajax="{{.Issue.Index}}/label">
<div class="pull-right action">
<button class="btn btn-default btn-sm" data-toggle="dropdown">
<i class="fa fa-tags"></i>
@@ -126,26 +126,24 @@
</button>
<div class="dropdown-menu dropdown-menu-right no">
<ul class="list-unstyled">
- <li class="checked" data-id="1">
- <span class="check pull-left"><i class="fa fa-check"></i></span>
- <span class="color" style="background-color: #f59e00"></span>
- <span class="name">bug</span>
- </li>
- <li class="no-checked" data-id="2">
- <span class="color" style="background-color: #f59e00"></span>
- <span class="name">bug</span>
- </li>
- <li class="no-checked" data-id="3">
- <span class="color" style="background-color: #f59e00"></span>
- <span class="name">bug</span>
+ {{range .Labels}}
+ <li class="{{if not .IsChecked}}no-{{end}}checked" data-id="{{.Id}}">
+ {{if .IsChecked}}<span class="check pull-left"><i class="fa fa-check"></i></span>{{end}}
+ <span class="color" style="background-color: {{.Color}}"></span>
+ <span class="name">{{.Name}}</span>
</li>
+ {{end}}
</ul>
</div>
</div>
<h4>Labels</h4>
- <p id="label-1" class="label-item label-white" style="background-color: #e75316"><strong>bug</strong></p>
- <p id="label-2" class="label-item label-white" style="background-color: #e8f0ff"><strong>bug</strong></p>
- <p>Not yet</p>
+ {{if .Issue.Labels}}
+ {{range .Issue.Labels}}
+ <p id="label-{{.Id}}" class="label-item label-white" style="background-color: {{.Color}}"><strong>{{.Name}}</strong></p>
+ {{end}}
+ {{else}}
+ <p>None yet</p>
+ {{end}}
</div>
<div class="milestone" data-milestone="{{.Milestone.Id}}" data-ajax="{{.Issue.Index}}/milestone">
<div class="pull-right action">
@@ -223,10 +221,7 @@
<h4>Assignee</h4>
<p>{{if .Issue.Assignee}}<img src="{{.Issue.Assignee.AvatarLink}}"><strong>{{.Issue.Assignee.Name}}</strong>{{else}}No one assigned{{end}}</p>
</div>
- </div><!--
- <div class="col-md-3">
- label dashboard
- </div>-->
+ </div>
</div>
</div>
</div>