Browse Source

Add create organization

Unknown 10 years ago
parent
commit
e0f9c628c5

+ 10 - 10
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)

+ 3 - 2
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)

+ 1 - 1
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())

+ 9 - 8
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)"` // <user name>/<repo name>
-	Mode     int       `xorm:"unique(s)"`
-	Created  time.Time `xorm:"created"`
+	UserName string     `xorm:"unique(s)"`
+	RepoName string     `xorm:"unique(s)"` // <user name>/<repo name>
+	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 <username>/<reponame>.
-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
 	}

+ 2 - 2
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

+ 2 - 2
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)
 }

+ 1 - 1
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() {

+ 69 - 0
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
+}

+ 6 - 4
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,

+ 149 - 37
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.

+ 33 - 0
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)
+}

+ 2 - 2
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

+ 2 - 2
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
 	}

+ 1 - 1
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

+ 90 - 6
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) {

+ 4 - 4
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

+ 2 - 2
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
 	}

+ 10 - 3
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
 			}
 		}

+ 4 - 3
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 {

+ 1 - 1
templates/VERSION

@@ -1 +1 @@
-0.4.5.0624 Alpha
+0.4.5.0625 Alpha

+ 0 - 73
templates/org/dashboard.tmpl

@@ -1,73 +0,0 @@
-{{template "base/head" .}}
-{{template "base/navbar" .}}
-<div id="body-nav">
-    <div class="container">
-        <div class="btn-group pull-left" id="dashboard-switch">
-            <button type="button" class="btn btn-default">
-                <img src="//1.gravatar.com/avatar/f72f7454ce9d710baa506394f68f4132?s=28" alt="user-avatar" title="username">
-                gogits
-            </button>
-            <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
-                <span class="caret"></span>
-            </button>
-            <div class="dropdown-menu clone-group-btn no-propagation">
-                <ul id="dashboard-switch-menu" class="list-unstyled">
-                    <li class="checked"><a href="#"><i class="fa fa-check"></i>
-                        <img src="//1.gravatar.com/avatar/f72f7454ce9d710baa506394f68f4132?s=28" alt="user-avatar" title="username">
-                        gogits/gogs</a>
-                    </li>
-                </ul>
-            </div>
-        </div>
-        <ul class="nav nav-pills pull-right">
-            <li class="active"><a href="/">Feed</a></li>
-            <li><a href="/issues">Issues</a></li>
-            <li><a href="#">Setting</a></li>
-            <!-- <li><a href="/pulls">Pull Requests</a></li>
-            <li><a href="/stars">Stars</a></li> -->
-        </ul>
-        <h3>News Feed</h3>
-    </div>
-</div>
-<div id="body" class="container" data-page="user">
-    {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}}
-    <div id="feed-left" class="col-md-8">
-        <ul class="list-unstyled activity-list">
-            {{range .Feeds}}
-            <li>
-                <i class="icon fa fa-{{ActionIcon .OpType}}"></i>
-                <div class="info"><span class="meta">{{TimeSince .Created}}</span><br>{{ActionDesc . | str2html}}</div>
-                <span class="clearfix"></span>
-            </li>
-            {{else}}
-            <li>Oh. Looks like there isn't any activity here yet. Get Busy!</li>
-            {{end}}
-        </ul>
-    </div>
-    <div id="feed-right" class="col-md-4">
-        <div class="panel panel-default repo-panel">
-            <div class="panel-heading">Repositories
-                <div class="btn-group pull-right" id="user-dashboard-repo-new">
-                    <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown"><i class="fa fa-plus-square"></i>New</button>
-                    <div class="dropdown-menu dropdown-menu-right">
-                        <ul class="list-unstyled">
-                            <li><a href="/repo/create"><i class="fa fa-book"></i>Repository</a></li>
-                            <li><a href="/repo/migrate"><i class="fa fa-clipboard"></i>Migration</a></li>
-                            <!-- <li><a href="#"><i class="fa fa-users"></i>Organization</a></li> -->
-                        </ul>
-                    </div>
-                </div>
-            </div>
-
-            <div class="panel-body">
-                <ul class="list-group">{{range .MyRepos}}
-                    <li class="list-group-item"><a href="/{{$.SignedUserName}}/{{.Name}}">
-                        <!-- <span class="stars pull-right"><i class="fa fa-star"></i>{{.NumStars}}</span> -->
-                        <i class="fa fa-book"></i>{{.Name}}{{if .IsPrivate}} <span class="label label-default">Private</span>{{end}}</a>
-                    </li>{{end}}
-                </ul>
-            </div>
-        </div>
-    </div>
-</div>
-{{template "base/footer" .}}

+ 4 - 20
templates/org/new.tmpl

@@ -1,22 +1,14 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
 <div class="container" id="body">
-    <form action="/repo/create" method="post" class="form-horizontal card" id="org-create">
+    <form action="/org/create" method="post" class="form-horizontal card" id="org-create">
         {{.CsrfTokenHtml}}
         <h3>Create New Organization</h3>
         {{template "base/alert" .}}
-        <div class="form-group">
-            <label class="col-md-2 control-label">Owner<strong class="text-danger">*</strong></label>
-            <div class="col-md-8">
-                <p class="form-control-static">{{.SignedUserName}}</p>
-                <input type="hidden" value="{{.SignedUserId}}" name="userId"/>
-            </div>
-        </div>
-
-        <div class="form-group {{if .Err_RepoName}}has-error has-feedback{{end}}">
+        <div class="form-group {{if .Err_OrgName}}has-error has-feedback{{end}}">
             <label class="col-md-2 control-label">Organization<strong class="text-danger">*</strong></label>
             <div class="col-md-8">
-                <input name="repo" type="text" class="form-control" placeholder="Type your repository name" value="{{.repo}}" required="required">
+                <input name="orgname" type="text" class="form-control" placeholder="Type your organization name" value="{{.orgname}}" required="required">
                 <span class="help-block">Great organization names are short and memorable. </span>
             </div>
         </div>
@@ -24,18 +16,10 @@
         <div class="form-group {{if .Err_Email}}has-error has-feedback{{end}}">
             <label class="col-md-2 control-label">Email<strong class="text-danger">*</strong></label>
             <div class="col-md-8">
-                <input name="email" type="text" class="form-control" placeholder="Type organization's email" value="" required="required">
+                <input name="email" type="text" class="form-control" placeholder="Type organization's email" value="{{.email}}" required="required">
                 <span class="help-block">Organization's Email receives all notifications and confirmations.</span>
             </div>
         </div>
-<!--
-        <div class="form-group">
-            <label class="col-md-2 control-label">Owners<strong class="text-danger">*</strong></label>
-            <div class="col-md-8">
-                owners
-            </div>
-        </div>-->
-
 
         <div class="form-group">
             <div class="col-md-offset-2 col-md-8">

+ 29 - 10
templates/user/dashboard.tmpl

@@ -4,29 +4,46 @@
     <div class="container">
         <div class="btn-group pull-left" id="dashboard-switch">
             <button type="button" class="btn btn-default">
-                <img src="//1.gravatar.com/avatar/f72f7454ce9d710baa506394f68f4132?s=28" alt="user-avatar" title="username">
-                fuxiaohei
+                <img src="{{.ContextUser.AvatarLink}}?s=28" alt="user-avatar" title="username">
+                {{.ContextUser.Name}}
             </button>
             <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
                 <span class="caret"></span>
             </button>
             <div class="dropdown-menu clone-group-btn no-propagation">
                 <ul id="dashboard-switch-menu" class="list-unstyled">
-                    <li class="checked"><a href="#"><i class="fa fa-check"></i>
-                        <img src="//1.gravatar.com/avatar/f72f7454ce9d710baa506394f68f4132?s=28" alt="user-avatar" title="username">
-                        gogits/gogs</a></li>
+                    <li{{if not .PageIsOrgDashboard}} class="checked"{{end}}>
+                        <a href="/">
+                            <i class="fa fa-check"></i>
+                            <img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username">
+                            {{.SignedUser.Name}}
+                        </a>
+                    </li>
+                    {{range .Orgs}}
+                    <li{{if eq $.ContextUser.Id .Id}} class="checked"{{end}}>
+                        <a href="/org/{{.Name}}/dashboard">
+                            <i class="fa fa-check"></i>
+                            <img src="{{.AvatarLink}}?s=28" alt="user-avatar" title="username">
+                            {{.Name}}
+                        </a>
+                    </li>
+                    {{end}}
+                    <li>
+                        <a href="/org/create">Create organization</a>
+                    </li>
                 </ul>
             </div>
         </div>
         <ul class="nav nav-pills pull-right">
-            <li class="active"><a href="/">Feed</a></li>
-            <li><a href="/issues">Issues</a></li>
+            <li class="active"><a href="/{{if .PageIsOrgDashboard}}org/{{.ContextUser.Name}}/dashboard{{end}}">News Feed</a></li>
+            <li><a href="/{{if .PageIsOrgDashboard}}org/{{.ContextUser.Name}}/dashboard/{{end}}issues">Issues</a></li>
+            {{if .PageIsOrgDashboard}}<li><a href="/org/{{.ContextUser.Name}}/settings">Settings</a></li>{{end}}
             <!-- <li><a href="/pulls">Pull Requests</a></li>
             <li><a href="/stars">Stars</a></li> -->
         </ul>
-        <h3>News Feed</h3>
     </div>
 </div>
+
 <div id="body" class="container" data-page="user">
     {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}}
     <div id="feed-left" class="col-md-8">
@@ -44,7 +61,7 @@
     </div>
     <div id="feed-right" class="col-md-4">
         <div class="panel panel-default repo-panel">
-            <div class="panel-heading">Your Repositories
+            <div class="panel-heading">{{if not .PageIsOrgDashboard}}Your {{end}}Repositories
                 <div class="btn-group pull-right" id="user-dashboard-repo-new">
                     <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown"><i class="fa fa-plus-square"></i>New</button>
                     <div class="dropdown-menu dropdown-menu-right">
@@ -66,7 +83,8 @@
                 </ul>
             </div>
         </div>
-
+        
+        {{if not .PageIsOrgDashboard}}
         <div class="panel panel-default repo-panel">
             <div class="panel-heading">Collaborative Repositories</div>
             <div class="panel-body">
@@ -78,6 +96,7 @@
                 </ul>
             </div>
         </div>
+        {{end}}
     </div>
 </div>
 {{template "base/footer" .}}

+ 2 - 1
templates/user/issues.tmpl

@@ -3,7 +3,7 @@
 <div id="body-nav">
     <div class="container">
         <ul class="nav nav-pills pull-right">
-            <li><a href="/">Feed</a></li>
+            <li><a href="/">News Feed</a></li>
             <li class="active"><a href="/issues">Issues</a></li>
             <!-- <li><a href="/pulls">Pull Requests</a></li>
             <li><a href="/stars">Stars</a></li> -->
@@ -11,6 +11,7 @@
         <h3>Your Issues</h3>
     </div>
 </div>
+
 <div id="body" class="container" data-page="user">
     {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}}
     <div id="issue">