From 514d7e19227555ecd9e38c642d0b7ea63d7883d2 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Sun, 22 Jun 2014 16:53:46 +0800 Subject: add organization memebers page --- cmd/web.go | 1 + 1 file changed, 1 insertion(+) (limited to 'cmd/web.go') diff --git a/cmd/web.go b/cmd/web.go index f62bc255..30e5b929 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -190,6 +190,7 @@ func runWeb(*cli.Context) { m.Group("/o", func(r martini.Router) { r.Get("/:org", org.Organization) + r.Get("/:org/members", org.Members) }) m.Group("/:username/:reponame", func(r martini.Router) { -- cgit v1.2.3 From adda10f4a44005880f881bcb9238b74d561f8a1b Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Sun, 22 Jun 2014 21:53:40 +0800 Subject: add organization teams page --- cmd/web.go | 1 + public/css/gogs.css | 36 ++++++++++++++++++++++++- routers/org/org.go | 6 +++++ templates/org/members.tmpl | 56 +++++++++++++++++++++++++++++++++++++++ templates/org/teams.tmpl | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 templates/org/members.tmpl create mode 100644 templates/org/teams.tmpl (limited to 'cmd/web.go') diff --git a/cmd/web.go b/cmd/web.go index 30e5b929..b2521de9 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -191,6 +191,7 @@ func runWeb(*cli.Context) { m.Group("/o", func(r martini.Router) { r.Get("/:org", org.Organization) r.Get("/:org/members", org.Members) + r.Get("/:org/teams", org.Teams) }) m.Group("/:username/:reponame", func(r martini.Router) { diff --git a/public/css/gogs.css b/public/css/gogs.css index 65ec79eb..7fdde19c 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -1898,6 +1898,10 @@ html, body { margin-top: 6px; } +#body-nav.org-nav-auto .nav a:hover { + text-decoration: none; +} + .org-description { font-size: 16px; } @@ -1978,7 +1982,7 @@ html, body { } #org-members { - margin-right: 24px; + margin-right: 30px; } #org-members .member .avatar img { @@ -2013,4 +2017,34 @@ html, body { #org-members .status, #org-members .role { line-height: 48px; text-align: right; +} + +#org-teams .org-team .panel-heading { + margin-top: 0; +} + +#org-teams .org-team .panel-heading a { + color: #444; +} + +#org-teams .org-team-members { + margin-top: 18px; +} + +#org-teams .org-team-members img { + width: 40px; + height: 40px; + margin-right: 12px; +} + +#org-teams .org-team-members a { + display: inline-block; +} + +#org-teams .org-team .panel-footer { + height: 60px; +} + +#org-teams .org-team { + border-bottom: none; } \ No newline at end of file diff --git a/routers/org/org.go b/routers/org/org.go index 69bf0fc4..21f2a189 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -14,3 +14,9 @@ func Members(ctx *middleware.Context,params martini.Params){ ctx.Data["Title"] = "Organization " + params["org"]+" Members" ctx.HTML(200,"org/members") } + +func Teams(ctx *middleware.Context,params martini.Params){ + ctx.Data["Title"] = "Organization " + params["org"]+" Teams" + ctx.HTML(200,"org/teams") +} + diff --git a/templates/org/members.tmpl b/templates/org/members.tmpl new file mode 100644 index 00000000..ba14cb4c --- /dev/null +++ b/templates/org/members.tmpl @@ -0,0 +1,56 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+
+
+ + +
+

Organization Name

+
+
+ +
+
+
+
+
+
  +
+ +
+ +
+ Member +
+
+ Public +
+
+
  +
+ +
+ +
+ Owner +
+
+ Private +
+
+
+
+
+{{template "base/footer" .}} diff --git a/templates/org/teams.tmpl b/templates/org/teams.tmpl new file mode 100644 index 00000000..a8218812 --- /dev/null +++ b/templates/org/teams.tmpl @@ -0,0 +1,65 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+
+
+ + +
+

Organization Name

+
+
+
+
+
+
+
+
+
+

Team Name

+
+

4 members · 10 repositories

+

+ + + + + + +

+
+ +
+
+
+
+

Team Name

+
+

4 members · 10 repositories

+

+ + + + + + +

+
+ +
+
+
+
+
+{{template "base/footer" .}} -- cgit v1.2.3 From ea507e20d45ef90b1552d72341038354f8801cf4 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Mon, 23 Jun 2014 11:40:49 +0800 Subject: add organization create page --- cmd/web.go | 1 + public/css/gogs.css | 2 +- routers/org/org.go | 18 +++++++++++------- templates/org/new.tmpl | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 templates/org/new.tmpl (limited to 'cmd/web.go') diff --git a/cmd/web.go b/cmd/web.go index b2521de9..9786576d 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -189,6 +189,7 @@ func runWeb(*cli.Context) { reqOwner := middleware.RequireOwner() m.Group("/o", func(r martini.Router) { + r.Get("/create",org.New) r.Get("/:org", org.Organization) r.Get("/:org/members", org.Members) r.Get("/:org/teams", org.Teams) diff --git a/public/css/gogs.css b/public/css/gogs.css index 7fdde19c..bb341a8a 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -372,7 +372,7 @@ html, body { /* gogits repo create */ -#repo-create { +#repo-create, #org-create { width: 800px; } diff --git a/routers/org/org.go b/routers/org/org.go index 21f2a189..4d9b831d 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -6,17 +6,21 @@ import ( ) func Organization(ctx *middleware.Context, params martini.Params) { - ctx.Data["Title"] = "Organization " + params["org"] + ctx.Data["Title"] = "Organization "+params["org"] ctx.HTML(200, "org/org") } -func Members(ctx *middleware.Context,params martini.Params){ - ctx.Data["Title"] = "Organization " + params["org"]+" Members" - ctx.HTML(200,"org/members") +func Members(ctx *middleware.Context, params martini.Params) { + ctx.Data["Title"] = "Organization "+params["org"]+" Members" + ctx.HTML(200, "org/members") } -func Teams(ctx *middleware.Context,params martini.Params){ - ctx.Data["Title"] = "Organization " + params["org"]+" Teams" - ctx.HTML(200,"org/teams") +func Teams(ctx *middleware.Context, params martini.Params) { + ctx.Data["Title"] = "Organization "+params["org"]+" Teams" + ctx.HTML(200, "org/teams") } +func New(ctx *middleware.Context) { + ctx.Data["Title"] = "Create an Organization" + ctx.HTML(200, "org/new") +} diff --git a/templates/org/new.tmpl b/templates/org/new.tmpl new file mode 100644 index 00000000..baa9c9df --- /dev/null +++ b/templates/org/new.tmpl @@ -0,0 +1,48 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+
+ {{.CsrfTokenHtml}} +

Create New Organization

+ {{template "base/alert" .}} +
+ +
+

{{.SignedUserName}}

+ +
+
+ +
+ +
+ + Great organization names are short and memorable. +
+
+ +
+ +
+ + Organization's Email receives all notifications and confirmations. +
+
+ + + +
+
+ + Cancel +
+
+
+
+{{template "base/footer" .}} \ No newline at end of file -- cgit v1.2.3 From f393dc652007238c27bd5d1954a03ba97412eee5 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Mon, 23 Jun 2014 12:08:53 +0800 Subject: add organization dashboard page --- cmd/web.go | 3 +- public/css/gogs.css | 8 +++++ routers/org/org.go | 5 +++ templates/org/dashboard.tmpl | 73 +++++++++++++++++++++++++++++++++++++++++++ templates/user/dashboard.tmpl | 4 ++- 5 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 templates/org/dashboard.tmpl (limited to 'cmd/web.go') diff --git a/cmd/web.go b/cmd/web.go index 9786576d..65ea6064 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -189,8 +189,9 @@ func runWeb(*cli.Context) { reqOwner := middleware.RequireOwner() m.Group("/o", func(r martini.Router) { - r.Get("/create",org.New) + r.Get("/create", org.New) r.Get("/:org", org.Organization) + r.Get("/:org/dashboard", org.Dashboard) r.Get("/:org/members", org.Members) r.Get("/:org/teams", org.Teams) }) diff --git a/public/css/gogs.css b/public/css/gogs.css index bb341a8a..1f465d84 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -662,6 +662,14 @@ html, body { padding: .8em 1.2em; } +#dashboard-switch-menu > li > a:hover { + text-decoration: none; +} + +#dashboard-switch-menu > li > a img, #dashboard-switch button img { + margin-right: 6px; +} + #dashboard-switch-menu > li { border-bottom: 1px solid #eaeaea; } diff --git a/routers/org/org.go b/routers/org/org.go index 4d9b831d..8073d10b 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -24,3 +24,8 @@ func New(ctx *middleware.Context) { ctx.Data["Title"] = "Create an Organization" ctx.HTML(200, "org/new") } + +func Dashboard(ctx *middleware.Context, params martini.Params) { + ctx.Data["Title"] = "Dashboard" + ctx.HTML(200, "org/dashboard") +} diff --git a/templates/org/dashboard.tmpl b/templates/org/dashboard.tmpl new file mode 100644 index 00000000..f86de1f4 --- /dev/null +++ b/templates/org/dashboard.tmpl @@ -0,0 +1,73 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+
+
+ + + +
+ +

News Feed

+
+
+
+ {{if .HasInfo}}
{{.InfoMsg}}
{{end}} +
+
    + {{range .Feeds}} +
  • + +
    {{TimeSince .Created}}
    {{ActionDesc . | str2html}}
    + +
  • + {{else}} +
  • Oh. Looks like there isn't any activity here yet. Get Busy!
  • + {{end}} +
+
+
+
+
Repositories +
+ + +
+
+ +
+ +
+
+
+
+{{template "base/footer" .}} diff --git a/templates/user/dashboard.tmpl b/templates/user/dashboard.tmpl index c44ba362..12018ad8 100644 --- a/templates/user/dashboard.tmpl +++ b/templates/user/dashboard.tmpl @@ -12,7 +12,9 @@ -- cgit v1.2.3 From fb53cc4fa82bbbbba4b8b0281d5f5657b1a6fafc Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Mon, 23 Jun 2014 19:11:20 +0800 Subject: add organization setting page --- cmd/web.go | 1 + routers/org/org.go | 5 ++ templates/org/setting.tmpl | 151 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 templates/org/setting.tmpl (limited to 'cmd/web.go') diff --git a/cmd/web.go b/cmd/web.go index 65ea6064..d29183a9 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -194,6 +194,7 @@ func runWeb(*cli.Context) { r.Get("/:org/dashboard", org.Dashboard) r.Get("/:org/members", org.Members) r.Get("/:org/teams", org.Teams) + r.Get("/:org/setting", org.Setting) }) m.Group("/:username/:reponame", func(r martini.Router) { diff --git a/routers/org/org.go b/routers/org/org.go index 8073d10b..ff97402e 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -29,3 +29,8 @@ func Dashboard(ctx *middleware.Context, params martini.Params) { ctx.Data["Title"] = "Dashboard" ctx.HTML(200, "org/dashboard") } + +func Setting(ctx *middleware.Context, param martini.Params) { + ctx.Data["Title"] = "Setting" + ctx.HTML(200, "org/setting") +} diff --git a/templates/org/setting.tmpl b/templates/org/setting.tmpl new file mode 100644 index 00000000..1be9707a --- /dev/null +++ b/templates/org/setting.tmpl @@ -0,0 +1,151 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+
+
+ + +
+ +
+
+
+
+ +
+
+ {{template "base/alert" .}} +
+
+ Repository Options +
+ +
+
+ {{.CsrfTokenHtml}} + + +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ Danger Zone +
+
+ +
+
Delete this organization
+
Once you delete this organization and all repositories in, there is no going back. Please be + certain. +
+ + + +
+
+
+
+{{template "base/footer" .}} -- cgit v1.2.3 From e0f9c628c5ff7399167944b3d0730698487af498 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 25 Jun 2014 00:44:48 -0400 Subject: Add create organization --- cmd/serve.go | 20 ++--- cmd/web.go | 5 +- gogs.go | 2 +- models/access.go | 17 ++-- models/issue.go | 4 +- models/login.go | 4 +- models/models.go | 2 +- models/org.go | 69 ++++++++++++++++ models/repo.go | 10 ++- models/user.go | 186 +++++++++++++++++++++++++++++++++--------- modules/auth/org.go | 33 ++++++++ modules/middleware/repo.go | 4 +- routers/admin/user.go | 4 +- routers/install.go | 2 +- routers/org/org.go | 96 ++++++++++++++++++++-- routers/repo/http.go | 8 +- routers/repo/setting.go | 4 +- routers/user/home.go | 13 ++- routers/user/user.go | 7 +- templates/VERSION | 2 +- templates/org/dashboard.tmpl | 73 ----------------- templates/org/new.tmpl | 24 +----- templates/user/dashboard.tmpl | 39 ++++++--- templates/user/issues.tmpl | 3 +- 24 files changed, 436 insertions(+), 195 deletions(-) create mode 100644 models/org.go create mode 100644 modules/auth/org.go delete mode 100644 templates/org/dashboard.tmpl (limited to 'cmd/web.go') diff --git a/cmd/serve.go b/cmd/serve.go index 62e290d8..2a76da79 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -56,19 +56,19 @@ func parseCmd(cmd string) (string, string) { } var ( - COMMANDS_READONLY = map[string]int{ - "git-upload-pack": models.AU_WRITABLE, - "git upload-pack": models.AU_WRITABLE, - "git-upload-archive": models.AU_WRITABLE, + COMMANDS_READONLY = map[string]models.AccessType{ + "git-upload-pack": models.WRITABLE, + "git upload-pack": models.WRITABLE, + "git-upload-archive": models.WRITABLE, } - COMMANDS_WRITE = map[string]int{ - "git-receive-pack": models.AU_READABLE, - "git receive-pack": models.AU_READABLE, + COMMANDS_WRITE = map[string]models.AccessType{ + "git-receive-pack": models.READABLE, + "git receive-pack": models.READABLE, } ) -func In(b string, sl map[string]int) bool { +func In(b string, sl map[string]models.AccessType) bool { _, e := sl[b] return e } @@ -129,7 +129,7 @@ func runServ(k *cli.Context) { // Access check. switch { case isWrite: - has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.AU_WRITABLE) + has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE) if err != nil { println("Gogs: internal error:", err) log.GitLogger.Fatal("Fail to check write access:", err) @@ -152,7 +152,7 @@ func runServ(k *cli.Context) { break } - has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.AU_READABLE) + has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE) if err != nil { println("Gogs: internal error:", err) log.GitLogger.Fatal("Fail to check read access:", err) diff --git a/cmd/web.go b/cmd/web.go index d29183a9..878fdeac 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -188,14 +188,15 @@ func runWeb(*cli.Context) { reqOwner := middleware.RequireOwner() - m.Group("/o", func(r martini.Router) { + m.Group("/org", func(r martini.Router) { r.Get("/create", org.New) + r.Post("/create", bindIgnErr(auth.CreateOrganizationForm{}), org.NewPost) r.Get("/:org", org.Organization) r.Get("/:org/dashboard", org.Dashboard) r.Get("/:org/members", org.Members) r.Get("/:org/teams", org.Teams) r.Get("/:org/setting", org.Setting) - }) + }, reqSignIn) m.Group("/:username/:reponame", func(r martini.Router) { r.Get("/settings", repo.Setting) diff --git a/gogs.go b/gogs.go index 75d3eb4b..5c2c6ed9 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.4.5.0624 Alpha" +const APP_VER = "0.4.5.0625 Alpha" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/access.go b/models/access.go index cf31fc13..5238daba 100644 --- a/models/access.go +++ b/models/access.go @@ -11,19 +11,20 @@ import ( "github.com/go-xorm/xorm" ) -// Access types. +type AccessType int + const ( - AU_READABLE = iota + 1 - AU_WRITABLE + READABLE AccessType = iota + 1 + WRITABLE ) // Access represents the accessibility of user to repository. type Access struct { Id int64 - UserName string `xorm:"unique(s)"` - RepoName string `xorm:"unique(s)"` // / - Mode int `xorm:"unique(s)"` - Created time.Time `xorm:"created"` + UserName string `xorm:"unique(s)"` + RepoName string `xorm:"unique(s)"` // / + Mode AccessType `xorm:"unique(s)"` + Created time.Time `xorm:"created"` } // AddAccess adds new access record. @@ -59,7 +60,7 @@ func UpdateAccessWithSession(sess *xorm.Session, access *Access) error { // HasAccess returns true if someone can read or write to given repository. // The repoName should be in format /. -func HasAccess(uname, repoName string, mode int) (bool, error) { +func HasAccess(uname, repoName string, mode AccessType) (bool, error) { if len(repoName) == 0 { return false, nil } diff --git a/models/issue.go b/models/issue.go index 11f6dd4e..6d67a72b 100644 --- a/models/issue.go +++ b/models/issue.go @@ -213,9 +213,9 @@ func GetIssueCountByPoster(uid, rid int64, isClosed bool) int64 { // IssueUser represents an issue-user relation. type IssueUser struct { Id int64 - Uid int64 // User ID. + Uid int64 `xorm:"INDEX"` // User ID. IssueId int64 - RepoId int64 + RepoId int64 `xorm:"INDEX"` MilestoneId int64 IsRead bool IsAssigned bool diff --git a/models/login.go b/models/login.go index 98c5c64e..e99b61e7 100644 --- a/models/login.go +++ b/models/login.go @@ -255,7 +255,7 @@ func LoginUserLdapSource(user *User, name, passwd string, sourceId int64, cfg *L Email: mail, } - return RegisterUser(user) + return CreateUser(user) } type loginAuth struct { @@ -359,5 +359,5 @@ func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *S Passwd: passwd, Email: name, } - return RegisterUser(user) + return CreateUser(user) } diff --git a/models/models.go b/models/models.go index d6273d7f..4e65c00b 100644 --- a/models/models.go +++ b/models/models.go @@ -35,7 +35,7 @@ func init() { tables = append(tables, new(User), new(PublicKey), new(Repository), new(Watch), new(Action), new(Access), new(Issue), new(Comment), new(Oauth2), new(Follow), new(Mirror), new(Release), new(LoginSource), new(Webhook), new(IssueUser), - new(Milestone), new(Label), new(HookTask)) + new(Milestone), new(Label), new(HookTask), new(Team), new(OrgUser), new(TeamUser)) } func LoadModelsConfig() { diff --git a/models/org.go b/models/org.go new file mode 100644 index 00000000..1cfe1798 --- /dev/null +++ b/models/org.go @@ -0,0 +1,69 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +type AuthorizeType int + +const ( + ORG_READABLE AuthorizeType = iota + 1 + ORG_WRITABLE + ORG_ADMIN +) + +// Team represents a organization team. +type Team struct { + Id int64 + OrgId int64 `xorm:"INDEX"` + Name string + Description string + Authorize AuthorizeType + NumMembers int + NumRepos int +} + +// NewTeam creates a record of new team. +func NewTeam(t *Team) error { + _, err := x.Insert(t) + return err +} + +// ________ ____ ___ +// \_____ \_______ ____ | | \______ ___________ +// / | \_ __ \/ ___\| | / ___// __ \_ __ \ +// / | \ | \/ /_/ > | /\___ \\ ___/| | \/ +// \_______ /__| \___ /|______//____ >\___ >__| +// \/ /_____/ \/ \/ + +// OrgUser represents an organization-user relation. +type OrgUser struct { + Id int64 + Uid int64 `xorm:"INDEX"` + OrgId int64 `xorm:"INDEX"` + IsPublic bool + IsOwner bool + NumTeam int +} + +// GetOrgUsersByUserId returns all organization-user relations by user ID. +func GetOrgUsersByUserId(uid int64) ([]*OrgUser, error) { + ous := make([]*OrgUser, 0, 10) + err := x.Where("uid=?", uid).Find(&ous) + return ous, err +} + +// ___________ ____ ___ +// \__ ___/___ _____ _____ | | \______ ___________ +// | |_/ __ \\__ \ / \| | / ___// __ \_ __ \ +// | |\ ___/ / __ \| Y Y \ | /\___ \\ ___/| | \/ +// |____| \___ >____ /__|_| /______//____ >\___ >__| +// \/ \/ \/ \/ \/ + +// TeamUser represents an team-user relation. +type TeamUser struct { + Id int64 + Uid int64 + OrgId int64 `xorm:"INDEX"` + TeamId int64 +} diff --git a/models/repo.go b/models/repo.go index 4ccaccbf..f0e46c71 100644 --- a/models/repo.go +++ b/models/repo.go @@ -158,7 +158,7 @@ func IsRepositoryExist(u *User, repoName string) (bool, error) { } var ( - illegalEquals = []string{"raw", "install", "api", "avatar", "user", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin"} + illegalEquals = []string{"raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin"} illegalSuffixs = []string{".git"} ) @@ -483,7 +483,9 @@ func CreateRepository(user *User, name, desc, lang, license string, private, mir sess := x.NewSession() defer sess.Close() - sess.Begin() + if err = sess.Begin(); err != nil { + return nil, err + } if _, err = sess.Insert(repo); err != nil { if err2 := os.RemoveAll(repoPath); err2 != nil { @@ -495,9 +497,9 @@ func CreateRepository(user *User, name, desc, lang, license string, private, mir return nil, err } - mode := AU_WRITABLE + mode := WRITABLE if mirror { - mode = AU_READABLE + mode = READABLE } access := Access{ UserName: user.LowerName, diff --git a/models/user.go b/models/user.go index 50d81c94..2388be9a 100644 --- a/models/user.go +++ b/models/user.go @@ -21,10 +21,11 @@ import ( "github.com/gogits/gogs/modules/setting" ) -// User types. +type UserType int + const ( - UT_INDIVIDUAL = iota + 1 - UT_ORGANIZATION + INDIVIDUAL UserType = iota // Historic reason to make it starts at 0. + ORGANIZATION ) var ( @@ -50,7 +51,8 @@ type User struct { LoginType LoginType LoginSource int64 `xorm:"not null default 0"` LoginName string - Type int + Type UserType + Orgs []*User `xorm:"-"` NumFollowers int NumFollowings int NumStars int @@ -65,36 +67,60 @@ type User struct { Salt string `xorm:"VARCHAR(10)"` Created time.Time `xorm:"created"` Updated time.Time `xorm:"updated"` + + // For organization. + NumTeams int + NumMembers int } // HomeLink returns the user home page link. -func (user *User) HomeLink() string { - return "/user/" + user.Name +func (u *User) HomeLink() string { + return "/user/" + u.Name } // AvatarLink returns user gravatar link. -func (user *User) AvatarLink() string { +func (u *User) AvatarLink() string { if setting.DisableGravatar { return "/img/avatar_default.jpg" } else if setting.Service.EnableCacheAvatar { - return "/avatar/" + user.Avatar + return "/avatar/" + u.Avatar } - return "//1.gravatar.com/avatar/" + user.Avatar + return "//1.gravatar.com/avatar/" + u.Avatar } // NewGitSig generates and returns the signature of given user. -func (user *User) NewGitSig() *git.Signature { +func (u *User) NewGitSig() *git.Signature { return &git.Signature{ - Name: user.Name, - Email: user.Email, + Name: u.Name, + Email: u.Email, When: time.Now(), } } // EncodePasswd encodes password to safe format. -func (user *User) EncodePasswd() { - newPasswd := base.PBKDF2([]byte(user.Passwd), []byte(user.Salt), 10000, 50, sha256.New) - user.Passwd = fmt.Sprintf("%x", newPasswd) +func (u *User) EncodePasswd() { + newPasswd := base.PBKDF2([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New) + u.Passwd = fmt.Sprintf("%x", newPasswd) +} + +func (u *User) IsOrganization() bool { + return u.Type == ORGANIZATION +} + +func (u *User) GetOrganizations() error { + ous, err := GetOrgUsersByUserId(u.Id) + if err != nil { + return err + } + + u.Orgs = make([]*User, len(ous)) + for i, ou := range ous { + u.Orgs[i], err = GetUserById(ou.OrgId) + if err != nil { + return err + } + } + return nil } // Member represents user is member of organization. @@ -126,49 +152,135 @@ func GetUserSalt() string { return base.GetRandomString(10) } -// RegisterUser creates record of a new user. -func RegisterUser(user *User) (*User, error) { +// CreateUser creates record of a new user. +func CreateUser(u *User) (*User, error) { + if !IsLegalName(u.Name) { + return nil, ErrUserNameIllegal + } + + isExist, err := IsUserExist(u.Name) + if err != nil { + return nil, err + } else if isExist { + return nil, ErrUserAlreadyExist + } + + isExist, err = IsEmailUsed(u.Email) + if err != nil { + return nil, err + } else if isExist { + return nil, ErrEmailAlreadyUsed + } + + u.LowerName = strings.ToLower(u.Name) + u.Avatar = base.EncodeMd5(u.Email) + u.AvatarEmail = u.Email + u.Rands = GetUserSalt() + u.Salt = GetUserSalt() + u.EncodePasswd() + + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return nil, err + } - if !IsLegalName(user.Name) { + if _, err = sess.Insert(u); err != nil { + sess.Rollback() + return nil, err + } + + if err = os.MkdirAll(UserPath(u.Name), os.ModePerm); err != nil { + sess.Rollback() + return nil, err + } + + if err = sess.Commit(); err != nil { + return nil, err + } + + // Auto-set admin for user whose ID is 1. + if u.Id == 1 { + u.IsAdmin = true + u.IsActive = true + _, err = x.Id(u.Id).UseBool().Update(u) + } + return u, err +} + +// CreateOrganization creates record of a new organization. +func CreateOrganization(org, owner *User) (*User, error) { + if !IsLegalName(org.Name) { return nil, ErrUserNameIllegal } - isExist, err := IsUserExist(user.Name) + isExist, err := IsUserExist(org.Name) if err != nil { return nil, err } else if isExist { return nil, ErrUserAlreadyExist } - isExist, err = IsEmailUsed(user.Email) + isExist, err = IsEmailUsed(org.Email) if err != nil { return nil, err } else if isExist { return nil, ErrEmailAlreadyUsed } - user.LowerName = strings.ToLower(user.Name) - user.Avatar = base.EncodeMd5(user.Email) - user.AvatarEmail = user.Email - user.Rands = GetUserSalt() - user.Salt = GetUserSalt() - user.EncodePasswd() - if _, err = x.Insert(user); err != nil { + org.LowerName = strings.ToLower(org.Name) + org.Avatar = base.EncodeMd5(org.Email) + org.AvatarEmail = org.Email + // No password for organization. + org.NumTeams = 1 + org.NumMembers = 1 + + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return nil, err + } + + if _, err = sess.Insert(org); err != nil { + sess.Rollback() return nil, err - } else if err = os.MkdirAll(UserPath(user.Name), os.ModePerm); err != nil { - if _, err := x.Id(user.Id).Delete(&User{}); err != nil { - return nil, errors.New(fmt.Sprintf( - "both create userpath %s and delete table record faild: %v", user.Name, err)) - } + } + + // Create default owner team. + t := &Team{ + OrgId: org.Id, + Name: "Owner", + Authorize: ORG_ADMIN, + NumMembers: 1, + } + if _, err = sess.Insert(t); err != nil { + sess.Rollback() return nil, err } - if user.Id == 1 { - user.IsAdmin = true - user.IsActive = true - _, err = x.Id(user.Id).UseBool().Update(user) + // Add initial creator to organization and owner team. + ou := &OrgUser{ + Uid: owner.Id, + OrgId: org.Id, + IsOwner: true, + NumTeam: 1, } - return user, err + if _, err = sess.Insert(ou); err != nil { + sess.Rollback() + return nil, err + } + + tu := &TeamUser{ + Uid: owner.Id, + OrgId: org.Id, + TeamId: t.Id, + } + if _, err = sess.Insert(tu); err != nil { + sess.Rollback() + return nil, err + } + + return org, sess.Commit() } // GetUsers returns given number of user objects with offset. diff --git a/modules/auth/org.go b/modules/auth/org.go new file mode 100644 index 00000000..a60fbb85 --- /dev/null +++ b/modules/auth/org.go @@ -0,0 +1,33 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package auth + +import ( + "net/http" + "reflect" + + "github.com/go-martini/martini" + + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/middleware/binding" +) + +type CreateOrganizationForm struct { + OrgName string `form:"orgname" binding:"Required;AlphaDashDot;MaxSize(30)"` + Email string `form:"email" binding:"Required;Email;MaxSize(50)"` +} + +func (f *CreateOrganizationForm) Name(field string) string { + names := map[string]string{ + "OrgName": "Organization name", + "Email": "E-mail address", + } + return names[field] +} + +func (f *CreateOrganizationForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { + data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) + validate(errs, data, f) +} diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index 6c77ed2a..43ba1e8c 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -46,7 +46,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { // Collaborators who have write access can be seen as owners. if ctx.IsSigned { - ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.AU_WRITABLE) + ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE) if err != nil { ctx.Handle(500, "RepoAssignment(HasAccess)", err) return @@ -107,7 +107,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { return } - hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.AU_READABLE) + hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE) if err != nil { ctx.Handle(500, "RepoAssignment(HasAccess)", err) return diff --git a/routers/admin/user.go b/routers/admin/user.go index d1bbb480..cf99db2b 100644 --- a/routers/admin/user.go +++ b/routers/admin/user.go @@ -67,7 +67,7 @@ func NewUserPost(ctx *middleware.Context, form auth.RegisterForm) { } var err error - if u, err = models.RegisterUser(u); err != nil { + if u, err = models.CreateUser(u); err != nil { switch err { case models.ErrUserAlreadyExist: ctx.RenderWithErr("Username has been already taken", USER_NEW, &form) @@ -76,7 +76,7 @@ func NewUserPost(ctx *middleware.Context, form auth.RegisterForm) { case models.ErrUserNameIllegal: ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), USER_NEW, &form) default: - ctx.Handle(500, "admin.user.NewUser(RegisterUser)", err) + ctx.Handle(500, "admin.user.NewUser(CreateUser)", err) } return } diff --git a/routers/install.go b/routers/install.go index 6ce7c980..bb3c16ea 100644 --- a/routers/install.go +++ b/routers/install.go @@ -227,7 +227,7 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) { GlobalInit() // Create admin account. - if _, err := models.RegisterUser(&models.User{Name: form.AdminName, Email: form.AdminEmail, Passwd: form.AdminPasswd, + if _, err := models.CreateUser(&models.User{Name: form.AdminName, Email: form.AdminEmail, Passwd: form.AdminPasswd, IsAdmin: true, IsActive: true}); err != nil { if err != models.ErrUserAlreadyExist { setting.InstallLock = false diff --git a/routers/org/org.go b/routers/org/org.go index ff97402e..0595a81b 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -1,33 +1,117 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + package org import ( "github.com/go-martini/martini" + + "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/routers/user" +) + +const ( + NEW base.TplName = "org/new" ) func Organization(ctx *middleware.Context, params martini.Params) { - ctx.Data["Title"] = "Organization "+params["org"] + ctx.Data["Title"] = "Organization " + params["org"] ctx.HTML(200, "org/org") } func Members(ctx *middleware.Context, params martini.Params) { - ctx.Data["Title"] = "Organization "+params["org"]+" Members" + ctx.Data["Title"] = "Organization " + params["org"] + " Members" ctx.HTML(200, "org/members") } func Teams(ctx *middleware.Context, params martini.Params) { - ctx.Data["Title"] = "Organization "+params["org"]+" Teams" + ctx.Data["Title"] = "Organization " + params["org"] + " Teams" ctx.HTML(200, "org/teams") } func New(ctx *middleware.Context) { - ctx.Data["Title"] = "Create an Organization" - ctx.HTML(200, "org/new") + ctx.Data["Title"] = "Create An Organization" + ctx.HTML(200, NEW) +} + +func NewPost(ctx *middleware.Context, form auth.CreateOrganizationForm) { + ctx.Data["Title"] = "Create An Organization" + + if ctx.HasError() { + ctx.HTML(200, NEW) + return + } + + org := &models.User{ + Name: form.OrgName, + Email: form.Email, + IsActive: true, // NOTE: may need to set false when require e-mail confirmation. + Type: models.ORGANIZATION, + } + + var err error + if org, err = models.CreateOrganization(org, ctx.User); err != nil { + switch err { + case models.ErrUserAlreadyExist: + ctx.Data["Err_OrgName"] = true + ctx.RenderWithErr("Organization name has been already taken", NEW, &form) + case models.ErrEmailAlreadyUsed: + ctx.Data["Err_Email"] = true + ctx.RenderWithErr("E-mail address has been already used", NEW, &form) + case models.ErrUserNameIllegal: + ctx.Data["Err_OrgName"] = true + ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), NEW, &form) + default: + ctx.Handle(500, "user.NewPost(CreateUser)", err) + } + return + } + log.Trace("%s Organization created: %s", ctx.Req.RequestURI, org.Name) + + ctx.Redirect("/org/" + form.OrgName + "/dashboard") } func Dashboard(ctx *middleware.Context, params martini.Params) { ctx.Data["Title"] = "Dashboard" - ctx.HTML(200, "org/dashboard") + ctx.Data["PageIsUserDashboard"] = true + ctx.Data["PageIsOrgDashboard"] = true + + org, err := models.GetUserByName(params["org"]) + if err != nil { + if err == models.ErrUserNotExist { + ctx.Handle(404, "org.Dashboard(GetUserByName)", err) + } else { + ctx.Handle(500, "org.Dashboard(GetUserByName)", err) + } + return + } + + 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) + if err != nil { + ctx.Handle(500, "org.Dashboard(GetRepositories)", err) + return + } + + actions, err := models.GetFeeds(org.Id, 0, false) + if err != nil { + ctx.Handle(500, "org.Dashboard(GetFeeds)", err) + return + } + ctx.Data["Feeds"] = actions + + ctx.HTML(200, user.DASHBOARD) } func Setting(ctx *middleware.Context, param martini.Params) { diff --git a/routers/repo/http.go b/routers/repo/http.go index d2bff299..981266d5 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -107,9 +107,9 @@ func Http(ctx *middleware.Context, params martini.Params) { } if !isPublicPull { - var tp = models.AU_WRITABLE + var tp = models.WRITABLE if isPull { - tp = models.AU_READABLE + tp = models.READABLE } has, err := models.HasAccess(authUsername, username+"/"+reponame, tp) @@ -117,8 +117,8 @@ func Http(ctx *middleware.Context, params martini.Params) { ctx.Handle(401, "no basic auth and digit auth", nil) return } else if !has { - if tp == models.AU_READABLE { - has, err = models.HasAccess(authUsername, username+"/"+reponame, models.AU_WRITABLE) + if tp == models.READABLE { + has, err = models.HasAccess(authUsername, username+"/"+reponame, models.WRITABLE) if err != nil || !has { ctx.Handle(401, "no basic auth and digit auth", nil) return diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 6479cb30..3d48e79c 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -175,7 +175,7 @@ func CollaborationPost(ctx *middleware.Context) { ctx.Redirect(ctx.Req.RequestURI) return } - has, err := models.HasAccess(name, repoLink, models.AU_WRITABLE) + has, err := models.HasAccess(name, repoLink, models.WRITABLE) if err != nil { ctx.Handle(500, "setting.CollaborationPost(HasAccess)", err) return @@ -196,7 +196,7 @@ func CollaborationPost(ctx *middleware.Context) { } if err = models.AddAccess(&models.Access{UserName: name, RepoName: repoLink, - Mode: models.AU_WRITABLE}); err != nil { + Mode: models.WRITABLE}); err != nil { ctx.Handle(500, "setting.CollaborationPost(AddAccess)", err) return } diff --git a/routers/user/home.go b/routers/user/home.go index 0f2cee25..86907b5a 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -20,7 +20,7 @@ import ( const ( DASHBOARD base.TplName = "user/dashboard" PROFILE base.TplName = "user/profile" - ISSUES base.TplName = "user/issue" + ISSUES base.TplName = "user/issues" PULLS base.TplName = "user/pulls" STARS base.TplName = "user/stars" ) @@ -29,6 +29,13 @@ func Dashboard(ctx *middleware.Context) { ctx.Data["Title"] = "Dashboard" ctx.Data["PageIsUserDashboard"] = true + 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 + var err error ctx.Data["MyRepos"], err = models.GetRepositories(ctx.User.Id, true) if err != nil { @@ -53,7 +60,7 @@ func Dashboard(ctx *middleware.Context) { for _, act := range actions { if act.IsPrivate { if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName, - models.AU_READABLE); !has { + models.READABLE); !has { continue } } @@ -131,7 +138,7 @@ func Feeds(ctx *middleware.Context, form auth.FeedsForm) { for _, act := range actions { if act.IsPrivate { if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName, - models.AU_READABLE); !has { + models.READABLE); !has { continue } } diff --git a/routers/user/user.go b/routers/user/user.go index 8144730e..a50f126c 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -226,7 +226,7 @@ func SignUpPost(ctx *middleware.Context, form auth.RegisterForm) { } var err error - if u, err = models.RegisterUser(u); err != nil { + if u, err = models.CreateUser(u); err != nil { switch err { case models.ErrUserAlreadyExist: ctx.Data["Err_UserName"] = true @@ -235,13 +235,14 @@ func SignUpPost(ctx *middleware.Context, form auth.RegisterForm) { ctx.Data["Err_Email"] = true ctx.RenderWithErr("E-mail address has been already used", SIGNUP, &form) case models.ErrUserNameIllegal: + ctx.Data["Err_UserName"] = true ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), SIGNUP, &form) default: - ctx.Handle(500, "user.SignUpPost(RegisterUser)", err) + ctx.Handle(500, "user.SignUpPost(CreateUser)", err) } return } - log.Trace("%s User created: %s", ctx.Req.RequestURI, form.UserName) + log.Trace("%s User created: %s", ctx.Req.RequestURI, u.Name) // Bind social account. if isOauth { diff --git a/templates/VERSION b/templates/VERSION index 8c2be60b..b5cda695 100644 --- a/templates/VERSION +++ b/templates/VERSION @@ -1 +1 @@ -0.4.5.0624 Alpha \ No newline at end of file +0.4.5.0625 Alpha \ No newline at end of file diff --git a/templates/org/dashboard.tmpl b/templates/org/dashboard.tmpl deleted file mode 100644 index f86de1f4..00000000 --- a/templates/org/dashboard.tmpl +++ /dev/null @@ -1,73 +0,0 @@ -{{template "base/head" .}} -{{template "base/navbar" .}} -
-
-
- - - -
- -

News Feed

-
-
-
- {{if .HasInfo}}
{{.InfoMsg}}
{{end}} -
-
    - {{range .Feeds}} -
  • - -
    {{TimeSince .Created}}
    {{ActionDesc . | str2html}}
    - -
  • - {{else}} -
  • Oh. Looks like there isn't any activity here yet. Get Busy!
  • - {{end}} -
-
-
-
-
Repositories -
- - -
-
- -
- -
-
-
-
-{{template "base/footer" .}} diff --git a/templates/org/new.tmpl b/templates/org/new.tmpl index baa9c9df..bb46db4a 100644 --- a/templates/org/new.tmpl +++ b/templates/org/new.tmpl @@ -1,22 +1,14 @@ {{template "base/head" .}} {{template "base/navbar" .}}
-
+ {{.CsrfTokenHtml}}

Create New Organization

{{template "base/alert" .}} -
- -
-

{{.SignedUserName}}

- -
-
- -
+
- + Great organization names are short and memorable.
@@ -24,18 +16,10 @@
- + Organization's Email receives all notifications and confirmations.
- -
diff --git a/templates/user/dashboard.tmpl b/templates/user/dashboard.tmpl index 12018ad8..e1b43e15 100644 --- a/templates/user/dashboard.tmpl +++ b/templates/user/dashboard.tmpl @@ -4,29 +4,46 @@
-

News Feed

+
{{if .HasInfo}}
{{.InfoMsg}}
{{end}}
@@ -44,7 +61,7 @@
-
Your Repositories +
{{if not .PageIsOrgDashboard}}Your {{end}}Repositories
- + + {{if not .PageIsOrgDashboard}}
Collaborative Repositories
@@ -78,6 +96,7 @@
+ {{end}}
{{template "base/footer" .}} diff --git a/templates/user/issues.tmpl b/templates/user/issues.tmpl index d1c2bd99..c4ad64a4 100644 --- a/templates/user/issues.tmpl +++ b/templates/user/issues.tmpl @@ -3,7 +3,7 @@
+
{{if .HasInfo}}
{{.InfoMsg}}
{{end}}
-- cgit v1.2.3 From 19e910428951135b9a341554dad54a6546d2ad50 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 27 Jun 2014 03:37:01 -0400 Subject: Organization settings page --- cmd/web.go | 5 +- gogs.go | 2 +- models/org.go | 86 +++++++++++++++++++++++++ models/repo.go | 2 +- models/user.go | 114 ++++++++------------------------- modules/auth/org.go | 30 ++++++++- modules/auth/repo.go | 2 +- modules/auth/user.go | 2 +- routers/org/org.go | 56 ++++++++++++++-- templates/VERSION | 2 +- templates/org/setting.tmpl | 151 -------------------------------------------- templates/org/settings.tmpl | 133 ++++++++++++++++++++++++++++++++++++++ 12 files changed, 331 insertions(+), 254 deletions(-) delete mode 100644 templates/org/setting.tmpl create mode 100644 templates/org/settings.tmpl (limited to 'cmd/web.go') diff --git a/cmd/web.go b/cmd/web.go index 878fdeac..729a1ba2 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -190,12 +190,13 @@ func runWeb(*cli.Context) { m.Group("/org", func(r martini.Router) { r.Get("/create", org.New) - r.Post("/create", bindIgnErr(auth.CreateOrganizationForm{}), org.NewPost) + r.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.NewPost) r.Get("/:org", org.Organization) r.Get("/:org/dashboard", org.Dashboard) r.Get("/:org/members", org.Members) r.Get("/:org/teams", org.Teams) - r.Get("/:org/setting", org.Setting) + r.Get("/:org/settings", org.Settings) + r.Post("/:org/settings", bindIgnErr(auth.OrgSettingForm{}), org.SettingsPost) }, reqSignIn) m.Group("/:username/:reponame", func(r martini.Router) { diff --git a/gogs.go b/gogs.go index 5c2c6ed9..d56760ab 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.4.5.0625 Alpha" +const APP_VER = "0.4.5.0627 Alpha" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/org.go b/models/org.go index 227151ab..553a46aa 100644 --- a/models/org.go +++ b/models/org.go @@ -4,6 +4,88 @@ package models +import ( + "strings" + + "github.com/gogits/gogs/modules/base" +) + +// CreateOrganization creates record of a new organization. +func CreateOrganization(org, owner *User) (*User, error) { + if !IsLegalName(org.Name) { + return nil, ErrUserNameIllegal + } + + isExist, err := IsUserExist(org.Name) + if err != nil { + return nil, err + } else if isExist { + return nil, ErrUserAlreadyExist + } + + isExist, err = IsEmailUsed(org.Email) + if err != nil { + return nil, err + } else if isExist { + return nil, ErrEmailAlreadyUsed + } + + org.LowerName = strings.ToLower(org.Name) + org.FullName = org.Name + org.Avatar = base.EncodeMd5(org.Email) + org.AvatarEmail = org.Email + // No password for organization. + org.NumTeams = 1 + org.NumMembers = 1 + + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return nil, err + } + + if _, err = sess.Insert(org); err != nil { + sess.Rollback() + return nil, err + } + + // Create default owner team. + t := &Team{ + OrgId: org.Id, + Name: OWNER_TEAM, + Authorize: ORG_ADMIN, + NumMembers: 1, + } + if _, err = sess.Insert(t); err != nil { + sess.Rollback() + return nil, err + } + + // Add initial creator to organization and owner team. + ou := &OrgUser{ + Uid: owner.Id, + OrgId: org.Id, + IsOwner: true, + NumTeam: 1, + } + if _, err = sess.Insert(ou); err != nil { + sess.Rollback() + return nil, err + } + + tu := &TeamUser{ + Uid: owner.Id, + OrgId: org.Id, + TeamId: t.Id, + } + if _, err = sess.Insert(tu); err != nil { + sess.Rollback() + return nil, err + } + + return org, sess.Commit() +} + type AuthorizeType int const ( @@ -72,6 +154,10 @@ func GetOrgUsersByOrgId(orgId int64) ([]*OrgUser, error) { return ous, err } +func GetOrganizationCount(u *User) (int64, error) { + return x.Where("uid=?", u.Id).Count(new(OrgUser)) +} + // ___________ ____ ___ // \__ ___/___ _____ _____ | | \______ ___________ // | |_/ __ \\__ \ / \| | / ___// __ \_ __ \ diff --git a/models/repo.go b/models/repo.go index 840529b9..85f2a913 100644 --- a/models/repo.go +++ b/models/repo.go @@ -240,7 +240,7 @@ func MirrorUpdate() { "git", "remote", "update"); err != nil { return errors.New("git remote update: " + stderr) } else if err = git.UnpackRefs(repoPath); err != nil { - return err + return errors.New("UnpackRefs: " + err.Error()) } m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) diff --git a/models/user.go b/models/user.go index f67911ca..8ffad266 100644 --- a/models/user.go +++ b/models/user.go @@ -30,6 +30,7 @@ const ( var ( ErrUserOwnRepos = errors.New("User still have ownership of repositories") + ErrUserHasOrgs = errors.New("User still have membership of organization") ErrUserAlreadyExist = errors.New("User already exist") ErrUserNotExist = errors.New("User does not exist") ErrUserNotKeyOwner = errors.New("User does not the owner of public key") @@ -69,8 +70,9 @@ type User struct { Updated time.Time `xorm:"updated"` // For organization. - NumTeams int - NumMembers int + Description string + NumTeams int + NumMembers int } // HomeLink returns the user home page link. @@ -211,81 +213,6 @@ func CreateUser(u *User) (*User, error) { return u, err } -// CreateOrganization creates record of a new organization. -func CreateOrganization(org, owner *User) (*User, error) { - if !IsLegalName(org.Name) { - return nil, ErrUserNameIllegal - } - - isExist, err := IsUserExist(org.Name) - if err != nil { - return nil, err - } else if isExist { - return nil, ErrUserAlreadyExist - } - - isExist, err = IsEmailUsed(org.Email) - if err != nil { - return nil, err - } else if isExist { - return nil, ErrEmailAlreadyUsed - } - - org.LowerName = strings.ToLower(org.Name) - org.Avatar = base.EncodeMd5(org.Email) - org.AvatarEmail = org.Email - // No password for organization. - org.NumTeams = 1 - org.NumMembers = 1 - - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return nil, err - } - - if _, err = sess.Insert(org); err != nil { - sess.Rollback() - return nil, err - } - - // Create default owner team. - t := &Team{ - OrgId: org.Id, - Name: OWNER_TEAM, - Authorize: ORG_ADMIN, - NumMembers: 1, - } - if _, err = sess.Insert(t); err != nil { - sess.Rollback() - return nil, err - } - - // Add initial creator to organization and owner team. - ou := &OrgUser{ - Uid: owner.Id, - OrgId: org.Id, - IsOwner: true, - NumTeam: 1, - } - if _, err = sess.Insert(ou); err != nil { - sess.Rollback() - return nil, err - } - - tu := &TeamUser{ - Uid: owner.Id, - OrgId: org.Id, - TeamId: t.Id, - } - if _, err = sess.Insert(tu); err != nil { - sess.Rollback() - return nil, err - } - - return org, sess.Commit() -} - // GetUsers returns given number of user objects with offset. func GetUsers(num, offset int) ([]User, error) { users := make([]User, 0, num) @@ -392,51 +319,62 @@ func UpdateUser(u *User) (err error) { if len(u.Website) > 255 { u.Website = u.Website[:255] } + if len(u.Description) > 255 { + u.Description = u.Description[:255] + } _, err = x.Id(u.Id).AllCols().Update(u) return err } // DeleteUser completely deletes everything of the user. -func DeleteUser(user *User) error { +func DeleteUser(u *User) error { // Check ownership of repository. - count, err := GetRepositoryCount(user) + count, err := GetRepositoryCount(u) if err != nil { - return errors.New("modesl.GetRepositories: " + err.Error()) + return errors.New("modesl.GetRepositories(GetRepositoryCount): " + err.Error()) } else if count > 0 { return ErrUserOwnRepos } + // Check membership of organization. + count, err = GetOrganizationCount(u) + if err != nil { + return errors.New("modesl.GetRepositories(GetOrganizationCount): " + err.Error()) + } else if count > 0 { + return ErrUserHasOrgs + } + // TODO: check issues, other repos' commits // Delete all followers. - if _, err = x.Delete(&Follow{FollowId: user.Id}); err != nil { + if _, err = x.Delete(&Follow{FollowId: u.Id}); err != nil { return err } // Delete oauth2. - if _, err = x.Delete(&Oauth2{Uid: user.Id}); err != nil { + if _, err = x.Delete(&Oauth2{Uid: u.Id}); err != nil { return err } // Delete all feeds. - if _, err = x.Delete(&Action{UserId: user.Id}); err != nil { + if _, err = x.Delete(&Action{UserId: u.Id}); err != nil { return err } // Delete all watches. - if _, err = x.Delete(&Watch{UserId: user.Id}); err != nil { + if _, err = x.Delete(&Watch{UserId: u.Id}); err != nil { return err } // Delete all accesses. - if _, err = x.Delete(&Access{UserName: user.LowerName}); err != nil { + if _, err = x.Delete(&Access{UserName: u.LowerName}); err != nil { return err } // Delete all SSH keys. keys := make([]*PublicKey, 0, 10) - if err = x.Find(&keys, &PublicKey{OwnerId: user.Id}); err != nil { + if err = x.Find(&keys, &PublicKey{OwnerId: u.Id}); err != nil { return err } for _, key := range keys { @@ -446,11 +384,11 @@ func DeleteUser(user *User) error { } // Delete user directory. - if err = os.RemoveAll(UserPath(user.Name)); err != nil { + if err = os.RemoveAll(UserPath(u.Name)); err != nil { return err } - _, err = x.Delete(user) + _, err = x.Delete(u) return err } diff --git a/modules/auth/org.go b/modules/auth/org.go index a60fbb85..f87d10a7 100644 --- a/modules/auth/org.go +++ b/modules/auth/org.go @@ -14,12 +14,12 @@ import ( "github.com/gogits/gogs/modules/middleware/binding" ) -type CreateOrganizationForm struct { +type CreateOrgForm struct { OrgName string `form:"orgname" binding:"Required;AlphaDashDot;MaxSize(30)"` Email string `form:"email" binding:"Required;Email;MaxSize(50)"` } -func (f *CreateOrganizationForm) Name(field string) string { +func (f *CreateOrgForm) Name(field string) string { names := map[string]string{ "OrgName": "Organization name", "Email": "E-mail address", @@ -27,7 +27,31 @@ func (f *CreateOrganizationForm) Name(field string) string { return names[field] } -func (f *CreateOrganizationForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { +func (f *CreateOrgForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) validate(errs, data, f) } + +type OrgSettingForm struct { + DisplayName string `form:"display_name" binding:"Required;MaxSize(100)"` + Email string `form:"email" binding:"Required;Email;MaxSize(50)"` + Description string `form:"desc" binding:"MaxSize(255)"` + Website string `form:"site" binding:"Url;MaxSize(100)"` + Location string `form:"location" binding:"MaxSize(50)"` +} + +func (f *OrgSettingForm) Name(field string) string { + names := map[string]string{ + "DisplayName": "Display name", + "Email": "E-mail address", + "Description": "Description", + "Website": "Website address", + "Location": "Location", + } + return names[field] +} + +func (f *OrgSettingForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { + data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) + validate(errors, data, f) +} diff --git a/modules/auth/repo.go b/modules/auth/repo.go index db13743d..d3d21532 100644 --- a/modules/auth/repo.go +++ b/modules/auth/repo.go @@ -71,7 +71,7 @@ func (f *MigrateRepoForm) Validate(errors *binding.Errors, req *http.Request, co type RepoSettingForm struct { RepoName string `form:"name" binding:"Required;AlphaDash;MaxSize(100)"` - Description string `form:"desc" binding:"MaxSize(100)"` + Description string `form:"desc" binding:"MaxSize(255)"` Website string `form:"site" binding:"Url;MaxSize(100)"` Branch string `form:"branch"` Interval int `form:"interval"` diff --git a/modules/auth/user.go b/modules/auth/user.go index 20f99336..4a781acf 100644 --- a/modules/auth/user.go +++ b/modules/auth/user.go @@ -93,7 +93,7 @@ func (f *UpdateProfileForm) Name(field string) string { names := map[string]string{ "UserName": "Username", "Email": "E-mail address", - "Website": "Website", + "Website": "Website address", "Location": "Location", "Avatar": "Gravatar Email", } diff --git a/routers/org/org.go b/routers/org/org.go index 0595a81b..4f57b9a9 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -16,7 +16,8 @@ import ( ) const ( - NEW base.TplName = "org/new" + NEW base.TplName = "org/new" + SETTINGS base.TplName = "org/settings" ) func Organization(ctx *middleware.Context, params martini.Params) { @@ -39,7 +40,7 @@ func New(ctx *middleware.Context) { ctx.HTML(200, NEW) } -func NewPost(ctx *middleware.Context, form auth.CreateOrganizationForm) { +func NewPost(ctx *middleware.Context, form auth.CreateOrgForm) { ctx.Data["Title"] = "Create An Organization" if ctx.HasError() { @@ -114,7 +115,52 @@ func Dashboard(ctx *middleware.Context, params martini.Params) { ctx.HTML(200, user.DASHBOARD) } -func Setting(ctx *middleware.Context, param martini.Params) { - ctx.Data["Title"] = "Setting" - ctx.HTML(200, "org/setting") +func Settings(ctx *middleware.Context, params martini.Params) { + ctx.Data["Title"] = "Settings" + + org, err := models.GetUserByName(params["org"]) + if err != nil { + if err == models.ErrUserNotExist { + ctx.Handle(404, "org.Settings(GetUserByName)", err) + } else { + ctx.Handle(500, "org.Settings(GetUserByName)", err) + } + return + } + ctx.Data["Org"] = org + + ctx.HTML(200, SETTINGS) +} + +func SettingsPost(ctx *middleware.Context, params martini.Params, form auth.OrgSettingForm) { + ctx.Data["Title"] = "Settings" + + org, err := models.GetUserByName(params["org"]) + if err != nil { + if err == models.ErrUserNotExist { + ctx.Handle(404, "org.SettingsPost(GetUserByName)", err) + } else { + ctx.Handle(500, "org.SettingsPost(GetUserByName)", err) + } + return + } + ctx.Data["Org"] = org + + if ctx.HasError() { + ctx.HTML(200, SETTINGS) + return + } + + org.FullName = form.DisplayName + org.Email = form.Email + org.Description = form.Description + org.Website = form.Website + org.Location = form.Location + if err = models.UpdateUser(org); err != nil { + ctx.Handle(500, "org.SettingsPost(UpdateUser)", err) + return + } + log.Trace("%s Organization setting updated: %s", ctx.Req.RequestURI, org.LowerName) + ctx.Flash.Success("Organization profile has been successfully updated.") + ctx.Redirect("/org/" + org.Name + "/settings") } diff --git a/templates/VERSION b/templates/VERSION index b5cda695..be0ebee0 100644 --- a/templates/VERSION +++ b/templates/VERSION @@ -1 +1 @@ -0.4.5.0625 Alpha \ No newline at end of file +0.4.5.0627 Alpha \ No newline at end of file diff --git a/templates/org/setting.tmpl b/templates/org/setting.tmpl deleted file mode 100644 index 1be9707a..00000000 --- a/templates/org/setting.tmpl +++ /dev/null @@ -1,151 +0,0 @@ -{{template "base/head" .}} -{{template "base/navbar" .}} -
-
-
- - -
- -
-
-
-
- -
-
- {{template "base/alert" .}} -
-
- Repository Options -
- -
- - {{.CsrfTokenHtml}} - - -
- - -
- -
-
- -
- - -
- -
-
- -
- - -
- -
-
- -
- - -
- -
-
- -
- - -
- -
-
- -
-
- -
-
- -
-
- -
-
- Danger Zone -
-
- -
-
Delete this organization
-
Once you delete this organization and all repositories in, there is no going back. Please be - certain. -
- - - -
-
-
-
-{{template "base/footer" .}} diff --git a/templates/org/settings.tmpl b/templates/org/settings.tmpl new file mode 100644 index 00000000..e19c027d --- /dev/null +++ b/templates/org/settings.tmpl @@ -0,0 +1,133 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+
+
+ +
+ +
+
+ +
+
+ +
+
+ {{template "base/alert" .}} +
+
+ Organization Options +
+ +
+
+ {{.CsrfTokenHtml}} + + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ Danger Zone +
+
+ +
+
Delete this organization
+
Once you delete this organization and all repositories in, there is no going back. Please be + certain. +
+ + + +
+
+
+
+{{template "base/footer" .}} -- cgit v1.2.3 From b5ba2bd268b144ae0c878fe17257d8685d92f9cc Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Fri, 27 Jun 2014 21:33:49 +0800 Subject: add organization team-create page --- cmd/web.go | 3 ++ public/css/gogs.css | 2 +- routers/org/org.go | 4 --- routers/org/teams.go | 16 ++++++++++ templates/org/new_team.tmpl | 74 +++++++++++++++++++++++++++++++++++++++++++++ templates/org/teams.tmpl | 6 ++++ 6 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 routers/org/teams.go create mode 100644 templates/org/new_team.tmpl (limited to 'cmd/web.go') diff --git a/cmd/web.go b/cmd/web.go index 729a1ba2..bf84d587 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -194,7 +194,10 @@ func runWeb(*cli.Context) { r.Get("/:org", org.Organization) r.Get("/:org/dashboard", org.Dashboard) r.Get("/:org/members", org.Members) + // organization teams + r.Get("/:org/teams/new",org.NewTeam) r.Get("/:org/teams", org.Teams) + r.Get("/:org/settings", org.Settings) r.Post("/:org/settings", bindIgnErr(auth.OrgSettingForm{}), org.SettingsPost) }, reqSignIn) diff --git a/public/css/gogs.css b/public/css/gogs.css index 960176f5..eb95a1d0 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -375,7 +375,7 @@ html, body { /* gogits repo create */ -#repo-create, #org-create { +#repo-create, #org-create, #org-teams-create { width: 800px; } diff --git a/routers/org/org.go b/routers/org/org.go index 4f57b9a9..c036a8e5 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -30,10 +30,6 @@ func Members(ctx *middleware.Context, params martini.Params) { ctx.HTML(200, "org/members") } -func Teams(ctx *middleware.Context, params martini.Params) { - ctx.Data["Title"] = "Organization " + params["org"] + " Teams" - ctx.HTML(200, "org/teams") -} func New(ctx *middleware.Context) { ctx.Data["Title"] = "Create An Organization" diff --git a/routers/org/teams.go b/routers/org/teams.go new file mode 100644 index 00000000..9585cb27 --- /dev/null +++ b/routers/org/teams.go @@ -0,0 +1,16 @@ +package org + +import ( + "github.com/go-martini/martini" + "github.com/gogits/gogs/modules/middleware" +) + +func Teams(ctx *middleware.Context, params martini.Params) { + ctx.Data["Title"] = "Organization "+params["org"]+" Teams" + ctx.HTML(200, "org/teams") +} + +func NewTeam(ctx *middleware.Context, params martini.Params) { + ctx.Data["Title"] = "Organization "+params["org"]+" New Team" + ctx.HTML(200, "org/new_team") +} diff --git a/templates/org/new_team.tmpl b/templates/org/new_team.tmpl new file mode 100644 index 00000000..752f37d2 --- /dev/null +++ b/templates/org/new_team.tmpl @@ -0,0 +1,74 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+
+
+ + +
+

Organization Name

+
+
+
+
+
+
+
+

Create new team

+
+ +
+ + You'll use this name to mention this team in conversations. +
+
+
+ +
+ +
+
+
+ +
+
+ +

This team will be able to view and clone its repositories.

+
+
+ +

This team will be able to read its repositories, as well as push to them.

+
+
+ +

This team will be able to push/pull to its repositories, as well as add other collaborators to them.

+
+
+
+
+
+ +
+ +
+
+
+
+
+{{template "base/footer" .}} diff --git a/templates/org/teams.tmpl b/templates/org/teams.tmpl index a8218812..90aab944 100644 --- a/templates/org/teams.tmpl +++ b/templates/org/teams.tmpl @@ -21,6 +21,12 @@
+
+
+ +
+
+

Team Name

-- cgit v1.2.3 From 1d55ecd29ce261cd3adf78649a44aaa30e4fd468 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Fri, 27 Jun 2014 22:04:04 +0800 Subject: add organization team-create page --- cmd/web.go | 3 +- public/css/gogs.css | 2 +- routers/org/teams.go | 5 +++ templates/org/edit_team.tmpl | 75 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 templates/org/edit_team.tmpl (limited to 'cmd/web.go') diff --git a/cmd/web.go b/cmd/web.go index bf84d587..1668fae2 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -195,7 +195,8 @@ func runWeb(*cli.Context) { r.Get("/:org/dashboard", org.Dashboard) r.Get("/:org/members", org.Members) // organization teams - r.Get("/:org/teams/new",org.NewTeam) + r.Get("/:org/teams/:team/edit", org.EditTeam) + r.Get("/:org/teams/new", org.NewTeam) r.Get("/:org/teams", org.Teams) r.Get("/:org/settings", org.Settings) diff --git a/public/css/gogs.css b/public/css/gogs.css index eb95a1d0..98cb5ee1 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -375,7 +375,7 @@ html, body { /* gogits repo create */ -#repo-create, #org-create, #org-teams-create { +#repo-create, #org-create, #org-teams-create, #org-teams-edit { width: 800px; } diff --git a/routers/org/teams.go b/routers/org/teams.go index 9585cb27..9ca5185a 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -14,3 +14,8 @@ func NewTeam(ctx *middleware.Context, params martini.Params) { ctx.Data["Title"] = "Organization "+params["org"]+" New Team" ctx.HTML(200, "org/new_team") } + +func EditTeam(ctx *middleware.Context, params martini.Params){ + ctx.Data["Title"] = "Organization "+params["org"]+" Edit Team" + ctx.HTML(200,"org/edit_team") +} diff --git a/templates/org/edit_team.tmpl b/templates/org/edit_team.tmpl new file mode 100644 index 00000000..4292575c --- /dev/null +++ b/templates/org/edit_team.tmpl @@ -0,0 +1,75 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+
+
+ + +
+

Organization Name

+
+
+
+
+
+
+
+

Edit team

+
+ +
+ + You'll use this name to mention this team in conversations. +
+
+
+ +
+ +
+
+
+ +
+
+ +

This team will be able to view and clone its repositories.

+
+
+ +

This team will be able to read its repositories, as well as push to them.

+
+
+ +

This team will be able to push/pull to its repositories, as well as add other collaborators to them.

+
+
+
+
+
+ +
+ + +
+
+
+
+
+{{template "base/footer" .}} -- cgit v1.2.3