| @@ -21,7 +21,8 @@ type Issue struct { | |||
| Id int64 | |||
| Index int64 // Index in one repository. | |||
| Name string | |||
| RepoId int64 `xorm:"index"` | |||
| RepoId int64 `xorm:"index"` | |||
| Repo *Repository `xorm:"-"` | |||
| PosterId int64 | |||
| Poster *User `xorm:"-"` | |||
| MilestoneId int64 | |||
| @@ -436,7 +436,8 @@ func GetRepositoryByName(userId int64, repoName string) (*Repository, error) { | |||
| } | |||
| // GetRepositoryById returns the repository by given id if exists. | |||
| func GetRepositoryById(id int64) (repo *Repository, err error) { | |||
| func GetRepositoryById(id int64) (*Repository, error) { | |||
| repo := &Repository{} | |||
| has, err := orm.Id(id).Get(repo) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -47,6 +47,7 @@ func HashEmail(email string) string { | |||
| return hex.EncodeToString(h.Sum(nil)) | |||
| } | |||
| // Avatar represents the avatar object. | |||
| type Avatar struct { | |||
| Hash string | |||
| AlterImage string // image path | |||
| @@ -96,8 +97,8 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) { | |||
| return | |||
| } | |||
| defer fd.Close() | |||
| img, err = jpeg.Decode(fd) | |||
| if err != nil { | |||
| if img, err = jpeg.Decode(fd); err != nil { | |||
| fd.Seek(0, os.SEEK_SET) | |||
| img, err = png.Decode(fd) | |||
| } | |||
| @@ -110,8 +111,8 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) { | |||
| } | |||
| imgPath = this.AlterImage | |||
| } | |||
| img, err = decodeImageFile(imgPath) | |||
| if err != nil { | |||
| if img, err = decodeImageFile(imgPath); err != nil { | |||
| return | |||
| } | |||
| m := resize.Resize(uint(size), 0, img, resize.Lanczos3) | |||
| @@ -124,8 +125,7 @@ func (this *Avatar) Update() { | |||
| this.imagePath) | |||
| } | |||
| func (this *Avatar) UpdateTimeout(timeout time.Duration) error { | |||
| var err error | |||
| func (this *Avatar) UpdateTimeout(timeout time.Duration) (err error) { | |||
| select { | |||
| case <-time.After(timeout): | |||
| err = fmt.Errorf("get gravatar image %s timeout", this.Hash) | |||
| @@ -140,8 +140,7 @@ type service struct { | |||
| altImage string | |||
| } | |||
| func (this *service) mustInt(r *http.Request, defaultValue int, keys ...string) int { | |||
| var v int | |||
| func (this *service) mustInt(r *http.Request, defaultValue int, keys ...string) (v int) { | |||
| for _, k := range keys { | |||
| if _, err := fmt.Sscanf(r.FormValue(k), "%d", &v); err == nil { | |||
| defaultValue = v | |||
| @@ -176,8 +175,8 @@ func (this *service) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
| w.Header().Set("ETag", etag) | |||
| } | |||
| w.Header().Set("Content-Type", "image/jpeg") | |||
| err := avatar.Encode(w, size) | |||
| if err != nil { | |||
| if err := avatar.Encode(w, size); err != nil { | |||
| log.Warn("avatar encode error: %v", err) | |||
| w.WriteHeader(500) | |||
| } | |||
| @@ -412,6 +412,11 @@ func (f StrTo) Int() (int, error) { | |||
| return int(v), err | |||
| } | |||
| func (f StrTo) Int64() (int64, error) { | |||
| v, err := strconv.ParseInt(f.String(), 10, 64) | |||
| return int64(v), err | |||
| } | |||
| func (f StrTo) String() string { | |||
| if f.Exist() { | |||
| return string(f) | |||
| @@ -541,16 +546,10 @@ func ActionDesc(act Actioner, avatarLink string) string { | |||
| } | |||
| func DiffTypeToStr(diffType int) string { | |||
| switch diffType { | |||
| case 1: | |||
| return "add" | |||
| case 2: | |||
| return "modify" | |||
| case 3: | |||
| return "del" | |||
| default: | |||
| return "unknown" | |||
| diffTypes := map[int]string{ | |||
| 1: "add", 2: "modify", 3: "del", | |||
| } | |||
| return diffTypes[diffType] | |||
| } | |||
| func DiffLineTypeToStr(diffType int) string { | |||
| @@ -289,6 +289,9 @@ func Issues(ctx *middleware.Context) { | |||
| ctx.Data["ViewType"] = "all" | |||
| page, _ := base.StrTo(ctx.Query("page")).Int() | |||
| repoId, _ := base.StrTo(ctx.Query("repoid")).Int64() | |||
| ctx.Data["RepoId"] = repoId | |||
| var posterId int64 = 0 | |||
| if ctx.Query("type") == "created_by" { | |||
| @@ -299,10 +302,12 @@ func Issues(ctx *middleware.Context) { | |||
| // Get all repositories. | |||
| repos, err := models.GetRepositories(ctx.User) | |||
| if err != nil { | |||
| ctx.Handle(200, "user.Issues(get repository)", err) | |||
| ctx.Handle(200, "user.Issues(get repositories)", err) | |||
| return | |||
| } | |||
| showRepos := make([]models.Repository, 0, len(repos)) | |||
| var closedIssueCount, createdByCount int | |||
| // Get all issues. | |||
| @@ -315,8 +320,18 @@ func Issues(ctx *middleware.Context) { | |||
| } | |||
| closedIssueCount += repo.NumClosedIssues | |||
| repos[i].NumOpenIssues = repo.NumIssues - repo.NumClosedIssues | |||
| // Set repository information to issues. | |||
| for j := range issues { | |||
| issues[j].Repo = &repos[i] | |||
| } | |||
| allIssues = append(allIssues, issues...) | |||
| repos[i].NumOpenIssues = repo.NumIssues - repo.NumClosedIssues | |||
| if repos[i].NumOpenIssues > 0 { | |||
| showRepos = append(showRepos, repos[i]) | |||
| } | |||
| } | |||
| showIssues := make([]models.Issue, 0, len(allIssues)) | |||
| @@ -335,12 +350,16 @@ func Issues(ctx *middleware.Context) { | |||
| createdByCount++ | |||
| } | |||
| if repoId > 0 && repoId != allIssues[i].Repo.Id { | |||
| continue | |||
| } | |||
| if isShowClosed == allIssues[i].IsClosed { | |||
| showIssues = append(showIssues, allIssues[i]) | |||
| } | |||
| } | |||
| ctx.Data["Repos"] = repos | |||
| ctx.Data["Repos"] = showRepos | |||
| ctx.Data["Issues"] = showIssues | |||
| ctx.Data["AllIssueCount"] = len(allIssues) | |||
| ctx.Data["ClosedIssueCount"] = closedIssueCount | |||
| @@ -15,8 +15,8 @@ | |||
| <div class="col-md-9"> | |||
| <div class="filter-option"> | |||
| <div class="btn-group"> | |||
| <a class="btn btn-default issue-open{{if not .IsShowClosed}} active{{end}}" href="/{{.RepositoryLink}}/issues">{{.OpenCount}} Open</a> | |||
| <a class="btn btn-default issue-close{{if .IsShowClosed}} active{{end}}" href="/{{.RepositoryLink}}/issues?state=closed">{{.ClosedCount}} Closed</a> | |||
| <a class="btn btn-default issue-open{{if not .IsShowClosed}} active{{end}}" href="/{{.RepositoryLink}}/issues?type={{.ViewType}}">{{.OpenCount}} Open</a> | |||
| <a class="btn btn-default issue-close{{if .IsShowClosed}} active{{end}}" href="/{{.RepositoryLink}}/issues?state=closed&type={{.ViewType}}">{{.ClosedCount}} Closed</a> | |||
| </div> | |||
| </div> | |||
| <div class="issues list-group"> | |||
| @@ -21,22 +21,22 @@ | |||
| <li><a href="/issues?type=created_by"{{if eq .ViewType "created_by"}} class="active"{{end}}>Created by you <strong class="pull-right">{{.CreatedByCount}}</strong></a></li> | |||
| <li><hr/></li> | |||
| {{range .Repos}} | |||
| <li><a href="" class="sm">{{.OwnerId}}/{{.Name}} <strong class="pull-right">{{.NumOpenIssues}}</strong></a></li> | |||
| <li><a href="/issues?type={{$.ViewType}}{{if eq $.RepoId .Id}}{{else}}&repoid={{.Id}}{{end}}" class="sm{{if eq $.RepoId .Id}} active{{end}}">{{$.SignedUser.Name}}/{{.Name}} <strong class="pull-right">{{.NumOpenIssues}}</strong></a></li> | |||
| {{end}} | |||
| </ul> | |||
| </div> | |||
| <div class="col-md-9"> | |||
| <div class="filter-option"> | |||
| <div class="btn-group"> | |||
| <a class="btn btn-default issue-open{{if not .IsShowClosed}} active{{end}}" href="/issues">{{.OpenIssueCount}} Open</a> | |||
| <a class="btn btn-default issue-close{{if .IsShowClosed}} active{{end}}" href="/issues?state=closed">{{.ClosedIssueCount}} Close</a> | |||
| <a class="btn btn-default issue-open{{if not .IsShowClosed}} active{{end}}" href="/issues?type={{.ViewType}}&repoid={{.RepoId}}">{{.OpenIssueCount}} Open</a> | |||
| <a class="btn btn-default issue-close{{if .IsShowClosed}} active{{end}}" href="/issues?state=closed&type={{.ViewType}}&repoid={{.RepoId}}">{{.ClosedIssueCount}} Close</a> | |||
| </div> | |||
| </div> | |||
| <div class="issues list-group"> | |||
| {{range .Issues}} | |||
| <div class="list-group-item issue-item" id="issue-{{.Id}}"> | |||
| <span class="number pull-right">#{{.Index}}</span> | |||
| <h5 class="title"><a href="/{{$.RepositoryLink}}/issues/{{.Index}}">{{.Name}}</a></h5> | |||
| <h5 class="title"><a href="/{{$.SignedUser.Name}}/{{.Repo.Name}}/issues/{{.Index}}">{{.Name}}</a></h5> | |||
| <p class="info"> | |||
| <span class="author"><img class="avatar" src="{{.Poster.AvatarLink}}" alt="" width="20"/> | |||
| <a href="/user/{{.Poster.Name}}">{{.Poster.Name}}</a></span> | |||