| @@ -3,7 +3,7 @@ Gogs - Go Git Service [ | |||
| ##### Current version: 0.9.8 | |||
| ##### Current version: 0.9.9 | |||
| | Web | UI | Preview | | |||
| |:-------------:|:-------:|:-------:| | |||
| @@ -88,7 +88,7 @@ func checkVersion() { | |||
| {"gopkg.in/ini.v1", ini.Version, "1.8.4"}, | |||
| {"gopkg.in/macaron.v1", macaron.Version, "1.1.2"}, | |||
| {"github.com/gogits/git-module", git.Version, "0.2.9"}, | |||
| {"github.com/gogits/go-gogs-client", gogs.Version, "0.7.3"}, | |||
| {"github.com/gogits/go-gogs-client", gogs.Version, "0.7.4"}, | |||
| } | |||
| for _, c := range checkers { | |||
| if !version.Compare(c.Version(), c.Expected, ">=") { | |||
| @@ -35,7 +35,7 @@ imports: | |||
| - name: github.com/go-xorm/core | |||
| version: 502158401cde814951eae62f064d9e5ff39e13ce | |||
| - name: github.com/go-xorm/xorm | |||
| version: bb8c5c04d7d65453c3f0176a307276857dccc3b0 | |||
| version: 99686972253975273816d1ebb4cdb0a53bf39100 | |||
| - name: github.com/gogits/chardet | |||
| version: 2404f777256163ea3eadb273dada5dcb037993c0 | |||
| - name: github.com/gogits/cron | |||
| @@ -43,7 +43,7 @@ imports: | |||
| - name: github.com/gogits/git-module | |||
| version: 76e8cce6c7ef3ba1cf75752261c721ebf14cd129 | |||
| - name: github.com/gogits/go-gogs-client | |||
| version: d584b1e0fb4d8ad0e8cf2fae2368838f2526b408 | |||
| version: 788ec59749df076b98e208909b44fdef02779deb | |||
| - name: github.com/issue9/identicon | |||
| version: f8c0d2ce04db79c663b1da33d3a9f62be753ee88 | |||
| - name: github.com/klauspost/compress | |||
| @@ -17,7 +17,7 @@ import ( | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| const APP_VER = "0.9.8.0312" | |||
| const APP_VER = "0.9.9.0313" | |||
| func init() { | |||
| runtime.GOMAXPROCS(runtime.NumCPU()) | |||
| @@ -88,6 +88,23 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) { | |||
| log.Error(3, "GetCommentsByIssueID[%d]: %v", i.ID, err) | |||
| } | |||
| i.Labels, err = GetLabelsByIssueID(i.ID) | |||
| if err != nil { | |||
| log.Error(3, "GetLabelsByIssueID[%d]: %v", i.ID, err) | |||
| } | |||
| case "poster_id": | |||
| i.Poster, err = GetUserByID(i.PosterID) | |||
| if err != nil { | |||
| if IsErrUserNotExist(err) { | |||
| i.PosterID = -1 | |||
| i.Poster = NewFakeUser() | |||
| } else { | |||
| log.Error(3, "GetUserByID[%d]: %v", i.ID, err) | |||
| } | |||
| return | |||
| } | |||
| case "milestone_id": | |||
| if i.MilestoneID == 0 { | |||
| return | |||
| @@ -97,6 +114,7 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) { | |||
| if err != nil { | |||
| log.Error(3, "GetMilestoneById[%d]: %v", i.ID, err) | |||
| } | |||
| case "assignee_id": | |||
| if i.AssigneeID == 0 { | |||
| return | |||
| @@ -106,6 +124,7 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) { | |||
| if err != nil { | |||
| log.Error(3, "GetUserByID[%d]: %v", i.ID, err) | |||
| } | |||
| case "deadline_unix": | |||
| i.Deadline = time.Unix(i.DeadlineUnix, 0).Local() | |||
| case "created_unix": | |||
| @@ -120,21 +139,19 @@ func (i *Issue) HashTag() string { | |||
| return "issue-" + com.ToStr(i.ID) | |||
| } | |||
| // State returns string representation of issue status. | |||
| func (i *Issue) State() string { | |||
| if i.IsClosed { | |||
| return "closed" | |||
| } | |||
| return "open" | |||
| } | |||
| // IsPoster returns true if given user by ID is the poster. | |||
| func (i *Issue) IsPoster(uid int64) bool { | |||
| return i.PosterID == uid | |||
| } | |||
| func (i *Issue) GetPoster() (err error) { | |||
| i.Poster, err = GetUserByID(i.PosterID) | |||
| if IsErrUserNotExist(err) { | |||
| i.PosterID = -1 | |||
| i.Poster = NewFakeUser() | |||
| return nil | |||
| } | |||
| return err | |||
| } | |||
| func (i *Issue) hasLabel(e Engine, labelID int64) bool { | |||
| return hasIssueLabel(e, i.ID, labelID) | |||
| } | |||
| @@ -175,11 +192,6 @@ func (i *Issue) getLabels(e Engine) (err error) { | |||
| return nil | |||
| } | |||
| // GetLabels retrieves all labels of issue and assign to corresponding field. | |||
| func (i *Issue) GetLabels() error { | |||
| return i.getLabels(x) | |||
| } | |||
| func (i *Issue) removeLabel(e *xorm.Session, label *Label) error { | |||
| return deleteIssueLabel(e, i, label) | |||
| } | |||
| @@ -303,6 +315,19 @@ func (i *Issue) GetPullRequest() (err error) { | |||
| // It's caller's responsibility to create action. | |||
| func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64, uuids []string, isPull bool) (err error) { | |||
| issue.Name = strings.TrimSpace(issue.Name) | |||
| issue.Index = repo.NextIssueIndex() | |||
| if issue.AssigneeID > 0 { | |||
| // Silently drop invalid assignee | |||
| valid, err := hasAccess(e, &User{Id: issue.AssigneeID}, repo, ACCESS_MODE_WRITE) | |||
| if err != nil { | |||
| return fmt.Errorf("hasAccess: %v", err) | |||
| } else if !valid { | |||
| issue.AssigneeID = 0 | |||
| } | |||
| } | |||
| if _, err = e.Insert(issue); err != nil { | |||
| return err | |||
| } | |||
| @@ -325,6 +350,10 @@ func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64, | |||
| } | |||
| for _, label := range labels { | |||
| if label.RepoID != repo.ID { | |||
| continue | |||
| } | |||
| if err = issue.addLabel(e, label); err != nil { | |||
| return fmt.Errorf("addLabel: %v", err) | |||
| } | |||
| @@ -377,6 +406,10 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) | |||
| return fmt.Errorf("newIssue: %v", err) | |||
| } | |||
| if err = sess.Commit(); err != nil { | |||
| return fmt.Errorf("Commit: %v", err) | |||
| } | |||
| // Notify watchers. | |||
| act := &Action{ | |||
| ActUserID: issue.Poster.Id, | |||
| @@ -389,11 +422,11 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) | |||
| RepoName: repo.Name, | |||
| IsPrivate: repo.IsPrivate, | |||
| } | |||
| if err = notifyWatchers(sess, act); err != nil { | |||
| return err | |||
| if err = NotifyWatchers(act); err != nil { | |||
| log.Error(4, "notifyWatchers: %v", err) | |||
| } | |||
| return sess.Commit() | |||
| return nil | |||
| } | |||
| // GetIssueByRef returns an Issue specified by a GFM reference. | |||
| @@ -467,6 +500,10 @@ type IssuesOptions struct { | |||
| // Issues returns a list of issues by given conditions. | |||
| func Issues(opts *IssuesOptions) ([]*Issue, error) { | |||
| if opts.Page <= 0 { | |||
| opts.Page = 1 | |||
| } | |||
| sess := x.Limit(setting.IssuePagingNum, (opts.Page-1)*setting.IssuePagingNum) | |||
| if opts.RepoID > 0 { | |||
| @@ -997,6 +1034,9 @@ func (m *Milestone) BeforeUpdate() { | |||
| func (m *Milestone) AfterSet(colName string, _ xorm.Cell) { | |||
| switch colName { | |||
| case "num_closed_issues": | |||
| m.NumOpenIssues = m.NumIssues - m.NumClosedIssues | |||
| case "deadline_unix": | |||
| m.Deadline = time.Unix(m.DeadlineUnix, 0).Local() | |||
| if m.Deadline.Year() == 9999 { | |||
| @@ -1007,14 +1047,18 @@ func (m *Milestone) AfterSet(colName string, _ xorm.Cell) { | |||
| if time.Now().Local().After(m.Deadline) { | |||
| m.IsOverDue = true | |||
| } | |||
| case "closed_date_unix": | |||
| m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local() | |||
| } | |||
| } | |||
| // CalOpenIssues calculates the open issues of milestone. | |||
| func (m *Milestone) CalOpenIssues() { | |||
| m.NumOpenIssues = m.NumIssues - m.NumClosedIssues | |||
| // State returns string representation of milestone status. | |||
| func (m *Milestone) State() string { | |||
| if m.IsClosed { | |||
| return "closed" | |||
| } | |||
| return "open" | |||
| } | |||
| // NewMilestone creates new milestone of repository. | |||
| @@ -5,10 +5,15 @@ | |||
| package context | |||
| import ( | |||
| "fmt" | |||
| "strings" | |||
| "github.com/Unknwon/paginater" | |||
| "gopkg.in/macaron.v1" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| type APIContext struct { | |||
| @@ -35,6 +40,27 @@ func (ctx *APIContext) Error(status int, title string, obj interface{}) { | |||
| }) | |||
| } | |||
| func (ctx *APIContext) SetLinkHeader(total, pageSize int) { | |||
| page := paginater.New(total, pageSize, ctx.QueryInt("page"), 0) | |||
| links := make([]string, 0, 4) | |||
| if page.HasNext() { | |||
| links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"next\"", setting.AppUrl, ctx.Req.URL.Path[1:], page.Next())) | |||
| } | |||
| if !page.IsLast() { | |||
| links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"last\"", setting.AppUrl, ctx.Req.URL.Path[1:], page.TotalPages())) | |||
| } | |||
| if !page.IsFirst() { | |||
| links = append(links, fmt.Sprintf("<%s%s?page=1>; rel=\"first\"", setting.AppUrl, ctx.Req.URL.Path[1:])) | |||
| } | |||
| if page.HasPrevious() { | |||
| links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"prev\"", setting.AppUrl, ctx.Req.URL.Path[1:], page.Previous())) | |||
| } | |||
| if len(links) > 0 { | |||
| ctx.Header().Set("Link", strings.Join(links, ",")) | |||
| } | |||
| } | |||
| func APIContexter() macaron.Handler { | |||
| return func(c *Context) { | |||
| ctx := &APIContext{ | |||
| @@ -40,5 +40,5 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) { | |||
| return | |||
| } | |||
| ctx.JSON(201, convert.ToApiOrganization(org)) | |||
| ctx.JSON(201, convert.ToOrganization(org)) | |||
| } | |||
| @@ -69,7 +69,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) { | |||
| mailer.SendRegisterNotifyMail(ctx.Context.Context, u) | |||
| } | |||
| ctx.JSON(201, convert.ToApiUser(u)) | |||
| ctx.JSON(201, convert.ToUser(u)) | |||
| } | |||
| // https://github.com/gogits/go-gogs-client/wiki/Administration-Users#edit-an-existing-user | |||
| @@ -118,7 +118,7 @@ func EditUser(ctx *context.APIContext, form api.EditUserOption) { | |||
| } | |||
| log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name) | |||
| ctx.JSON(200, convert.ToApiUser(u)) | |||
| ctx.JSON(200, convert.ToUser(u)) | |||
| } | |||
| // https://github.com/gogits/go-gogs-client/wiki/Administration-Users#delete-a-user | |||
| @@ -62,16 +62,18 @@ func RepoAssignment() macaron.Handler { | |||
| return | |||
| } | |||
| mode, err := models.AccessLevel(ctx.User, repo) | |||
| if err != nil { | |||
| ctx.Error(500, "AccessLevel", err) | |||
| return | |||
| if ctx.IsSigned && ctx.User.IsAdmin { | |||
| ctx.Repo.AccessMode = models.ACCESS_MODE_OWNER | |||
| } else { | |||
| mode, err := models.AccessLevel(ctx.User, repo) | |||
| if err != nil { | |||
| ctx.Error(500, "AccessLevel", err) | |||
| return | |||
| } | |||
| ctx.Repo.AccessMode = mode | |||
| } | |||
| ctx.Repo.AccessMode = mode | |||
| // Check access. | |||
| if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE { | |||
| if !ctx.Repo.HasAccess() { | |||
| ctx.Status(404) | |||
| return | |||
| } | |||
| @@ -193,9 +195,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Combo("/:id").Get(repo.GetDeployKey). | |||
| Delete(repo.DeleteDeploykey) | |||
| }) | |||
| m.Group("/issue", func() { | |||
| m.Combo("").Get().Post() | |||
| m.Combo("/:index").Get().Patch() | |||
| m.Group("/issues", func() { | |||
| m.Combo("").Get(repo.ListIssues).Post(bind(api.CreateIssueOption{}), repo.CreateIssue) | |||
| m.Combo("/:index").Get(repo.GetIssue).Patch(bind(api.EditIssueOption{}), repo.EditIssue) | |||
| }) | |||
| }, RepoAssignment()) | |||
| }, ReqToken()) | |||
| @@ -9,15 +9,19 @@ import ( | |||
| "github.com/Unknwon/com" | |||
| api "github.com/gogits/go-gogs-client" | |||
| "github.com/gogits/git-module" | |||
| api "github.com/gogits/go-gogs-client" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| // ToApiUser converts user to its API format. | |||
| func ToApiUser(u *models.User) *api.User { | |||
| func ToUser(u *models.User) *api.User { | |||
| if u == nil { | |||
| return nil | |||
| } | |||
| return &api.User{ | |||
| ID: u.Id, | |||
| UserName: u.Name, | |||
| @@ -27,7 +31,7 @@ func ToApiUser(u *models.User) *api.User { | |||
| } | |||
| } | |||
| func ToApiEmail(email *models.EmailAddress) *api.Email { | |||
| func ToEmail(email *models.EmailAddress) *api.Email { | |||
| return &api.Email{ | |||
| Email: email.Email, | |||
| Verified: email.IsActivated, | |||
| @@ -35,12 +39,11 @@ func ToApiEmail(email *models.EmailAddress) *api.Email { | |||
| } | |||
| } | |||
| // ToApiRepository converts repository to API format. | |||
| func ToApiRepository(owner *models.User, repo *models.Repository, permission api.Permission) *api.Repository { | |||
| func ToRepository(owner *models.User, repo *models.Repository, permission api.Permission) *api.Repository { | |||
| cl := repo.CloneLink() | |||
| return &api.Repository{ | |||
| Id: repo.ID, | |||
| Owner: *ToApiUser(owner), | |||
| ID: repo.ID, | |||
| Owner: ToUser(owner), | |||
| FullName: owner.Name + "/" + repo.Name, | |||
| Private: repo.IsPrivate, | |||
| Fork: repo.IsFork, | |||
| @@ -51,30 +54,27 @@ func ToApiRepository(owner *models.User, repo *models.Repository, permission api | |||
| } | |||
| } | |||
| // ToApiBranch converts user to its API format. | |||
| func ToApiBranch(b *models.Branch,c *git.Commit) *api.Branch { | |||
| func ToBranch(b *models.Branch, c *git.Commit) *api.Branch { | |||
| return &api.Branch{ | |||
| Name: b.Name, | |||
| Commit: ToApiCommit(c), | |||
| } | |||
| Name: b.Name, | |||
| Commit: ToCommit(c), | |||
| } | |||
| } | |||
| // ToApiCommit converts user to its API format. | |||
| func ToApiCommit(c *git.Commit) *api.PayloadCommit { | |||
| func ToCommit(c *git.Commit) *api.PayloadCommit { | |||
| return &api.PayloadCommit{ | |||
| ID: c.ID.String(), | |||
| ID: c.ID.String(), | |||
| Message: c.Message(), | |||
| URL: "Not implemented", | |||
| URL: "Not implemented", | |||
| Author: &api.PayloadAuthor{ | |||
| Name: c.Committer.Name, | |||
| Name: c.Committer.Name, | |||
| Email: c.Committer.Email, | |||
| /* UserName: c.Committer.UserName, */ | |||
| }, | |||
| } | |||
| } | |||
| // ToApiPublicKey converts public key to its API format. | |||
| func ToApiPublicKey(apiLink string, key *models.PublicKey) *api.PublicKey { | |||
| func ToPublicKey(apiLink string, key *models.PublicKey) *api.PublicKey { | |||
| return &api.PublicKey{ | |||
| ID: key.ID, | |||
| Key: key.Content, | |||
| @@ -84,8 +84,7 @@ func ToApiPublicKey(apiLink string, key *models.PublicKey) *api.PublicKey { | |||
| } | |||
| } | |||
| // ToApiHook converts webhook to its API format. | |||
| func ToApiHook(repoLink string, w *models.Webhook) *api.Hook { | |||
| func ToHook(repoLink string, w *models.Webhook) *api.Hook { | |||
| config := map[string]string{ | |||
| "url": w.URL, | |||
| "content_type": w.ContentType.Name(), | |||
| @@ -110,8 +109,7 @@ func ToApiHook(repoLink string, w *models.Webhook) *api.Hook { | |||
| } | |||
| } | |||
| // ToApiDeployKey converts deploy key to its API format. | |||
| func ToApiDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey { | |||
| func ToDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey { | |||
| return &api.DeployKey{ | |||
| ID: key.ID, | |||
| Key: key.Content, | |||
| @@ -122,7 +120,72 @@ func ToApiDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey { | |||
| } | |||
| } | |||
| func ToApiOrganization(org *models.User) *api.Organization { | |||
| func ToLabel(label *models.Label) *api.Label { | |||
| return &api.Label{ | |||
| Name: label.Name, | |||
| Color: label.Color, | |||
| } | |||
| } | |||
| func ToMilestone(milestone *models.Milestone) *api.Milestone { | |||
| if milestone == nil { | |||
| return nil | |||
| } | |||
| apiMilestone := &api.Milestone{ | |||
| ID: milestone.ID, | |||
| State: milestone.State(), | |||
| Title: milestone.Name, | |||
| Description: milestone.Content, | |||
| OpenIssues: milestone.NumOpenIssues, | |||
| ClosedIssues: milestone.NumClosedIssues, | |||
| } | |||
| if milestone.IsClosed { | |||
| apiMilestone.Closed = &milestone.ClosedDate | |||
| } | |||
| if milestone.Deadline.Year() < 9999 { | |||
| apiMilestone.Deadline = &milestone.Deadline | |||
| } | |||
| return apiMilestone | |||
| } | |||
| func ToIssue(issue *models.Issue) *api.Issue { | |||
| apiLabels := make([]*api.Label, len(issue.Labels)) | |||
| for i := range issue.Labels { | |||
| apiLabels[i] = ToLabel(issue.Labels[i]) | |||
| } | |||
| apiIssue := &api.Issue{ | |||
| ID: issue.ID, | |||
| Index: issue.Index, | |||
| State: issue.State(), | |||
| Title: issue.Name, | |||
| Body: issue.Content, | |||
| User: ToUser(issue.Poster), | |||
| Labels: apiLabels, | |||
| Assignee: ToUser(issue.Assignee), | |||
| Milestone: ToMilestone(issue.Milestone), | |||
| Comments: issue.NumComments, | |||
| Created: issue.Created, | |||
| Updated: issue.Updated, | |||
| } | |||
| if issue.IsPull { | |||
| if err := issue.GetPullRequest(); err != nil { | |||
| log.Error(4, "GetPullRequest", err) | |||
| } else { | |||
| apiIssue.PullRequest = &api.PullRequestMeta{ | |||
| HasMerged: issue.PullRequest.HasMerged, | |||
| } | |||
| if issue.PullRequest.HasMerged { | |||
| apiIssue.PullRequest.Merged = &issue.PullRequest.Merged | |||
| } | |||
| } | |||
| } | |||
| return apiIssue | |||
| } | |||
| func ToOrganization(org *models.User) *api.Organization { | |||
| return &api.Organization{ | |||
| ID: org.Id, | |||
| AvatarUrl: org.AvatarLink(), | |||
| @@ -21,7 +21,7 @@ func listUserOrgs(ctx *context.APIContext, u *models.User, all bool) { | |||
| apiOrgs := make([]*api.Organization, len(u.Orgs)) | |||
| for i := range u.Orgs { | |||
| apiOrgs[i] = convert.ToApiOrganization(u.Orgs[i]) | |||
| apiOrgs[i] = convert.ToOrganization(u.Orgs[i]) | |||
| } | |||
| ctx.JSON(200, &apiOrgs) | |||
| } | |||
| @@ -46,7 +46,7 @@ func Get(ctx *context.APIContext) { | |||
| if ctx.Written() { | |||
| return | |||
| } | |||
| ctx.JSON(200, convert.ToApiOrganization(org)) | |||
| ctx.JSON(200, convert.ToOrganization(org)) | |||
| } | |||
| // https://github.com/gogits/go-gogs-client/wiki/Organizations#edit-an-organization | |||
| @@ -70,5 +70,5 @@ func Edit(ctx *context.APIContext, form api.EditOrgOption) { | |||
| return | |||
| } | |||
| ctx.JSON(200, convert.ToApiOrganization(org)) | |||
| ctx.JSON(200, convert.ToOrganization(org)) | |||
| } | |||
| @@ -25,7 +25,7 @@ func GetBranch(ctx *context.APIContext) { | |||
| return | |||
| } | |||
| ctx.JSON(200, convert.ToApiBranch(branch, c)) | |||
| ctx.JSON(200, convert.ToBranch(branch, c)) | |||
| } | |||
| // https://github.com/gogits/go-gogs-client/wiki/Repositories#list-branches | |||
| @@ -43,7 +43,7 @@ func ListBranches(ctx *context.APIContext) { | |||
| ctx.Error(500, "GetCommit", err) | |||
| return | |||
| } | |||
| apiBranches[i] = convert.ToApiBranch(branches[i], c) | |||
| apiBranches[i] = convert.ToBranch(branches[i], c) | |||
| } | |||
| ctx.JSON(200, &apiBranches) | |||
| @@ -26,9 +26,8 @@ func ListHooks(ctx *context.APIContext) { | |||
| apiHooks := make([]*api.Hook, len(hooks)) | |||
| for i := range hooks { | |||
| apiHooks[i] = convert.ToApiHook(ctx.Repo.RepoLink, hooks[i]) | |||
| apiHooks[i] = convert.ToHook(ctx.Repo.RepoLink, hooks[i]) | |||
| } | |||
| ctx.JSON(200, &apiHooks) | |||
| } | |||
| @@ -94,7 +93,7 @@ func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { | |||
| return | |||
| } | |||
| ctx.JSON(201, convert.ToApiHook(ctx.Repo.RepoLink, w)) | |||
| ctx.JSON(201, convert.ToHook(ctx.Repo.RepoLink, w)) | |||
| } | |||
| // https://github.com/gogits/go-gogs-client/wiki/Repositories#edit-a-hook | |||
| @@ -161,5 +160,5 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) { | |||
| return | |||
| } | |||
| ctx.JSON(200, convert.ToApiHook(ctx.Repo.RepoLink, w)) | |||
| ctx.JSON(200, convert.ToHook(ctx.Repo.RepoLink, w)) | |||
| } | |||
| @@ -0,0 +1,165 @@ | |||
| // Copyright 2016 The Gogs Authors. All rights reserved. | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| package repo | |||
| import ( | |||
| "fmt" | |||
| "strings" | |||
| api "github.com/gogits/go-gogs-client" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/context" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| "github.com/gogits/gogs/routers/api/v1/convert" | |||
| "github.com/gogits/gogs/routers/repo" | |||
| ) | |||
| func ListIssues(ctx *context.APIContext) { | |||
| issues, err := models.Issues(&models.IssuesOptions{ | |||
| RepoID: ctx.Repo.Repository.ID, | |||
| Page: ctx.QueryInt("page"), | |||
| }) | |||
| if err != nil { | |||
| ctx.Error(500, "Issues", err) | |||
| return | |||
| } | |||
| apiIssues := make([]*api.Issue, len(issues)) | |||
| for i := range issues { | |||
| apiIssues[i] = convert.ToIssue(issues[i]) | |||
| } | |||
| ctx.SetLinkHeader(ctx.Repo.Repository.NumIssues, setting.IssuePagingNum) | |||
| ctx.JSON(200, &apiIssues) | |||
| } | |||
| func GetIssue(ctx *context.APIContext) { | |||
| issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) | |||
| if err != nil { | |||
| if models.IsErrIssueNotExist(err) { | |||
| ctx.Status(404) | |||
| } else { | |||
| ctx.Error(500, "GetIssueByIndex", err) | |||
| } | |||
| return | |||
| } | |||
| ctx.JSON(200, convert.ToIssue(issue)) | |||
| } | |||
| func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { | |||
| issue := &models.Issue{ | |||
| RepoID: ctx.Repo.Repository.ID, | |||
| Name: form.Title, | |||
| PosterID: ctx.User.Id, | |||
| Poster: ctx.User, | |||
| Content: form.Body, | |||
| } | |||
| if ctx.Repo.IsWriter() { | |||
| if len(form.Assignee) > 0 { | |||
| assignee, err := models.GetUserByName(form.Assignee) | |||
| if err != nil { | |||
| if models.IsErrUserNotExist(err) { | |||
| ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", form.Assignee)) | |||
| } else { | |||
| ctx.Error(500, "GetUserByName", err) | |||
| } | |||
| return | |||
| } | |||
| issue.AssigneeID = assignee.Id | |||
| } | |||
| issue.MilestoneID = form.Milestone | |||
| } else { | |||
| form.Labels = nil | |||
| } | |||
| if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, nil); err != nil { | |||
| ctx.Error(500, "NewIssue", err) | |||
| return | |||
| } else if err := repo.MailWatchersAndMentions(ctx.Context, issue); err != nil { | |||
| ctx.Error(500, "MailWatchersAndMentions", err) | |||
| return | |||
| } | |||
| // Refetch from database to assign some automatic values | |||
| var err error | |||
| issue, err = models.GetIssueByID(issue.ID) | |||
| if err != nil { | |||
| ctx.Error(500, "GetIssueByID", err) | |||
| return | |||
| } | |||
| ctx.JSON(201, convert.ToIssue(issue)) | |||
| } | |||
| func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { | |||
| issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) | |||
| if err != nil { | |||
| if models.IsErrIssueNotExist(err) { | |||
| ctx.Status(404) | |||
| } else { | |||
| ctx.Error(500, "GetIssueByIndex", err) | |||
| } | |||
| return | |||
| } | |||
| if !issue.IsPoster(ctx.User.Id) && !ctx.Repo.IsWriter() { | |||
| ctx.Status(403) | |||
| return | |||
| } | |||
| if len(form.Title) > 0 { | |||
| issue.Name = form.Title | |||
| } | |||
| if form.Body != nil { | |||
| issue.Content = *form.Body | |||
| } | |||
| if ctx.Repo.IsWriter() && form.Assignee != nil && | |||
| (issue.Assignee == nil || issue.Assignee.LowerName != strings.ToLower(*form.Assignee)) { | |||
| if len(*form.Assignee) == 0 { | |||
| issue.AssigneeID = 0 | |||
| } else { | |||
| assignee, err := models.GetUserByName(*form.Assignee) | |||
| if err != nil { | |||
| if models.IsErrUserNotExist(err) { | |||
| ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", *form.Assignee)) | |||
| } else { | |||
| ctx.Error(500, "GetUserByName", err) | |||
| } | |||
| return | |||
| } | |||
| issue.AssigneeID = assignee.Id | |||
| } | |||
| if err = models.UpdateIssueUserByAssignee(issue); err != nil { | |||
| ctx.Error(500, "UpdateIssueUserByAssignee", err) | |||
| return | |||
| } | |||
| } | |||
| if ctx.Repo.IsWriter() && form.Milestone != nil && | |||
| issue.MilestoneID != *form.Milestone { | |||
| oldMid := issue.MilestoneID | |||
| issue.MilestoneID = *form.Milestone | |||
| if err = models.ChangeMilestoneAssign(oldMid, issue); err != nil { | |||
| ctx.Error(500, "ChangeMilestoneAssign", err) | |||
| return | |||
| } | |||
| } | |||
| if err = models.UpdateIssue(issue); err != nil { | |||
| ctx.Error(500, "UpdateIssue", err) | |||
| return | |||
| } | |||
| // Refetch from database to assign some automatic values | |||
| issue, err = models.GetIssueByID(issue.ID) | |||
| if err != nil { | |||
| ctx.Error(500, "GetIssueByID", err) | |||
| return | |||
| } | |||
| ctx.JSON(201, convert.ToIssue(issue)) | |||
| } | |||
| @@ -34,7 +34,7 @@ func ListDeployKeys(ctx *context.APIContext) { | |||
| ctx.Error(500, "GetContent", err) | |||
| return | |||
| } | |||
| apiKeys[i] = convert.ToApiDeployKey(apiLink, keys[i]) | |||
| apiKeys[i] = convert.ToDeployKey(apiLink, keys[i]) | |||
| } | |||
| ctx.JSON(200, &apiKeys) | |||
| @@ -58,7 +58,7 @@ func GetDeployKey(ctx *context.APIContext) { | |||
| } | |||
| apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name) | |||
| ctx.JSON(200, convert.ToApiDeployKey(apiLink, key)) | |||
| ctx.JSON(200, convert.ToDeployKey(apiLink, key)) | |||
| } | |||
| func HandleCheckKeyStringError(ctx *context.APIContext, err error) { | |||
| @@ -96,7 +96,7 @@ func CreateDeployKey(ctx *context.APIContext, form api.CreateKeyOption) { | |||
| key.Content = content | |||
| apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name) | |||
| ctx.JSON(201, convert.ToApiDeployKey(apiLink, key)) | |||
| ctx.JSON(201, convert.ToDeployKey(apiLink, key)) | |||
| } | |||
| // https://github.com/gogits/go-gogs-client/wiki/Repositories-Deploy-Keys#remove-a-deploy-key | |||
| @@ -69,7 +69,7 @@ func Search(ctx *context.APIContext) { | |||
| return | |||
| } | |||
| results[i] = &api.Repository{ | |||
| Id: repos[i].ID, | |||
| ID: repos[i].ID, | |||
| FullName: path.Join(repos[i].Owner.Name, repos[i].Name), | |||
| } | |||
| } | |||
| @@ -97,12 +97,12 @@ func ListMyRepos(ctx *context.APIContext) { | |||
| repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos)) | |||
| for i := range ownRepos { | |||
| repos[i] = convert.ToApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true}) | |||
| repos[i] = convert.ToRepository(ctx.User, ownRepos[i], api.Permission{true, true, true}) | |||
| } | |||
| i := numOwnRepos | |||
| for repo, access := range accessibleRepos { | |||
| repos[i] = convert.ToApiRepository(repo.Owner, repo, api.Permission{ | |||
| repos[i] = convert.ToRepository(repo.Owner, repo, api.Permission{ | |||
| Admin: access >= models.ACCESS_MODE_ADMIN, | |||
| Push: access >= models.ACCESS_MODE_WRITE, | |||
| Pull: true, | |||
| @@ -139,7 +139,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR | |||
| return | |||
| } | |||
| ctx.JSON(201, convert.ToApiRepository(owner, repo, api.Permission{true, true, true})) | |||
| ctx.JSON(201, convert.ToRepository(owner, repo, api.Permission{true, true, true})) | |||
| } | |||
| // https://github.com/gogits/go-gogs-client/wiki/Repositories#create | |||
| @@ -239,7 +239,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { | |||
| } | |||
| log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName) | |||
| ctx.JSON(201, convert.ToApiRepository(ctxUser, repo, api.Permission{true, true, true})) | |||
| ctx.JSON(201, convert.ToRepository(ctxUser, repo, api.Permission{true, true, true})) | |||
| } | |||
| func parseOwnerAndRepo(ctx *context.APIContext) (*models.User, *models.Repository) { | |||
| @@ -273,7 +273,7 @@ func Get(ctx *context.APIContext) { | |||
| return | |||
| } | |||
| ctx.JSON(200, convert.ToApiRepository(owner, repo, api.Permission{true, true, true})) | |||
| ctx.JSON(200, convert.ToRepository(owner, repo, api.Permission{true, true, true})) | |||
| } | |||
| // https://github.com/gogits/go-gogs-client/wiki/Repositories#delete | |||
| @@ -22,7 +22,7 @@ func ListEmails(ctx *context.APIContext) { | |||
| } | |||
| apiEmails := make([]*api.Email, len(emails)) | |||
| for i := range emails { | |||
| apiEmails[i] = convert.ToApiEmail(emails[i]) | |||
| apiEmails[i] = convert.ToEmail(emails[i]) | |||
| } | |||
| ctx.JSON(200, &apiEmails) | |||
| } | |||
| @@ -54,7 +54,7 @@ func AddEmail(ctx *context.APIContext, form api.CreateEmailOption) { | |||
| apiEmails := make([]*api.Email, len(emails)) | |||
| for i := range emails { | |||
| apiEmails[i] = convert.ToApiEmail(emails[i]) | |||
| apiEmails[i] = convert.ToEmail(emails[i]) | |||
| } | |||
| ctx.JSON(201, &apiEmails) | |||
| } | |||
| @@ -15,7 +15,7 @@ import ( | |||
| func responseApiUsers(ctx *context.APIContext, users []*models.User) { | |||
| apiUsers := make([]*api.User, len(users)) | |||
| for i := range users { | |||
| apiUsers[i] = convert.ToApiUser(users[i]) | |||
| apiUsers[i] = convert.ToUser(users[i]) | |||
| } | |||
| ctx.JSON(200, &apiUsers) | |||
| } | |||
| @@ -46,7 +46,7 @@ func listPublicKeys(ctx *context.APIContext, uid int64) { | |||
| apiLink := composePublicKeysAPILink() | |||
| apiKeys := make([]*api.PublicKey, len(keys)) | |||
| for i := range keys { | |||
| apiKeys[i] = convert.ToApiPublicKey(apiLink, keys[i]) | |||
| apiKeys[i] = convert.ToPublicKey(apiLink, keys[i]) | |||
| } | |||
| ctx.JSON(200, &apiKeys) | |||
| @@ -79,7 +79,7 @@ func GetPublicKey(ctx *context.APIContext) { | |||
| } | |||
| apiLink := composePublicKeysAPILink() | |||
| ctx.JSON(200, convert.ToApiPublicKey(apiLink, key)) | |||
| ctx.JSON(200, convert.ToPublicKey(apiLink, key)) | |||
| } | |||
| // CreateUserPublicKey creates new public key to given user by ID. | |||
| @@ -96,7 +96,7 @@ func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid | |||
| return | |||
| } | |||
| apiLink := composePublicKeysAPILink() | |||
| ctx.JSON(201, convert.ToApiPublicKey(apiLink, key)) | |||
| ctx.JSON(201, convert.ToPublicKey(apiLink, key)) | |||
| } | |||
| // https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#create-a-public-key | |||
| @@ -195,16 +195,6 @@ func Issues(ctx *context.Context) { | |||
| // Get posters. | |||
| for i := range issues { | |||
| if err = issues[i].GetPoster(); err != nil { | |||
| ctx.Handle(500, "GetPoster", fmt.Errorf("[#%d]%v", issues[i].ID, err)) | |||
| return | |||
| } | |||
| if err = issues[i].GetLabels(); err != nil { | |||
| ctx.Handle(500, "GetLabels", fmt.Errorf("[#%d]%v", issues[i].ID, err)) | |||
| return | |||
| } | |||
| if !ctx.IsSigned { | |||
| issues[i].IsRead = true | |||
| continue | |||
| @@ -405,7 +395,7 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64 | |||
| return labelIDs, milestoneID, assigneeID | |||
| } | |||
| func notifyWatchersAndMentions(ctx *context.Context, issue *models.Issue) { | |||
| func MailWatchersAndMentions(ctx *context.Context, issue *models.Issue) error { | |||
| // Update mentions | |||
| mentions := markdown.MentionPattern.FindAllString(issue.Content, -1) | |||
| if len(mentions) > 0 { | |||
| @@ -414,8 +404,7 @@ func notifyWatchersAndMentions(ctx *context.Context, issue *models.Issue) { | |||
| } | |||
| if err := models.UpdateMentions(mentions, issue.ID); err != nil { | |||
| ctx.Handle(500, "UpdateMentions", err) | |||
| return | |||
| return fmt.Errorf("UpdateMentions: %v", err) | |||
| } | |||
| } | |||
| @@ -425,8 +414,7 @@ func notifyWatchersAndMentions(ctx *context.Context, issue *models.Issue) { | |||
| if setting.Service.EnableNotifyMail { | |||
| tos, err := mailer.SendIssueNotifyMail(ctx.User, ctx.Repo.Owner, repo, issue) | |||
| if err != nil { | |||
| ctx.Handle(500, "SendIssueNotifyMail", err) | |||
| return | |||
| return fmt.Errorf("SendIssueNotifyMail: %v", err) | |||
| } | |||
| tos = append(tos, ctx.User.LowerName) | |||
| @@ -440,10 +428,11 @@ func notifyWatchersAndMentions(ctx *context.Context, issue *models.Issue) { | |||
| } | |||
| if err = mailer.SendIssueMentionMail(ctx.Render, ctx.User, ctx.Repo.Owner, | |||
| repo, issue, models.GetUserEmailsByNames(newTos)); err != nil { | |||
| ctx.Handle(500, "SendIssueMentionMail", err) | |||
| return | |||
| return fmt.Errorf("SendIssueMentionMail: %v", err) | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { | |||
| @@ -471,9 +460,8 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { | |||
| } | |||
| issue := &models.Issue{ | |||
| RepoID: ctx.Repo.Repository.ID, | |||
| Index: repo.NextIssueIndex(), | |||
| Name: strings.TrimSpace(form.Title), | |||
| RepoID: repo.ID, | |||
| Name: form.Title, | |||
| PosterID: ctx.User.Id, | |||
| Poster: ctx.User, | |||
| MilestoneID: milestoneID, | |||
| @@ -483,10 +471,8 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { | |||
| if err := models.NewIssue(repo, issue, labelIDs, attachments); err != nil { | |||
| ctx.Handle(500, "NewIssue", err) | |||
| return | |||
| } | |||
| notifyWatchersAndMentions(ctx, issue) | |||
| if ctx.Written() { | |||
| } else if err := MailWatchersAndMentions(ctx, issue); err != nil { | |||
| ctx.Handle(500, "MailWatchersAndMentions", err) | |||
| return | |||
| } | |||
| @@ -586,10 +572,6 @@ func ViewIssue(ctx *context.Context) { | |||
| ctx.Data["PageIsIssueList"] = true | |||
| } | |||
| if err = issue.GetPoster(); err != nil { | |||
| ctx.Handle(500, "GetPoster", err) | |||
| return | |||
| } | |||
| issue.RenderedContent = string(markdown.Render([]byte(issue.Content), ctx.Repo.RepoLink, | |||
| ctx.Repo.Repository.ComposeMetas())) | |||
| @@ -610,10 +592,6 @@ func ViewIssue(ctx *context.Context) { | |||
| // Metas. | |||
| // Check labels. | |||
| if err = issue.GetLabels(); err != nil { | |||
| ctx.Handle(500, "GetLabels", err) | |||
| return | |||
| } | |||
| labelIDMark := make(map[int64]bool) | |||
| for i := range issue.Labels { | |||
| labelIDMark[issue.Labels[i].ID] = true | |||
| @@ -698,7 +676,7 @@ func ViewIssue(ctx *context.Context) { | |||
| ctx.Data["Participants"] = participants | |||
| ctx.Data["NumParticipants"] = len(participants) | |||
| ctx.Data["Issue"] = issue | |||
| ctx.Data["IsIssueOwner"] = ctx.Repo.IsWriter() || (ctx.IsSigned && (issue.IsPoster(ctx.User.Id) || ctx.User.IsAdmin)) | |||
| ctx.Data["IsIssueOwner"] = ctx.Repo.IsWriter() || (ctx.IsSigned && issue.IsPoster(ctx.User.Id)) | |||
| ctx.Data["SignInLink"] = setting.AppSubUrl + "/user/login" | |||
| ctx.Data["RequireHighlightJS"] = true | |||
| @@ -725,7 +703,7 @@ func UpdateIssueTitle(ctx *context.Context) { | |||
| return | |||
| } | |||
| if !ctx.IsSigned || (ctx.User.Id != issue.PosterID && !ctx.Repo.IsWriter() && !ctx.User.IsAdmin) { | |||
| if !ctx.IsSigned || (!issue.IsPoster(ctx.User.Id) && !ctx.Repo.IsWriter()) { | |||
| ctx.Error(403) | |||
| return | |||
| } | |||
| @@ -752,7 +730,7 @@ func UpdateIssueContent(ctx *context.Context) { | |||
| return | |||
| } | |||
| if !ctx.IsSigned || (ctx.User.Id != issue.PosterID && !ctx.Repo.IsWriter() && !ctx.User.IsAdmin) { | |||
| if !ctx.IsSigned || (ctx.User.Id != issue.PosterID && !ctx.Repo.IsWriter()) { | |||
| ctx.Error(403) | |||
| return | |||
| } | |||
| @@ -955,7 +933,7 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) { | |||
| return | |||
| } | |||
| notifyWatchersAndMentions(ctx, &models.Issue{ | |||
| MailWatchersAndMentions(ctx, &models.Issue{ | |||
| ID: issue.ID, | |||
| Index: issue.Index, | |||
| Name: issue.Name, | |||
| @@ -1099,7 +1077,6 @@ func Milestones(ctx *context.Context) { | |||
| } | |||
| for _, m := range miles { | |||
| m.RenderedContent = string(markdown.Render([]byte(m.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) | |||
| m.CalOpenIssues() | |||
| } | |||
| ctx.Data["Milestones"] = miles | |||
| @@ -156,10 +156,7 @@ func checkPullInfo(ctx *context.Context) *models.Issue { | |||
| return nil | |||
| } | |||
| if err = issue.GetPoster(); err != nil { | |||
| ctx.Handle(500, "GetPoster", err) | |||
| return nil | |||
| } else if err = issue.GetPullRequest(); err != nil { | |||
| if err = issue.GetPullRequest(); err != nil { | |||
| ctx.Handle(500, "GetPullRequest", err) | |||
| return nil | |||
| } else if err = issue.GetHeadRepo(); err != nil { | |||
| @@ -682,10 +679,8 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) | |||
| } else if err := pullRequest.PushToBaseRepo(); err != nil { | |||
| ctx.Handle(500, "PushToBaseRepo", err) | |||
| return | |||
| } | |||
| notifyWatchersAndMentions(ctx, pullIssue) | |||
| if ctx.Written() { | |||
| } else if err := MailWatchersAndMentions(ctx, pullIssue); err != nil { | |||
| ctx.Handle(500, "MailWatchersAndMentions", err) | |||
| return | |||
| } | |||
| @@ -284,11 +284,6 @@ func Issues(ctx *context.Context) { | |||
| ctx.Handle(500, "GetOwner", fmt.Errorf("[#%d]%v", issues[i].ID, err)) | |||
| return | |||
| } | |||
| if err = issues[i].GetPoster(); err != nil { | |||
| ctx.Handle(500, "GetPoster", fmt.Errorf("[#%d]%v", issues[i].ID, err)) | |||
| return | |||
| } | |||
| } | |||
| ctx.Data["Issues"] = issues | |||
| @@ -1 +1 @@ | |||
| 0.9.8.0312 | |||
| 0.9.9.0313 | |||