| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278 | 
							- // Copyright 2014 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"
 
- 	"io"
 
- 	"io/ioutil"
 
- 	"net/http"
 
- 	"net/url"
 
- 	"strings"
 
- 	"time"
 
- 	"github.com/Unknwon/com"
 
- 	"github.com/Unknwon/paginater"
 
- 	log "gopkg.in/clog.v1"
 
- 	"github.com/gogs/gogs/models"
 
- 	"github.com/gogs/gogs/models/errors"
 
- 	"github.com/gogs/gogs/pkg/context"
 
- 	"github.com/gogs/gogs/pkg/form"
 
- 	"github.com/gogs/gogs/pkg/markup"
 
- 	"github.com/gogs/gogs/pkg/setting"
 
- 	"github.com/gogs/gogs/pkg/template"
 
- 	"github.com/gogs/gogs/pkg/tool"
 
- )
 
- const (
 
- 	ISSUES     = "repo/issue/list"
 
- 	ISSUE_NEW  = "repo/issue/new"
 
- 	ISSUE_VIEW = "repo/issue/view"
 
- 	LABELS = "repo/issue/labels"
 
- 	MILESTONE      = "repo/issue/milestones"
 
- 	MILESTONE_NEW  = "repo/issue/milestone_new"
 
- 	MILESTONE_EDIT = "repo/issue/milestone_edit"
 
- 	ISSUE_TEMPLATE_KEY = "IssueTemplate"
 
- )
 
- var (
 
- 	ErrFileTypeForbidden = errors.New("File type is not allowed")
 
- 	ErrTooManyFiles      = errors.New("Maximum number of files to upload exceeded")
 
- 	IssueTemplateCandidates = []string{
 
- 		"ISSUE_TEMPLATE.md",
 
- 		".gogs/ISSUE_TEMPLATE.md",
 
- 		".github/ISSUE_TEMPLATE.md",
 
- 	}
 
- )
 
- func MustEnableIssues(c *context.Context) {
 
- 	if !c.Repo.Repository.EnableIssues {
 
- 		c.Handle(404, "MustEnableIssues", nil)
 
- 		return
 
- 	}
 
- 	if c.Repo.Repository.EnableExternalTracker {
 
- 		c.Redirect(c.Repo.Repository.ExternalTrackerURL)
 
- 		return
 
- 	}
 
- }
 
- func MustAllowPulls(c *context.Context) {
 
- 	if !c.Repo.Repository.AllowsPulls() {
 
- 		c.Handle(404, "MustAllowPulls", nil)
 
- 		return
 
- 	}
 
- 	// User can send pull request if owns a forked repository.
 
- 	if c.IsLogged && c.User.HasForkedRepo(c.Repo.Repository.ID) {
 
- 		c.Repo.PullRequest.Allowed = true
 
- 		c.Repo.PullRequest.HeadInfo = c.User.Name + ":" + c.Repo.BranchName
 
- 	}
 
- }
 
- func RetrieveLabels(c *context.Context) {
 
- 	labels, err := models.GetLabelsByRepoID(c.Repo.Repository.ID)
 
- 	if err != nil {
 
- 		c.Handle(500, "RetrieveLabels.GetLabels", err)
 
- 		return
 
- 	}
 
- 	for _, l := range labels {
 
- 		l.CalOpenIssues()
 
- 	}
 
- 	c.Data["Labels"] = labels
 
- 	c.Data["NumLabels"] = len(labels)
 
- }
 
- func issues(c *context.Context, isPullList bool) {
 
- 	if isPullList {
 
- 		MustAllowPulls(c)
 
- 		if c.Written() {
 
- 			return
 
- 		}
 
- 		c.Data["Title"] = c.Tr("repo.pulls")
 
- 		c.Data["PageIsPullList"] = true
 
- 	} else {
 
- 		MustEnableIssues(c)
 
- 		if c.Written() {
 
- 			return
 
- 		}
 
- 		c.Data["Title"] = c.Tr("repo.issues")
 
- 		c.Data["PageIsIssueList"] = true
 
- 	}
 
- 	viewType := c.Query("type")
 
- 	sortType := c.Query("sort")
 
- 	types := []string{"assigned", "created_by", "mentioned"}
 
- 	if !com.IsSliceContainsStr(types, viewType) {
 
- 		viewType = "all"
 
- 	}
 
- 	// Must sign in to see issues about you.
 
- 	if viewType != "all" && !c.IsLogged {
 
- 		c.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubURL+c.Req.RequestURI), 0, setting.AppSubURL)
 
- 		c.Redirect(setting.AppSubURL + "/user/login")
 
- 		return
 
- 	}
 
- 	var (
 
- 		assigneeID = c.QueryInt64("assignee")
 
- 		posterID   int64
 
- 	)
 
- 	filterMode := models.FILTER_MODE_YOUR_REPOS
 
- 	switch viewType {
 
- 	case "assigned":
 
- 		filterMode = models.FILTER_MODE_ASSIGN
 
- 		assigneeID = c.User.ID
 
- 	case "created_by":
 
- 		filterMode = models.FILTER_MODE_CREATE
 
- 		posterID = c.User.ID
 
- 	case "mentioned":
 
- 		filterMode = models.FILTER_MODE_MENTION
 
- 	}
 
- 	var uid int64 = -1
 
- 	if c.IsLogged {
 
- 		uid = c.User.ID
 
- 	}
 
- 	repo := c.Repo.Repository
 
- 	selectLabels := c.Query("labels")
 
- 	milestoneID := c.QueryInt64("milestone")
 
- 	isShowClosed := c.Query("state") == "closed"
 
- 	issueStats := models.GetIssueStats(&models.IssueStatsOptions{
 
- 		RepoID:      repo.ID,
 
- 		UserID:      uid,
 
- 		Labels:      selectLabels,
 
- 		MilestoneID: milestoneID,
 
- 		AssigneeID:  assigneeID,
 
- 		FilterMode:  filterMode,
 
- 		IsPull:      isPullList,
 
- 	})
 
- 	page := c.QueryInt("page")
 
- 	if page <= 1 {
 
- 		page = 1
 
- 	}
 
- 	var total int
 
- 	if !isShowClosed {
 
- 		total = int(issueStats.OpenCount)
 
- 	} else {
 
- 		total = int(issueStats.ClosedCount)
 
- 	}
 
- 	pager := paginater.New(total, setting.UI.IssuePagingNum, page, 5)
 
- 	c.Data["Page"] = pager
 
- 	issues, err := models.Issues(&models.IssuesOptions{
 
- 		UserID:      uid,
 
- 		AssigneeID:  assigneeID,
 
- 		RepoID:      repo.ID,
 
- 		PosterID:    posterID,
 
- 		MilestoneID: milestoneID,
 
- 		Page:        pager.Current(),
 
- 		IsClosed:    isShowClosed,
 
- 		IsMention:   filterMode == models.FILTER_MODE_MENTION,
 
- 		IsPull:      isPullList,
 
- 		Labels:      selectLabels,
 
- 		SortType:    sortType,
 
- 	})
 
- 	if err != nil {
 
- 		c.Handle(500, "Issues", err)
 
- 		return
 
- 	}
 
- 	// Get issue-user relations.
 
- 	pairs, err := models.GetIssueUsers(repo.ID, posterID, isShowClosed)
 
- 	if err != nil {
 
- 		c.Handle(500, "GetIssueUsers", err)
 
- 		return
 
- 	}
 
- 	// Get posters.
 
- 	for i := range issues {
 
- 		if !c.IsLogged {
 
- 			issues[i].IsRead = true
 
- 			continue
 
- 		}
 
- 		// Check read status.
 
- 		idx := models.PairsContains(pairs, issues[i].ID, c.User.ID)
 
- 		if idx > -1 {
 
- 			issues[i].IsRead = pairs[idx].IsRead
 
- 		} else {
 
- 			issues[i].IsRead = true
 
- 		}
 
- 	}
 
- 	c.Data["Issues"] = issues
 
- 	// Get milestones.
 
- 	c.Data["Milestones"], err = models.GetMilestonesByRepoID(repo.ID)
 
- 	if err != nil {
 
- 		c.Handle(500, "GetAllRepoMilestones", err)
 
- 		return
 
- 	}
 
- 	// Get assignees.
 
- 	c.Data["Assignees"], err = repo.GetAssignees()
 
- 	if err != nil {
 
- 		c.Handle(500, "GetAssignees", err)
 
- 		return
 
- 	}
 
- 	if viewType == "assigned" {
 
- 		assigneeID = 0 // Reset ID to prevent unexpected selection of assignee.
 
- 	}
 
- 	c.Data["IssueStats"] = issueStats
 
- 	c.Data["SelectLabels"] = com.StrTo(selectLabels).MustInt64()
 
- 	c.Data["ViewType"] = viewType
 
- 	c.Data["SortType"] = sortType
 
- 	c.Data["MilestoneID"] = milestoneID
 
- 	c.Data["AssigneeID"] = assigneeID
 
- 	c.Data["IsShowClosed"] = isShowClosed
 
- 	if isShowClosed {
 
- 		c.Data["State"] = "closed"
 
- 	} else {
 
- 		c.Data["State"] = "open"
 
- 	}
 
- 	c.HTML(200, ISSUES)
 
- }
 
- func Issues(c *context.Context) {
 
- 	issues(c, false)
 
- }
 
- func Pulls(c *context.Context) {
 
- 	issues(c, true)
 
- }
 
- func renderAttachmentSettings(c *context.Context) {
 
- 	c.Data["RequireDropzone"] = true
 
- 	c.Data["IsAttachmentEnabled"] = setting.AttachmentEnabled
 
- 	c.Data["AttachmentAllowedTypes"] = setting.AttachmentAllowedTypes
 
- 	c.Data["AttachmentMaxSize"] = setting.AttachmentMaxSize
 
- 	c.Data["AttachmentMaxFiles"] = setting.AttachmentMaxFiles
 
- }
 
- func RetrieveRepoMilestonesAndAssignees(c *context.Context, repo *models.Repository) {
 
- 	var err error
 
- 	c.Data["OpenMilestones"], err = models.GetMilestones(repo.ID, -1, false)
 
- 	if err != nil {
 
- 		c.Handle(500, "GetMilestones", err)
 
- 		return
 
- 	}
 
- 	c.Data["ClosedMilestones"], err = models.GetMilestones(repo.ID, -1, true)
 
- 	if err != nil {
 
- 		c.Handle(500, "GetMilestones", err)
 
- 		return
 
- 	}
 
- 	c.Data["Assignees"], err = repo.GetAssignees()
 
- 	if err != nil {
 
- 		c.Handle(500, "GetAssignees", err)
 
- 		return
 
- 	}
 
- }
 
- func RetrieveRepoMetas(c *context.Context, repo *models.Repository) []*models.Label {
 
- 	if !c.Repo.IsWriter() {
 
- 		return nil
 
- 	}
 
- 	labels, err := models.GetLabelsByRepoID(repo.ID)
 
- 	if err != nil {
 
- 		c.Handle(500, "GetLabelsByRepoID", err)
 
- 		return nil
 
- 	}
 
- 	c.Data["Labels"] = labels
 
- 	RetrieveRepoMilestonesAndAssignees(c, repo)
 
- 	if c.Written() {
 
- 		return nil
 
- 	}
 
- 	return labels
 
- }
 
- func getFileContentFromDefaultBranch(c *context.Context, filename string) (string, bool) {
 
- 	var r io.Reader
 
- 	var bytes []byte
 
- 	if c.Repo.Commit == nil {
 
- 		var err error
 
- 		c.Repo.Commit, err = c.Repo.GitRepo.GetBranchCommit(c.Repo.Repository.DefaultBranch)
 
- 		if err != nil {
 
- 			return "", false
 
- 		}
 
- 	}
 
- 	entry, err := c.Repo.Commit.GetTreeEntryByPath(filename)
 
- 	if err != nil {
 
- 		return "", false
 
- 	}
 
- 	r, err = entry.Blob().Data()
 
- 	if err != nil {
 
- 		return "", false
 
- 	}
 
- 	bytes, err = ioutil.ReadAll(r)
 
- 	if err != nil {
 
- 		return "", false
 
- 	}
 
- 	return string(bytes), true
 
- }
 
- func setTemplateIfExists(c *context.Context, ctxDataKey string, possibleFiles []string) {
 
- 	for _, filename := range possibleFiles {
 
- 		content, found := getFileContentFromDefaultBranch(c, filename)
 
- 		if found {
 
- 			c.Data[ctxDataKey] = content
 
- 			return
 
- 		}
 
- 	}
 
- }
 
- func NewIssue(c *context.Context) {
 
- 	c.Data["Title"] = c.Tr("repo.issues.new")
 
- 	c.Data["PageIsIssueList"] = true
 
- 	c.Data["RequireHighlightJS"] = true
 
- 	c.Data["RequireSimpleMDE"] = true
 
- 	c.Data["title"] = c.Query("title")
 
- 	c.Data["content"] = c.Query("content")
 
- 	setTemplateIfExists(c, ISSUE_TEMPLATE_KEY, IssueTemplateCandidates)
 
- 	renderAttachmentSettings(c)
 
- 	RetrieveRepoMetas(c, c.Repo.Repository)
 
- 	if c.Written() {
 
- 		return
 
- 	}
 
- 	c.HTML(200, ISSUE_NEW)
 
- }
 
- func ValidateRepoMetas(c *context.Context, f form.NewIssue) ([]int64, int64, int64) {
 
- 	var (
 
- 		repo = c.Repo.Repository
 
- 		err  error
 
- 	)
 
- 	labels := RetrieveRepoMetas(c, c.Repo.Repository)
 
- 	if c.Written() {
 
- 		return nil, 0, 0
 
- 	}
 
- 	if !c.Repo.IsWriter() {
 
- 		return nil, 0, 0
 
- 	}
 
- 	// Check labels.
 
- 	labelIDs := tool.StringsToInt64s(strings.Split(f.LabelIDs, ","))
 
- 	labelIDMark := tool.Int64sToMap(labelIDs)
 
- 	hasSelected := false
 
- 	for i := range labels {
 
- 		if labelIDMark[labels[i].ID] {
 
- 			labels[i].IsChecked = true
 
- 			hasSelected = true
 
- 		}
 
- 	}
 
- 	c.Data["HasSelectedLabel"] = hasSelected
 
- 	c.Data["label_ids"] = f.LabelIDs
 
- 	c.Data["Labels"] = labels
 
- 	// Check milestone.
 
- 	milestoneID := f.MilestoneID
 
- 	if milestoneID > 0 {
 
- 		c.Data["Milestone"], err = repo.GetMilestoneByID(milestoneID)
 
- 		if err != nil {
 
- 			c.Handle(500, "GetMilestoneByID", err)
 
- 			return nil, 0, 0
 
- 		}
 
- 		c.Data["milestone_id"] = milestoneID
 
- 	}
 
- 	// Check assignee.
 
- 	assigneeID := f.AssigneeID
 
- 	if assigneeID > 0 {
 
- 		c.Data["Assignee"], err = repo.GetAssigneeByID(assigneeID)
 
- 		if err != nil {
 
- 			c.Handle(500, "GetAssigneeByID", err)
 
- 			return nil, 0, 0
 
- 		}
 
- 		c.Data["assignee_id"] = assigneeID
 
- 	}
 
- 	return labelIDs, milestoneID, assigneeID
 
- }
 
- func NewIssuePost(c *context.Context, f form.NewIssue) {
 
- 	c.Data["Title"] = c.Tr("repo.issues.new")
 
- 	c.Data["PageIsIssueList"] = true
 
- 	c.Data["RequireHighlightJS"] = true
 
- 	c.Data["RequireSimpleMDE"] = true
 
- 	renderAttachmentSettings(c)
 
- 	labelIDs, milestoneID, assigneeID := ValidateRepoMetas(c, f)
 
- 	if c.Written() {
 
- 		return
 
- 	}
 
- 	if c.HasError() {
 
- 		c.HTML(200, ISSUE_NEW)
 
- 		return
 
- 	}
 
- 	var attachments []string
 
- 	if setting.AttachmentEnabled {
 
- 		attachments = f.Files
 
- 	}
 
- 	issue := &models.Issue{
 
- 		RepoID:      c.Repo.Repository.ID,
 
- 		Title:       f.Title,
 
- 		PosterID:    c.User.ID,
 
- 		Poster:      c.User,
 
- 		MilestoneID: milestoneID,
 
- 		AssigneeID:  assigneeID,
 
- 		Content:     f.Content,
 
- 	}
 
- 	if err := models.NewIssue(c.Repo.Repository, issue, labelIDs, attachments); err != nil {
 
- 		c.Handle(500, "NewIssue", err)
 
- 		return
 
- 	}
 
- 	log.Trace("Issue created: %d/%d", c.Repo.Repository.ID, issue.ID)
 
- 	c.Redirect(c.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
 
- }
 
- func uploadAttachment(c *context.Context, allowedTypes []string) {
 
- 	file, header, err := c.Req.FormFile("file")
 
- 	if err != nil {
 
- 		c.Error(500, fmt.Sprintf("FormFile: %v", err))
 
- 		return
 
- 	}
 
- 	defer file.Close()
 
- 	buf := make([]byte, 1024)
 
- 	n, _ := file.Read(buf)
 
- 	if n > 0 {
 
- 		buf = buf[:n]
 
- 	}
 
- 	fileType := http.DetectContentType(buf)
 
- 	allowed := false
 
- 	for _, t := range allowedTypes {
 
- 		t := strings.Trim(t, " ")
 
- 		if t == "*/*" || t == fileType {
 
- 			allowed = true
 
- 			break
 
- 		}
 
- 	}
 
- 	if !allowed {
 
- 		c.Error(400, ErrFileTypeForbidden.Error())
 
- 		return
 
- 	}
 
- 	attach, err := models.NewAttachment(header.Filename, buf, file)
 
- 	if err != nil {
 
- 		c.Error(500, fmt.Sprintf("NewAttachment: %v", err))
 
- 		return
 
- 	}
 
- 	log.Trace("New attachment uploaded: %s", attach.UUID)
 
- 	c.JSON(200, map[string]string{
 
- 		"uuid": attach.UUID,
 
- 	})
 
- }
 
- func UploadIssueAttachment(c *context.Context) {
 
- 	if !setting.AttachmentEnabled {
 
- 		c.NotFound()
 
- 		return
 
- 	}
 
- 	uploadAttachment(c, strings.Split(setting.AttachmentAllowedTypes, ","))
 
- }
 
- func viewIssue(c *context.Context, isPullList bool) {
 
- 	c.Data["RequireHighlightJS"] = true
 
- 	c.Data["RequireDropzone"] = true
 
- 	renderAttachmentSettings(c)
 
- 	index := c.ParamsInt64(":index")
 
- 	if index <= 0 {
 
- 		c.NotFound()
 
- 		return
 
- 	}
 
- 	issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, index)
 
- 	if err != nil {
 
- 		c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err)
 
- 		return
 
- 	}
 
- 	c.Data["Title"] = issue.Title
 
- 	// Make sure type and URL matches.
 
- 	if !isPullList && issue.IsPull {
 
- 		c.Redirect(c.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
 
- 		return
 
- 	} else if isPullList && !issue.IsPull {
 
- 		c.Redirect(c.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
 
- 		return
 
- 	}
 
- 	if issue.IsPull {
 
- 		MustAllowPulls(c)
 
- 		if c.Written() {
 
- 			return
 
- 		}
 
- 		c.Data["PageIsPullList"] = true
 
- 		c.Data["PageIsPullConversation"] = true
 
- 	} else {
 
- 		MustEnableIssues(c)
 
- 		if c.Written() {
 
- 			return
 
- 		}
 
- 		c.Data["PageIsIssueList"] = true
 
- 	}
 
- 	issue.RenderedContent = string(markup.Markdown(issue.Content, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas()))
 
- 	repo := c.Repo.Repository
 
- 	// Get more information if it's a pull request.
 
- 	if issue.IsPull {
 
- 		if issue.PullRequest.HasMerged {
 
- 			c.Data["DisableStatusChange"] = issue.PullRequest.HasMerged
 
- 			PrepareMergedViewPullInfo(c, issue)
 
- 		} else {
 
- 			PrepareViewPullInfo(c, issue)
 
- 		}
 
- 		if c.Written() {
 
- 			return
 
- 		}
 
- 	}
 
- 	// Metas.
 
- 	// Check labels.
 
- 	labelIDMark := make(map[int64]bool)
 
- 	for i := range issue.Labels {
 
- 		labelIDMark[issue.Labels[i].ID] = true
 
- 	}
 
- 	labels, err := models.GetLabelsByRepoID(repo.ID)
 
- 	if err != nil {
 
- 		c.Handle(500, "GetLabelsByRepoID", err)
 
- 		return
 
- 	}
 
- 	hasSelected := false
 
- 	for i := range labels {
 
- 		if labelIDMark[labels[i].ID] {
 
- 			labels[i].IsChecked = true
 
- 			hasSelected = true
 
- 		}
 
- 	}
 
- 	c.Data["HasSelectedLabel"] = hasSelected
 
- 	c.Data["Labels"] = labels
 
- 	// Check milestone and assignee.
 
- 	if c.Repo.IsWriter() {
 
- 		RetrieveRepoMilestonesAndAssignees(c, repo)
 
- 		if c.Written() {
 
- 			return
 
- 		}
 
- 	}
 
- 	if c.IsLogged {
 
- 		// Update issue-user.
 
- 		if err = issue.ReadBy(c.User.ID); err != nil {
 
- 			c.Handle(500, "ReadBy", err)
 
- 			return
 
- 		}
 
- 	}
 
- 	var (
 
- 		tag          models.CommentTag
 
- 		ok           bool
 
- 		marked       = make(map[int64]models.CommentTag)
 
- 		comment      *models.Comment
 
- 		participants = make([]*models.User, 1, 10)
 
- 	)
 
- 	// Render comments and and fetch participants.
 
- 	participants[0] = issue.Poster
 
- 	for _, comment = range issue.Comments {
 
- 		if comment.Type == models.COMMENT_TYPE_COMMENT {
 
- 			comment.RenderedContent = string(markup.Markdown(comment.Content, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas()))
 
- 			// Check tag.
 
- 			tag, ok = marked[comment.PosterID]
 
- 			if ok {
 
- 				comment.ShowTag = tag
 
- 				continue
 
- 			}
 
- 			if repo.IsOwnedBy(comment.PosterID) ||
 
- 				(repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(comment.PosterID)) {
 
- 				comment.ShowTag = models.COMMENT_TAG_OWNER
 
- 			} else if comment.Poster.IsWriterOfRepo(repo) {
 
- 				comment.ShowTag = models.COMMENT_TAG_WRITER
 
- 			} else if comment.PosterID == issue.PosterID {
 
- 				comment.ShowTag = models.COMMENT_TAG_POSTER
 
- 			}
 
- 			marked[comment.PosterID] = comment.ShowTag
 
- 			isAdded := false
 
- 			for j := range participants {
 
- 				if comment.Poster == participants[j] {
 
- 					isAdded = true
 
- 					break
 
- 				}
 
- 			}
 
- 			if !isAdded && !issue.IsPoster(comment.Poster.ID) {
 
- 				participants = append(participants, comment.Poster)
 
- 			}
 
- 		}
 
- 	}
 
- 	if issue.IsPull && issue.PullRequest.HasMerged {
 
- 		pull := issue.PullRequest
 
- 		branchProtected := false
 
- 		protectBranch, err := models.GetProtectBranchOfRepoByName(pull.BaseRepoID, pull.HeadBranch)
 
- 		if err != nil {
 
- 			if !errors.IsErrBranchNotExist(err) {
 
- 				c.ServerError("GetProtectBranchOfRepoByName", err)
 
- 				return
 
- 			}
 
- 		} else {
 
- 			branchProtected = protectBranch.Protected
 
- 		}
 
- 		c.Data["IsPullBranchDeletable"] = pull.BaseRepoID == pull.HeadRepoID &&
 
- 			c.Repo.IsWriter() && c.Repo.GitRepo.IsBranchExist(pull.HeadBranch) &&
 
- 			!branchProtected
 
- 		deleteBranchUrl := template.EscapePound(c.Repo.RepoLink + "/branches/delete/" + pull.HeadBranch)
 
- 		c.Data["DeleteBranchLink"] = fmt.Sprintf("%s?commit=%s&redirect_to=%s", deleteBranchUrl, pull.MergedCommitID, c.Data["Link"])
 
- 	}
 
- 	c.Data["Participants"] = participants
 
- 	c.Data["NumParticipants"] = len(participants)
 
- 	c.Data["Issue"] = issue
 
- 	c.Data["IsIssueOwner"] = c.Repo.IsWriter() || (c.IsLogged && issue.IsPoster(c.User.ID))
 
- 	c.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + c.Data["Link"].(string)
 
- 	c.HTML(200, ISSUE_VIEW)
 
- }
 
- func ViewIssue(c *context.Context) {
 
- 	viewIssue(c, false)
 
- }
 
- func ViewPull(c *context.Context) {
 
- 	viewIssue(c, true)
 
- }
 
- func getActionIssue(c *context.Context) *models.Issue {
 
- 	issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
 
- 	if err != nil {
 
- 		c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err)
 
- 		return nil
 
- 	}
 
- 	// Prevent guests accessing pull requests
 
- 	if !c.Repo.HasAccess() && issue.IsPull {
 
- 		c.NotFound()
 
- 		return nil
 
- 	}
 
- 	return issue
 
- }
 
- func UpdateIssueTitle(c *context.Context) {
 
- 	issue := getActionIssue(c)
 
- 	if c.Written() {
 
- 		return
 
- 	}
 
- 	if !c.IsLogged || (!issue.IsPoster(c.User.ID) && !c.Repo.IsWriter()) {
 
- 		c.Error(403)
 
- 		return
 
- 	}
 
- 	title := c.QueryTrim("title")
 
- 	if len(title) == 0 {
 
- 		c.Error(204)
 
- 		return
 
- 	}
 
- 	if err := issue.ChangeTitle(c.User, title); err != nil {
 
- 		c.Handle(500, "ChangeTitle", err)
 
- 		return
 
- 	}
 
- 	c.JSON(200, map[string]interface{}{
 
- 		"title": issue.Title,
 
- 	})
 
- }
 
- func UpdateIssueContent(c *context.Context) {
 
- 	issue := getActionIssue(c)
 
- 	if c.Written() {
 
- 		return
 
- 	}
 
- 	if !c.IsLogged || (c.User.ID != issue.PosterID && !c.Repo.IsWriter()) {
 
- 		c.Error(403)
 
- 		return
 
- 	}
 
- 	content := c.Query("content")
 
- 	if err := issue.ChangeContent(c.User, content); err != nil {
 
- 		c.Handle(500, "ChangeContent", err)
 
- 		return
 
- 	}
 
- 	c.JSON(200, map[string]string{
 
- 		"content": string(markup.Markdown(issue.Content, c.Query("context"), c.Repo.Repository.ComposeMetas())),
 
- 	})
 
- }
 
- func UpdateIssueLabel(c *context.Context) {
 
- 	issue := getActionIssue(c)
 
- 	if c.Written() {
 
- 		return
 
- 	}
 
- 	if c.Query("action") == "clear" {
 
- 		if err := issue.ClearLabels(c.User); err != nil {
 
- 			c.Handle(500, "ClearLabels", err)
 
- 			return
 
- 		}
 
- 	} else {
 
- 		isAttach := c.Query("action") == "attach"
 
- 		label, err := models.GetLabelOfRepoByID(c.Repo.Repository.ID, c.QueryInt64("id"))
 
- 		if err != nil {
 
- 			if models.IsErrLabelNotExist(err) {
 
- 				c.Error(404, "GetLabelByID")
 
- 			} else {
 
- 				c.Handle(500, "GetLabelByID", err)
 
- 			}
 
- 			return
 
- 		}
 
- 		if isAttach && !issue.HasLabel(label.ID) {
 
- 			if err = issue.AddLabel(c.User, label); err != nil {
 
- 				c.Handle(500, "AddLabel", err)
 
- 				return
 
- 			}
 
- 		} else if !isAttach && issue.HasLabel(label.ID) {
 
- 			if err = issue.RemoveLabel(c.User, label); err != nil {
 
- 				c.Handle(500, "RemoveLabel", err)
 
- 				return
 
- 			}
 
- 		}
 
- 	}
 
- 	c.JSON(200, map[string]interface{}{
 
- 		"ok": true,
 
- 	})
 
- }
 
- func UpdateIssueMilestone(c *context.Context) {
 
- 	issue := getActionIssue(c)
 
- 	if c.Written() {
 
- 		return
 
- 	}
 
- 	oldMilestoneID := issue.MilestoneID
 
- 	milestoneID := c.QueryInt64("id")
 
- 	if oldMilestoneID == milestoneID {
 
- 		c.JSON(200, map[string]interface{}{
 
- 			"ok": true,
 
- 		})
 
- 		return
 
- 	}
 
- 	// Not check for invalid milestone id and give responsibility to owners.
 
- 	issue.MilestoneID = milestoneID
 
- 	if err := models.ChangeMilestoneAssign(c.User, issue, oldMilestoneID); err != nil {
 
- 		c.Handle(500, "ChangeMilestoneAssign", err)
 
- 		return
 
- 	}
 
- 	c.JSON(200, map[string]interface{}{
 
- 		"ok": true,
 
- 	})
 
- }
 
- func UpdateIssueAssignee(c *context.Context) {
 
- 	issue := getActionIssue(c)
 
- 	if c.Written() {
 
- 		return
 
- 	}
 
- 	assigneeID := c.QueryInt64("id")
 
- 	if issue.AssigneeID == assigneeID {
 
- 		c.JSON(200, map[string]interface{}{
 
- 			"ok": true,
 
- 		})
 
- 		return
 
- 	}
 
- 	if err := issue.ChangeAssignee(c.User, assigneeID); err != nil {
 
- 		c.Handle(500, "ChangeAssignee", err)
 
- 		return
 
- 	}
 
- 	c.JSON(200, map[string]interface{}{
 
- 		"ok": true,
 
- 	})
 
- }
 
- func NewComment(c *context.Context, f form.CreateComment) {
 
- 	issue := getActionIssue(c)
 
- 	if c.Written() {
 
- 		return
 
- 	}
 
- 	var attachments []string
 
- 	if setting.AttachmentEnabled {
 
- 		attachments = f.Files
 
- 	}
 
- 	if c.HasError() {
 
- 		c.Flash.Error(c.Data["ErrorMsg"].(string))
 
- 		c.Redirect(fmt.Sprintf("%s/issues/%d", c.Repo.RepoLink, issue.Index))
 
- 		return
 
- 	}
 
- 	var err error
 
- 	var comment *models.Comment
 
- 	defer func() {
 
- 		// Check if issue admin/poster changes the status of issue.
 
- 		if (c.Repo.IsWriter() || (c.IsLogged && issue.IsPoster(c.User.ID))) &&
 
- 			(f.Status == "reopen" || f.Status == "close") &&
 
- 			!(issue.IsPull && issue.PullRequest.HasMerged) {
 
- 			// Duplication and conflict check should apply to reopen pull request.
 
- 			var pr *models.PullRequest
 
- 			if f.Status == "reopen" && issue.IsPull {
 
- 				pull := issue.PullRequest
 
- 				pr, err = models.GetUnmergedPullRequest(pull.HeadRepoID, pull.BaseRepoID, pull.HeadBranch, pull.BaseBranch)
 
- 				if err != nil {
 
- 					if !models.IsErrPullRequestNotExist(err) {
 
- 						c.ServerError("GetUnmergedPullRequest", err)
 
- 						return
 
- 					}
 
- 				}
 
- 				// Regenerate patch and test conflict.
 
- 				if pr == nil {
 
- 					if err = issue.PullRequest.UpdatePatch(); err != nil {
 
- 						c.ServerError("UpdatePatch", err)
 
- 						return
 
- 					}
 
- 					issue.PullRequest.AddToTaskQueue()
 
- 				}
 
- 			}
 
- 			if pr != nil {
 
- 				c.Flash.Info(c.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index))
 
- 			} else {
 
- 				if err = issue.ChangeStatus(c.User, c.Repo.Repository, f.Status == "close"); err != nil {
 
- 					log.Error(2, "ChangeStatus: %v", err)
 
- 				} else {
 
- 					log.Trace("Issue [%d] status changed to closed: %v", issue.ID, issue.IsClosed)
 
- 				}
 
- 			}
 
- 		}
 
- 		// Redirect to comment hashtag if there is any actual content.
 
- 		typeName := "issues"
 
- 		if issue.IsPull {
 
- 			typeName = "pulls"
 
- 		}
 
- 		if comment != nil {
 
- 			c.RawRedirect(fmt.Sprintf("%s/%s/%d#%s", c.Repo.RepoLink, typeName, issue.Index, comment.HashTag()))
 
- 		} else {
 
- 			c.Redirect(fmt.Sprintf("%s/%s/%d", c.Repo.RepoLink, typeName, issue.Index))
 
- 		}
 
- 	}()
 
- 	// Fix #321: Allow empty comments, as long as we have attachments.
 
- 	if len(f.Content) == 0 && len(attachments) == 0 {
 
- 		return
 
- 	}
 
- 	comment, err = models.CreateIssueComment(c.User, c.Repo.Repository, issue, f.Content, attachments)
 
- 	if err != nil {
 
- 		c.ServerError("CreateIssueComment", err)
 
- 		return
 
- 	}
 
- 	log.Trace("Comment created: %d/%d/%d", c.Repo.Repository.ID, issue.ID, comment.ID)
 
- }
 
- func UpdateCommentContent(c *context.Context) {
 
- 	comment, err := models.GetCommentByID(c.ParamsInt64(":id"))
 
- 	if err != nil {
 
- 		c.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err)
 
- 		return
 
- 	}
 
- 	if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() {
 
- 		c.Error(404)
 
- 		return
 
- 	} else if comment.Type != models.COMMENT_TYPE_COMMENT {
 
- 		c.Error(204)
 
- 		return
 
- 	}
 
- 	oldContent := comment.Content
 
- 	comment.Content = c.Query("content")
 
- 	if len(comment.Content) == 0 {
 
- 		c.JSON(200, map[string]interface{}{
 
- 			"content": "",
 
- 		})
 
- 		return
 
- 	}
 
- 	if err = models.UpdateComment(c.User, comment, oldContent); err != nil {
 
- 		c.Handle(500, "UpdateComment", err)
 
- 		return
 
- 	}
 
- 	c.JSON(200, map[string]string{
 
- 		"content": string(markup.Markdown(comment.Content, c.Query("context"), c.Repo.Repository.ComposeMetas())),
 
- 	})
 
- }
 
- func DeleteComment(c *context.Context) {
 
- 	comment, err := models.GetCommentByID(c.ParamsInt64(":id"))
 
- 	if err != nil {
 
- 		c.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err)
 
- 		return
 
- 	}
 
- 	if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() {
 
- 		c.Error(404)
 
- 		return
 
- 	} else if comment.Type != models.COMMENT_TYPE_COMMENT {
 
- 		c.Error(204)
 
- 		return
 
- 	}
 
- 	if err = models.DeleteCommentByID(c.User, comment.ID); err != nil {
 
- 		c.Handle(500, "DeleteCommentByID", err)
 
- 		return
 
- 	}
 
- 	c.Status(200)
 
- }
 
- func Labels(c *context.Context) {
 
- 	c.Data["Title"] = c.Tr("repo.labels")
 
- 	c.Data["PageIsIssueList"] = true
 
- 	c.Data["PageIsLabels"] = true
 
- 	c.Data["RequireMinicolors"] = true
 
- 	c.Data["LabelTemplates"] = models.LabelTemplates
 
- 	c.HTML(200, LABELS)
 
- }
 
- func InitializeLabels(c *context.Context, f form.InitializeLabels) {
 
- 	if c.HasError() {
 
- 		c.Redirect(c.Repo.RepoLink + "/labels")
 
- 		return
 
- 	}
 
- 	list, err := models.GetLabelTemplateFile(f.TemplateName)
 
- 	if err != nil {
 
- 		c.Flash.Error(c.Tr("repo.issues.label_templates.fail_to_load_file", f.TemplateName, err))
 
- 		c.Redirect(c.Repo.RepoLink + "/labels")
 
- 		return
 
- 	}
 
- 	labels := make([]*models.Label, len(list))
 
- 	for i := 0; i < len(list); i++ {
 
- 		labels[i] = &models.Label{
 
- 			RepoID: c.Repo.Repository.ID,
 
- 			Name:   list[i][0],
 
- 			Color:  list[i][1],
 
- 		}
 
- 	}
 
- 	if err := models.NewLabels(labels...); err != nil {
 
- 		c.Handle(500, "NewLabels", err)
 
- 		return
 
- 	}
 
- 	c.Redirect(c.Repo.RepoLink + "/labels")
 
- }
 
- func NewLabel(c *context.Context, f form.CreateLabel) {
 
- 	c.Data["Title"] = c.Tr("repo.labels")
 
- 	c.Data["PageIsLabels"] = true
 
- 	if c.HasError() {
 
- 		c.Flash.Error(c.Data["ErrorMsg"].(string))
 
- 		c.Redirect(c.Repo.RepoLink + "/labels")
 
- 		return
 
- 	}
 
- 	l := &models.Label{
 
- 		RepoID: c.Repo.Repository.ID,
 
- 		Name:   f.Title,
 
- 		Color:  f.Color,
 
- 	}
 
- 	if err := models.NewLabels(l); err != nil {
 
- 		c.Handle(500, "NewLabel", err)
 
- 		return
 
- 	}
 
- 	c.Redirect(c.Repo.RepoLink + "/labels")
 
- }
 
- func UpdateLabel(c *context.Context, f form.CreateLabel) {
 
- 	l, err := models.GetLabelByID(f.ID)
 
- 	if err != nil {
 
- 		switch {
 
- 		case models.IsErrLabelNotExist(err):
 
- 			c.Error(404)
 
- 		default:
 
- 			c.Handle(500, "UpdateLabel", err)
 
- 		}
 
- 		return
 
- 	}
 
- 	l.Name = f.Title
 
- 	l.Color = f.Color
 
- 	if err := models.UpdateLabel(l); err != nil {
 
- 		c.Handle(500, "UpdateLabel", err)
 
- 		return
 
- 	}
 
- 	c.Redirect(c.Repo.RepoLink + "/labels")
 
- }
 
- func DeleteLabel(c *context.Context) {
 
- 	if err := models.DeleteLabel(c.Repo.Repository.ID, c.QueryInt64("id")); err != nil {
 
- 		c.Flash.Error("DeleteLabel: " + err.Error())
 
- 	} else {
 
- 		c.Flash.Success(c.Tr("repo.issues.label_deletion_success"))
 
- 	}
 
- 	c.JSON(200, map[string]interface{}{
 
- 		"redirect": c.Repo.RepoLink + "/labels",
 
- 	})
 
- 	return
 
- }
 
- func Milestones(c *context.Context) {
 
- 	c.Data["Title"] = c.Tr("repo.milestones")
 
- 	c.Data["PageIsIssueList"] = true
 
- 	c.Data["PageIsMilestones"] = true
 
- 	isShowClosed := c.Query("state") == "closed"
 
- 	openCount, closedCount := models.MilestoneStats(c.Repo.Repository.ID)
 
- 	c.Data["OpenCount"] = openCount
 
- 	c.Data["ClosedCount"] = closedCount
 
- 	page := c.QueryInt("page")
 
- 	if page <= 1 {
 
- 		page = 1
 
- 	}
 
- 	var total int
 
- 	if !isShowClosed {
 
- 		total = int(openCount)
 
- 	} else {
 
- 		total = int(closedCount)
 
- 	}
 
- 	c.Data["Page"] = paginater.New(total, setting.UI.IssuePagingNum, page, 5)
 
- 	miles, err := models.GetMilestones(c.Repo.Repository.ID, page, isShowClosed)
 
- 	if err != nil {
 
- 		c.Handle(500, "GetMilestones", err)
 
- 		return
 
- 	}
 
- 	for _, m := range miles {
 
- 		m.NumOpenIssues = int(m.CountIssues(false, false))
 
- 		m.NumClosedIssues = int(m.CountIssues(true, false))
 
- 		if m.NumOpenIssues+m.NumClosedIssues > 0 {
 
- 			m.Completeness = m.NumClosedIssues * 100 / (m.NumOpenIssues + m.NumClosedIssues)
 
- 		}
 
- 		m.RenderedContent = string(markup.Markdown(m.Content, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas()))
 
- 	}
 
- 	c.Data["Milestones"] = miles
 
- 	if isShowClosed {
 
- 		c.Data["State"] = "closed"
 
- 	} else {
 
- 		c.Data["State"] = "open"
 
- 	}
 
- 	c.Data["IsShowClosed"] = isShowClosed
 
- 	c.HTML(200, MILESTONE)
 
- }
 
- func NewMilestone(c *context.Context) {
 
- 	c.Data["Title"] = c.Tr("repo.milestones.new")
 
- 	c.Data["PageIsIssueList"] = true
 
- 	c.Data["PageIsMilestones"] = true
 
- 	c.Data["RequireDatetimepicker"] = true
 
- 	c.Data["DateLang"] = setting.DateLang(c.Locale.Language())
 
- 	c.HTML(200, MILESTONE_NEW)
 
- }
 
- func NewMilestonePost(c *context.Context, f form.CreateMilestone) {
 
- 	c.Data["Title"] = c.Tr("repo.milestones.new")
 
- 	c.Data["PageIsIssueList"] = true
 
- 	c.Data["PageIsMilestones"] = true
 
- 	c.Data["RequireDatetimepicker"] = true
 
- 	c.Data["DateLang"] = setting.DateLang(c.Locale.Language())
 
- 	if c.HasError() {
 
- 		c.HTML(200, MILESTONE_NEW)
 
- 		return
 
- 	}
 
- 	if len(f.Deadline) == 0 {
 
- 		f.Deadline = "9999-12-31"
 
- 	}
 
- 	deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local)
 
- 	if err != nil {
 
- 		c.Data["Err_Deadline"] = true
 
- 		c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), MILESTONE_NEW, &f)
 
- 		return
 
- 	}
 
- 	if err = models.NewMilestone(&models.Milestone{
 
- 		RepoID:   c.Repo.Repository.ID,
 
- 		Name:     f.Title,
 
- 		Content:  f.Content,
 
- 		Deadline: deadline,
 
- 	}); err != nil {
 
- 		c.Handle(500, "NewMilestone", err)
 
- 		return
 
- 	}
 
- 	c.Flash.Success(c.Tr("repo.milestones.create_success", f.Title))
 
- 	c.Redirect(c.Repo.RepoLink + "/milestones")
 
- }
 
- func EditMilestone(c *context.Context) {
 
- 	c.Data["Title"] = c.Tr("repo.milestones.edit")
 
- 	c.Data["PageIsMilestones"] = true
 
- 	c.Data["PageIsEditMilestone"] = true
 
- 	c.Data["RequireDatetimepicker"] = true
 
- 	c.Data["DateLang"] = setting.DateLang(c.Locale.Language())
 
- 	m, err := models.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
 
- 	if err != nil {
 
- 		if models.IsErrMilestoneNotExist(err) {
 
- 			c.Handle(404, "", nil)
 
- 		} else {
 
- 			c.Handle(500, "GetMilestoneByRepoID", err)
 
- 		}
 
- 		return
 
- 	}
 
- 	c.Data["title"] = m.Name
 
- 	c.Data["content"] = m.Content
 
- 	if len(m.DeadlineString) > 0 {
 
- 		c.Data["deadline"] = m.DeadlineString
 
- 	}
 
- 	c.HTML(200, MILESTONE_NEW)
 
- }
 
- func EditMilestonePost(c *context.Context, f form.CreateMilestone) {
 
- 	c.Data["Title"] = c.Tr("repo.milestones.edit")
 
- 	c.Data["PageIsMilestones"] = true
 
- 	c.Data["PageIsEditMilestone"] = true
 
- 	c.Data["RequireDatetimepicker"] = true
 
- 	c.Data["DateLang"] = setting.DateLang(c.Locale.Language())
 
- 	if c.HasError() {
 
- 		c.HTML(200, MILESTONE_NEW)
 
- 		return
 
- 	}
 
- 	if len(f.Deadline) == 0 {
 
- 		f.Deadline = "9999-12-31"
 
- 	}
 
- 	deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local)
 
- 	if err != nil {
 
- 		c.Data["Err_Deadline"] = true
 
- 		c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), MILESTONE_NEW, &f)
 
- 		return
 
- 	}
 
- 	m, err := models.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
 
- 	if err != nil {
 
- 		if models.IsErrMilestoneNotExist(err) {
 
- 			c.Handle(404, "", nil)
 
- 		} else {
 
- 			c.Handle(500, "GetMilestoneByRepoID", err)
 
- 		}
 
- 		return
 
- 	}
 
- 	m.Name = f.Title
 
- 	m.Content = f.Content
 
- 	m.Deadline = deadline
 
- 	if err = models.UpdateMilestone(m); err != nil {
 
- 		c.Handle(500, "UpdateMilestone", err)
 
- 		return
 
- 	}
 
- 	c.Flash.Success(c.Tr("repo.milestones.edit_success", m.Name))
 
- 	c.Redirect(c.Repo.RepoLink + "/milestones")
 
- }
 
- func ChangeMilestonStatus(c *context.Context) {
 
- 	m, err := models.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
 
- 	if err != nil {
 
- 		if models.IsErrMilestoneNotExist(err) {
 
- 			c.Handle(404, "", err)
 
- 		} else {
 
- 			c.Handle(500, "GetMilestoneByRepoID", err)
 
- 		}
 
- 		return
 
- 	}
 
- 	switch c.Params(":action") {
 
- 	case "open":
 
- 		if m.IsClosed {
 
- 			if err = models.ChangeMilestoneStatus(m, false); err != nil {
 
- 				c.Handle(500, "ChangeMilestoneStatus", err)
 
- 				return
 
- 			}
 
- 		}
 
- 		c.Redirect(c.Repo.RepoLink + "/milestones?state=open")
 
- 	case "close":
 
- 		if !m.IsClosed {
 
- 			m.ClosedDate = time.Now()
 
- 			if err = models.ChangeMilestoneStatus(m, true); err != nil {
 
- 				c.Handle(500, "ChangeMilestoneStatus", err)
 
- 				return
 
- 			}
 
- 		}
 
- 		c.Redirect(c.Repo.RepoLink + "/milestones?state=closed")
 
- 	default:
 
- 		c.Redirect(c.Repo.RepoLink + "/milestones")
 
- 	}
 
- }
 
- func DeleteMilestone(c *context.Context) {
 
- 	if err := models.DeleteMilestoneOfRepoByID(c.Repo.Repository.ID, c.QueryInt64("id")); err != nil {
 
- 		c.Flash.Error("DeleteMilestoneByRepoID: " + err.Error())
 
- 	} else {
 
- 		c.Flash.Success(c.Tr("repo.milestones.deletion_success"))
 
- 	}
 
- 	c.JSON(200, map[string]interface{}{
 
- 		"redirect": c.Repo.RepoLink + "/milestones",
 
- 	})
 
- }
 
 
  |