diff --git a/cmd/web.go b/cmd/web.go index 9329b6142..744614f6b 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -30,7 +30,7 @@ import ( "github.com/gogits/gogs/routers/admin" "github.com/gogits/gogs/routers/api/v1" "github.com/gogits/gogs/routers/dev" - // "github.com/gogits/gogs/routers/org" + "github.com/gogits/gogs/routers/org" "github.com/gogits/gogs/routers/repo" "github.com/gogits/gogs/routers/user" ) @@ -101,8 +101,8 @@ func runWeb(*cli.Context) { // Routers. m.Get("/", ignSignIn, routers.Home) - // m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install) - // m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost) + m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install) + m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost) m.Group("", func(r *macaron.Router) { r.Get("/issues", user.Issues) r.Get("/pulls", user.Pulls) @@ -151,6 +151,7 @@ func runWeb(*cli.Context) { r.Get("/ssh", user.SettingsSSHKeys) r.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost) r.Get("/social", user.SettingsSocial) + r.Get("/orgs", user.SettingsOrgs) r.Route("/delete", "GET,POST", user.SettingsDelete) }) }, reqSignIn) @@ -173,8 +174,8 @@ func runWeb(*cli.Context) { m.Group("/repo", func(r *macaron.Router) { r.Get("/create", repo.Create) r.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost) - // r.Get("/migrate", repo.Migrate) - // r.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost) + r.Get("/migrate", repo.Migrate) + r.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost) }, reqSignIn) adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true}) @@ -210,91 +211,92 @@ func runWeb(*cli.Context) { dev.RegisterDebugRoutes(m) } - // reqTrueOwner := middleware.RequireTrueOwner() - - // m.Group("/org", func(r *macaron.Router) { - // r.Get("/create", org.New) - // r.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.NewPost) - // r.Get("/:org", org.Home) - // r.Get("/:org/dashboard", org.Dashboard) - // r.Get("/:org/members", org.Members) - - // r.Get("/:org/teams", org.Teams) - // r.Get("/:org/teams/new", org.NewTeam) - // r.Post("/:org/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost) - // r.Get("/:org/teams/:team/edit", org.EditTeam) - - // r.Get("/:org/team/:team", org.SingleTeam) - - // r.Get("/:org/settings", org.Settings) - // r.Post("/:org/settings", bindIgnErr(auth.OrgSettingForm{}), org.SettingsPost) - // r.Post("/:org/settings/delete", org.DeletePost) - // }, reqSignIn) - - // m.Group("/:username/:reponame", func(r *macaron.Router) { - // r.Get("/settings", repo.Setting) - // r.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingPost) - - // m.Group("/settings", func(r *macaron.Router) { - // r.Get("/collaboration", repo.Collaboration) - // r.Post("/collaboration", repo.CollaborationPost) - // r.Get("/hooks", repo.WebHooks) - // r.Get("/hooks/add", repo.WebHooksAdd) - // r.Post("/hooks/add", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksAddPost) - // r.Get("/hooks/:id", repo.WebHooksEdit) - // r.Post("/hooks/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) - // }) - // }, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner) - - // m.Group("/:username/:reponame", func(r *macaron.Router) { - // r.Get("/action/:action", repo.Action) - - // m.Group("/issues", func(r *macaron.Router) { - // r.Get("/new", repo.CreateIssue) - // r.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost) - // r.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue) - // r.Post("/:index/label", repo.UpdateIssueLabel) - // r.Post("/:index/milestone", repo.UpdateIssueMilestone) - // r.Post("/:index/assignee", repo.UpdateAssignee) - // r.Get("/:index/attachment/:id", repo.IssueGetAttachment) - // 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) - // r.Get("/milestones/:index/edit", repo.UpdateMilestone) - // r.Post("/milestones/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.UpdateMilestonePost) - // r.Get("/milestones/:index/:action", repo.UpdateMilestone) - // }) - - // r.Post("/comment/:action", repo.Comment) - // r.Get("/releases/new", repo.NewRelease) - // r.Get("/releases/edit/:tagname", repo.EditRelease) - // }, reqSignIn, middleware.RepoAssignment(true)) - - // m.Group("/:username/:reponame", func(r *macaron.Router) { - // r.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) - // r.Post("/releases/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) - // }, reqSignIn, middleware.RepoAssignment(true, true)) - - // m.Group("/:username/:reponame", func(r *macaron.Router) { - // r.Get("/issues", repo.Issues) - // r.Get("/issues/:index", repo.ViewIssue) - // r.Get("/pulls", repo.Pulls) - // r.Get("/branches", repo.Branches) - // }, ignSignIn, middleware.RepoAssignment(true)) + reqTrueOwner := middleware.RequireTrueOwner() + + // Organization routers. + m.Group("/org", func(r *macaron.Router) { + r.Get("/create", org.New) + r.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.NewPost) + r.Get("/:org", org.Home) + r.Get("/:org/dashboard", org.Dashboard) + r.Get("/:org/members", org.Members) + + r.Get("/:org/teams", org.Teams) + r.Get("/:org/teams/new", org.NewTeam) + r.Post("/:org/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost) + r.Get("/:org/teams/:team/edit", org.EditTeam) + + r.Get("/:org/team/:team", org.SingleTeam) + + r.Get("/:org/settings", org.Settings) + r.Post("/:org/settings", bindIgnErr(auth.OrgSettingForm{}), org.SettingsPost) + r.Post("/:org/settings/delete", org.DeletePost) + }, reqSignIn) + + m.Group("/:username/:reponame", func(r *macaron.Router) { + r.Get("/settings", repo.Setting) + r.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingPost) + + m.Group("/settings", func(r *macaron.Router) { + r.Get("/collaboration", repo.Collaboration) + r.Post("/collaboration", repo.CollaborationPost) + r.Get("/hooks", repo.WebHooks) + r.Get("/hooks/add", repo.WebHooksAdd) + r.Post("/hooks/add", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksAddPost) + r.Get("/hooks/:id", repo.WebHooksEdit) + r.Post("/hooks/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + }) + }, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner) + + m.Group("/:username/:reponame", func(r *macaron.Router) { + // r.Get("/action/:action", repo.Action) + + m.Group("/issues", func(r *macaron.Router) { + r.Get("/new", repo.CreateIssue) + r.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost) + r.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue) + r.Post("/:index/label", repo.UpdateIssueLabel) + r.Post("/:index/milestone", repo.UpdateIssueMilestone) + r.Post("/:index/assignee", repo.UpdateAssignee) + r.Get("/:index/attachment/:id", repo.IssueGetAttachment) + 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) + r.Get("/milestones/:index/edit", repo.UpdateMilestone) + r.Post("/milestones/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.UpdateMilestonePost) + r.Get("/milestones/:index/:action", repo.UpdateMilestone) + }) + + r.Post("/comment/:action", repo.Comment) + r.Get("/releases/new", repo.NewRelease) + r.Get("/releases/edit/:tagname", repo.EditRelease) + }, reqSignIn, middleware.RepoAssignment(true)) + + m.Group("/:username/:reponame", func(r *macaron.Router) { + r.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) + r.Post("/releases/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) + }, reqSignIn, middleware.RepoAssignment(true, true)) + + m.Group("/:username/:reponame", func(r *macaron.Router) { + r.Get("/issues", repo.Issues) + r.Get("/issues/:index", repo.ViewIssue) + r.Get("/pulls", repo.Pulls) + r.Get("/branches", repo.Branches) + }, ignSignIn, middleware.RepoAssignment(true)) m.Group("/:username/:reponame", func(r *macaron.Router) { r.Get("/src/:branchname", repo.Home) r.Get("/src/:branchname/*", repo.Home) - r.Get("/raw/:branchname/**", repo.SingleDownload) - // r.Get("/commits/:branchname", repo.Commits) - // r.Get("/commits/:branchname/search", repo.SearchCommits) - // r.Get("/commits/:branchname/**", repo.FileHistory) - // r.Get("/commit/:branchname", repo.Diff) - // r.Get("/commit/:branchname/**", repo.Diff) - // r.Get("/releases", repo.Releases) + r.Get("/raw/:branchname/*", repo.SingleDownload) + r.Get("/commits/:branchname", repo.Commits) + r.Get("/commits/:branchname/search", repo.SearchCommits) + r.Get("/commits/:branchname/*", repo.FileHistory) + r.Get("/commit/:branchname", repo.Diff) + r.Get("/commit/:branchname/*", repo.Diff) + r.Get("/releases", repo.Releases) r.Get("/archive/*.*", repo.Download) }, ignSignIn, middleware.RepoAssignment(true, true)) diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index b8832167a..c673ca9f4 100644 --- a/conf/locale/locale_en-US.ini +++ b/conf/locale/locale_en-US.ini @@ -89,6 +89,7 @@ profile = Profile password = Password ssh_keys = SSH Keys social = Social Accounts +orgs = Organizations delete = Delete Accoount public_profile = Public Profile @@ -118,6 +119,7 @@ add_on = Added on last_used = Last used on no_activity = No recent activity +manage_orgs = Manage Organizations manage_social = Manage Associated Social Accounts delete_account = Delete Your Account diff --git a/conf/locale/locale_zh-CN.ini b/conf/locale/locale_zh-CN.ini index 30e683071..ff542a374 100644 --- a/conf/locale/locale_zh-CN.ini +++ b/conf/locale/locale_zh-CN.ini @@ -89,6 +89,7 @@ profile = 个人信息 password = 修改密码 ssh_keys = 管理 SSH 密钥 social = 社交帐号绑定 +orgs = 管理组织 delete = 删除帐户 public_profile = 公开信息 @@ -118,6 +119,7 @@ add_on = 增加于 last_used = 上次使用在 no_activity = 没有最近活动 +manage_orgs = 管理我的组织 manage_social = 管理关联社交帐户 delete_account = 删除当前帐户 diff --git a/gogs.go b/gogs.go index e432d6bed..53f96e90f 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.4.7.0725 Alpha" +const APP_VER = "0.4.7.0726 Alpha" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/release.go b/models/release.go index 3e1a78118..012b6cc5c 100644 --- a/models/release.go +++ b/models/release.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/gogits/git" + "github.com/gogits/gogs/modules/git" ) var ( diff --git a/modules/base/template.go b/modules/base/template.go index 7589fdaaf..b9968bae6 100644 --- a/modules/base/template.go +++ b/modules/base/template.go @@ -106,7 +106,6 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ "CreateCaptcha": func() string { return "" }, } -// TODO: Legacy type Actioner interface { GetOpType() int GetActUserName() string diff --git a/modules/git/commit.go b/modules/git/commit.go index f61f2927b..52348fefe 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -73,6 +73,14 @@ func (c *Commit) CommitsCount() (int, error) { return c.repo.commitsCount(c.Id) } +func (c *Commit) SearchCommits(keyword string) (*list.List, error) { + return c.repo.searchCommits(c.Id, keyword) +} + +func (c *Commit) CommitsByRange(page int) (*list.List, error) { + return c.repo.commitsByRange(c.Id, page) +} + func (c *Commit) GetCommitOfRelPath(relPath string) (*Commit, error) { return c.repo.getCommitOfRelPath(c.Id, relPath) } diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index b1ea5a29a..0e39963e6 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -32,7 +32,18 @@ func (repo *Repository) GetCommitOfBranch(branchName string) (*Commit, error) { if err != nil { return nil, err } + return repo.GetCommit(commitId) +} + +func (repo *Repository) GetCommitIdOfTag(tagName string) (string, error) { + return repo.getCommitIdOfRef("refs/tags/" + tagName) +} +func (repo *Repository) GetCommitOfTag(tagName string) (*Commit, error) { + commitId, err := repo.GetCommitIdOfTag(tagName) + if err != nil { + return nil, err + } return repo.GetCommit(commitId) } @@ -212,6 +223,32 @@ func (repo *Repository) commitsBefore(lock *sync.Mutex, l *list.List, parent *li return nil } +func (repo *Repository) CommitsCount(commitId string) (int, error) { + id, err := NewIdFromString(commitId) + if err != nil { + return 0, err + } + return repo.commitsCount(id) +} + +func (repo *Repository) FileCommitsCount(branch, file string) (int, error) { + stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", + branch, "--", file) + if err != nil { + return 0, errors.New(stderr) + } + return com.StrTo(strings.TrimSpace(stdout)).Int() +} + +func (repo *Repository) CommitsByFileAndRange(branch, file string, page int) (*list.List, error) { + stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", branch, + "--skip="+com.ToStr((page-1)*50), "--max-count=50", prettyLogFormat, "--", file) + if err != nil { + return nil, errors.New(string(stderr)) + } + return parsePrettyFormatLog(repo, stdout) +} + func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) { l := list.New() lock := new(sync.Mutex) @@ -219,6 +256,26 @@ func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) { return l, err } +func (repo *Repository) searchCommits(id sha1, keyword string) (*list.List, error) { + stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", id.String(), "-100", + "-i", "--grep="+keyword, prettyLogFormat) + if err != nil { + return nil, err + } else if len(stderr) > 0 { + return nil, errors.New(string(stderr)) + } + return parsePrettyFormatLog(repo, stdout) +} + +func (repo *Repository) commitsByRange(id sha1, page int) (*list.List, error) { + stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", id.String(), + "--skip="+com.ToStr((page-1)*50), "--max-count=50", prettyLogFormat) + if err != nil { + return nil, errors.New(string(stderr)) + } + return parsePrettyFormatLog(repo, stdout) +} + func (repo *Repository) getCommitOfRelPath(id sha1, relPath string) (*Commit, error) { stdout, _, err := com.ExecCmdDir(repo.Path, "git", "log", "-1", prettyLogFormat, id.String(), "--", relPath) if err != nil { diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index cefbe783a..21818f3e6 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -30,6 +30,14 @@ func (repo *Repository) GetTags() ([]string, error) { return tags[:len(tags)-1], nil } +func (repo *Repository) CreateTag(tagName, idStr string) error { + _, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", tagName, idStr) + if err != nil { + return errors.New(stderr) + } + return nil +} + func (repo *Repository) getTag(id sha1) (*Tag, error) { if repo.tagCache != nil { if t, ok := repo.tagCache[id]; ok { diff --git a/modules/git/tree_blob.go b/modules/git/tree_blob.go index debc722bc..f996aba37 100644 --- a/modules/git/tree_blob.go +++ b/modules/git/tree_blob.go @@ -44,3 +44,16 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) { } return nil, fmt.Errorf("GetTreeEntryByPath: %v", ErrNotExist) } + +func (t *Tree) GetBlobByPath(rpath string) (*Blob, error) { + entry, err := t.GetTreeEntryByPath(rpath) + if err != nil { + return nil, err + } + + if !entry.IsDir() { + return entry.Blob(), nil + } + + return nil, ErrNotExist +} diff --git a/modules/git/utils.go b/modules/git/utils.go index 3c0c60f23..26eef2319 100644 --- a/modules/git/utils.go +++ b/modules/git/utils.go @@ -5,12 +5,33 @@ package git import ( + "bytes" + "container/list" "path/filepath" "strings" ) const prettyLogFormat = `--pretty=format:%H` +func parsePrettyFormatLog(repo *Repository, logByts []byte) (*list.List, error) { + l := list.New() + if len(logByts) == 0 { + return l, nil + } + + parts := bytes.Split(logByts, []byte{'\n'}) + + for _, commitId := range parts { + commit, err := repo.GetCommit(string(commitId)) + if err != nil { + return nil, err + } + l.PushBack(commit) + } + + return l, nil +} + func RefEndName(refStr string) string { index := strings.LastIndex(refStr, "/") if index != -1 { diff --git a/public/ng/css/github.min.css b/public/css/github.min.css similarity index 100% rename from public/ng/css/github.min.css rename to public/css/github.min.css diff --git a/public/img/avatar_default.jpg b/public/img/avatar_default.jpg index 728ec5af6..f97aaaf04 100644 Binary files a/public/img/avatar_default.jpg and b/public/img/avatar_default.jpg differ diff --git a/public/img/favicon.bak.png b/public/img/favicon.bak.png index ba9bd0375..33d8f1cd2 100644 Binary files a/public/img/favicon.bak.png and b/public/img/favicon.bak.png differ diff --git a/public/img/gogs-lg.png b/public/img/gogs-lg.png index 384a58d20..fde06eb2b 100644 Binary files a/public/img/gogs-lg.png and b/public/img/gogs-lg.png differ diff --git a/public/ng/css/font-awesome.min.css b/public/ng/css/font-awesome.min.css deleted file mode 100644 index 3d920fc87..000000000 --- a/public/ng/css/font-awesome.min.css +++ /dev/null @@ -1,4 +0,0 @@ -/*! - * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.1.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-square:before,.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"} \ No newline at end of file diff --git a/public/ng/css/gogs.css b/public/ng/css/gogs.css index 7afba6b62..96cf77a63 100644 --- a/public/ng/css/gogs.css +++ b/public/ng/css/gogs.css @@ -373,7 +373,7 @@ img.avatar-30 { display: inline-block; text-decoration: none; -webkit-font-smoothing: antialiased; - margin-right: 8px; + margin-left: 30px; } .markdown a span.octicon-link { opacity: 0; @@ -1058,6 +1058,9 @@ The register and sign-in page style } #repo-bare-start pre { margin: 0 40px; + padding: 6px 10px; + border: 1px solid #ddd; + background: #f8f8f8; } .repo-bare #repo-bare-start h2 { margin-top: 30px; @@ -1073,6 +1076,7 @@ The register and sign-in page style margin-right: 200px; } .repo-bare #repo-clone-help { + clear: both; width: 100%; } .repo-bare #repo-clone-url { diff --git a/routers/org/members.go b/routers/org/members.go index c6f49b14b..ac278d4e6 100644 --- a/routers/org/members.go +++ b/routers/org/members.go @@ -5,12 +5,10 @@ package org import ( - "github.com/go-martini/martini" - "github.com/gogits/gogs/modules/middleware" ) -func Members(ctx *middleware.Context, params martini.Params) { - ctx.Data["Title"] = "Organization " + params["org"] + " Members" +func Members(ctx *middleware.Context) { + ctx.Data["Title"] = "Organization " + ctx.Params(":org") + " Members" ctx.HTML(200, "org/members") } diff --git a/routers/org/org.go b/routers/org/org.go index 4c8854974..74527d600 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -5,9 +5,7 @@ package org import ( - "github.com/go-martini/martini" - - "github.com/gogits/gogs-ng/models" + "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" @@ -21,10 +19,10 @@ const ( SETTINGS base.TplName = "org/settings" ) -func Home(ctx *middleware.Context, params martini.Params) { - ctx.Data["Title"] = "Organization " + params["org"] +func Home(ctx *middleware.Context) { + ctx.Data["Title"] = "Organization " + ctx.Params(":org") - org, err := models.GetUserByName(params["org"]) + org, err := models.GetUserByName(ctx.Params(":org")) if err != nil { if err == models.ErrUserNotExist { ctx.Handle(404, "org.Home(GetUserByName)", err) @@ -99,12 +97,12 @@ func NewPost(ctx *middleware.Context, form auth.CreateOrgForm) { ctx.Redirect("/org/" + form.OrgName + "/dashboard") } -func Dashboard(ctx *middleware.Context, params martini.Params) { +func Dashboard(ctx *middleware.Context) { ctx.Data["Title"] = "Dashboard" ctx.Data["PageIsUserDashboard"] = true ctx.Data["PageIsOrgDashboard"] = true - org, err := models.GetUserByName(params["org"]) + org, err := models.GetUserByName(ctx.Params(":org")) if err != nil { if err == models.ErrUserNotExist { ctx.Handle(404, "org.Dashboard(GetUserByName)", err) @@ -114,11 +112,11 @@ func Dashboard(ctx *middleware.Context, params martini.Params) { return } - // if err := ctx.User.GetOrganizations(); err != nil { - // ctx.Handle(500, "home.Dashboard(GetOrganizations)", err) - // return - // } - // ctx.Data["Orgs"] = ctx.User.Orgs + if err := ctx.User.GetOrganizations(); err != nil { + ctx.Handle(500, "home.Dashboard(GetOrganizations)", err) + return + } + ctx.Data["Orgs"] = ctx.User.Orgs ctx.Data["ContextUser"] = org ctx.Data["MyRepos"], err = models.GetRepositories(org.Id, true) @@ -137,10 +135,10 @@ func Dashboard(ctx *middleware.Context, params martini.Params) { ctx.HTML(200, user.DASHBOARD) } -func Settings(ctx *middleware.Context, params martini.Params) { +func Settings(ctx *middleware.Context) { ctx.Data["Title"] = "Settings" - org, err := models.GetUserByName(params["org"]) + org, err := models.GetUserByName(ctx.Params(":org")) if err != nil { if err == models.ErrUserNotExist { ctx.Handle(404, "org.Settings(GetUserByName)", err) @@ -154,10 +152,10 @@ func Settings(ctx *middleware.Context, params martini.Params) { ctx.HTML(200, SETTINGS) } -func SettingsPost(ctx *middleware.Context, params martini.Params, form auth.OrgSettingForm) { +func SettingsPost(ctx *middleware.Context, form auth.OrgSettingForm) { ctx.Data["Title"] = "Settings" - org, err := models.GetUserByName(params["org"]) + org, err := models.GetUserByName(ctx.Params(":org")) if err != nil { if err == models.ErrUserNotExist { ctx.Handle(404, "org.SettingsPost(GetUserByName)", err) @@ -187,10 +185,10 @@ func SettingsPost(ctx *middleware.Context, params martini.Params, form auth.OrgS ctx.Redirect("/org/" + org.Name + "/settings") } -func DeletePost(ctx *middleware.Context, params martini.Params) { +func DeletePost(ctx *middleware.Context) { ctx.Data["Title"] = "Settings" - org, err := models.GetUserByName(params["org"]) + org, err := models.GetUserByName(ctx.Params(":org")) if err != nil { if err == models.ErrUserNotExist { ctx.Handle(404, "org.DeletePost(GetUserByName)", err) diff --git a/routers/org/teams.go b/routers/org/teams.go index 0b17f4636..d494ddc04 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -5,8 +5,6 @@ package org import ( - "github.com/go-martini/martini" - "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/base" @@ -19,10 +17,10 @@ const ( TEAM_NEW base.TplName = "org/team_new" ) -func Teams(ctx *middleware.Context, params martini.Params) { - ctx.Data["Title"] = "Organization " + params["org"] + " Teams" +func Teams(ctx *middleware.Context) { + ctx.Data["Title"] = "Organization " + ctx.Params(":org") + " Teams" - org, err := models.GetUserByName(params["org"]) + org, err := models.GetUserByName(ctx.Params(":org")) if err != nil { if err == models.ErrUserNotExist { ctx.Handle(404, "org.Teams(GetUserByName)", err) @@ -48,8 +46,8 @@ func Teams(ctx *middleware.Context, params martini.Params) { ctx.HTML(200, TEAMS) } -func NewTeam(ctx *middleware.Context, params martini.Params) { - org, err := models.GetUserByName(params["org"]) +func NewTeam(ctx *middleware.Context) { + org, err := models.GetUserByName(ctx.Params(":org")) if err != nil { if err == models.ErrUserNotExist { ctx.Handle(404, "org.NewTeam(GetUserByName)", err) @@ -69,8 +67,8 @@ func NewTeam(ctx *middleware.Context, params martini.Params) { ctx.HTML(200, TEAM_NEW) } -func NewTeamPost(ctx *middleware.Context, params martini.Params, form auth.CreateTeamForm) { - org, err := models.GetUserByName(params["org"]) +func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { + org, err := models.GetUserByName(ctx.Params(":org")) if err != nil { if err == models.ErrUserNotExist { ctx.Handle(404, "org.NewTeamPost(GetUserByName)", err) @@ -125,12 +123,12 @@ func NewTeamPost(ctx *middleware.Context, params martini.Params, form auth.Creat ctx.Redirect("/org/" + org.LowerName + "/teams/" + t.LowerName) } -func EditTeam(ctx *middleware.Context, params martini.Params) { - ctx.Data["Title"] = "Organization " + params["org"] + " Edit Team" +func EditTeam(ctx *middleware.Context) { + ctx.Data["Title"] = "Organization " + ctx.Params(":org") + " Edit Team" ctx.HTML(200, "org/edit_team") } -func SingleTeam(ctx *middleware.Context,params martini.Params){ - ctx.Data["Title"] = "single-team"+params["org"] - ctx.HTML(200,"org/team") +func SingleTeam(ctx *middleware.Context) { + ctx.Data["Title"] = "single-team" + ctx.Params(":org") + ctx.HTML(200, "org/team") } diff --git a/routers/repo/commit.go b/routers/repo/commit.go index 71b483823..6320123b4 100644 --- a/routers/repo/commit.go +++ b/routers/repo/commit.go @@ -4,224 +4,221 @@ package repo -// import ( -// "path" - -// "github.com/Unknwon/com" -// "github.com/go-martini/martini" - -// "github.com/gogits/gogs/models" -// "github.com/gogits/gogs/modules/base" -// "github.com/gogits/gogs/modules/middleware" -// ) - -// const ( -// COMMITS base.TplName = "repo/commits" -// DIFF base.TplName = "repo/diff" -// ) - -// func Commits(ctx *middleware.Context, params martini.Params) { -// ctx.Data["IsRepoToolbarCommits"] = true - -// userName := ctx.Repo.Owner.Name -// repoName := ctx.Repo.Repository.Name - -// brs, err := ctx.Repo.GitRepo.GetBranches() -// if err != nil { -// ctx.Handle(500, "repo.Commits(GetBranches)", err) -// return -// } else if len(brs) == 0 { -// ctx.Handle(404, "repo.Commits(GetBranches)", nil) -// return -// } - -// commitsCount, err := ctx.Repo.Commit.CommitsCount() -// if err != nil { -// ctx.Handle(500, "repo.Commits(GetCommitsCount)", err) -// return -// } - -// // Calculate and validate page number. -// page, _ := com.StrTo(ctx.Query("p")).Int() -// if page < 1 { -// page = 1 -// } -// lastPage := page - 1 -// if lastPage < 0 { -// lastPage = 0 -// } -// nextPage := page + 1 -// if nextPage*50 > commitsCount { -// nextPage = 0 -// } - -// // Both `git log branchName` and `git log commitId` work. -// // ctx.Data["Commits"], err = ctx.Repo.Commit.CommitsByRange(page) -// // if err != nil { -// // ctx.Handle(500, "repo.Commits(CommitsByRange)", err) -// // return -// // } - -// ctx.Data["Username"] = userName -// ctx.Data["Reponame"] = repoName -// ctx.Data["CommitCount"] = commitsCount -// ctx.Data["LastPageNum"] = lastPage -// ctx.Data["NextPageNum"] = nextPage -// ctx.HTML(200, COMMITS) -// } - -// func SearchCommits(ctx *middleware.Context, params martini.Params) { -// ctx.Data["IsSearchPage"] = true -// ctx.Data["IsRepoToolbarCommits"] = true - -// keyword := ctx.Query("q") -// if len(keyword) == 0 { -// ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchName) -// return -// } - -// userName := params["username"] -// repoName := params["reponame"] - -// brs, err := ctx.Repo.GitRepo.GetBranches() -// if err != nil { -// ctx.Handle(500, "repo.SearchCommits(GetBranches)", err) -// return -// } else if len(brs) == 0 { -// ctx.Handle(404, "repo.SearchCommits(GetBranches)", nil) -// return -// } - -// // commits, err := ctx.Repo.Commit.SearchCommits(keyword) -// // if err != nil { -// // ctx.Handle(500, "repo.SearchCommits(SearchCommits)", err) -// // return -// // } - -// ctx.Data["Keyword"] = keyword -// ctx.Data["Username"] = userName -// ctx.Data["Reponame"] = repoName -// // ctx.Data["CommitCount"] = commits.Len() -// // ctx.Data["Commits"] = commits -// ctx.HTML(200, COMMITS) -// } - -// func Diff(ctx *middleware.Context, params martini.Params) { -// ctx.Data["IsRepoToolbarCommits"] = true - -// userName := ctx.Repo.Owner.Name -// repoName := ctx.Repo.Repository.Name -// commitId := ctx.Repo.CommitId - -// commit := ctx.Repo.Commit - -// diff, err := models.GetDiff(models.RepoPath(userName, repoName), commitId) -// if err != nil { -// ctx.Handle(404, "repo.Diff(GetDiff)", err) -// return -// } - -// isImageFile := func(name string) bool { -// // blob, err := ctx.Repo.Commit.GetBlobByPath(name) -// // if err != nil { -// // return false -// // } - -// // dataRc, err := blob.Data() -// // if err != nil { -// // return false -// // } -// // buf := make([]byte, 1024) -// // n, _ := dataRc.Read(buf) -// // if n > 0 { -// // buf = buf[:n] -// // } -// // dataRc.Close() -// // _, isImage := base.IsImageFile(buf) -// // return isImage -// return false -// } - -// parents := make([]string, commit.ParentCount()) -// for i := 0; i < commit.ParentCount(); i++ { -// sha, err := commit.ParentId(i) -// parents[i] = sha.String() -// if err != nil { -// ctx.Handle(404, "repo.Diff", err) -// return -// } -// } - -// ctx.Data["Username"] = userName -// ctx.Data["Reponame"] = repoName -// ctx.Data["IsImageFile"] = isImageFile -// ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitId) -// ctx.Data["Commit"] = commit -// ctx.Data["Diff"] = diff -// ctx.Data["Parents"] = parents -// ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 -// ctx.Data["SourcePath"] = "/" + path.Join(userName, repoName, "src", commitId) -// ctx.Data["RawPath"] = "/" + path.Join(userName, repoName, "raw", commitId) -// ctx.HTML(200, DIFF) -// } - -// func FileHistory(ctx *middleware.Context, params martini.Params) { -// ctx.Data["IsRepoToolbarCommits"] = true - -// fileName := params["_1"] -// if len(fileName) == 0 { -// Commits(ctx, params) -// return -// } - -// userName := ctx.Repo.Owner.Name -// repoName := ctx.Repo.Repository.Name -// branchName := params["branchname"] - -// brs, err := ctx.Repo.GitRepo.GetBranches() -// if err != nil { -// ctx.Handle(500, "repo.FileHistory", err) -// return -// } else if len(brs) == 0 { -// ctx.Handle(404, "repo.FileHistory", nil) -// return -// } - -// // commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(branchName, fileName) -// // if err != nil { -// // ctx.Handle(500, "repo.FileHistory(GetCommitsCount)", err) -// // return -// // } else if commitsCount == 0 { -// // ctx.Handle(404, "repo.FileHistory", nil) -// // return -// // } - -// // Calculate and validate page number. -// // page, _ := base.StrTo(ctx.Query("p")).Int() -// // if page < 1 { -// // page = 1 -// // } -// // lastPage := page - 1 -// // if lastPage < 0 { -// // lastPage = 0 -// // } -// // nextPage := page + 1 -// // if nextPage*50 > commitsCount { -// // nextPage = 0 -// // } - -// // ctx.Data["Commits"], err = ctx.Repo.GitRepo.CommitsByFileAndRange( -// // branchName, fileName, page) -// // if err != nil { -// // ctx.Handle(500, "repo.FileHistory(CommitsByRange)", err) -// // return -// // } - -// ctx.Data["Username"] = userName -// ctx.Data["Reponame"] = repoName -// ctx.Data["FileName"] = fileName -// // ctx.Data["CommitCount"] = commitsCount -// // ctx.Data["LastPageNum"] = lastPage -// // ctx.Data["NextPageNum"] = nextPage -// ctx.HTML(200, COMMITS) -// } +import ( + "path" + + "github.com/Unknwon/com" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/middleware" +) + +const ( + COMMITS base.TplName = "repo/commits" + DIFF base.TplName = "repo/diff" +) + +func Commits(ctx *middleware.Context) { + ctx.Data["IsRepoToolbarCommits"] = true + + userName := ctx.Repo.Owner.Name + repoName := ctx.Repo.Repository.Name + + brs, err := ctx.Repo.GitRepo.GetBranches() + if err != nil { + ctx.Handle(500, "GetBranches", err) + return + } else if len(brs) == 0 { + ctx.Handle(404, "GetBranches", nil) + return + } + + commitsCount, err := ctx.Repo.Commit.CommitsCount() + if err != nil { + ctx.Handle(500, "GetCommitsCount", err) + return + } + + // Calculate and validate page number. + page, _ := com.StrTo(ctx.Query("p")).Int() + if page < 1 { + page = 1 + } + lastPage := page - 1 + if lastPage < 0 { + lastPage = 0 + } + nextPage := page + 1 + if nextPage*50 > commitsCount { + nextPage = 0 + } + + // Both `git log branchName` and `git log commitId` work. + ctx.Data["Commits"], err = ctx.Repo.Commit.CommitsByRange(page) + if err != nil { + ctx.Handle(500, "CommitsByRange", err) + return + } + + ctx.Data["Username"] = userName + ctx.Data["Reponame"] = repoName + ctx.Data["CommitCount"] = commitsCount + ctx.Data["LastPageNum"] = lastPage + ctx.Data["NextPageNum"] = nextPage + ctx.HTML(200, COMMITS) +} + +func SearchCommits(ctx *middleware.Context) { + ctx.Data["IsSearchPage"] = true + ctx.Data["IsRepoToolbarCommits"] = true + + keyword := ctx.Query("q") + if len(keyword) == 0 { + ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchName) + return + } + + userName := ctx.Params(":username") + repoName := ctx.Params(":reponame") + + brs, err := ctx.Repo.GitRepo.GetBranches() + if err != nil { + ctx.Handle(500, "GetBranches", err) + return + } else if len(brs) == 0 { + ctx.Handle(404, "GetBranches", nil) + return + } + + commits, err := ctx.Repo.Commit.SearchCommits(keyword) + if err != nil { + ctx.Handle(500, "repo.SearchCommits(SearchCommits)", err) + return + } + + ctx.Data["Keyword"] = keyword + ctx.Data["Username"] = userName + ctx.Data["Reponame"] = repoName + ctx.Data["CommitCount"] = commits.Len() + ctx.Data["Commits"] = commits + ctx.HTML(200, COMMITS) +} + +func Diff(ctx *middleware.Context) { + ctx.Data["IsRepoToolbarCommits"] = true + + userName := ctx.Repo.Owner.Name + repoName := ctx.Repo.Repository.Name + commitId := ctx.Repo.CommitId + + commit := ctx.Repo.Commit + + diff, err := models.GetDiff(models.RepoPath(userName, repoName), commitId) + if err != nil { + ctx.Handle(404, "GetDiff", err) + return + } + + isImageFile := func(name string) bool { + blob, err := ctx.Repo.Commit.GetBlobByPath(name) + if err != nil { + return false + } + + dataRc, err := blob.Data() + if err != nil { + return false + } + buf := make([]byte, 1024) + n, _ := dataRc.Read(buf) + if n > 0 { + buf = buf[:n] + } + _, isImage := base.IsImageFile(buf) + return isImage + } + + parents := make([]string, commit.ParentCount()) + for i := 0; i < commit.ParentCount(); i++ { + sha, err := commit.ParentId(i) + parents[i] = sha.String() + if err != nil { + ctx.Handle(404, "repo.Diff", err) + return + } + } + + ctx.Data["Username"] = userName + ctx.Data["Reponame"] = repoName + ctx.Data["IsImageFile"] = isImageFile + ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitId) + ctx.Data["Commit"] = commit + ctx.Data["Diff"] = diff + ctx.Data["Parents"] = parents + ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 + ctx.Data["SourcePath"] = "/" + path.Join(userName, repoName, "src", commitId) + ctx.Data["RawPath"] = "/" + path.Join(userName, repoName, "raw", commitId) + ctx.HTML(200, DIFF) +} + +func FileHistory(ctx *middleware.Context) { + ctx.Data["IsRepoToolbarCommits"] = true + + fileName := ctx.Params("*") + if len(fileName) == 0 { + Commits(ctx) + return + } + + userName := ctx.Repo.Owner.Name + repoName := ctx.Repo.Repository.Name + branchName := ctx.Params(":branchname") + + brs, err := ctx.Repo.GitRepo.GetBranches() + if err != nil { + ctx.Handle(500, "GetBranches", err) + return + } else if len(brs) == 0 { + ctx.Handle(404, "GetBranches", nil) + return + } + + commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(branchName, fileName) + if err != nil { + ctx.Handle(500, "repo.FileHistory(GetCommitsCount)", err) + return + } else if commitsCount == 0 { + ctx.Handle(404, "repo.FileHistory", nil) + return + } + + // Calculate and validate page number. + page := com.StrTo(ctx.Query("p")).MustInt() + if page < 1 { + page = 1 + } + lastPage := page - 1 + if lastPage < 0 { + lastPage = 0 + } + nextPage := page + 1 + if nextPage*50 > commitsCount { + nextPage = 0 + } + + ctx.Data["Commits"], err = ctx.Repo.GitRepo.CommitsByFileAndRange( + branchName, fileName, page) + if err != nil { + ctx.Handle(500, "repo.FileHistory(CommitsByRange)", err) + return + } + + ctx.Data["Username"] = userName + ctx.Data["Reponame"] = repoName + ctx.Data["FileName"] = fileName + ctx.Data["CommitCount"] = commitsCount + ctx.Data["LastPageNum"] = lastPage + ctx.Data["NextPageNum"] = nextPage + ctx.HTML(200, COMMITS) +} diff --git a/routers/repo/download.go b/routers/repo/download.go index 42bce2b1f..abb9b0629 100644 --- a/routers/repo/download.go +++ b/routers/repo/download.go @@ -5,50 +5,41 @@ package repo import ( - // "io" - // "os" - // "path/filepath" + "io" + "path" - // "github.com/Unknwon/com" - - // "github.com/gogits/git" - - // "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/middleware" ) func SingleDownload(ctx *middleware.Context) { - // treename := params["_1"] - - // blob, err := ctx.Repo.Commit.GetBlobByPath(treename) - // if err != nil { - // ctx.Handle(500, "repo.SingleDownload(GetBlobByPath)", err) - // return - // } - - // dataRc, err := blob.Data() - // if err != nil { - // ctx.Handle(500, "repo.SingleDownload(Data)", err) - // return - // } - - // buf := make([]byte, 1024) - // n, _ := dataRc.Read(buf) - // if n > 0 { - // buf = buf[:n] - // } - - // defer func() { - // dataRc.Close() - // }() - - // contentType, isTextFile := base.IsTextFile(buf) - // _, isImageFile := base.IsImageFile(buf) - // ctx.Res.Header().Set("Content-Type", contentType) - // if !isTextFile && !isImageFile { - // ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(treename)) - // ctx.Res.Header().Set("Content-Transfer-Encoding", "binary") - // } - // ctx.Res.Write(buf) - // io.Copy(ctx.Res, dataRc) + treename := ctx.Params("*") + + blob, err := ctx.Repo.Commit.GetBlobByPath(treename) + if err != nil { + ctx.Handle(500, "GetBlobByPath", err) + return + } + + dataRc, err := blob.Data() + if err != nil { + ctx.Handle(500, "repo.SingleDownload(Data)", err) + return + } + + buf := make([]byte, 1024) + n, _ := dataRc.Read(buf) + if n > 0 { + buf = buf[:n] + } + + contentType, isTextFile := base.IsTextFile(buf) + _, isImageFile := base.IsImageFile(buf) + ctx.Resp.Header().Set("Content-Type", contentType) + if !isTextFile && !isImageFile { + ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+path.Base(treename)) + ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") + } + ctx.Resp.Write(buf) + io.Copy(ctx.Resp, dataRc) } diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 005596bde..ab9b2b646 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -4,1114 +4,1118 @@ package repo -// import ( -// "errors" -// // "fmt" -// // "io" -// // "io/ioutil" -// // "mime" -// "net/url" -// // "strings" -// // "time" - -// "github.com/Unknwon/com" -// "github.com/go-martini/martini" - -// // "github.com/gogits/gogs-ng/models" -// "github.com/gogits/gogs/modules/auth" -// "github.com/gogits/gogs/modules/base" -// // "github.com/gogits/gogs/modules/log" -// // "github.com/gogits/gogs/modules/mailer" -// "github.com/gogits/gogs/modules/middleware" -// // "github.com/gogits/gogs/modules/setting" -// ) - -// const ( -// ISSUES base.TplName = "repo/issue/list" -// ISSUE_CREATE base.TplName = "repo/issue/create" -// ISSUE_VIEW base.TplName = "repo/issue/view" - -// MILESTONE base.TplName = "repo/issue/milestone" -// MILESTONE_NEW base.TplName = "repo/issue/milestone_new" -// MILESTONE_EDIT base.TplName = "repo/issue/milestone_edit" -// ) - -// var ( -// ErrFileTypeForbidden = errors.New("File type is not allowed") -// ErrTooManyFiles = errors.New("Maximum number of files to upload exceeded") -// ) - -// func Issues(ctx *middleware.Context) { -// ctx.Data["Title"] = "Issues" -// ctx.Data["IsRepoToolbarIssues"] = true -// ctx.Data["IsRepoToolbarIssuesList"] = true - -// viewType := ctx.Query("type") -// types := []string{"assigned", "created_by", "mentioned"} -// if !com.IsSliceContainsStr(types, viewType) { -// viewType = "all" -// } - -// isShowClosed := ctx.Query("state") == "closed" - -// if viewType != "all" && !ctx.IsSigned { -// ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI)) -// ctx.Redirect("/user/login") -// return -// } - -// // var assigneeId, posterId int64 -// // var filterMode int -// // switch viewType { -// // case "assigned": -// // assigneeId = ctx.User.Id -// // filterMode = models.FM_ASSIGN -// // case "created_by": -// // posterId = ctx.User.Id -// // filterMode = models.FM_CREATE -// // case "mentioned": -// // filterMode = models.FM_MENTION -// // } - -// // var mid int64 -// // midx, _ := com.StrTo(ctx.Query("milestone")).Int64() -// // if midx > 0 { -// // mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, midx) -// // if err != nil { -// // ctx.Handle(500, "issue.Issues(GetMilestoneByIndex): %v", err) -// // return -// // } -// // mid = mile.Id -// // } - -// // selectLabels := ctx.Query("labels") -// // labels, err := models.GetLabels(ctx.Repo.Repository.Id) -// // if err != nil { -// // ctx.Handle(500, "issue.Issues(GetLabels): %v", err) -// // return -// // } -// // for _, l := range labels { -// // l.CalOpenIssues() -// // } -// // ctx.Data["Labels"] = labels - -// page, _ := com.StrTo(ctx.Query("page")).Int() - -// // Get issues. -// // issues, err := models.GetIssues(assigneeId, ctx.Repo.Repository.Id, posterId, mid, page, -// // isShowClosed, selectLabels, ctx.Query("sortType")) -// // if err != nil { -// // ctx.Handle(500, "issue.Issues(GetIssues): %v", err) -// // return -// // } - -// // Get issue-user pairs. -// // pairs, err := models.GetIssueUserPairs(ctx.Repo.Repository.Id, posterId, isShowClosed) -// // if err != nil { -// // ctx.Handle(500, "issue.Issues(GetIssueUserPairs): %v", err) -// // return -// // } - -// // Get posters. -// // for i := range issues { -// // if err = issues[i].GetLabels(); err != nil { -// // ctx.Handle(500, "issue.Issues(GetLabels)", fmt.Errorf("[#%d]%v", issues[i].Id, err)) -// // return -// // } - -// // idx := models.PairsContains(pairs, issues[i].Id) - -// // if filterMode == models.FM_MENTION && (idx == -1 || !pairs[idx].IsMentioned) { -// // continue -// // } - -// // if idx > -1 { -// // issues[i].IsRead = pairs[idx].IsRead -// // } else { -// // issues[i].IsRead = true -// // } - -// // if err = issues[i].GetPoster(); err != nil { -// // ctx.Handle(500, "issue.Issues(GetPoster)", fmt.Errorf("[#%d]%v", issues[i].Id, err)) -// // return -// // } -// // } - -// // var uid int64 = -1 -// // if ctx.User != nil { -// // uid = ctx.User.Id -// // } -// // issueStats := models.GetIssueStats(ctx.Repo.Repository.Id, uid, isShowClosed, filterMode) -// // ctx.Data["IssueStats"] = issueStats -// // ctx.Data["SelectLabels"], _ = com.StrTo(selectLabels).Int64() -// // ctx.Data["ViewType"] = viewType -// // ctx.Data["Issues"] = issues -// // ctx.Data["IsShowClosed"] = isShowClosed -// // if isShowClosed { -// // ctx.Data["State"] = "closed" -// // ctx.Data["ShowCount"] = issueStats.ClosedCount -// // } else { -// // ctx.Data["ShowCount"] = issueStats.OpenCount -// // } -// // ctx.HTML(200, ISSUES) -// } - -// func CreateIssue(ctx *middleware.Context, params martini.Params) { -// // ctx.Data["Title"] = "Create issue" -// // ctx.Data["IsRepoToolbarIssues"] = true -// // ctx.Data["IsRepoToolbarIssuesList"] = false -// // ctx.Data["AttachmentsEnabled"] = setting.AttachmentEnabled - -// // var err error -// // // Get all milestones. -// // ctx.Data["OpenMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, false) -// // if err != nil { -// // ctx.Handle(500, "issue.ViewIssue(GetMilestones.1): %v", err) -// // return -// // } -// // ctx.Data["ClosedMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, true) -// // if err != nil { -// // ctx.Handle(500, "issue.ViewIssue(GetMilestones.2): %v", err) -// // return -// // } - -// // us, err := models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/")) -// // if err != nil { -// // ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err) -// // return -// // } - -// // ctx.Data["AllowedTypes"] = setting.AttachmentAllowedTypes -// // ctx.Data["Collaborators"] = us - -// // ctx.HTML(200, ISSUE_CREATE) -// } - -// func CreateIssuePost(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) { -// // send := func(status int, data interface{}, err error) { -// // if err != nil { -// // log.Error(4, "issue.CreateIssuePost(?): %s", err.Error()) - -// // ctx.JSON(status, map[string]interface{}{ -// // "ok": false, -// // "status": status, -// // "error": err.Error(), -// // }) -// // } else { -// // ctx.JSON(status, map[string]interface{}{ -// // "ok": true, -// // "status": status, -// // "data": data, -// // }) -// // } -// // } - -// // var err error -// // // Get all milestones. -// // _, err = models.GetMilestones(ctx.Repo.Repository.Id, false) -// // if err != nil { -// // send(500, nil, err) -// // return -// // } -// // _, err = models.GetMilestones(ctx.Repo.Repository.Id, true) -// // if err != nil { -// // send(500, nil, err) -// // return -// // } - -// // _, err = models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/")) -// // if err != nil { -// // send(500, nil, err) -// // return -// // } - -// // if ctx.HasError() { -// // send(400, nil, errors.New(ctx.Flash.ErrorMsg)) -// // return -// // } - -// // // Only collaborators can assign. -// // if !ctx.Repo.IsOwner { -// // form.AssigneeId = 0 -// // } -// // issue := &models.Issue{ -// // RepoId: ctx.Repo.Repository.Id, -// // Index: int64(ctx.Repo.Repository.NumIssues) + 1, -// // Name: form.IssueName, -// // PosterId: ctx.User.Id, -// // MilestoneId: form.MilestoneId, -// // AssigneeId: form.AssigneeId, -// // LabelIds: form.Labels, -// // Content: form.Content, -// // } -// // if err := models.NewIssue(issue); err != nil { -// // send(500, nil, err) -// // return -// // } else if err := models.NewIssueUserPairs(issue.RepoId, issue.Id, ctx.Repo.Owner.Id, -// // ctx.User.Id, form.AssigneeId, ctx.Repo.Repository.Name); err != nil { -// // send(500, nil, err) -// // return -// // } - -// // if setting.AttachmentEnabled { -// // uploadFiles(ctx, issue.Id, 0) -// // } - -// // // Update mentions. -// // ms := base.MentionPattern.FindAllString(issue.Content, -1) -// // if len(ms) > 0 { -// // for i := range ms { -// // ms[i] = ms[i][1:] -// // } - -// // if err := models.UpdateMentions(ms, issue.Id); err != nil { -// // send(500, nil, err) -// // return -// // } -// // } - -// // act := &models.Action{ -// // ActUserId: ctx.User.Id, -// // ActUserName: ctx.User.Name, -// // ActEmail: ctx.User.Email, -// // OpType: models.OP_CREATE_ISSUE, -// // Content: fmt.Sprintf("%d|%s", issue.Index, issue.Name), -// // RepoId: ctx.Repo.Repository.Id, -// // RepoUserName: ctx.Repo.Owner.Name, -// // RepoName: ctx.Repo.Repository.Name, -// // RefName: ctx.Repo.BranchName, -// // IsPrivate: ctx.Repo.Repository.IsPrivate, -// // } -// // // Notify watchers. -// // if err := models.NotifyWatchers(act); err != nil { -// // send(500, nil, err) -// // return -// // } - -// // // Mail watchers and mentions. -// // if setting.Service.EnableNotifyMail { -// // tos, err := mailer.SendIssueNotifyMail(ctx.User, ctx.Repo.Owner, ctx.Repo.Repository, issue) -// // if err != nil { -// // send(500, nil, err) -// // return -// // } - -// // tos = append(tos, ctx.User.LowerName) -// // newTos := make([]string, 0, len(ms)) -// // for _, m := range ms { -// // if com.IsSliceContainsStr(tos, m) { -// // continue -// // } - -// // newTos = append(newTos, m) -// // } -// // if err = mailer.SendIssueMentionMail(ctx.Render, ctx.User, ctx.Repo.Owner, -// // ctx.Repo.Repository, issue, models.GetUserEmailsByNames(newTos)); err != nil { -// // send(500, nil, err) -// // return -// // } -// // } -// // log.Trace("%d Issue created: %d", ctx.Repo.Repository.Id, issue.Id) - -// // send(200, fmt.Sprintf("/%s/%s/issues/%d", params["username"], params["reponame"], issue.Index), nil) -// } - -// // 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) { -// // ctx.Data["AttachmentsEnabled"] = setting.AttachmentEnabled - -// // idx, _ := base.StrTo(params["index"]).Int64() -// // if idx == 0 { -// // ctx.Handle(404, "issue.ViewIssue", nil) -// // return -// // } - -// // issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, idx) -// // if err != nil { -// // if err == models.ErrIssueNotExist { -// // ctx.Handle(404, "issue.ViewIssue(GetIssueByIndex)", err) -// // } else { -// // ctx.Handle(500, "issue.ViewIssue(GetIssueByIndex)", err) -// // } -// // 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) -// // if err != nil { -// // if err == models.ErrMilestoneNotExist { -// // log.Warn("issue.ViewIssue(GetMilestoneById): %v", err) -// // } else { -// // ctx.Handle(500, "issue.ViewIssue(GetMilestoneById)", err) -// // return -// // } -// // } -// // } - -// // // Get all milestones. -// // ctx.Data["OpenMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, false) -// // if err != nil { -// // ctx.Handle(500, "issue.ViewIssue(GetMilestones.1): %v", err) -// // return -// // } -// // ctx.Data["ClosedMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, true) -// // if err != nil { -// // ctx.Handle(500, "issue.ViewIssue(GetMilestones.2): %v", err) -// // return -// // } - -// // // Get all collaborators. -// // ctx.Data["Collaborators"], err = models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/")) -// // if err != nil { -// // ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err) -// // return -// // } - -// // if ctx.IsSigned { -// // // Update issue-user. -// // if err = models.UpdateIssueUserPairByRead(ctx.User.Id, issue.Id); err != nil { -// // ctx.Handle(500, "issue.ViewIssue(UpdateIssueUserPairByRead): %v", err) -// // return -// // } -// // } - -// // // Get poster and Assignee. -// // if err = issue.GetPoster(); err != nil { -// // ctx.Handle(500, "issue.ViewIssue(GetPoster): %v", err) -// // return -// // } else if err = issue.GetAssignee(); err != nil { -// // ctx.Handle(500, "issue.ViewIssue(GetAssignee): %v", err) -// // return -// // } -// // issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink)) - -// // // Get comments. -// // comments, err := models.GetIssueComments(issue.Id) -// // if err != nil { -// // ctx.Handle(500, "issue.ViewIssue(GetIssueComments): %v", err) -// // return -// // } - -// // // Get posters. -// // for i := range comments { -// // u, err := models.GetUserById(comments[i].PosterId) -// // if err != nil { -// // ctx.Handle(500, "issue.ViewIssue(GetUserById.2): %v", err) -// // return -// // } -// // comments[i].Poster = u - -// // if comments[i].Type == models.COMMENT { -// // comments[i].Content = string(base.RenderMarkdown([]byte(comments[i].Content), ctx.Repo.RepoLink)) -// // } -// // } - -// // ctx.Data["AllowedTypes"] = setting.AttachmentAllowedTypes - -// // ctx.Data["Title"] = issue.Name -// // ctx.Data["Issue"] = issue -// // ctx.Data["Comments"] = comments -// // ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner || (ctx.IsSigned && issue.PosterId == ctx.User.Id) -// // ctx.Data["IsRepoToolbarIssues"] = true -// // ctx.Data["IsRepoToolbarIssuesList"] = false -// // ctx.HTML(200, ISSUE_VIEW) -// // } - -// // func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) { -// // 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.UpdateIssue", err) -// // } else { -// // ctx.Handle(500, "issue.UpdateIssue(GetIssueByIndex)", err) -// // } -// // return -// // } - -// // if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner { -// // ctx.Error(403) -// // return -// // } - -// // issue.Name = form.IssueName -// // issue.MilestoneId = form.MilestoneId -// // issue.AssigneeId = form.AssigneeId -// // issue.LabelIds = form.Labels -// // issue.Content = form.Content -// // // try get content from text, ignore conflict with preview ajax -// // if form.Content == "" { -// // issue.Content = ctx.Query("text") -// // } -// // if err = models.UpdateIssue(issue); err != nil { -// // ctx.Handle(500, "issue.UpdateIssue(UpdateIssue)", err) -// // return -// // } - -// // ctx.JSON(200, map[string]interface{}{ -// // "ok": true, -// // "title": issue.Name, -// // "content": string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink)), -// // }) -// // } - -// // 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(GetIssueByIndex)", err) -// // } else { -// // ctx.Handle(500, "issue.UpdateIssueLabel(GetIssueByIndex)", err) -// // } -// // return -// // } - -// // isAttach := ctx.Query("action") == "attach" -// // labelStrId := ctx.Query("id") -// // labelId, _ := base.StrTo(labelStrId).Int64() -// // label, err := models.GetLabelById(labelId) -// // if err != nil { -// // if err == models.ErrLabelNotExist { -// // ctx.Handle(404, "issue.UpdateIssueLabel(GetLabelById)", err) -// // } else { -// // ctx.Handle(500, "issue.UpdateIssueLabel(GetLabelById)", err) -// // } -// // return -// // } - -// // 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 -// // } - -// // if isAttach { -// // label.NumIssues++ -// // if issue.IsClosed { -// // label.NumClosedIssues++ -// // } -// // } else { -// // label.NumIssues-- -// // if issue.IsClosed { -// // label.NumClosedIssues-- -// // } -// // } -// // if err = models.UpdateLabel(label); err != nil { -// // ctx.Handle(500, "issue.UpdateIssueLabel(UpdateLabel)", err) -// // return -// // } -// // } -// // ctx.JSON(200, map[string]interface{}{ -// // "ok": true, -// // }) -// // } - -// // func UpdateIssueMilestone(ctx *middleware.Context) { -// // if !ctx.Repo.IsOwner { -// // ctx.Error(403) -// // return -// // } - -// // issueId, err := base.StrTo(ctx.Query("issue")).Int64() -// // if err != nil { -// // ctx.Error(404) -// // return -// // } - -// // issue, err := models.GetIssueById(issueId) -// // if err != nil { -// // if err == models.ErrIssueNotExist { -// // ctx.Handle(404, "issue.UpdateIssueMilestone(GetIssueById)", err) -// // } else { -// // ctx.Handle(500, "issue.UpdateIssueMilestone(GetIssueById)", err) -// // } -// // return -// // } - -// // oldMid := issue.MilestoneId -// // mid, _ := base.StrTo(ctx.Query("milestone")).Int64() -// // if oldMid == mid { -// // ctx.JSON(200, map[string]interface{}{ -// // "ok": true, -// // }) -// // return -// // } - -// // // Not check for invalid milestone id and give responsibility to owners. -// // issue.MilestoneId = mid -// // if err = models.ChangeMilestoneAssign(oldMid, mid, issue); err != nil { -// // ctx.Handle(500, "issue.UpdateIssueMilestone(ChangeMilestoneAssign)", err) -// // return -// // } else if err = models.UpdateIssue(issue); err != nil { -// // ctx.Handle(500, "issue.UpdateIssueMilestone(UpdateIssue)", err) -// // return -// // } - -// // ctx.JSON(200, map[string]interface{}{ -// // "ok": true, -// // }) -// // } - -// // func UpdateAssignee(ctx *middleware.Context) { -// // if !ctx.Repo.IsOwner { -// // ctx.Error(403) -// // return -// // } - -// // issueId, err := base.StrTo(ctx.Query("issue")).Int64() -// // if err != nil { -// // ctx.Error(404) -// // return -// // } - -// // issue, err := models.GetIssueById(issueId) -// // if err != nil { -// // if err == models.ErrIssueNotExist { -// // ctx.Handle(404, "issue.UpdateAssignee(GetIssueById)", err) -// // } else { -// // ctx.Handle(500, "issue.UpdateAssignee(GetIssueById)", err) -// // } -// // return -// // } - -// // aid, _ := base.StrTo(ctx.Query("assigneeid")).Int64() -// // // Not check for invalid assignne id and give responsibility to owners. -// // issue.AssigneeId = aid -// // if err = models.UpdateIssueUserPairByAssignee(aid, issue.Id); err != nil { -// // ctx.Handle(500, "issue.UpdateAssignee(UpdateIssueUserPairByAssignee): %v", err) -// // return -// // } else if err = models.UpdateIssue(issue); err != nil { -// // ctx.Handle(500, "issue.UpdateAssignee(UpdateIssue)", err) -// // return -// // } - -// // ctx.JSON(200, map[string]interface{}{ -// // "ok": true, -// // }) -// // } - -// // func uploadFiles(ctx *middleware.Context, issueId, commentId int64) { -// // if !setting.AttachmentEnabled { -// // return -// // } - -// // allowedTypes := strings.Split(setting.AttachmentAllowedTypes, "|") -// // attachments := ctx.Req.MultipartForm.File["attachments"] - -// // if len(attachments) > setting.AttachmentMaxFiles { -// // ctx.Handle(400, "issue.Comment", ErrTooManyFiles) -// // return -// // } - -// // for _, header := range attachments { -// // file, err := header.Open() - -// // if err != nil { -// // ctx.Handle(500, "issue.Comment(header.Open)", err) -// // return -// // } - -// // defer file.Close() - -// // allowed := false -// // fileType := mime.TypeByExtension(header.Filename) - -// // for _, t := range allowedTypes { -// // t := strings.Trim(t, " ") - -// // if t == "*/*" || t == fileType { -// // allowed = true -// // break -// // } -// // } - -// // if !allowed { -// // ctx.Handle(400, "issue.Comment", ErrFileTypeForbidden) -// // return -// // } - -// // out, err := ioutil.TempFile(setting.AttachmentPath, "attachment_") - -// // if err != nil { -// // ctx.Handle(500, "issue.Comment(ioutil.TempFile)", err) -// // return -// // } - -// // defer out.Close() - -// // _, err = io.Copy(out, file) - -// // if err != nil { -// // ctx.Handle(500, "issue.Comment(io.Copy)", err) -// // return -// // } - -// // _, err = models.CreateAttachment(issueId, commentId, header.Filename, out.Name()) - -// // if err != nil { -// // ctx.Handle(500, "issue.Comment(io.Copy)", err) -// // return -// // } -// // } -// // } - -// // func Comment(ctx *middleware.Context, params martini.Params) { -// // send := func(status int, data interface{}, err error) { -// // if err != nil { -// // log.Error("issue.Comment(?): %s", err.Error()) - -// // ctx.JSON(status, map[string]interface{}{ -// // "ok": false, -// // "status": status, -// // "error": err.Error(), -// // }) -// // } else { -// // ctx.JSON(status, map[string]interface{}{ -// // "ok": true, -// // "status": status, -// // "data": data, -// // }) -// // } -// // } - -// // index, err := base.StrTo(ctx.Query("issueIndex")).Int64() -// // if err != nil { -// // send(404, nil, err) -// // return -// // } - -// // issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, index) -// // if err != nil { -// // if err == models.ErrIssueNotExist { -// // send(404, nil, err) -// // } else { -// // send(200, nil, err) -// // } - -// // return -// // } - -// // // Check if issue owner changes the status of issue. -// // var newStatus string -// // if ctx.Repo.IsOwner || issue.PosterId == ctx.User.Id { -// // newStatus = ctx.Query("change_status") -// // } -// // if len(newStatus) > 0 { -// // if (strings.Contains(newStatus, "Reopen") && issue.IsClosed) || -// // (strings.Contains(newStatus, "Close") && !issue.IsClosed) { -// // issue.IsClosed = !issue.IsClosed -// // if err = models.UpdateIssue(issue); err != nil { -// // send(500, nil, err) -// // return -// // } else if err = models.UpdateIssueUserPairsByStatus(issue.Id, issue.IsClosed); err != nil { -// // send(500, nil, err) -// // return -// // } - -// // // Change open/closed issue counter for the associated milestone -// // if issue.MilestoneId > 0 { -// // if err = models.ChangeMilestoneIssueStats(issue); err != nil { -// // send(500, nil, err) -// // } -// // } - -// // cmtType := models.CLOSE -// // if !issue.IsClosed { -// // cmtType = models.REOPEN -// // } - -// // if _, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, cmtType, "", nil); err != nil { -// // send(200, nil, err) -// // return -// // } -// // log.Trace("%s Issue(%d) status changed: %v", ctx.Req.RequestURI, issue.Id, !issue.IsClosed) -// // } -// // } - -// // var comment *models.Comment - -// // var ms []string -// // content := ctx.Query("content") -// // // Fix #321. Allow empty comments, as long as we have attachments. -// // if len(content) > 0 || len(ctx.Req.MultipartForm.File["attachments"]) > 0 { -// // switch params["action"] { -// // case "new": -// // if comment, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, models.COMMENT, content, nil); err != nil { -// // send(500, nil, err) -// // return -// // } - -// // // Update mentions. -// // ms = base.MentionPattern.FindAllString(issue.Content, -1) -// // if len(ms) > 0 { -// // for i := range ms { -// // ms[i] = ms[i][1:] -// // } - -// // if err := models.UpdateMentions(ms, issue.Id); err != nil { -// // send(500, nil, err) -// // return -// // } -// // } - -// // log.Trace("%s Comment created: %d", ctx.Req.RequestURI, issue.Id) -// // default: -// // ctx.Handle(404, "issue.Comment", err) -// // return -// // } -// // } - -// // if comment != nil { -// // uploadFiles(ctx, issue.Id, comment.Id) -// // } - -// // // Notify watchers. -// // act := &models.Action{ -// // ActUserId: ctx.User.Id, -// // ActUserName: ctx.User.LowerName, -// // ActEmail: ctx.User.Email, -// // OpType: models.OP_COMMENT_ISSUE, -// // Content: fmt.Sprintf("%d|%s", issue.Index, strings.Split(content, "\n")[0]), -// // RepoId: ctx.Repo.Repository.Id, -// // RepoUserName: ctx.Repo.Owner.LowerName, -// // RepoName: ctx.Repo.Repository.LowerName, -// // } -// // if err = models.NotifyWatchers(act); err != nil { -// // send(500, nil, err) -// // return -// // } - -// // // Mail watchers and mentions. -// // if setting.Service.EnableNotifyMail { -// // issue.Content = content -// // tos, err := mailer.SendIssueNotifyMail(ctx.User, ctx.Repo.Owner, ctx.Repo.Repository, issue) -// // if err != nil { -// // send(500, nil, err) -// // return -// // } - -// // tos = append(tos, ctx.User.LowerName) -// // newTos := make([]string, 0, len(ms)) -// // for _, m := range ms { -// // if com.IsSliceContainsStr(tos, m) { -// // continue -// // } - -// // newTos = append(newTos, m) -// // } -// // if err = mailer.SendIssueMentionMail(ctx.Render, ctx.User, ctx.Repo.Owner, -// // ctx.Repo.Repository, issue, models.GetUserEmailsByNames(newTos)); err != nil { -// // send(500, nil, err) -// // return -// // } -// // } - -// // send(200, fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, index), nil) -// // } - -// // func NewLabel(ctx *middleware.Context, form auth.CreateLabelForm) { -// // if ctx.HasError() { -// // Issues(ctx) -// // return -// // } - -// // l := &models.Label{ -// // RepoId: ctx.Repo.Repository.Id, -// // Name: form.Title, -// // Color: form.Color, -// // } -// // if err := models.NewLabel(l); err != nil { -// // ctx.Handle(500, "issue.NewLabel(NewLabel)", err) -// // return -// // } -// // ctx.Redirect(ctx.Repo.RepoLink + "/issues") -// // } - -// // 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) { -// // removes := ctx.Query("remove") -// // if len(strings.TrimSpace(removes)) == 0 { -// // ctx.JSON(200, map[string]interface{}{ -// // "ok": true, -// // }) -// // return -// // } - -// // strIds := strings.Split(removes, ",") -// // 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) { -// // ctx.Data["Title"] = "Milestones" -// // ctx.Data["IsRepoToolbarIssues"] = true -// // ctx.Data["IsRepoToolbarIssuesList"] = true - -// // isShowClosed := ctx.Query("state") == "closed" - -// // miles, err := models.GetMilestones(ctx.Repo.Repository.Id, isShowClosed) -// // if err != nil { -// // ctx.Handle(500, "issue.Milestones(GetMilestones)", err) -// // return -// // } -// // for _, m := range miles { -// // m.RenderedContent = string(base.RenderSpecialLink([]byte(m.Content), ctx.Repo.RepoLink)) -// // m.CalOpenIssues() -// // } -// // ctx.Data["Milestones"] = miles - -// // if isShowClosed { -// // ctx.Data["State"] = "closed" -// // } else { -// // ctx.Data["State"] = "open" -// // } -// // ctx.HTML(200, MILESTONE) -// // } - -// // func NewMilestone(ctx *middleware.Context) { -// // ctx.Data["Title"] = "New Milestone" -// // ctx.Data["IsRepoToolbarIssues"] = true -// // ctx.Data["IsRepoToolbarIssuesList"] = true -// // ctx.HTML(200, MILESTONE_NEW) -// // } - -// // func NewMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm) { -// // ctx.Data["Title"] = "New Milestone" -// // ctx.Data["IsRepoToolbarIssues"] = true -// // ctx.Data["IsRepoToolbarIssuesList"] = true - -// // if ctx.HasError() { -// // ctx.HTML(200, MILESTONE_NEW) -// // return -// // } - -// // var deadline time.Time -// // var err error -// // if len(form.Deadline) == 0 { -// // form.Deadline = "12/31/9999" -// // } -// // deadline, err = time.Parse("01/02/2006", form.Deadline) -// // if err != nil { -// // ctx.Handle(500, "issue.NewMilestonePost(time.Parse)", err) -// // return -// // } - -// // mile := &models.Milestone{ -// // RepoId: ctx.Repo.Repository.Id, -// // Index: int64(ctx.Repo.Repository.NumMilestones) + 1, -// // Name: form.Title, -// // Content: form.Content, -// // Deadline: deadline, -// // } -// // if err = models.NewMilestone(mile); err != nil { -// // ctx.Handle(500, "issue.NewMilestonePost(NewMilestone)", err) -// // return -// // } - -// // ctx.Redirect(ctx.Repo.RepoLink + "/issues/milestones") -// // } - -// // func UpdateMilestone(ctx *middleware.Context, params martini.Params) { -// // ctx.Data["Title"] = "Update Milestone" -// // ctx.Data["IsRepoToolbarIssues"] = true -// // ctx.Data["IsRepoToolbarIssuesList"] = true - -// // idx, _ := base.StrTo(params["index"]).Int64() -// // if idx == 0 { -// // ctx.Handle(404, "issue.UpdateMilestone", nil) -// // return -// // } - -// // mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx) -// // if err != nil { -// // if err == models.ErrMilestoneNotExist { -// // ctx.Handle(404, "issue.UpdateMilestone(GetMilestoneByIndex)", err) -// // } else { -// // ctx.Handle(500, "issue.UpdateMilestone(GetMilestoneByIndex)", err) -// // } -// // return -// // } - -// // action := params["action"] -// // if len(action) > 0 { -// // switch action { -// // case "open": -// // if mile.IsClosed { -// // if err = models.ChangeMilestoneStatus(mile, false); err != nil { -// // ctx.Handle(500, "issue.UpdateMilestone(ChangeMilestoneStatus)", err) -// // return -// // } -// // } -// // case "close": -// // if !mile.IsClosed { -// // mile.ClosedDate = time.Now() -// // if err = models.ChangeMilestoneStatus(mile, true); err != nil { -// // ctx.Handle(500, "issue.UpdateMilestone(ChangeMilestoneStatus)", err) -// // return -// // } -// // } -// // case "delete": -// // if err = models.DeleteMilestone(mile); err != nil { -// // ctx.Handle(500, "issue.UpdateMilestone(DeleteMilestone)", err) -// // return -// // } -// // } -// // ctx.Redirect(ctx.Repo.RepoLink + "/issues/milestones") -// // return -// // } - -// // mile.DeadlineString = mile.Deadline.UTC().Format("01/02/2006") -// // if mile.DeadlineString == "12/31/9999" { -// // mile.DeadlineString = "" -// // } -// // ctx.Data["Milestone"] = mile - -// // ctx.HTML(200, MILESTONE_EDIT) -// // } - -// // func UpdateMilestonePost(ctx *middleware.Context, params martini.Params, form auth.CreateMilestoneForm) { -// // ctx.Data["Title"] = "Update Milestone" -// // ctx.Data["IsRepoToolbarIssues"] = true -// // ctx.Data["IsRepoToolbarIssuesList"] = true - -// // idx, _ := base.StrTo(params["index"]).Int64() -// // if idx == 0 { -// // ctx.Handle(404, "issue.UpdateMilestonePost", nil) -// // return -// // } - -// // mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx) -// // if err != nil { -// // if err == models.ErrMilestoneNotExist { -// // ctx.Handle(404, "issue.UpdateMilestonePost(GetMilestoneByIndex)", err) -// // } else { -// // ctx.Handle(500, "issue.UpdateMilestonePost(GetMilestoneByIndex)", err) -// // } -// // return -// // } - -// // if ctx.HasError() { -// // ctx.HTML(200, MILESTONE_EDIT) -// // return -// // } - -// // var deadline time.Time -// // if len(form.Deadline) == 0 { -// // form.Deadline = "12/31/9999" -// // } -// // deadline, err = time.Parse("01/02/2006", form.Deadline) -// // if err != nil { -// // ctx.Handle(500, "issue.UpdateMilestonePost(time.Parse)", err) -// // return -// // } - -// // mile.Name = form.Title -// // mile.Content = form.Content -// // mile.Deadline = deadline -// // if err = models.UpdateMilestone(mile); err != nil { -// // ctx.Handle(500, "issue.UpdateMilestonePost(UpdateMilestone)", err) -// // return -// // } - -// // ctx.Redirect(ctx.Repo.RepoLink + "/issues/milestones") -// // } - -// // func IssueGetAttachment(ctx *middleware.Context, params martini.Params) { -// // id, err := base.StrTo(params["id"]).Int64() - -// // if err != nil { -// // ctx.Handle(400, "issue.IssueGetAttachment(base.StrTo.Int64)", err) -// // return -// // } - -// // attachment, err := models.GetAttachmentById(id) - -// // if err != nil { -// // ctx.Handle(404, "issue.IssueGetAttachment(models.GetAttachmentById)", err) -// // return -// // } - -// // // Fix #312. Attachments with , in their name are not handled correctly by Google Chrome. -// // // We must put the name in " manually. -// // ctx.ServeFile(attachment.Path, "\""+attachment.Name+"\"") -// // } +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + "time" + + "github.com/Unknwon/com" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/auth" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" + "github.com/gogits/gogs/modules/middleware" + "github.com/gogits/gogs/modules/setting" +) + +const ( + ISSUES base.TplName = "repo/issue/list" + ISSUE_CREATE base.TplName = "repo/issue/create" + ISSUE_VIEW base.TplName = "repo/issue/view" + + MILESTONE base.TplName = "repo/issue/milestone" + MILESTONE_NEW base.TplName = "repo/issue/milestone_new" + MILESTONE_EDIT base.TplName = "repo/issue/milestone_edit" +) + +var ( + ErrFileTypeForbidden = errors.New("File type is not allowed") + ErrTooManyFiles = errors.New("Maximum number of files to upload exceeded") +) + +func Issues(ctx *middleware.Context) { + ctx.Data["Title"] = "Issues" + ctx.Data["IsRepoToolbarIssues"] = true + ctx.Data["IsRepoToolbarIssuesList"] = true + + viewType := ctx.Query("type") + types := []string{"assigned", "created_by", "mentioned"} + if !com.IsSliceContainsStr(types, viewType) { + viewType = "all" + } + + isShowClosed := ctx.Query("state") == "closed" + + if viewType != "all" && !ctx.IsSigned { + ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI)) + ctx.Redirect("/user/login") + return + } + + var assigneeId, posterId int64 + var filterMode int + switch viewType { + case "assigned": + assigneeId = ctx.User.Id + filterMode = models.FM_ASSIGN + case "created_by": + posterId = ctx.User.Id + filterMode = models.FM_CREATE + case "mentioned": + filterMode = models.FM_MENTION + } + + var mid int64 + midx, _ := com.StrTo(ctx.Query("milestone")).Int64() + if midx > 0 { + mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, midx) + if err != nil { + ctx.Handle(500, "issue.Issues(GetMilestoneByIndex): %v", err) + return + } + mid = mile.Id + } + + selectLabels := ctx.Query("labels") + labels, err := models.GetLabels(ctx.Repo.Repository.Id) + if err != nil { + ctx.Handle(500, "issue.Issues(GetLabels): %v", err) + return + } + for _, l := range labels { + l.CalOpenIssues() + } + ctx.Data["Labels"] = labels + + page, _ := com.StrTo(ctx.Query("page")).Int() + + // Get issues. + issues, err := models.GetIssues(assigneeId, ctx.Repo.Repository.Id, posterId, mid, page, + isShowClosed, selectLabels, ctx.Query("sortType")) + if err != nil { + ctx.Handle(500, "issue.Issues(GetIssues): %v", err) + return + } + + // Get issue-user pairs. + pairs, err := models.GetIssueUserPairs(ctx.Repo.Repository.Id, posterId, isShowClosed) + if err != nil { + ctx.Handle(500, "issue.Issues(GetIssueUserPairs): %v", err) + return + } + + // Get posters. + for i := range issues { + if err = issues[i].GetLabels(); err != nil { + ctx.Handle(500, "GetLabels", fmt.Errorf("[#%d]%v", issues[i].Id, err)) + return + } + + idx := models.PairsContains(pairs, issues[i].Id) + + if filterMode == models.FM_MENTION && (idx == -1 || !pairs[idx].IsMentioned) { + continue + } + + if idx > -1 { + issues[i].IsRead = pairs[idx].IsRead + } else { + issues[i].IsRead = true + } + + if err = issues[i].GetPoster(); err != nil { + ctx.Handle(500, "issue.Issues(GetPoster)", fmt.Errorf("[#%d]%v", issues[i].Id, err)) + return + } + } + + var uid int64 = -1 + if ctx.User != nil { + uid = ctx.User.Id + } + issueStats := models.GetIssueStats(ctx.Repo.Repository.Id, uid, isShowClosed, filterMode) + ctx.Data["IssueStats"] = issueStats + ctx.Data["SelectLabels"], _ = com.StrTo(selectLabels).Int64() + ctx.Data["ViewType"] = viewType + ctx.Data["Issues"] = issues + ctx.Data["IsShowClosed"] = isShowClosed + if isShowClosed { + ctx.Data["State"] = "closed" + ctx.Data["ShowCount"] = issueStats.ClosedCount + } else { + ctx.Data["ShowCount"] = issueStats.OpenCount + } + ctx.HTML(200, ISSUES) +} + +func CreateIssue(ctx *middleware.Context) { + ctx.Data["Title"] = "Create issue" + ctx.Data["IsRepoToolbarIssues"] = true + ctx.Data["IsRepoToolbarIssuesList"] = false + ctx.Data["AttachmentsEnabled"] = setting.AttachmentEnabled + + var err error + // Get all milestones. + ctx.Data["OpenMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, false) + if err != nil { + ctx.Handle(500, "issue.ViewIssue(GetMilestones.1): %v", err) + return + } + ctx.Data["ClosedMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, true) + if err != nil { + ctx.Handle(500, "issue.ViewIssue(GetMilestones.2): %v", err) + return + } + + us, err := models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/")) + if err != nil { + ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err) + return + } + + ctx.Data["AllowedTypes"] = setting.AttachmentAllowedTypes + ctx.Data["Collaborators"] = us + + ctx.HTML(200, ISSUE_CREATE) +} + +func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) { + send := func(status int, data interface{}, err error) { + if err != nil { + log.Error(4, "issue.CreateIssuePost(?): %s", err) + + ctx.JSON(status, map[string]interface{}{ + "ok": false, + "status": status, + "error": err.Error(), + }) + } else { + ctx.JSON(status, map[string]interface{}{ + "ok": true, + "status": status, + "data": data, + }) + } + } + + var err error + // Get all milestones. + _, err = models.GetMilestones(ctx.Repo.Repository.Id, false) + if err != nil { + send(500, nil, err) + return + } + _, err = models.GetMilestones(ctx.Repo.Repository.Id, true) + if err != nil { + send(500, nil, err) + return + } + + _, err = models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/")) + if err != nil { + send(500, nil, err) + return + } + + if ctx.HasError() { + send(400, nil, errors.New(ctx.Flash.ErrorMsg)) + return + } + + // Only collaborators can assign. + if !ctx.Repo.IsOwner { + form.AssigneeId = 0 + } + issue := &models.Issue{ + RepoId: ctx.Repo.Repository.Id, + Index: int64(ctx.Repo.Repository.NumIssues) + 1, + Name: form.IssueName, + PosterId: ctx.User.Id, + MilestoneId: form.MilestoneId, + AssigneeId: form.AssigneeId, + LabelIds: form.Labels, + Content: form.Content, + } + if err := models.NewIssue(issue); err != nil { + send(500, nil, err) + return + } else if err := models.NewIssueUserPairs(issue.RepoId, issue.Id, ctx.Repo.Owner.Id, + ctx.User.Id, form.AssigneeId, ctx.Repo.Repository.Name); err != nil { + send(500, nil, err) + return + } + + if setting.AttachmentEnabled { + uploadFiles(ctx, issue.Id, 0) + } + + // Update mentions. + ms := base.MentionPattern.FindAllString(issue.Content, -1) + if len(ms) > 0 { + for i := range ms { + ms[i] = ms[i][1:] + } + + if err := models.UpdateMentions(ms, issue.Id); err != nil { + send(500, nil, err) + return + } + } + + act := &models.Action{ + ActUserId: ctx.User.Id, + ActUserName: ctx.User.Name, + ActEmail: ctx.User.Email, + OpType: models.CREATE_ISSUE, + Content: fmt.Sprintf("%d|%s", issue.Index, issue.Name), + RepoId: ctx.Repo.Repository.Id, + RepoUserName: ctx.Repo.Owner.Name, + RepoName: ctx.Repo.Repository.Name, + RefName: ctx.Repo.BranchName, + IsPrivate: ctx.Repo.Repository.IsPrivate, + } + // Notify watchers. + if err := models.NotifyWatchers(act); err != nil { + send(500, nil, err) + return + } + + // Mail watchers and mentions. + if setting.Service.EnableNotifyMail { + tos, err := mailer.SendIssueNotifyMail(ctx.User, ctx.Repo.Owner, ctx.Repo.Repository, issue) + if err != nil { + send(500, nil, err) + return + } + + tos = append(tos, ctx.User.LowerName) + newTos := make([]string, 0, len(ms)) + for _, m := range ms { + if com.IsSliceContainsStr(tos, m) { + continue + } + + newTos = append(newTos, m) + } + if err = mailer.SendIssueMentionMail(ctx.Render, ctx.User, ctx.Repo.Owner, + ctx.Repo.Repository, issue, models.GetUserEmailsByNames(newTos)); err != nil { + send(500, nil, err) + return + } + } + log.Trace("%d Issue created: %d", ctx.Repo.Repository.Id, issue.Id) + + send(200, fmt.Sprintf("/%s/%s/issues/%d", ctx.Params(":username"), ctx.Params(":reponame"), issue.Index), nil) +} + +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) { + ctx.Data["AttachmentsEnabled"] = setting.AttachmentEnabled + + idx := com.StrTo(ctx.Params(":index")).MustInt64() + if idx == 0 { + ctx.Handle(404, "issue.ViewIssue", nil) + return + } + + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, idx) + if err != nil { + if err == models.ErrIssueNotExist { + ctx.Handle(404, "issue.ViewIssue(GetIssueByIndex)", err) + } else { + ctx.Handle(500, "issue.ViewIssue(GetIssueByIndex)", err) + } + 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) + if err != nil { + if err == models.ErrMilestoneNotExist { + log.Warn("issue.ViewIssue(GetMilestoneById): %v", err) + } else { + ctx.Handle(500, "issue.ViewIssue(GetMilestoneById)", err) + return + } + } + } + + // Get all milestones. + ctx.Data["OpenMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, false) + if err != nil { + ctx.Handle(500, "issue.ViewIssue(GetMilestones.1): %v", err) + return + } + ctx.Data["ClosedMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, true) + if err != nil { + ctx.Handle(500, "issue.ViewIssue(GetMilestones.2): %v", err) + return + } + + // Get all collaborators. + ctx.Data["Collaborators"], err = models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/")) + if err != nil { + ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err) + return + } + + if ctx.IsSigned { + // Update issue-user. + if err = models.UpdateIssueUserPairByRead(ctx.User.Id, issue.Id); err != nil { + ctx.Handle(500, "issue.ViewIssue(UpdateIssueUserPairByRead): %v", err) + return + } + } + + // Get poster and Assignee. + if err = issue.GetPoster(); err != nil { + ctx.Handle(500, "issue.ViewIssue(GetPoster): %v", err) + return + } else if err = issue.GetAssignee(); err != nil { + ctx.Handle(500, "issue.ViewIssue(GetAssignee): %v", err) + return + } + issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink)) + + // Get comments. + comments, err := models.GetIssueComments(issue.Id) + if err != nil { + ctx.Handle(500, "issue.ViewIssue(GetIssueComments): %v", err) + return + } + + // Get posters. + for i := range comments { + u, err := models.GetUserById(comments[i].PosterId) + if err != nil { + ctx.Handle(500, "issue.ViewIssue(GetUserById.2): %v", err) + return + } + comments[i].Poster = u + + if comments[i].Type == models.COMMENT { + comments[i].Content = string(base.RenderMarkdown([]byte(comments[i].Content), ctx.Repo.RepoLink)) + } + } + + ctx.Data["AllowedTypes"] = setting.AttachmentAllowedTypes + + ctx.Data["Title"] = issue.Name + ctx.Data["Issue"] = issue + ctx.Data["Comments"] = comments + ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner || (ctx.IsSigned && issue.PosterId == ctx.User.Id) + ctx.Data["IsRepoToolbarIssues"] = true + ctx.Data["IsRepoToolbarIssuesList"] = false + ctx.HTML(200, ISSUE_VIEW) +} + +func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) { + idx := com.StrTo(ctx.Params(":index")).MustInt64() + 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.UpdateIssue", err) + } else { + ctx.Handle(500, "issue.UpdateIssue(GetIssueByIndex)", err) + } + return + } + + if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner { + ctx.Error(403) + return + } + + issue.Name = form.IssueName + issue.MilestoneId = form.MilestoneId + issue.AssigneeId = form.AssigneeId + issue.LabelIds = form.Labels + issue.Content = form.Content + // try get content from text, ignore conflict with preview ajax + if form.Content == "" { + issue.Content = ctx.Query("text") + } + if err = models.UpdateIssue(issue); err != nil { + ctx.Handle(500, "issue.UpdateIssue(UpdateIssue)", err) + return + } + + ctx.JSON(200, map[string]interface{}{ + "ok": true, + "title": issue.Name, + "content": string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink)), + }) +} + +func UpdateIssueLabel(ctx *middleware.Context) { + if !ctx.Repo.IsOwner { + ctx.Error(403) + return + } + + idx := com.StrTo(ctx.Params(":index")).MustInt64() + 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(GetIssueByIndex)", err) + } else { + ctx.Handle(500, "issue.UpdateIssueLabel(GetIssueByIndex)", err) + } + return + } + + isAttach := ctx.Query("action") == "attach" + labelStrId := ctx.Query("id") + labelId := com.StrTo(labelStrId).MustInt64() + label, err := models.GetLabelById(labelId) + if err != nil { + if err == models.ErrLabelNotExist { + ctx.Handle(404, "issue.UpdateIssueLabel(GetLabelById)", err) + } else { + ctx.Handle(500, "issue.UpdateIssueLabel(GetLabelById)", err) + } + return + } + + 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 + } + + if isAttach { + label.NumIssues++ + if issue.IsClosed { + label.NumClosedIssues++ + } + } else { + label.NumIssues-- + if issue.IsClosed { + label.NumClosedIssues-- + } + } + if err = models.UpdateLabel(label); err != nil { + ctx.Handle(500, "issue.UpdateIssueLabel(UpdateLabel)", err) + return + } + } + ctx.JSON(200, map[string]interface{}{ + "ok": true, + }) +} + +func UpdateIssueMilestone(ctx *middleware.Context) { + if !ctx.Repo.IsOwner { + ctx.Error(403) + return + } + + issueId := com.StrTo(ctx.Params(":issue")).MustInt64() + if issueId == 0 { + ctx.Error(404) + return + } + + issue, err := models.GetIssueById(issueId) + if err != nil { + if err == models.ErrIssueNotExist { + ctx.Handle(404, "issue.UpdateIssueMilestone(GetIssueById)", err) + } else { + ctx.Handle(500, "issue.UpdateIssueMilestone(GetIssueById)", err) + } + return + } + + oldMid := issue.MilestoneId + mid := com.StrTo(ctx.Params(":milestone")).MustInt64() + if oldMid == mid { + ctx.JSON(200, map[string]interface{}{ + "ok": true, + }) + return + } + + // Not check for invalid milestone id and give responsibility to owners. + issue.MilestoneId = mid + if err = models.ChangeMilestoneAssign(oldMid, mid, issue); err != nil { + ctx.Handle(500, "issue.UpdateIssueMilestone(ChangeMilestoneAssign)", err) + return + } else if err = models.UpdateIssue(issue); err != nil { + ctx.Handle(500, "issue.UpdateIssueMilestone(UpdateIssue)", err) + return + } + + ctx.JSON(200, map[string]interface{}{ + "ok": true, + }) +} + +func UpdateAssignee(ctx *middleware.Context) { + if !ctx.Repo.IsOwner { + ctx.Error(403) + return + } + + issueId := com.StrTo(ctx.Params(":index")).MustInt64() + if issueId == 0 { + ctx.Error(404) + return + } + + issue, err := models.GetIssueById(issueId) + if err != nil { + if err == models.ErrIssueNotExist { + ctx.Handle(404, "GetIssueById", err) + } else { + ctx.Handle(500, "GetIssueById", err) + } + return + } + + aid := com.StrTo(ctx.Params(":assigneeid")).MustInt64() + // Not check for invalid assignne id and give responsibility to owners. + issue.AssigneeId = aid + if err = models.UpdateIssueUserPairByAssignee(aid, issue.Id); err != nil { + ctx.Handle(500, "UpdateIssueUserPairByAssignee: %v", err) + return + } else if err = models.UpdateIssue(issue); err != nil { + ctx.Handle(500, "UpdateIssue", err) + return + } + + ctx.JSON(200, map[string]interface{}{ + "ok": true, + }) +} + +func uploadFiles(ctx *middleware.Context, issueId, commentId int64) { + if !setting.AttachmentEnabled { + return + } + + allowedTypes := strings.Split(setting.AttachmentAllowedTypes, "|") + attachments := ctx.Req.MultipartForm.File["attachments"] + + if len(attachments) > setting.AttachmentMaxFiles { + ctx.Handle(400, "issue.Comment", ErrTooManyFiles) + return + } + + for _, header := range attachments { + file, err := header.Open() + + if err != nil { + ctx.Handle(500, "issue.Comment(header.Open)", err) + return + } + + defer file.Close() + + buf := make([]byte, 1024) + n, _ := file.Read(buf) + if n > 0 { + buf = buf[:n] + } + fileType := http.DetectContentType(buf) + fmt.Println(fileType) + + allowed := false + + for _, t := range allowedTypes { + t := strings.Trim(t, " ") + + if t == "*/*" || t == fileType { + allowed = true + break + } + } + + if !allowed { + ctx.Handle(400, "issue.Comment", ErrFileTypeForbidden) + return + } + + out, err := ioutil.TempFile(setting.AttachmentPath, "attachment_") + + if err != nil { + ctx.Handle(500, "ioutil.TempFile", err) + return + } + + defer out.Close() + + out.Write(buf) + _, err = io.Copy(out, file) + if err != nil { + ctx.Handle(500, "io.Copy", err) + return + } + + _, err = models.CreateAttachment(issueId, commentId, header.Filename, out.Name()) + if err != nil { + ctx.Handle(500, "CreateAttachment", err) + return + } + } +} + +func Comment(ctx *middleware.Context) { + send := func(status int, data interface{}, err error) { + if err != nil { + log.Error(4, "issue.Comment(?): %s", err) + + ctx.JSON(status, map[string]interface{}{ + "ok": false, + "status": status, + "error": err.Error(), + }) + } else { + ctx.JSON(status, map[string]interface{}{ + "ok": true, + "status": status, + "data": data, + }) + } + } + + index := com.StrTo(ctx.Query("issueIndex")).MustInt64() + if index == 0 { + ctx.Error(404) + return + } + + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, index) + if err != nil { + if err == models.ErrIssueNotExist { + send(404, nil, err) + } else { + send(200, nil, err) + } + + return + } + + // Check if issue owner changes the status of issue. + var newStatus string + if ctx.Repo.IsOwner || issue.PosterId == ctx.User.Id { + newStatus = ctx.Query("change_status") + } + if len(newStatus) > 0 { + if (strings.Contains(newStatus, "Reopen") && issue.IsClosed) || + (strings.Contains(newStatus, "Close") && !issue.IsClosed) { + issue.IsClosed = !issue.IsClosed + if err = models.UpdateIssue(issue); err != nil { + send(500, nil, err) + return + } else if err = models.UpdateIssueUserPairsByStatus(issue.Id, issue.IsClosed); err != nil { + send(500, nil, err) + return + } + + // Change open/closed issue counter for the associated milestone + if issue.MilestoneId > 0 { + if err = models.ChangeMilestoneIssueStats(issue); err != nil { + send(500, nil, err) + } + } + + cmtType := models.CLOSE + if !issue.IsClosed { + cmtType = models.REOPEN + } + + if _, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, cmtType, "", nil); err != nil { + send(200, nil, err) + return + } + log.Trace("%s Issue(%d) status changed: %v", ctx.Req.RequestURI, issue.Id, !issue.IsClosed) + } + } + + var comment *models.Comment + + var ms []string + content := ctx.Query("content") + // Fix #321. Allow empty comments, as long as we have attachments. + if len(content) > 0 || len(ctx.Req.MultipartForm.File["attachments"]) > 0 { + switch ctx.Params(":action") { + case "new": + if comment, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, models.COMMENT, content, nil); err != nil { + send(500, nil, err) + return + } + + // Update mentions. + ms = base.MentionPattern.FindAllString(issue.Content, -1) + if len(ms) > 0 { + for i := range ms { + ms[i] = ms[i][1:] + } + + if err := models.UpdateMentions(ms, issue.Id); err != nil { + send(500, nil, err) + return + } + } + + log.Trace("%s Comment created: %d", ctx.Req.RequestURI, issue.Id) + default: + ctx.Handle(404, "issue.Comment", err) + return + } + } + + if comment != nil { + uploadFiles(ctx, issue.Id, comment.Id) + } + + // Notify watchers. + act := &models.Action{ + ActUserId: ctx.User.Id, + ActUserName: ctx.User.LowerName, + ActEmail: ctx.User.Email, + OpType: models.COMMENT_ISSUE, + Content: fmt.Sprintf("%d|%s", issue.Index, strings.Split(content, "\n")[0]), + RepoId: ctx.Repo.Repository.Id, + RepoUserName: ctx.Repo.Owner.LowerName, + RepoName: ctx.Repo.Repository.LowerName, + } + if err = models.NotifyWatchers(act); err != nil { + send(500, nil, err) + return + } + + // Mail watchers and mentions. + if setting.Service.EnableNotifyMail { + issue.Content = content + tos, err := mailer.SendIssueNotifyMail(ctx.User, ctx.Repo.Owner, ctx.Repo.Repository, issue) + if err != nil { + send(500, nil, err) + return + } + + tos = append(tos, ctx.User.LowerName) + newTos := make([]string, 0, len(ms)) + for _, m := range ms { + if com.IsSliceContainsStr(tos, m) { + continue + } + + newTos = append(newTos, m) + } + if err = mailer.SendIssueMentionMail(ctx.Render, ctx.User, ctx.Repo.Owner, + ctx.Repo.Repository, issue, models.GetUserEmailsByNames(newTos)); err != nil { + send(500, nil, err) + return + } + } + + send(200, fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, index), nil) +} + +func NewLabel(ctx *middleware.Context, form auth.CreateLabelForm) { + if ctx.HasError() { + Issues(ctx) + return + } + + l := &models.Label{ + RepoId: ctx.Repo.Repository.Id, + Name: form.Title, + Color: form.Color, + } + if err := models.NewLabel(l); err != nil { + ctx.Handle(500, "issue.NewLabel(NewLabel)", err) + return + } + ctx.Redirect(ctx.Repo.RepoLink + "/issues") +} + +func UpdateLabel(ctx *middleware.Context, form auth.CreateLabelForm) { + id := com.StrTo(ctx.Query("id")).MustInt64() + 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) { + removes := ctx.Query("remove") + if len(strings.TrimSpace(removes)) == 0 { + ctx.JSON(200, map[string]interface{}{ + "ok": true, + }) + return + } + + strIds := strings.Split(removes, ",") + 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) { + ctx.Data["Title"] = "Milestones" + ctx.Data["IsRepoToolbarIssues"] = true + ctx.Data["IsRepoToolbarIssuesList"] = true + + isShowClosed := ctx.Query("state") == "closed" + + miles, err := models.GetMilestones(ctx.Repo.Repository.Id, isShowClosed) + if err != nil { + ctx.Handle(500, "issue.Milestones(GetMilestones)", err) + return + } + for _, m := range miles { + m.RenderedContent = string(base.RenderSpecialLink([]byte(m.Content), ctx.Repo.RepoLink)) + m.CalOpenIssues() + } + ctx.Data["Milestones"] = miles + + if isShowClosed { + ctx.Data["State"] = "closed" + } else { + ctx.Data["State"] = "open" + } + ctx.HTML(200, MILESTONE) +} + +func NewMilestone(ctx *middleware.Context) { + ctx.Data["Title"] = "New Milestone" + ctx.Data["IsRepoToolbarIssues"] = true + ctx.Data["IsRepoToolbarIssuesList"] = true + ctx.HTML(200, MILESTONE_NEW) +} + +func NewMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm) { + ctx.Data["Title"] = "New Milestone" + ctx.Data["IsRepoToolbarIssues"] = true + ctx.Data["IsRepoToolbarIssuesList"] = true + + if ctx.HasError() { + ctx.HTML(200, MILESTONE_NEW) + return + } + + var deadline time.Time + var err error + if len(form.Deadline) == 0 { + form.Deadline = "12/31/9999" + } + deadline, err = time.Parse("01/02/2006", form.Deadline) + if err != nil { + ctx.Handle(500, "issue.NewMilestonePost(time.Parse)", err) + return + } + + mile := &models.Milestone{ + RepoId: ctx.Repo.Repository.Id, + Index: int64(ctx.Repo.Repository.NumMilestones) + 1, + Name: form.Title, + Content: form.Content, + Deadline: deadline, + } + if err = models.NewMilestone(mile); err != nil { + ctx.Handle(500, "issue.NewMilestonePost(NewMilestone)", err) + return + } + + ctx.Redirect(ctx.Repo.RepoLink + "/issues/milestones") +} + +func UpdateMilestone(ctx *middleware.Context) { + ctx.Data["Title"] = "Update Milestone" + ctx.Data["IsRepoToolbarIssues"] = true + ctx.Data["IsRepoToolbarIssuesList"] = true + + idx := com.StrTo(ctx.Params(":index")).MustInt64() + if idx == 0 { + ctx.Handle(404, "issue.UpdateMilestone", nil) + return + } + + mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx) + if err != nil { + if err == models.ErrMilestoneNotExist { + ctx.Handle(404, "issue.UpdateMilestone(GetMilestoneByIndex)", err) + } else { + ctx.Handle(500, "issue.UpdateMilestone(GetMilestoneByIndex)", err) + } + return + } + + action := ctx.Params(":action") + if len(action) > 0 { + switch action { + case "open": + if mile.IsClosed { + if err = models.ChangeMilestoneStatus(mile, false); err != nil { + ctx.Handle(500, "issue.UpdateMilestone(ChangeMilestoneStatus)", err) + return + } + } + case "close": + if !mile.IsClosed { + mile.ClosedDate = time.Now() + if err = models.ChangeMilestoneStatus(mile, true); err != nil { + ctx.Handle(500, "issue.UpdateMilestone(ChangeMilestoneStatus)", err) + return + } + } + case "delete": + if err = models.DeleteMilestone(mile); err != nil { + ctx.Handle(500, "issue.UpdateMilestone(DeleteMilestone)", err) + return + } + } + ctx.Redirect(ctx.Repo.RepoLink + "/issues/milestones") + return + } + + mile.DeadlineString = mile.Deadline.UTC().Format("01/02/2006") + if mile.DeadlineString == "12/31/9999" { + mile.DeadlineString = "" + } + ctx.Data["Milestone"] = mile + + ctx.HTML(200, MILESTONE_EDIT) +} + +func UpdateMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm) { + ctx.Data["Title"] = "Update Milestone" + ctx.Data["IsRepoToolbarIssues"] = true + ctx.Data["IsRepoToolbarIssuesList"] = true + + idx := com.StrTo(ctx.Params(":index")).MustInt64() + if idx == 0 { + ctx.Handle(404, "issue.UpdateMilestonePost", nil) + return + } + + mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx) + if err != nil { + if err == models.ErrMilestoneNotExist { + ctx.Handle(404, "issue.UpdateMilestonePost(GetMilestoneByIndex)", err) + } else { + ctx.Handle(500, "issue.UpdateMilestonePost(GetMilestoneByIndex)", err) + } + return + } + + if ctx.HasError() { + ctx.HTML(200, MILESTONE_EDIT) + return + } + + var deadline time.Time + if len(form.Deadline) == 0 { + form.Deadline = "12/31/9999" + } + deadline, err = time.Parse("01/02/2006", form.Deadline) + if err != nil { + ctx.Handle(500, "issue.UpdateMilestonePost(time.Parse)", err) + return + } + + mile.Name = form.Title + mile.Content = form.Content + mile.Deadline = deadline + if err = models.UpdateMilestone(mile); err != nil { + ctx.Handle(500, "issue.UpdateMilestonePost(UpdateMilestone)", err) + return + } + + ctx.Redirect(ctx.Repo.RepoLink + "/issues/milestones") +} + +func IssueGetAttachment(ctx *middleware.Context) { + id := com.StrTo(ctx.Params(":id")).MustInt64() + if id == 0 { + ctx.Error(404) + return + } + + attachment, err := models.GetAttachmentById(id) + + if err != nil { + ctx.Handle(404, "issue.IssueGetAttachment(models.GetAttachmentById)", err) + return + } + + // Fix #312. Attachments with , in their name are not handled correctly by Google Chrome. + // We must put the name in " manually. + ctx.ServeFile(attachment.Path, "\""+attachment.Name+"\"") +} diff --git a/routers/repo/release.go b/routers/repo/release.go index 509796fb1..addeb1ce5 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -5,13 +5,11 @@ package repo import ( - // "github.com/go-martini/martini" - - // "github.com/gogits/gogs/models" - // "github.com/gogits/gogs/modules/auth" + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/base" - // "github.com/gogits/gogs/modules/log" - // "github.com/gogits/gogs/modules/middleware" + "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/middleware" ) const ( @@ -20,215 +18,215 @@ const ( RELEASE_EDIT base.TplName = "repo/release/edit" ) -// func Releases(ctx *middleware.Context) { -// ctx.Data["Title"] = "Releases" -// ctx.Data["IsRepoToolbarReleases"] = true -// ctx.Data["IsRepoReleaseNew"] = false -// rawTags, err := ctx.Repo.GitRepo.GetTags() -// if err != nil { -// ctx.Handle(500, "release.Releases(GetTags)", err) -// return -// } - -// rels, err := models.GetReleasesByRepoId(ctx.Repo.Repository.Id) -// if err != nil { -// ctx.Handle(500, "release.Releases(GetReleasesByRepoId)", err) -// return -// } - -// commitsCount, err := ctx.Repo.Commit.CommitsCount() -// if err != nil { -// ctx.Handle(500, "release.Releases(CommitsCount)", err) -// return -// } - -// // Temproray cache commits count of used branches to speed up. -// countCache := make(map[string]int) - -// tags := make([]*models.Release, len(rawTags)) -// for i, rawTag := range rawTags { -// for _, rel := range rels { -// if rel.IsDraft && !ctx.Repo.IsOwner { -// continue -// } -// if rel.TagName == rawTag { -// rel.Publisher, err = models.GetUserById(rel.PublisherId) -// if err != nil { -// ctx.Handle(500, "release.Releases(GetUserById)", err) -// return -// } -// // Get corresponding target if it's not the current branch. -// if ctx.Repo.BranchName != rel.Target { -// // Get count if not exists. -// if _, ok := countCache[rel.Target]; !ok { -// commit, err := ctx.Repo.GitRepo.GetCommitOfTag(rel.TagName) -// if err != nil { -// ctx.Handle(500, "release.Releases(GetCommitOfTag)", err) -// return -// } -// countCache[rel.Target], err = commit.CommitsCount() -// if err != nil { -// ctx.Handle(500, "release.Releases(CommitsCount2)", err) -// return -// } -// } -// rel.NumCommitsBehind = countCache[rel.Target] - rel.NumCommits -// } else { -// rel.NumCommitsBehind = commitsCount - rel.NumCommits -// } - -// rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink) -// tags[i] = rel -// break -// } -// } - -// if tags[i] == nil { -// commit, err := ctx.Repo.GitRepo.GetCommitOfTag(rawTag) -// if err != nil { -// ctx.Handle(500, "release.Releases(GetCommitOfTag2)", err) -// return -// } - -// tags[i] = &models.Release{ -// Title: rawTag, -// TagName: rawTag, -// Sha1: commit.Id.String(), -// } - -// tags[i].NumCommits, err = ctx.Repo.GitRepo.CommitsCount(commit.Id.String()) -// if err != nil { -// ctx.Handle(500, "release.Releases(CommitsCount)", err) -// return -// } -// tags[i].NumCommitsBehind = commitsCount - tags[i].NumCommits -// } -// } -// models.SortReleases(tags) -// ctx.Data["Releases"] = tags -// ctx.HTML(200, RELEASES) -// } - -// func NewRelease(ctx *middleware.Context) { -// if !ctx.Repo.IsOwner { -// ctx.Handle(403, "release.ReleasesNew", nil) -// return -// } - -// ctx.Data["Title"] = "New Release" -// ctx.Data["IsRepoToolbarReleases"] = true -// ctx.Data["IsRepoReleaseNew"] = true -// ctx.HTML(200, RELEASE_NEW) -// } - -// func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) { -// if !ctx.Repo.IsOwner { -// ctx.Handle(403, "release.ReleasesNew", nil) -// return -// } - -// ctx.Data["Title"] = "New Release" -// ctx.Data["IsRepoToolbarReleases"] = true -// ctx.Data["IsRepoReleaseNew"] = true - -// if ctx.HasError() { -// ctx.HTML(200, RELEASE_NEW) -// return -// } - -// commitsCount, err := ctx.Repo.Commit.CommitsCount() -// if err != nil { -// ctx.Handle(500, "release.ReleasesNewPost(CommitsCount)", err) -// return -// } - -// if !ctx.Repo.GitRepo.IsBranchExist(form.Target) { -// ctx.RenderWithErr("Target branch does not exist", "release/new", &form) -// return -// } - -// rel := &models.Release{ -// RepoId: ctx.Repo.Repository.Id, -// PublisherId: ctx.User.Id, -// Title: form.Title, -// TagName: form.TagName, -// Target: form.Target, -// Sha1: ctx.Repo.Commit.Id.String(), -// NumCommits: commitsCount, -// Note: form.Content, -// IsDraft: len(form.Draft) > 0, -// IsPrerelease: form.Prerelease, -// } - -// if err = models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil { -// if err == models.ErrReleaseAlreadyExist { -// ctx.RenderWithErr("Release with this tag name has already existed", "release/new", &form) -// } else { -// ctx.Handle(500, "release.ReleasesNewPost(IsReleaseExist)", err) -// } -// return -// } -// log.Trace("%s Release created: %s/%s:%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.Name, form.TagName) - -// ctx.Redirect(ctx.Repo.RepoLink + "/releases") -// } - -// func EditRelease(ctx *middleware.Context, params martini.Params) { -// if !ctx.Repo.IsOwner { -// ctx.Handle(403, "release.ReleasesEdit", nil) -// return -// } - -// tagName := params["tagname"] -// rel, err := models.GetRelease(ctx.Repo.Repository.Id, tagName) -// if err != nil { -// if err == models.ErrReleaseNotExist { -// ctx.Handle(404, "release.ReleasesEdit(GetRelease)", err) -// } else { -// ctx.Handle(500, "release.ReleasesEdit(GetRelease)", err) -// } -// return -// } -// ctx.Data["Release"] = rel - -// ctx.Data["Title"] = "Edit Release" -// ctx.Data["IsRepoToolbarReleases"] = true -// ctx.HTML(200, RELEASE_EDIT) -// } - -// func EditReleasePost(ctx *middleware.Context, params martini.Params, form auth.EditReleaseForm) { -// if !ctx.Repo.IsOwner { -// ctx.Handle(403, "release.EditReleasePost", nil) -// return -// } - -// tagName := params["tagname"] -// rel, err := models.GetRelease(ctx.Repo.Repository.Id, tagName) -// if err != nil { -// if err == models.ErrReleaseNotExist { -// ctx.Handle(404, "release.EditReleasePost(GetRelease)", err) -// } else { -// ctx.Handle(500, "release.EditReleasePost(GetRelease)", err) -// } -// return -// } -// ctx.Data["Release"] = rel - -// if ctx.HasError() { -// ctx.HTML(200, RELEASE_EDIT) -// return -// } - -// ctx.Data["Title"] = "Edit Release" -// ctx.Data["IsRepoToolbarReleases"] = true - -// rel.Title = form.Title -// rel.Note = form.Content -// rel.IsDraft = len(form.Draft) > 0 -// rel.IsPrerelease = form.Prerelease -// if err = models.UpdateRelease(ctx.Repo.GitRepo, rel); err != nil { -// ctx.Handle(500, "release.EditReleasePost(UpdateRelease)", err) -// return -// } -// ctx.Redirect(ctx.Repo.RepoLink + "/releases") -// } +func Releases(ctx *middleware.Context) { + ctx.Data["Title"] = "Releases" + ctx.Data["IsRepoToolbarReleases"] = true + ctx.Data["IsRepoReleaseNew"] = false + rawTags, err := ctx.Repo.GitRepo.GetTags() + if err != nil { + ctx.Handle(500, "release.Releases(GetTags)", err) + return + } + + rels, err := models.GetReleasesByRepoId(ctx.Repo.Repository.Id) + if err != nil { + ctx.Handle(500, "release.Releases(GetReleasesByRepoId)", err) + return + } + + commitsCount, err := ctx.Repo.Commit.CommitsCount() + if err != nil { + ctx.Handle(500, "release.Releases(CommitsCount)", err) + return + } + + // Temproray cache commits count of used branches to speed up. + countCache := make(map[string]int) + + tags := make([]*models.Release, len(rawTags)) + for i, rawTag := range rawTags { + for _, rel := range rels { + if rel.IsDraft && !ctx.Repo.IsOwner { + continue + } + if rel.TagName == rawTag { + rel.Publisher, err = models.GetUserById(rel.PublisherId) + if err != nil { + ctx.Handle(500, "GetUserById", err) + return + } + // Get corresponding target if it's not the current branch. + if ctx.Repo.BranchName != rel.Target { + // Get count if not exists. + if _, ok := countCache[rel.Target]; !ok { + commit, err := ctx.Repo.GitRepo.GetCommitOfTag(rel.TagName) + if err != nil { + ctx.Handle(500, "GetCommitOfTag", err) + return + } + countCache[rel.Target], err = commit.CommitsCount() + if err != nil { + ctx.Handle(500, "CommitsCount2", err) + return + } + } + rel.NumCommitsBehind = countCache[rel.Target] - rel.NumCommits + } else { + rel.NumCommitsBehind = commitsCount - rel.NumCommits + } + + rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink) + tags[i] = rel + break + } + } + + if tags[i] == nil { + commit, err := ctx.Repo.GitRepo.GetCommitOfTag(rawTag) + if err != nil { + ctx.Handle(500, "GetCommitOfTag2", err) + return + } + + tags[i] = &models.Release{ + Title: rawTag, + TagName: rawTag, + Sha1: commit.Id.String(), + } + + tags[i].NumCommits, err = ctx.Repo.GitRepo.CommitsCount(commit.Id.String()) + if err != nil { + ctx.Handle(500, "CommitsCount", err) + return + } + tags[i].NumCommitsBehind = commitsCount - tags[i].NumCommits + } + } + models.SortReleases(tags) + ctx.Data["Releases"] = tags + ctx.HTML(200, RELEASES) +} + +func NewRelease(ctx *middleware.Context) { + if !ctx.Repo.IsOwner { + ctx.Handle(403, "release.ReleasesNew", nil) + return + } + + ctx.Data["Title"] = "New Release" + ctx.Data["IsRepoToolbarReleases"] = true + ctx.Data["IsRepoReleaseNew"] = true + ctx.HTML(200, RELEASE_NEW) +} + +func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) { + if !ctx.Repo.IsOwner { + ctx.Handle(403, "release.ReleasesNew", nil) + return + } + + ctx.Data["Title"] = "New Release" + ctx.Data["IsRepoToolbarReleases"] = true + ctx.Data["IsRepoReleaseNew"] = true + + if ctx.HasError() { + ctx.HTML(200, RELEASE_NEW) + return + } + + commitsCount, err := ctx.Repo.Commit.CommitsCount() + if err != nil { + ctx.Handle(500, "release.ReleasesNewPost(CommitsCount)", err) + return + } + + if !ctx.Repo.GitRepo.IsBranchExist(form.Target) { + ctx.RenderWithErr("Target branch does not exist", "release/new", &form) + return + } + + rel := &models.Release{ + RepoId: ctx.Repo.Repository.Id, + PublisherId: ctx.User.Id, + Title: form.Title, + TagName: form.TagName, + Target: form.Target, + Sha1: ctx.Repo.Commit.Id.String(), + NumCommits: commitsCount, + Note: form.Content, + IsDraft: len(form.Draft) > 0, + IsPrerelease: form.Prerelease, + } + + if err = models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil { + if err == models.ErrReleaseAlreadyExist { + ctx.RenderWithErr("Release with this tag name has already existed", "release/new", &form) + } else { + ctx.Handle(500, "release.ReleasesNewPost(IsReleaseExist)", err) + } + return + } + log.Trace("%s Release created: %s/%s:%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.Name, form.TagName) + + ctx.Redirect(ctx.Repo.RepoLink + "/releases") +} + +func EditRelease(ctx *middleware.Context) { + if !ctx.Repo.IsOwner { + ctx.Handle(403, "release.ReleasesEdit", nil) + return + } + + tagName := ctx.Params(":tagname") + rel, err := models.GetRelease(ctx.Repo.Repository.Id, tagName) + if err != nil { + if err == models.ErrReleaseNotExist { + ctx.Handle(404, "release.ReleasesEdit(GetRelease)", err) + } else { + ctx.Handle(500, "release.ReleasesEdit(GetRelease)", err) + } + return + } + ctx.Data["Release"] = rel + + ctx.Data["Title"] = "Edit Release" + ctx.Data["IsRepoToolbarReleases"] = true + ctx.HTML(200, RELEASE_EDIT) +} + +func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) { + if !ctx.Repo.IsOwner { + ctx.Handle(403, "release.EditReleasePost", nil) + return + } + + tagName := ctx.Params(":tagname") + rel, err := models.GetRelease(ctx.Repo.Repository.Id, tagName) + if err != nil { + if err == models.ErrReleaseNotExist { + ctx.Handle(404, "release.EditReleasePost(GetRelease)", err) + } else { + ctx.Handle(500, "release.EditReleasePost(GetRelease)", err) + } + return + } + ctx.Data["Release"] = rel + + if ctx.HasError() { + ctx.HTML(200, RELEASE_EDIT) + return + } + + ctx.Data["Title"] = "Edit Release" + ctx.Data["IsRepoToolbarReleases"] = true + + rel.Title = form.Title + rel.Note = form.Content + rel.IsDraft = len(form.Draft) > 0 + rel.IsPrerelease = form.Prerelease + if err = models.UpdateRelease(ctx.Repo.GitRepo, rel); err != nil { + ctx.Handle(500, "release.EditReleasePost(UpdateRelease)", err) + return + } + ctx.Redirect(ctx.Repo.RepoLink + "/releases") +} diff --git a/routers/repo/repo.go b/routers/repo/repo.go index d1e196a0b..8de1e0936 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -5,8 +5,10 @@ package repo import ( + "fmt" "os" "path" + "strings" "github.com/Unknwon/com" @@ -34,22 +36,22 @@ func Create(ctx *middleware.Context) { ctx.Data["Licenses"] = models.Licenses ctxUser := ctx.User - // orgId := com.StrTo(ctx.Query("org")).MustInt64() - // if orgId > 0 { - // org, err := models.GetUserById(orgId) - // if err != nil && err != models.ErrUserNotExist { - // ctx.Handle(500, "home.Dashboard(GetUserById)", err) - // return - // } - // ctxUser = org - // } + orgId := com.StrTo(ctx.Query("org")).MustInt64() + if orgId > 0 { + org, err := models.GetUserById(orgId) + if err != nil && err != models.ErrUserNotExist { + ctx.Handle(500, "home.Dashboard(GetUserById)", err) + return + } + ctxUser = org + } ctx.Data["ContextUser"] = ctxUser - // if err := ctx.User.GetOrganizations(); err != nil { - // ctx.Handle(500, "home.Dashboard(GetOrganizations)", err) - // return - // } - // ctx.Data["AllUsers"] = append([]*models.User{ctx.User}, ctx.User.Orgs...) + if err := ctx.User.GetOrganizations(); err != nil { + ctx.Handle(500, "home.Dashboard(GetOrganizations)", err) + return + } + ctx.Data["AllUsers"] = append([]*models.User{ctx.User}, ctx.User.Orgs...) ctx.HTML(200, CREATE) } @@ -62,22 +64,22 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) { ctx.Data["Licenses"] = models.Licenses ctxUser := ctx.User - // orgId := com.StrTo(ctx.Query("org")).MustInt64() - // if orgId > 0 { - // org, err := models.GetUserById(orgId) - // if err != nil && err != models.ErrUserNotExist { - // ctx.Handle(500, "home.Dashboard(GetUserById)", err) - // return - // } - // ctxUser = org - // } + orgId := com.StrTo(ctx.Query("org")).MustInt64() + if orgId > 0 { + org, err := models.GetUserById(orgId) + if err != nil && err != models.ErrUserNotExist { + ctx.Handle(500, "home.Dashboard(GetUserById)", err) + return + } + ctxUser = org + } ctx.Data["ContextUser"] = ctxUser - // if err := ctx.User.GetOrganizations(); err != nil { - // ctx.Handle(500, "home.CreatePost(GetOrganizations)", err) - // return - // } - // ctx.Data["Orgs"] = ctx.User.Orgs + if err := ctx.User.GetOrganizations(); err != nil { + ctx.Handle(500, "home.CreatePost(GetOrganizations)", err) + return + } + ctx.Data["Orgs"] = ctx.User.Orgs if ctx.HasError() { ctx.HTML(200, CREATE) @@ -127,78 +129,78 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) { ctx.Handle(500, "CreateRepository", err) } -// func Migrate(ctx *middleware.Context) { -// ctx.Data["Title"] = "Migrate repository" -// ctx.Data["PageIsNewRepo"] = true +func Migrate(ctx *middleware.Context) { + ctx.Data["Title"] = "Migrate repository" + ctx.Data["PageIsNewRepo"] = true -// if err := ctx.User.GetOrganizations(); err != nil { -// ctx.Handle(500, "home.Migrate(GetOrganizations)", err) -// return -// } -// ctx.Data["Orgs"] = ctx.User.Orgs + if err := ctx.User.GetOrganizations(); err != nil { + ctx.Handle(500, "home.Migrate(GetOrganizations)", err) + return + } + ctx.Data["Orgs"] = ctx.User.Orgs -// ctx.HTML(200, MIGRATE) -// } + ctx.HTML(200, MIGRATE) +} -// func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) { -// ctx.Data["Title"] = "Migrate repository" -// ctx.Data["PageIsNewRepo"] = true +func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) { + ctx.Data["Title"] = "Migrate repository" + ctx.Data["PageIsNewRepo"] = true -// if err := ctx.User.GetOrganizations(); err != nil { -// ctx.Handle(500, "home.MigratePost(GetOrganizations)", err) -// return -// } -// ctx.Data["Orgs"] = ctx.User.Orgs + if err := ctx.User.GetOrganizations(); err != nil { + ctx.Handle(500, "home.MigratePost(GetOrganizations)", err) + return + } + ctx.Data["Orgs"] = ctx.User.Orgs -// if ctx.HasError() { -// ctx.HTML(200, MIGRATE) -// return -// } + if ctx.HasError() { + ctx.HTML(200, MIGRATE) + return + } -// u := ctx.User -// // Not equal means current user is an organization. -// if u.Id != form.Uid { -// var err error -// u, err = models.GetUserById(form.Uid) -// if err != nil { -// if err == models.ErrUserNotExist { -// ctx.Handle(404, "home.MigratePost(GetUserById)", err) -// } else { -// ctx.Handle(500, "home.MigratePost(GetUserById)", err) -// } -// return -// } -// } + u := ctx.User + // Not equal means current user is an organization. + if u.Id != form.Uid { + var err error + u, err = models.GetUserById(form.Uid) + if err != nil { + if err == models.ErrUserNotExist { + ctx.Handle(404, "home.MigratePost(GetUserById)", err) + } else { + ctx.Handle(500, "home.MigratePost(GetUserById)", err) + } + return + } + } -// authStr := strings.Replace(fmt.Sprintf("://%s:%s", -// form.AuthUserName, form.AuthPasswd), "@", "%40", -1) -// url := strings.Replace(form.Url, "://", authStr+"@", 1) -// repo, err := models.MigrateRepository(u, form.RepoName, form.Description, form.Private, -// form.Mirror, url) -// if err == nil { -// log.Trace("%s Repository migrated: %s/%s", ctx.Req.RequestURI, u.LowerName, form.RepoName) -// ctx.Redirect("/" + u.Name + "/" + form.RepoName) -// return -// } else if err == models.ErrRepoAlreadyExist { -// ctx.RenderWithErr("Repository name has already been used", MIGRATE, &form) -// return -// } else if err == models.ErrRepoNameIllegal { -// ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), MIGRATE, &form) -// return -// } + authStr := strings.Replace(fmt.Sprintf("://%s:%s", + form.AuthUserName, form.AuthPasswd), "@", "%40", -1) + url := strings.Replace(form.Url, "://", authStr+"@", 1) + repo, err := models.MigrateRepository(u, form.RepoName, form.Description, form.Private, + form.Mirror, url) + if err == nil { + log.Trace("%s Repository migrated: %s/%s", ctx.Req.RequestURI, u.LowerName, form.RepoName) + ctx.Redirect("/" + u.Name + "/" + form.RepoName) + return + } else if err == models.ErrRepoAlreadyExist { + ctx.RenderWithErr("Repository name has already been used", MIGRATE, &form) + return + } else if err == models.ErrRepoNameIllegal { + ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), MIGRATE, &form) + return + } -// if repo != nil { -// if errDelete := models.DeleteRepository(u.Id, repo.Id, u.Name); errDelete != nil { -// log.Error("repo.MigratePost(DeleteRepository): %v", errDelete) -// } -// } + if repo != nil { + if errDelete := models.DeleteRepository(u.Id, repo.Id, u.Name); errDelete != nil { + log.Error(4, "DeleteRepository: %v", errDelete) + } + } -// if strings.Contains(err.Error(), "Authentication failed") { -// ctx.RenderWithErr(err.Error(), MIGRATE, &form) -// return -// } -// ctx.Handle(500, "repo.Migrate(MigrateRepository)", err) -// } + if strings.Contains(err.Error(), "Authentication failed") { + ctx.RenderWithErr(err.Error(), MIGRATE, &form) + return + } + ctx.Handle(500, "MigrateRepository", err) +} // func Action(ctx *middleware.Context, params martini.Params) { // var err error diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 48f2e219e..26f391346 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -4,362 +4,362 @@ package repo -// import ( -// "fmt" -// "strings" -// "time" - -// "github.com/go-martini/martini" - -// "github.com/gogits/gogs-ng/models" -// "github.com/gogits/gogs/modules/auth" -// "github.com/gogits/gogs/modules/base" -// "github.com/gogits/gogs/modules/log" -// "github.com/gogits/gogs/modules/mailer" -// "github.com/gogits/gogs/modules/middleware" -// "github.com/gogits/gogs/modules/setting" -// ) - -// const ( -// SETTING base.TplName = "repo/setting" -// COLLABORATION base.TplName = "repo/collaboration" - -// HOOKS base.TplName = "repo/hooks" -// HOOK_ADD base.TplName = "repo/hook_add" -// HOOK_EDIT base.TplName = "repo/hook_edit" -// ) - -// func Setting(ctx *middleware.Context) { -// ctx.Data["IsRepoToolbarSetting"] = true -// ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - settings" -// ctx.HTML(200, SETTING) -// } - -// func SettingPost(ctx *middleware.Context, form auth.RepoSettingForm) { -// ctx.Data["IsRepoToolbarSetting"] = true - -// switch ctx.Query("action") { -// case "update": -// if ctx.HasError() { -// ctx.HTML(200, SETTING) -// return -// } - -// newRepoName := form.RepoName -// // Check if repository name has been changed. -// if ctx.Repo.Repository.Name != newRepoName { -// isExist, err := models.IsRepositoryExist(ctx.Repo.Owner, newRepoName) -// if err != nil { -// ctx.Handle(500, "setting.SettingPost(update: check existence)", err) -// return -// } else if isExist { -// ctx.RenderWithErr("Repository name has been taken in your repositories.", SETTING, nil) -// return -// } else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil { -// ctx.Handle(500, "setting.SettingPost(change repository name)", err) -// return -// } -// log.Trace("%s Repository name changed: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newRepoName) - -// ctx.Repo.Repository.Name = newRepoName -// } - -// br := form.Branch - -// if ctx.Repo.GitRepo.IsBranchExist(br) { -// ctx.Repo.Repository.DefaultBranch = br -// } -// ctx.Repo.Repository.Description = form.Description -// ctx.Repo.Repository.Website = form.Website -// ctx.Repo.Repository.IsPrivate = form.Private -// ctx.Repo.Repository.IsGoget = form.GoGet -// if err := models.UpdateRepository(ctx.Repo.Repository); err != nil { -// ctx.Handle(404, "setting.SettingPost(update)", err) -// return -// } -// log.Trace("%s Repository updated: %s/%s", ctx.Req.RequestURI, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) - -// if ctx.Repo.Repository.IsMirror { -// if form.Interval > 0 { -// ctx.Repo.Mirror.Interval = form.Interval -// ctx.Repo.Mirror.NextUpdate = time.Now().Add(time.Duration(form.Interval) * time.Hour) -// if err := models.UpdateMirror(ctx.Repo.Mirror); err != nil { -// log.Error("setting.SettingPost(UpdateMirror): %v", err) -// } -// } -// } - -// ctx.Flash.Success("Repository options has been successfully updated.") -// ctx.Redirect(fmt.Sprintf("/%s/%s/settings", ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)) -// case "transfer": -// if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") { -// ctx.RenderWithErr("Please make sure you entered repository name is correct.", SETTING, nil) -// return -// } else if ctx.Repo.Repository.IsMirror { -// ctx.Error(404) -// return -// } - -// newOwner := ctx.Query("owner") -// // Check if new owner exists. -// isExist, err := models.IsUserExist(newOwner) -// if err != nil { -// ctx.Handle(500, "setting.SettingPost(transfer: check existence)", err) -// return -// } else if !isExist { -// ctx.RenderWithErr("Please make sure you entered owner name is correct.", SETTING, nil) -// return -// } else if err = models.TransferOwnership(ctx.Repo.Owner, newOwner, ctx.Repo.Repository); err != nil { -// ctx.Handle(500, "setting.SettingPost(transfer repository)", err) -// return -// } -// log.Trace("%s Repository transfered: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newOwner) - -// ctx.Redirect("/") -// case "delete": -// if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") { -// ctx.RenderWithErr("Please make sure you entered repository name is correct.", SETTING, nil) -// return -// } - -// if ctx.Repo.Owner.IsOrganization() && -// !ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) { -// ctx.Error(403) -// return -// } - -// if err := models.DeleteRepository(ctx.Repo.Owner.Id, ctx.Repo.Repository.Id, ctx.Repo.Owner.Name); err != nil { -// ctx.Handle(500, "setting.Delete(DeleteRepository)", err) -// return -// } -// log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.Repo.Owner.LowerName, ctx.Repo.Repository.LowerName) - -// if ctx.Repo.Owner.IsOrganization() { -// ctx.Redirect("/org/" + ctx.Repo.Owner.Name + "/dashboard") -// } else { -// ctx.Redirect("/") -// } -// } -// } - -// func Collaboration(ctx *middleware.Context) { -// repoLink := strings.TrimPrefix(ctx.Repo.RepoLink, "/") -// ctx.Data["IsRepoToolbarCollaboration"] = true -// ctx.Data["Title"] = repoLink + " - collaboration" - -// // Delete collaborator. -// remove := strings.ToLower(ctx.Query("remove")) -// if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName { -// if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil { -// ctx.Handle(500, "setting.Collaboration(DeleteAccess)", err) -// return -// } -// ctx.Flash.Success("Collaborator has been removed.") -// ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") -// return -// } - -// names, err := models.GetCollaboratorNames(repoLink) -// if err != nil { -// ctx.Handle(500, "setting.Collaboration(GetCollaborators)", err) -// return -// } - -// us := make([]*models.User, len(names)) -// for i, name := range names { -// us[i], err = models.GetUserByName(name) -// if err != nil { -// ctx.Handle(500, "setting.Collaboration(GetUserByName)", err) -// return -// } -// } - -// ctx.Data["Collaborators"] = us -// ctx.HTML(200, COLLABORATION) -// } - -// func CollaborationPost(ctx *middleware.Context) { -// repoLink := strings.TrimPrefix(ctx.Repo.RepoLink, "/") -// name := strings.ToLower(ctx.Query("collaborator")) -// if len(name) == 0 || ctx.Repo.Owner.LowerName == name { -// ctx.Redirect(ctx.Req.RequestURI) -// return -// } -// has, err := models.HasAccess(name, repoLink, models.WRITABLE) -// if err != nil { -// ctx.Handle(500, "setting.CollaborationPost(HasAccess)", err) -// return -// } else if has { -// ctx.Redirect(ctx.Req.RequestURI) -// return -// } - -// u, err := models.GetUserByName(name) -// if err != nil { -// if err == models.ErrUserNotExist { -// ctx.Flash.Error("Given user does not exist.") -// ctx.Redirect(ctx.Req.RequestURI) -// } else { -// ctx.Handle(500, "setting.CollaborationPost(GetUserByName)", err) -// } -// return -// } - -// if err = models.AddAccess(&models.Access{UserName: name, RepoName: repoLink, -// Mode: models.WRITABLE}); err != nil { -// ctx.Handle(500, "setting.CollaborationPost(AddAccess)", err) -// return -// } - -// if setting.Service.EnableNotifyMail { -// if err = mailer.SendCollaboratorMail(ctx.Render, u, ctx.User, ctx.Repo.Repository); err != nil { -// ctx.Handle(500, "setting.CollaborationPost(SendCollaboratorMail)", err) -// return -// } -// } - -// ctx.Flash.Success("New collaborator has been added.") -// ctx.Redirect(ctx.Req.RequestURI) -// } - -// func WebHooks(ctx *middleware.Context) { -// ctx.Data["IsRepoToolbarWebHooks"] = true -// ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhooks" - -// // Delete webhook. -// remove, _ := base.StrTo(ctx.Query("remove")).Int64() -// if remove > 0 { -// if err := models.DeleteWebhook(remove); err != nil { -// ctx.Handle(500, "setting.WebHooks(DeleteWebhook)", err) -// return -// } -// ctx.Flash.Success("Webhook has been removed.") -// ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks") -// return -// } - -// ws, err := models.GetWebhooksByRepoId(ctx.Repo.Repository.Id) -// if err != nil { -// ctx.Handle(500, "setting.WebHooks(GetWebhooksByRepoId)", err) -// return -// } - -// ctx.Data["Webhooks"] = ws -// ctx.HTML(200, HOOKS) -// } - -// func WebHooksAdd(ctx *middleware.Context) { -// ctx.Data["IsRepoToolbarWebHooks"] = true -// ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook" -// ctx.HTML(200, HOOK_ADD) -// } - -// func WebHooksAddPost(ctx *middleware.Context, form auth.NewWebhookForm) { -// ctx.Data["IsRepoToolbarWebHooks"] = true -// ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook" - -// if ctx.HasError() { -// ctx.HTML(200, HOOK_ADD) -// return -// } - -// ct := models.JSON -// if form.ContentType == "2" { -// ct = models.FORM -// } - -// w := &models.Webhook{ -// RepoId: ctx.Repo.Repository.Id, -// Url: form.Url, -// ContentType: ct, -// Secret: form.Secret, -// HookEvent: &models.HookEvent{ -// PushOnly: form.PushOnly, -// }, -// IsActive: form.Active, -// } -// if err := w.UpdateEvent(); err != nil { -// ctx.Handle(500, "setting.WebHooksAddPost(UpdateEvent)", err) -// return -// } else if err := models.CreateWebhook(w); err != nil { -// ctx.Handle(500, "setting.WebHooksAddPost(CreateWebhook)", err) -// return -// } - -// ctx.Flash.Success("New webhook has been added.") -// ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks") -// } - -// func WebHooksEdit(ctx *middleware.Context, params martini.Params) { -// ctx.Data["IsRepoToolbarWebHooks"] = true -// ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhook" - -// hookId, _ := base.StrTo(params["id"]).Int64() -// if hookId == 0 { -// ctx.Handle(404, "setting.WebHooksEdit", nil) -// return -// } - -// w, err := models.GetWebhookById(hookId) -// if err != nil { -// if err == models.ErrWebhookNotExist { -// ctx.Handle(404, "setting.WebHooksEdit(GetWebhookById)", nil) -// } else { -// ctx.Handle(500, "setting.WebHooksEdit(GetWebhookById)", err) -// } -// return -// } - -// w.GetEvent() -// ctx.Data["Webhook"] = w -// ctx.HTML(200, HOOK_EDIT) -// } - -// func WebHooksEditPost(ctx *middleware.Context, params martini.Params, form auth.NewWebhookForm) { -// ctx.Data["IsRepoToolbarWebHooks"] = true -// ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhook" - -// hookId, _ := base.StrTo(params["id"]).Int64() -// if hookId == 0 { -// ctx.Handle(404, "setting.WebHooksEditPost", nil) -// return -// } - -// w, err := models.GetWebhookById(hookId) -// if err != nil { -// if err == models.ErrWebhookNotExist { -// ctx.Handle(404, "setting.WebHooksEditPost(GetWebhookById)", nil) -// } else { -// ctx.Handle(500, "setting.WebHooksEditPost(GetWebhookById)", err) -// } -// return -// } - -// if ctx.HasError() { -// ctx.HTML(200, HOOK_EDIT) -// return -// } - -// ct := models.JSON -// if form.ContentType == "2" { -// ct = models.FORM -// } - -// w.Url = form.Url -// w.ContentType = ct -// w.Secret = form.Secret -// w.HookEvent = &models.HookEvent{ -// PushOnly: form.PushOnly, -// } -// w.IsActive = form.Active -// if err := w.UpdateEvent(); err != nil { -// ctx.Handle(500, "setting.WebHooksEditPost(UpdateEvent)", err) -// return -// } else if err := models.UpdateWebhook(w); err != nil { -// ctx.Handle(500, "setting.WebHooksEditPost(WebHooksEditPost)", err) -// return -// } - -// ctx.Flash.Success("Webhook has been updated.") -// ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", ctx.Repo.RepoLink, hookId)) -// } +import ( + "fmt" + "strings" + "time" + + "github.com/Unknwon/com" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/auth" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" + "github.com/gogits/gogs/modules/middleware" + "github.com/gogits/gogs/modules/setting" +) + +const ( + SETTING base.TplName = "repo/setting" + COLLABORATION base.TplName = "repo/collaboration" + + HOOKS base.TplName = "repo/hooks" + HOOK_ADD base.TplName = "repo/hook_add" + HOOK_EDIT base.TplName = "repo/hook_edit" +) + +func Setting(ctx *middleware.Context) { + ctx.Data["IsRepoToolbarSetting"] = true + ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - settings" + ctx.HTML(200, SETTING) +} + +func SettingPost(ctx *middleware.Context, form auth.RepoSettingForm) { + ctx.Data["IsRepoToolbarSetting"] = true + + switch ctx.Query("action") { + case "update": + if ctx.HasError() { + ctx.HTML(200, SETTING) + return + } + + newRepoName := form.RepoName + // Check if repository name has been changed. + if ctx.Repo.Repository.Name != newRepoName { + isExist, err := models.IsRepositoryExist(ctx.Repo.Owner, newRepoName) + if err != nil { + ctx.Handle(500, "setting.SettingPost(update: check existence)", err) + return + } else if isExist { + ctx.RenderWithErr("Repository name has been taken in your repositories.", SETTING, nil) + return + } else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil { + ctx.Handle(500, "setting.SettingPost(change repository name)", err) + return + } + log.Trace("%s Repository name changed: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newRepoName) + + ctx.Repo.Repository.Name = newRepoName + } + + br := form.Branch + + if ctx.Repo.GitRepo.IsBranchExist(br) { + ctx.Repo.Repository.DefaultBranch = br + } + ctx.Repo.Repository.Description = form.Description + ctx.Repo.Repository.Website = form.Website + ctx.Repo.Repository.IsPrivate = form.Private + ctx.Repo.Repository.IsGoget = form.GoGet + if err := models.UpdateRepository(ctx.Repo.Repository); err != nil { + ctx.Handle(404, "UpdateRepository", err) + return + } + log.Trace("%s Repository updated: %s/%s", ctx.Req.RequestURI, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) + + if ctx.Repo.Repository.IsMirror { + if form.Interval > 0 { + ctx.Repo.Mirror.Interval = form.Interval + ctx.Repo.Mirror.NextUpdate = time.Now().Add(time.Duration(form.Interval) * time.Hour) + if err := models.UpdateMirror(ctx.Repo.Mirror); err != nil { + log.Error(4, "UpdateMirror: %v", err) + } + } + } + + ctx.Flash.Success("Repository options has been successfully updated.") + ctx.Redirect(fmt.Sprintf("/%s/%s/settings", ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)) + case "transfer": + if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") { + ctx.RenderWithErr("Please make sure you entered repository name is correct.", SETTING, nil) + return + } else if ctx.Repo.Repository.IsMirror { + ctx.Error(404) + return + } + + newOwner := ctx.Query("owner") + // Check if new owner exists. + isExist, err := models.IsUserExist(newOwner) + if err != nil { + ctx.Handle(500, "setting.SettingPost(transfer: check existence)", err) + return + } else if !isExist { + ctx.RenderWithErr("Please make sure you entered owner name is correct.", SETTING, nil) + return + } else if err = models.TransferOwnership(ctx.Repo.Owner, newOwner, ctx.Repo.Repository); err != nil { + ctx.Handle(500, "setting.SettingPost(transfer repository)", err) + return + } + log.Trace("%s Repository transfered: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newOwner) + + ctx.Redirect("/") + case "delete": + if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") { + ctx.RenderWithErr("Please make sure you entered repository name is correct.", SETTING, nil) + return + } + + if ctx.Repo.Owner.IsOrganization() && + !ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) { + ctx.Error(403) + return + } + + if err := models.DeleteRepository(ctx.Repo.Owner.Id, ctx.Repo.Repository.Id, ctx.Repo.Owner.Name); err != nil { + ctx.Handle(500, "setting.Delete(DeleteRepository)", err) + return + } + log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.Repo.Owner.LowerName, ctx.Repo.Repository.LowerName) + + if ctx.Repo.Owner.IsOrganization() { + ctx.Redirect("/org/" + ctx.Repo.Owner.Name + "/dashboard") + } else { + ctx.Redirect("/") + } + } +} + +func Collaboration(ctx *middleware.Context) { + repoLink := strings.TrimPrefix(ctx.Repo.RepoLink, "/") + ctx.Data["IsRepoToolbarCollaboration"] = true + ctx.Data["Title"] = repoLink + " - collaboration" + + // Delete collaborator. + remove := strings.ToLower(ctx.Query("remove")) + if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName { + if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil { + ctx.Handle(500, "setting.Collaboration(DeleteAccess)", err) + return + } + ctx.Flash.Success("Collaborator has been removed.") + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + names, err := models.GetCollaboratorNames(repoLink) + if err != nil { + ctx.Handle(500, "setting.Collaboration(GetCollaborators)", err) + return + } + + us := make([]*models.User, len(names)) + for i, name := range names { + us[i], err = models.GetUserByName(name) + if err != nil { + ctx.Handle(500, "setting.Collaboration(GetUserByName)", err) + return + } + } + + ctx.Data["Collaborators"] = us + ctx.HTML(200, COLLABORATION) +} + +func CollaborationPost(ctx *middleware.Context) { + repoLink := strings.TrimPrefix(ctx.Repo.RepoLink, "/") + name := strings.ToLower(ctx.Query("collaborator")) + if len(name) == 0 || ctx.Repo.Owner.LowerName == name { + ctx.Redirect(ctx.Req.RequestURI) + return + } + has, err := models.HasAccess(name, repoLink, models.WRITABLE) + if err != nil { + ctx.Handle(500, "setting.CollaborationPost(HasAccess)", err) + return + } else if has { + ctx.Redirect(ctx.Req.RequestURI) + return + } + + u, err := models.GetUserByName(name) + if err != nil { + if err == models.ErrUserNotExist { + ctx.Flash.Error("Given user does not exist.") + ctx.Redirect(ctx.Req.RequestURI) + } else { + ctx.Handle(500, "setting.CollaborationPost(GetUserByName)", err) + } + return + } + + if err = models.AddAccess(&models.Access{UserName: name, RepoName: repoLink, + Mode: models.WRITABLE}); err != nil { + ctx.Handle(500, "setting.CollaborationPost(AddAccess)", err) + return + } + + if setting.Service.EnableNotifyMail { + if err = mailer.SendCollaboratorMail(ctx.Render, u, ctx.User, ctx.Repo.Repository); err != nil { + ctx.Handle(500, "setting.CollaborationPost(SendCollaboratorMail)", err) + return + } + } + + ctx.Flash.Success("New collaborator has been added.") + ctx.Redirect(ctx.Req.RequestURI) +} + +func WebHooks(ctx *middleware.Context) { + ctx.Data["IsRepoToolbarWebHooks"] = true + ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhooks" + + // Delete webhook. + remove := com.StrTo(ctx.Query("remove")).MustInt64() + if remove > 0 { + if err := models.DeleteWebhook(remove); err != nil { + ctx.Handle(500, "setting.WebHooks(DeleteWebhook)", err) + return + } + ctx.Flash.Success("Webhook has been removed.") + ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks") + return + } + + ws, err := models.GetWebhooksByRepoId(ctx.Repo.Repository.Id) + if err != nil { + ctx.Handle(500, "setting.WebHooks(GetWebhooksByRepoId)", err) + return + } + + ctx.Data["Webhooks"] = ws + ctx.HTML(200, HOOKS) +} + +func WebHooksAdd(ctx *middleware.Context) { + ctx.Data["IsRepoToolbarWebHooks"] = true + ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook" + ctx.HTML(200, HOOK_ADD) +} + +func WebHooksAddPost(ctx *middleware.Context, form auth.NewWebhookForm) { + ctx.Data["IsRepoToolbarWebHooks"] = true + ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook" + + if ctx.HasError() { + ctx.HTML(200, HOOK_ADD) + return + } + + ct := models.JSON + if form.ContentType == "2" { + ct = models.FORM + } + + w := &models.Webhook{ + RepoId: ctx.Repo.Repository.Id, + Url: form.Url, + ContentType: ct, + Secret: form.Secret, + HookEvent: &models.HookEvent{ + PushOnly: form.PushOnly, + }, + IsActive: form.Active, + } + if err := w.UpdateEvent(); err != nil { + ctx.Handle(500, "setting.WebHooksAddPost(UpdateEvent)", err) + return + } else if err := models.CreateWebhook(w); err != nil { + ctx.Handle(500, "setting.WebHooksAddPost(CreateWebhook)", err) + return + } + + ctx.Flash.Success("New webhook has been added.") + ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks") +} + +func WebHooksEdit(ctx *middleware.Context) { + ctx.Data["IsRepoToolbarWebHooks"] = true + ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhook" + + hookId := com.StrTo(ctx.Params(":id")).MustInt64() + if hookId == 0 { + ctx.Handle(404, "setting.WebHooksEdit", nil) + return + } + + w, err := models.GetWebhookById(hookId) + if err != nil { + if err == models.ErrWebhookNotExist { + ctx.Handle(404, "setting.WebHooksEdit(GetWebhookById)", nil) + } else { + ctx.Handle(500, "setting.WebHooksEdit(GetWebhookById)", err) + } + return + } + + w.GetEvent() + ctx.Data["Webhook"] = w + ctx.HTML(200, HOOK_EDIT) +} + +func WebHooksEditPost(ctx *middleware.Context, form auth.NewWebhookForm) { + ctx.Data["IsRepoToolbarWebHooks"] = true + ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhook" + + hookId := com.StrTo(ctx.Params(":id")).MustInt64() + if hookId == 0 { + ctx.Handle(404, "setting.WebHooksEditPost", nil) + return + } + + w, err := models.GetWebhookById(hookId) + if err != nil { + if err == models.ErrWebhookNotExist { + ctx.Handle(404, "GetWebhookById", nil) + } else { + ctx.Handle(500, "GetWebhookById", err) + } + return + } + + if ctx.HasError() { + ctx.HTML(200, HOOK_EDIT) + return + } + + ct := models.JSON + if form.ContentType == "2" { + ct = models.FORM + } + + w.Url = form.Url + w.ContentType = ct + w.Secret = form.Secret + w.HookEvent = &models.HookEvent{ + PushOnly: form.PushOnly, + } + w.IsActive = form.Active + if err := w.UpdateEvent(); err != nil { + ctx.Handle(500, "UpdateEvent", err) + return + } else if err := models.UpdateWebhook(w); err != nil { + ctx.Handle(500, "WebHooksEditPost", err) + return + } + + ctx.Flash.Success("Webhook has been updated.") + ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", ctx.Repo.RepoLink, hookId)) +} diff --git a/routers/user/home.go b/routers/user/home.go index 16e88a942..b5a789ae0 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -28,11 +28,11 @@ func Dashboard(ctx *middleware.Context) { ctx.Data["PageIsDashboard"] = true ctx.Data["PageIsNews"] = true - // if err := ctx.User.GetOrganizations(); err != nil { - // ctx.Handle(500, "home.Dashboard(GetOrganizations)", err) - // return - // } - // ctx.Data["Orgs"] = ctx.User.Orgs + if err := ctx.User.GetOrganizations(); err != nil { + ctx.Handle(500, "home.Dashboard(GetOrganizations)", err) + return + } + ctx.Data["Orgs"] = ctx.User.Orgs ctx.Data["ContextUser"] = ctx.User repos, err := models.GetRepositories(ctx.User.Id, true) @@ -40,13 +40,16 @@ func Dashboard(ctx *middleware.Context) { ctx.Handle(500, "GetRepositories", err) return } + for _, repo := range repos { + repo.Owner = ctx.User + } ctx.Data["Repos"] = repos - // ctx.Data["CollaborativeRepos"], err = models.GetCollaborativeRepos(ctx.User.Name) - // if err != nil { - // ctx.Handle(500, "home.Dashboard(GetCollaborativeRepos)", err) - // return - // } + ctx.Data["CollaborativeRepos"], err = models.GetCollaborativeRepos(ctx.User.Name) + if err != nil { + ctx.Handle(500, "GetCollaborativeRepos", err) + return + } actions, err := models.GetFeeds(ctx.User.Id, 0, true) if err != nil { diff --git a/routers/user/setting.go b/routers/user/setting.go index e4d6ff9ce..761052144 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -19,6 +19,7 @@ const ( SETTINGS_PASSWORD base.TplName = "user/settings/password" SETTINGS_SSH_KEYS base.TplName = "user/settings/sshkeys" SETTINGS_SOCIAL base.TplName = "user/settings/social" + SETTINGS_ORGS base.TplName = "user/settings/orgs" SETTINGS_DELETE base.TplName = "user/settings/delete" NOTIFICATION base.TplName = "user/notification" SECURITY base.TplName = "user/security" @@ -232,6 +233,13 @@ func SettingsSocial(ctx *middleware.Context) { ctx.HTML(200, SETTINGS_SOCIAL) } +func SettingsOrgs(ctx *middleware.Context) { + ctx.Data["Title"] = ctx.Tr("settings") + ctx.Data["PageIsUserSettings"] = true + ctx.Data["PageIsSettingsOrgs"] = true + ctx.HTML(200, SETTINGS_ORGS) +} + func SettingsDelete(ctx *middleware.Context) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsUserSettings"] = true diff --git a/templates/.VERSION b/templates/.VERSION index 036f90911..a4f4593d7 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.4.7.0725 Alpha \ No newline at end of file +0.4.7.0726 Alpha \ No newline at end of file diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 10a53b539..34e710bf6 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -176,11 +176,11 @@
Enable Set Cookie
GC Interval Time
-
{{.SessionConfig.GcIntervalTime}} seconds
+
{{.SessionConfig.Gclifetime}} seconds
Session Life Time
-
{{.SessionConfig.SessionLifeTime}} seconds
+
{{.SessionConfig.Maxlifetime}} seconds
HTTPS Only
-
+
Cookie Life Time
{{.SessionConfig.CookieLifeTime}} seconds
Session ID Hash Function
diff --git a/templates/ng/base/head.tmpl b/templates/ng/base/head.tmpl index 0cd7686ff..bab914bfe 100644 --- a/templates/ng/base/head.tmpl +++ b/templates/ng/base/head.tmpl @@ -12,10 +12,10 @@ - + - + diff --git a/templates/repo/commits.tmpl b/templates/repo/commits.tmpl index 385f9d5ba..420e973a5 100644 --- a/templates/repo/commits.tmpl +++ b/templates/repo/commits.tmpl @@ -34,7 +34,7 @@ {{.Author.Name}} {{SubStr .Id.String 0 10}} {{.Summary}} - {{TimeSince .Author.When}} + {{TimeSince .Author.When $.Lang}} {{end}} diff --git a/templates/repo/diff.tmpl b/templates/repo/diff.tmpl index c85caa21e..6adea0459 100644 --- a/templates/repo/diff.tmpl +++ b/templates/repo/diff.tmpl @@ -20,7 +20,7 @@

{{.Commit.Author.Name}} - {{TimeSince .Commit.Author.When}} + {{TimeSince .Commit.Author.When $.Lang}}

diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index ffbdcc837..099e41b2d 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -86,7 +86,7 @@

{{.Poster.Name}} - {{TimeSince .Created}} + {{TimeSince .Created $.Lang}} {{.NumComments}}

diff --git a/templates/repo/issue/view.tmpl b/templates/repo/issue/view.tmpl index aec50ca62..c12de7dd7 100644 --- a/templates/repo/issue/view.tmpl +++ b/templates/repo/issue/view.tmpl @@ -18,7 +18,7 @@ {{end}} {{if .Issue.IsClosed}}Closed{{else}}Open{{end}} {{.Issue.Poster.Name}} opened this issue - {{TimeSince .Issue.Created}} · {{.Issue.NumComments}} comments + {{TimeSince .Issue.Created $.Lang}} · {{.Issue.NumComments}} comments

@@ -66,7 +66,7 @@
- {{.Poster.Name}} commented {{TimeSince .Created}} + {{.Poster.Name}} commented {{TimeSince .Created $.Lang}} Owner @@ -95,14 +95,14 @@
- {{.Poster.Name}} Reopened this issue {{TimeSince .Created}} + {{.Poster.Name}} Reopened this issue {{TimeSince .Created $.Lang}}
{{else if eq .Type 2}}
- {{.Poster.Name}} Closed this issue {{TimeSince .Created}} + {{.Poster.Name}} Closed this issue {{TimeSince .Created $.Lang}}
{{else if eq .Type 4}} diff --git a/templates/status/401.tmpl b/templates/status/401.tmpl index 6e24302fe..2c38d90fb 100644 --- a/templates/status/401.tmpl +++ b/templates/status/401.tmpl @@ -1,6 +1,6 @@ -{{template "base/head" .}} -{{template "base/header" .}} +{{template "ng/base/head" .}} +{{template "ng/base/header" .}}
401 Unauthorized: {{.ErrorMsg}}
-{{template "base/footer" .}} \ No newline at end of file +{{template "ng/base/footer" .}} \ No newline at end of file diff --git a/templates/status/404.tmpl b/templates/status/404.tmpl index 7062fb122..2d04b5591 100644 --- a/templates/status/404.tmpl +++ b/templates/status/404.tmpl @@ -6,5 +6,6 @@

Application Version: {{AppVer}}

If you think this is an error, please open an issue on GitHub.

+

We're currently working on 0.5 beta version, many pages may be missing at this time. Sorry for confusion!

{{template "ng/base/footer" .}} diff --git a/templates/user/dashboard/dashboard.tmpl b/templates/user/dashboard/dashboard.tmpl index e9027230a..209a495b5 100644 --- a/templates/user/dashboard/dashboard.tmpl +++ b/templates/user/dashboard/dashboard.tmpl @@ -70,15 +70,7 @@
@@ -87,42 +79,9 @@
diff --git a/templates/user/dashboard/repo_list.tmpl b/templates/user/dashboard/repo_list.tmpl new file mode 100644 index 000000000..e3d35e8ec --- /dev/null +++ b/templates/user/dashboard/repo_list.tmpl @@ -0,0 +1,12 @@ +
  • + + + + + {{.Name}} + + + {{.NumStars}} + + +
  • \ No newline at end of file diff --git a/templates/user/issues.tmpl b/templates/user/issues.tmpl index c4ad64a4c..93e798aa3 100644 --- a/templates/user/issues.tmpl +++ b/templates/user/issues.tmpl @@ -41,7 +41,7 @@

    {{.Poster.Name}} - {{TimeSince .Created}} + {{TimeSince .Created $.Lang}} {{.NumComments}}

    diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index 0c9ada013..4f80f1659 100644 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -50,8 +50,8 @@ diff --git a/templates/user/settings/nav.tmpl b/templates/user/settings/nav.tmpl index d6d20dee9..cae120527 100644 --- a/templates/user/settings/nav.tmpl +++ b/templates/user/settings/nav.tmpl @@ -6,6 +6,7 @@
  • {{.i18n.Tr "settings.password"}}
  • {{.i18n.Tr "settings.ssh_keys"}}
  • {{.i18n.Tr "settings.social"}}
  • +
  • {{.i18n.Tr "settings.orgs"}}
  • {{.i18n.Tr "settings.delete"}}
  • diff --git a/templates/user/settings/orgs.tmpl b/templates/user/settings/orgs.tmpl new file mode 100644 index 000000000..fb9096c3d --- /dev/null +++ b/templates/user/settings/orgs.tmpl @@ -0,0 +1,18 @@ +{{template "ng/base/head" .}} +{{template "ng/base/header" .}} +
    +
    + {{template "user/settings/nav" .}} +
    +
    + {{template "ng/base/alert" .}} +
    +
    +

    {{.i18n.Tr "settings.manage_orgs"}}

    +
    +
    +
    +
    +
    +
    +{{template "ng/base/footer" .}} \ No newline at end of file