Finish create organization team

This commit is contained in:
Unknown 2014-07-02 16:42:16 -04:00
parent e57aebb316
commit 465dc962b5
13 changed files with 389 additions and 247 deletions

View file

@ -195,9 +195,10 @@ func runWeb(*cli.Context) {
r.Get("/:org/dashboard", org.Dashboard)
r.Get("/:org/members", org.Members)
r.Get("/:org/teams/:team/edit", org.EditTeam)
r.Get("/:org/teams/new", org.NewTeam)
r.Get("/:org/teams", org.Teams)
r.Get("/:org/teams/new", org.NewTeam)
r.Post("/:org/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost)
r.Get("/:org/teams/:team/edit", org.EditTeam)
r.Get("/:org/settings", org.Settings)
r.Post("/:org/settings", bindIgnErr(auth.OrgSettingForm{}), org.SettingsPost)

View file

@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/setting"
)
const APP_VER = "0.4.5.0629 Alpha"
const APP_VER = "0.4.5.0702 Alpha"
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())

View file

@ -5,11 +5,17 @@
package models
import (
"errors"
"strings"
"github.com/gogits/gogs/modules/base"
)
var (
ErrOrgNotExist = errors.New("Organization does not exist")
ErrTeamAlreadyExist = errors.New("Team already exist")
)
// IsOrgOwner returns true if given user is in the owner team.
func (org *User) IsOrgOwner(uid int64) bool {
return IsOrganizationOwner(org.Id, uid)
@ -156,6 +162,13 @@ func DeleteOrganization(org *User) (err error) {
return sess.Commit()
}
// ___________
// \__ ___/___ _____ _____
// | |_/ __ \\__ \ / \
// | |\ ___/ / __ \| Y Y \
// |____| \___ >____ /__|_| /
// \/ \/ \/
type AuthorizeType int
const (
@ -192,11 +205,41 @@ func (t *Team) GetMembers() (err error) {
}
// NewTeam creates a record of new team.
// It's caller's responsibility to assign organization ID.
func NewTeam(t *Team) error {
// TODO: check if same name team of organization exists.
t.LowerName = strings.ToLower(t.Name)
_, err := x.Insert(t)
has, err := x.Id(t.OrgId).Get(new(User))
if err != nil {
return err
} else if !has {
return ErrOrgNotExist
}
t.LowerName = strings.ToLower(t.Name)
has, err = x.Where("org_id=?", t.OrgId).And("lower_name=?", t.LowerName).Get(new(Team))
if err != nil {
return err
} else if has {
return ErrTeamAlreadyExist
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Insert(t); err != nil {
sess.Rollback()
return err
}
// Update organization number of teams.
rawSql := "UPDATE `user` SET num_teams = num_teams + 1 WHERE id = ?"
if _, err = sess.Exec(rawSql, t.OrgId); err != nil {
sess.Rollback()
return err
}
return sess.Commit()
}
// UpdateTeam updates information of team.

View file

@ -158,7 +158,7 @@ func IsRepositoryExist(u *User, repoName string) (bool, error) {
}
var (
illegalEquals = []string{"raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin"}
illegalEquals = []string{"raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new"}
illegalSuffixs = []string{".git"}
)

View file

@ -7,183 +7,49 @@ package auth
import (
"net/http"
"reflect"
"strings"
"github.com/go-martini/martini"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/middleware/binding"
)
// Web form interface.
type Form interface {
Name(field string) string
}
type RegisterForm struct {
UserName string `form:"username" binding:"Required;AlphaDashDot;MaxSize(30)"`
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"`
RetypePasswd string `form:"retypepasswd"`
LoginType string `form:"logintype"`
LoginName string `form:"loginname"`
}
func (f *RegisterForm) Name(field string) string {
names := map[string]string{
"UserName": "Username",
"Email": "E-mail address",
"Password": "Password",
"RetypePasswd": "Re-type password",
}
return names[field]
}
func (f *RegisterForm) 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 LogInForm struct {
UserName string `form:"username" binding:"Required;MaxSize(35)"`
Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"`
Remember bool `form:"remember"`
}
func (f *LogInForm) Name(field string) string {
names := map[string]string{
"UserName": "Username",
"Password": "Password",
}
return names[field]
}
func (f *LogInForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
validate(errs, data, f)
}
func GetMinMaxSize(field reflect.StructField) string {
for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") {
return rule[8 : len(rule)-1]
}
}
return ""
}
func validate(errs *binding.Errors, data base.TmplData, f Form) {
if errs.Count() == 0 {
return
} else if len(errs.Overall) > 0 {
for _, err := range errs.Overall {
log.Error("%s: %v", reflect.TypeOf(f), err)
}
return
}
data["HasError"] = true
AssignForm(f, data)
typ := reflect.TypeOf(f)
val := reflect.ValueOf(f)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fieldName := field.Tag.Get("form")
// Allow ignored fields in the struct
if fieldName == "-" {
continue
}
if err, ok := errs.Fields[field.Name]; ok {
data["Err_"+field.Name] = true
switch err {
case binding.BindingRequireError:
data["ErrorMsg"] = f.Name(field.Name) + " cannot be empty"
case binding.BindingAlphaDashError:
data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) characters"
case binding.BindingAlphaDashDotError:
data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) or dot characters"
case binding.BindingMinSizeError:
data["ErrorMsg"] = f.Name(field.Name) + " must contain at least " + GetMinMaxSize(field) + " characters"
case binding.BindingMaxSizeError:
data["ErrorMsg"] = f.Name(field.Name) + " must contain at most " + GetMinMaxSize(field) + " characters"
case binding.BindingEmailError:
data["ErrorMsg"] = f.Name(field.Name) + " is not a valid e-mail address"
case binding.BindingUrlError:
data["ErrorMsg"] = f.Name(field.Name) + " is not a valid URL"
default:
data["ErrorMsg"] = "Unknown error: " + err
}
return
}
}
}
// AssignForm assign form values back to the template data.
func AssignForm(form interface{}, data base.TmplData) {
typ := reflect.TypeOf(form)
val := reflect.ValueOf(form)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fieldName := field.Tag.Get("form")
// Allow ignored fields in the struct
if fieldName == "-" {
continue
}
data[fieldName] = val.Field(i).Interface()
}
}
type InstallForm struct {
Database string `form:"database" binding:"Required"`
Host string `form:"host"`
User string `form:"user"`
Passwd string `form:"passwd"`
DatabaseName string `form:"database_name"`
SslMode string `form:"ssl_mode"`
DatabasePath string `form:"database_path"`
RepoRootPath string `form:"repo_path"`
RunUser string `form:"run_user"`
type AuthenticationForm struct {
Id int64 `form:"id"`
Type int `form:"type"`
AuthName string `form:"name" binding:"Required;MaxSize(50)"`
Domain string `form:"domain"`
AppUrl string `form:"app_url"`
AdminName string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"`
AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"`
AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"`
SmtpHost string `form:"smtp_host"`
SmtpEmail string `form:"mailer_user"`
SmtpPasswd string `form:"mailer_pwd"`
RegisterConfirm string `form:"register_confirm"`
MailNotify string `form:"mail_notify"`
Host string `form:"host"`
Port int `form:"port"`
UseSSL bool `form:"usessl"`
BaseDN string `form:"base_dn"`
Attributes string `form:"attributes"`
Filter string `form:"filter"`
MsAdSA string `form:"ms_ad_sa"`
IsActived bool `form:"is_actived"`
SmtpAuth string `form:"smtpauth"`
SmtpHost string `form:"smtphost"`
SmtpPort int `form:"smtpport"`
Tls bool `form:"tls"`
AllowAutoRegister bool `form:"allowautoregister"`
}
func (f *InstallForm) Name(field string) string {
func (f *AuthenticationForm) Name(field string) string {
names := map[string]string{
"Database": "Database name",
"AdminName": "Admin user name",
"AdminPasswd": "Admin password",
"AdminEmail": "Admin e-maill address",
"AuthName": "Authentication's name",
"Domain": "Domain name",
"Host": "Host address",
"Port": "Port Number",
"UseSSL": "Use SSL",
"BaseDN": "Base DN",
"Attributes": "Search attributes",
"Filter": "Search filter",
"MsAdSA": "Ms Ad SA",
}
return names[field]
}
func (f *InstallForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
func (f *AuthenticationForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
validate(errors, data, f)
}

View file

@ -1,55 +0,0 @@
// 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 AuthenticationForm struct {
Id int64 `form:"id"`
Type int `form:"type"`
AuthName string `form:"name" binding:"Required;MaxSize(50)"`
Domain string `form:"domain"`
Host string `form:"host"`
Port int `form:"port"`
UseSSL bool `form:"usessl"`
BaseDN string `form:"base_dn"`
Attributes string `form:"attributes"`
Filter string `form:"filter"`
MsAdSA string `form:"ms_ad_sa"`
IsActived bool `form:"is_actived"`
SmtpAuth string `form:"smtpauth"`
SmtpHost string `form:"smtphost"`
SmtpPort int `form:"smtpport"`
Tls bool `form:"tls"`
AllowAutoRegister bool `form:"allowautoregister"`
}
func (f *AuthenticationForm) Name(field string) string {
names := map[string]string{
"AuthName": "Authentication's name",
"Domain": "Domain name",
"Host": "Host address",
"Port": "Port Number",
"UseSSL": "Use SSL",
"BaseDN": "Base DN",
"Attributes": "Search attributes",
"Filter": "Search filter",
"MsAdSA": "Ms Ad SA",
}
return names[field]
}
func (f *AuthenticationForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
validate(errors, data, f)
}

View file

@ -14,6 +14,13 @@ import (
"github.com/gogits/gogs/modules/middleware/binding"
)
// ________ .__ __ .__
// \_____ \_______ _________ ____ |__|____________ _/ |_|__| ____ ____
// / | \_ __ \/ ___\__ \ / \| \___ /\__ \\ __\ |/ _ \ / \
// / | \ | \/ /_/ > __ \| | \ |/ / / __ \| | | ( <_> ) | \
// \_______ /__| \___ (____ /___| /__/_____ \(____ /__| |__|\____/|___| /
// \/ /_____/ \/ \/ \/ \/ \/
type CreateOrgForm struct {
OrgName string `form:"orgname" binding:"Required;AlphaDashDot;MaxSize(30)"`
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
@ -55,3 +62,29 @@ func (f *OrgSettingForm) Validate(errors *binding.Errors, req *http.Request, con
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
validate(errors, data, f)
}
// ___________
// \__ ___/___ _____ _____
// | |_/ __ \\__ \ / \
// | |\ ___/ / __ \| Y Y \
// |____| \___ >____ /__|_| /
// \/ \/ \/
type CreateTeamForm struct {
TeamName string `form:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
Description string `form:"desc" binding:"MaxSize(255)"`
Permission string `form:"permission"`
}
func (f *CreateTeamForm) Name(field string) string {
names := map[string]string{
"TeamName": "Team name",
"Description": "Team description",
}
return names[field]
}
func (f *CreateTeamForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
validate(errs, data, f)
}

View file

@ -7,6 +7,7 @@ package auth
import (
"net/http"
"reflect"
"strings"
"github.com/go-martini/martini"
@ -19,6 +20,178 @@ import (
"github.com/gogits/gogs/modules/setting"
)
// Web form interface.
type Form interface {
Name(field string) string
}
type RegisterForm struct {
UserName string `form:"username" binding:"Required;AlphaDashDot;MaxSize(30)"`
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"`
RetypePasswd string `form:"retypepasswd"`
LoginType string `form:"logintype"`
LoginName string `form:"loginname"`
}
func (f *RegisterForm) Name(field string) string {
names := map[string]string{
"UserName": "Username",
"Email": "E-mail address",
"Password": "Password",
"RetypePasswd": "Re-type password",
}
return names[field]
}
func (f *RegisterForm) 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 LogInForm struct {
UserName string `form:"username" binding:"Required;MaxSize(35)"`
Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"`
Remember bool `form:"remember"`
}
func (f *LogInForm) Name(field string) string {
names := map[string]string{
"UserName": "Username",
"Password": "Password",
}
return names[field]
}
func (f *LogInForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
validate(errs, data, f)
}
func GetMinMaxSize(field reflect.StructField) string {
for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") {
return rule[8 : len(rule)-1]
}
}
return ""
}
func validate(errs *binding.Errors, data base.TmplData, f Form) {
if errs.Count() == 0 {
return
} else if len(errs.Overall) > 0 {
for _, err := range errs.Overall {
log.Error("%s: %v", reflect.TypeOf(f), err)
}
return
}
data["HasError"] = true
AssignForm(f, data)
typ := reflect.TypeOf(f)
val := reflect.ValueOf(f)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fieldName := field.Tag.Get("form")
// Allow ignored fields in the struct
if fieldName == "-" {
continue
}
if err, ok := errs.Fields[field.Name]; ok {
data["Err_"+field.Name] = true
switch err {
case binding.BindingRequireError:
data["ErrorMsg"] = f.Name(field.Name) + " cannot be empty"
case binding.BindingAlphaDashError:
data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) characters"
case binding.BindingAlphaDashDotError:
data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) or dot characters"
case binding.BindingMinSizeError:
data["ErrorMsg"] = f.Name(field.Name) + " must contain at least " + GetMinMaxSize(field) + " characters"
case binding.BindingMaxSizeError:
data["ErrorMsg"] = f.Name(field.Name) + " must contain at most " + GetMinMaxSize(field) + " characters"
case binding.BindingEmailError:
data["ErrorMsg"] = f.Name(field.Name) + " is not a valid e-mail address"
case binding.BindingUrlError:
data["ErrorMsg"] = f.Name(field.Name) + " is not a valid URL"
default:
data["ErrorMsg"] = "Unknown error: " + err
}
return
}
}
}
// AssignForm assign form values back to the template data.
func AssignForm(form interface{}, data base.TmplData) {
typ := reflect.TypeOf(form)
val := reflect.ValueOf(form)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fieldName := field.Tag.Get("form")
// Allow ignored fields in the struct
if fieldName == "-" {
continue
}
data[fieldName] = val.Field(i).Interface()
}
}
type InstallForm struct {
Database string `form:"database" binding:"Required"`
Host string `form:"host"`
User string `form:"user"`
Passwd string `form:"passwd"`
DatabaseName string `form:"database_name"`
SslMode string `form:"ssl_mode"`
DatabasePath string `form:"database_path"`
RepoRootPath string `form:"repo_path"`
RunUser string `form:"run_user"`
Domain string `form:"domain"`
AppUrl string `form:"app_url"`
AdminName string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"`
AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"`
AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"`
SmtpHost string `form:"smtp_host"`
SmtpEmail string `form:"mailer_user"`
SmtpPasswd string `form:"mailer_pwd"`
RegisterConfirm string `form:"register_confirm"`
MailNotify string `form:"mail_notify"`
}
func (f *InstallForm) Name(field string) string {
names := map[string]string{
"Database": "Database name",
"AdminName": "Admin user name",
"AdminPasswd": "Admin password",
"AdminEmail": "Admin e-maill address",
}
return names[field]
}
func (f *InstallForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
validate(errors, data, f)
}
// SignedInId returns the id of signed in user.
func SignedInId(header http.Header, sess session.SessionStore) int64 {
if !models.HasEngine {

View file

@ -8,12 +8,15 @@ 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"
)
const (
TEAMS base.TplName = "org/teams"
TEAM_NEW base.TplName = "org/team_new"
)
func Teams(ctx *middleware.Context, params martini.Params) {
@ -46,8 +49,80 @@ func Teams(ctx *middleware.Context, params martini.Params) {
}
func NewTeam(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Organization " + params["org"] + " New Team"
ctx.HTML(200, "org/new_team")
org, err := models.GetUserByName(params["org"])
if err != nil {
if err == models.ErrUserNotExist {
ctx.Handle(404, "org.NewTeam(GetUserByName)", err)
} else {
ctx.Handle(500, "org.NewTeam(GetUserByName)", err)
}
return
}
ctx.Data["Org"] = org
// Check ownership of organization.
if !org.IsOrgOwner(ctx.User.Id) {
ctx.Error(403)
return
}
ctx.HTML(200, TEAM_NEW)
}
func NewTeamPost(ctx *middleware.Context, params martini.Params, form auth.CreateTeamForm) {
org, err := models.GetUserByName(params["org"])
if err != nil {
if err == models.ErrUserNotExist {
ctx.Handle(404, "org.NewTeamPost(GetUserByName)", err)
} else {
ctx.Handle(500, "org.NewTeamPost(GetUserByName)", err)
}
return
}
ctx.Data["Org"] = org
// Check ownership of organization.
if !org.IsOrgOwner(ctx.User.Id) {
ctx.Error(403)
return
}
if ctx.HasError() {
ctx.HTML(200, TEAM_NEW)
return
}
// Validate permission level.
var auth models.AuthorizeType
switch form.Permission {
case "read":
auth = models.ORG_READABLE
case "write":
auth = models.ORG_WRITABLE
case "admin":
auth = models.ORG_ADMIN
default:
ctx.Error(401)
return
}
t := &models.Team{
OrgId: org.Id,
Name: form.TeamName,
Description: form.Description,
Authorize: auth,
}
if err = models.NewTeam(t); err != nil {
if err == models.ErrTeamAlreadyExist {
ctx.Data["Err_TeamName"] = true
ctx.RenderWithErr("Team name has already been used", TEAM_NEW, &form)
} else {
ctx.Handle(500, "org.NewTeamPost(NewTeam)", err)
}
return
}
log.Trace("%s Team created: %s/%s", ctx.Req.RequestURI, org.Name, t.Name)
ctx.Redirect("/org/" + org.LowerName + "/teams/" + t.LowerName)
}
func EditTeam(ctx *middleware.Context, params martini.Params) {

View file

@ -1 +1 @@
0.4.5.0629 Alpha
0.4.5.0702 Alpha

View file

@ -13,7 +13,7 @@
</div>
</div>
<div class="form-group {{if .Err_Email}}has-error has-feedback{{end}}">
<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="{{.email}}" required="required">

View file

@ -4,58 +4,63 @@
<div class="container clearfix">
<div id="org-nav-wrapper">
<ul class="nav nav-pills pull-right">
<li><a href="#"><i class="fa fa-users"></i>Members
<span class="label label-default">5</span></a>
<li><a href="/org/{{.Org.Name}}/members"><i class="fa fa-users"></i>Members
<span class="label label-default">{{.Org.NumMembers}}</span></a>
</li>
<li class="active"><a href="#"><i class="fa fa-tags"></i>Teams
<span class="label label-default">2</span></a>
<li class="active"><a href="/org/{{.Org.Name}}/teams"><i class="fa fa-tags"></i>Teams
<span class="label label-default">{{.Org.NumTeams}}</span></a>
</li>
</ul>
<img class="pull-left org-small-logo" src="https://avatars3.githubusercontent.com/u/6656686?s=140" alt="" width="60"/>
<img class="pull-left org-small-logo" src="{{.Org.AvatarLink}}?s=140" alt="" width="60"/>
<div id="org-nav-info">
<h2 class="org-name">Organization Name</h2>
<h2 class="org-name">{{.Org.FullName}}</h2>
</div>
</div>
</div>
</div>
<div id="body" class="container">
<div id="org">
<form id="org-teams-create" class="form-horizontal card">
<form action="/org/{{.Org.Name}}/teams/new" method="post" id="org-teams-create" class="form-horizontal card">
{{.CsrfTokenHtml}}
<h3>Create new team</h3>
<div class="form-group">
{{template "base/alert" .}}
<div class="form-group{{if .Err_TeamName}} has-error has-feedback{{end}}">
<label class="col-md-2 control-label">Team Name<strong class="text-danger">*</strong></label>
<div class="col-md-8">
<input name="team" type="text" class="form-control" placeholder="Type your team name" value="" required="required">
<input name="name" type="text" class="form-control" placeholder="Type your team name" value="{{.name}}" required="required">
<span class="help-block">You'll use this name to mention this team in conversations.</span>
</div>
</div>
<div class="form-group">
<div class="form-group{{if .Err_Description}} has-error has-feedback{{end}}">
<label class="col-md-2 control-label">Description</label>
<div class="col-md-8">
<input name="desc" type="text" class="form-control" placeholder="Type your team description (optional)" value="">
<input name="desc" type="text" class="form-control" placeholder="Type your team description (optional)" value="{{.desc}}">
</div>
</div>
<div class="form-group">
<div class="form-group{{if .Err_Permission}} has-error has-feedback{{end}}">
<label class="col-md-2 control-label">Permission</label>
<div class="col-md-8">
<div class="radio">
<label>
<input type="radio" name="permission" value="pull" checked="">
<strong>Read & Clone</strong>
<input type="radio" name="permission" value="read" checked="">
<strong>Read Access</strong>
</label>
<p>This team will be able to view and clone its repositories.</p>
</div>
<div class="radio">
<label>
<input type="radio" name="permission" value="push">
<strong>Push, Read & Clone</strong>
<input type="radio" name="permission" value="write">
<strong>Write Access</strong>
</label>
<p>This team will be able to read its repositories, as well as push to them.</p>
</div>
<div class="radio">
<label>
<input type="radio" name="permission" value="admin">
<strong>Collaboration, Push, Read & Clone</strong>
<strong>Admin Access</strong>
</label>
<p>This team will be able to push/pull to its repositories, as well as add other collaborators to them.</p>
</div>

View file

@ -18,6 +18,7 @@
</div>
</div>
</div>
<div id="body" class="container">
<div id="org">
<div id="org-teams">