Browse Source

Organization settings page

Unknown 10 years ago
parent
commit
19e9104289
11 changed files with 217 additions and 140 deletions
  1. 3 2
      cmd/web.go
  2. 1 1
      gogs.go
  3. 86 0
      models/org.go
  4. 1 1
      models/repo.go
  5. 26 88
      models/user.go
  6. 27 3
      modules/auth/org.go
  7. 1 1
      modules/auth/repo.go
  8. 1 1
      modules/auth/user.go
  9. 51 5
      routers/org/org.go
  10. 1 1
      templates/VERSION
  11. 19 37
      templates/org/settings.tmpl

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

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

+ 86 - 0
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))
+}
+
 // ___________                    ____ ___
 // \__    ___/___ _____    _____ |    |   \______ ___________
 //   |    |_/ __ \\__  \  /     \|    |   /  ___// __ \_  __ \

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

+ 26 - 88
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
 }
 

+ 27 - 3
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)
+}

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

+ 1 - 1
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",
 	}

+ 51 - 5
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")
 }

+ 1 - 1
templates/VERSION

@@ -1 +1 @@
-0.4.5.0625 Alpha
+0.4.5.0627 Alpha

+ 19 - 37
templates/org/setting.tmpl → templates/org/settings.tmpl

@@ -4,32 +4,20 @@
     <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
+                <img src="{{.Org.AvatarLink}}?s=28" alt="user-avatar" title="username">
+                {{.Org.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>
-                            </ul>
-                        </div>-->
         </div>
         <ul class="nav nav-pills pull-right">
-            <li><a href="/">Feed</a></li>
-            <li><a href="/issues">Issues</a></li>
-            <li class="active"><a href="#">Setting</a></li>
+            <li><a href="/org/{{.Org.Name}}/dashboard/">News Feed</a></li>
+            <li><a href="/org/{{.Org.Name}}/dashboard/issues">Issues</a></li>
+            <li class="active"><a href="/org/{{.Org.Name}}/settings">Settings</a></li>
             <!-- <li><a href="/pulls">Pull Requests</a></li>
             <li><a href="/stars">Stars</a></li> -->
         </ul>
     </div>
 </div>
+
 <div id="body" class="container" data-page="org">
     <div id="user-setting-nav" class="col-md-2 repo-setting-nav">
         <ul class="list-group">
@@ -40,52 +28,46 @@
         {{template "base/alert" .}}
         <div class="panel panel-default">
             <div class="panel-heading">
-                Repository Options
+                Organization Options
             </div>
 
             <div class="panel-body">
-                <form action="/{{.Owner.Name}}/{{.Repository.Name}}/settings" method="post" class="form-horizontal">
+                <form action="/org/{{.Org.Name}}/settings" method="post" class="form-horizontal">
                     {{.CsrfTokenHtml}}
                     <input type="hidden" name="action" value="update">
 
-                    <div class="form-group">
-                        <label class="col-md-3 text-right" for="org-setting-name">Name</label>
-
+                    <div class="form-group{{if .Err_DisplayName}} has-error has-feedback{{end}}">
+                        <label class="col-md-3 text-right" for="org-setting-name">Display Name</label>
                         <div class="col-md-9">
-                            <input class="form-control" name="name" value="" title="" id="org-setting-name"/>
+                            <input class="form-control" name="display_name" value="{{.Org.FullName}}" title="" id="org-setting-name"/>
                         </div>
                     </div>
 
-                    <div class="form-group">
+                    <div class="form-group{{if .Err_Email}} has-error has-feedback{{end}}">
                         <label class="col-md-3 text-right" for="org-email">Email</label>
-
                         <div class="col-md-9">
-                            <input class="form-control" name="email" value="" title="" id="org-email" type="email"/>
+                            <input class="form-control" name="email" value="{{.Org.Email}}" title="" id="org-email" type="email"/>
                         </div>
                     </div>
 
-                    <div class="form-group">
+                    <div class="form-group{{if .Err_Description}} has-error has-feedback{{end}}">
                         <label class="col-md-3 text-right" for="org-desc">Description</label>
-
                         <div class="col-md-9">
-                            <textarea class="form-control" name="desc" id="org-desc" rows="3">{{.Repository.Description}}</textarea>
+                            <textarea class="form-control" name="desc" id="org-desc" rows="3">{{.Org.Description}}</textarea>
                         </div>
                     </div>
 
-                    <div class="form-group">
+                    <div class="form-group{{if .Err_Website}} has-error has-feedback{{end}}">
                         <label class="col-md-3 text-right" for="org-site">Official Site</label>
-
                         <div class="col-md-9">
-                            <input type="url" class="form-control" name="site" value="{{.Repository.Website}}"
-                                   id="org-site"/>
+                            <input type="url" class="form-control" name="site" value="{{.Org.Website}}" id="org-site"/>
                         </div>
                     </div>
 
-                    <div class="form-group">
+                    <div class="form-group{{if .Err_Location}} has-error has-feedback{{end}}">
                         <label class="col-md-3 text-right" for="org-location">Location</label>
-
                         <div class="col-md-9">
-                            <input class="form-control" name="email" value="" title="" id="org-location"/>
+                            <input class="form-control" name="location" value="{{.Org.Location}}" title="" id="org-location"/>
                         </div>
                     </div>