Finish new organization members and invitation page
This commit is contained in:
		
							parent
							
								
									3e32b14ad4
								
							
						
					
					
						commit
						36b4c57ff1
					
				| @ -11,14 +11,17 @@ github.com/codegangsta/cli = | ||||
| github.com/go-sql-driver/mysql =  | ||||
| github.com/go-xorm/core =  | ||||
| github.com/go-xorm/xorm =  | ||||
| github.com/gogits/cache =  | ||||
| github.com/gogits/gfm =  | ||||
| github.com/gogits/git =  | ||||
| github.com/gogits/oauth2 =  | ||||
| github.com/juju2013/goldap =  | ||||
| github.com/lib/pq =  | ||||
| github.com/macaron-contrib/cache =  | ||||
| github.com/macaron-contrib/captcha =  | ||||
| github.com/macaron-contrib/csrf =  | ||||
| github.com/macaron-contrib/i18n =  | ||||
| github.com/macaron-contrib/session =  | ||||
| github.com/macaron-contrib/toolbox =  | ||||
| github.com/nfnt/resize =  | ||||
| 
 | ||||
| [res] | ||||
|  | ||||
| @ -232,6 +232,7 @@ func runWeb(*cli.Context) { | ||||
| 		m.Group("/:org", func(r *macaron.Router) { | ||||
| 			r.Get("/dashboard", user.Dashboard) | ||||
| 			r.Get("/members", org.Members) | ||||
| 			r.Get("/members/action/:action", org.MembersAction) | ||||
| 
 | ||||
| 			r.Get("/teams", org.Teams) | ||||
| 			r.Get("/teams/:team", org.SingleTeam) | ||||
| @ -248,6 +249,10 @@ func runWeb(*cli.Context) { | ||||
| 				r.Route("/delete", "GET,POST", org.SettingsDelete) | ||||
| 			}) | ||||
| 		}, middleware.OrgAssignment(true, true, true)) | ||||
| 
 | ||||
| 		m.Group("/:org", func(r *macaron.Router) { | ||||
| 			r.Route("/invitations/new", "GET,POST", org.Invitation) | ||||
| 		}, middleware.OrgAssignment(true, false, false, true)) | ||||
| 	}, reqSignIn) | ||||
| 
 | ||||
| 	// Repository routers.
 | ||||
|  | ||||
| @ -254,5 +254,5 @@ DRIVER = | ||||
| CONN =  | ||||
| 
 | ||||
| [i18n] | ||||
| LANGS = en-US,zh-CN | ||||
| NAMES = English,简体中文 | ||||
| LANGS = en-US,zh-CN,de-DE | ||||
| NAMES = English,简体中文,Deutsch | ||||
|  | ||||
| @ -250,6 +250,17 @@ settings.delete_account = Delete This Organization | ||||
| settings.delete_prompt = The operation will delete this organization permanently, and <strong>CANNOT</strong> be undo! | ||||
| settings.confirm_delete_account = Confirm Deletion | ||||
| 
 | ||||
| members.public = Public | ||||
| members.public_helper = make private | ||||
| members.private = Private | ||||
| members.private_helper = make public | ||||
| members.owner = Owner | ||||
| members.member = Member | ||||
| members.conceal = Conceal | ||||
| members.remove = Remove | ||||
| members.invite_desc = Start typing a username to invite a new member to %s: | ||||
| members.invite_now = Invite Now | ||||
| 
 | ||||
| [action] | ||||
| create_repo = created repository <a href="/%s">%s</a> | ||||
| commit_repo = pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a> | ||||
|  | ||||
| @ -250,6 +250,17 @@ settings.delete_account = 删除当前组织 | ||||
| settings.delete_prompt = 删除操作会永久清除该组织的信息,并且 <strong>不可恢复</strong>! | ||||
| settings.confirm_delete_account = 确认删除组织 | ||||
| 
 | ||||
| members.public = 公开成员 | ||||
| members.public_helper = 设为私有 | ||||
| members.private = 私有成员 | ||||
| members.private_helper = 设为公开 | ||||
| members.owner = 管理员 | ||||
| members.member = 普通成员 | ||||
| members.conceal = 隐藏身份 | ||||
| members.remove = 移除成员 | ||||
| members.invite_desc = 请输入被邀请到组织 %s 的用户名称: | ||||
| members.invite_now = 立即邀请 | ||||
| 
 | ||||
| [action] | ||||
| create_repo = 创建了仓库 <a href="/%s">%s</a> | ||||
| commit_repo = 推送了 <a href="/%s/src/%s">%s</a> 分支的代码到 <a href="/%s">%s</a> | ||||
|  | ||||
							
								
								
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							| @ -17,7 +17,7 @@ import ( | ||||
| 	"github.com/gogits/gogs/modules/setting" | ||||
| ) | ||||
| 
 | ||||
| const APP_VER = "0.4.7.0814 Alpha" | ||||
| const APP_VER = "0.4.7.0815 Alpha" | ||||
| 
 | ||||
| func init() { | ||||
| 	runtime.GOMAXPROCS(runtime.NumCPU()) | ||||
|  | ||||
| @ -59,6 +59,16 @@ func (org *User) GetMembers() error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // AddMember adds new member to organization.
 | ||||
| func (org *User) AddMember(uid int64) error { | ||||
| 	return AddOrgUser(org.Id, uid) | ||||
| } | ||||
| 
 | ||||
| // RemoveMember removes member from organization.
 | ||||
| func (org *User) RemoveMember(uid int64) error { | ||||
| 	return RemoveOrgUser(org.Id, uid) | ||||
| } | ||||
| 
 | ||||
| // CreateOrganization creates record of a new organization.
 | ||||
| func CreateOrganization(org, owner *User) (*User, error) { | ||||
| 	if !IsLegalName(org.Name) { | ||||
| @ -241,8 +251,7 @@ func NewTeam(t *Team) error { | ||||
| 	} | ||||
| 
 | ||||
| 	// 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 { | ||||
| 	if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams + 1 WHERE id = ?", t.OrgId); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| @ -270,8 +279,8 @@ func UpdateTeam(t *Team) error { | ||||
| // OrgUser represents an organization-user relation.
 | ||||
| type OrgUser struct { | ||||
| 	Id       int64 | ||||
| 	Uid      int64 `xorm:"INDEX"` | ||||
| 	OrgId    int64 `xorm:"INDEX"` | ||||
| 	Uid      int64 `xorm:"INDEX UNIQUE(s)"` | ||||
| 	OrgId    int64 `xorm:"INDEX UNIQUE(s)"` | ||||
| 	IsPublic bool | ||||
| 	IsOwner  bool | ||||
| 	NumTeam  int | ||||
| @ -289,6 +298,12 @@ func IsOrganizationMember(orgId, uid int64) bool { | ||||
| 	return has | ||||
| } | ||||
| 
 | ||||
| // IsPublicMembership returns ture if given user public his/her membership.
 | ||||
| func IsPublicMembership(orgId, uid int64) bool { | ||||
| 	has, _ := x.Where("uid=?", uid).And("org_id=?", orgId).And("is_public=?", true).Get(new(OrgUser)) | ||||
| 	return has | ||||
| } | ||||
| 
 | ||||
| // GetOrgUsersByUserId returns all organization-user relations by user ID.
 | ||||
| func GetOrgUsersByUserId(uid int64) ([]*OrgUser, error) { | ||||
| 	ous := make([]*OrgUser, 0, 10) | ||||
| @ -303,6 +318,77 @@ func GetOrgUsersByOrgId(orgId int64) ([]*OrgUser, error) { | ||||
| 	return ous, err | ||||
| } | ||||
| 
 | ||||
| // ChangeOrgUserStatus changes public or private membership status.
 | ||||
| func ChangeOrgUserStatus(orgId, uid int64, public bool) error { | ||||
| 	ou := new(OrgUser) | ||||
| 	has, err := x.Where("uid=?", uid).And("org_id=?", orgId).Get(ou) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if !has { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	ou.IsPublic = public | ||||
| 	_, err = x.Id(ou.Id).AllCols().Update(ou) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // AddOrgUser adds new user to given organization.
 | ||||
| func AddOrgUser(orgId, uid int64) error { | ||||
| 	if IsOrganizationMember(orgId, uid) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	ou := &OrgUser{ | ||||
| 		Uid:   uid, | ||||
| 		OrgId: orgId, | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err := sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err := sess.Insert(ou); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgId); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // RemoveOrgUser removes user from given organization.
 | ||||
| func RemoveOrgUser(orgId, uid int64) error { | ||||
| 	ou := new(OrgUser) | ||||
| 
 | ||||
| 	has, err := x.Where("uid=?", uid).And("org_id=?", orgId).Get(ou) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if !has { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err := sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err := sess.Id(ou.Id).Delete(ou); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members - 1 WHERE id = ?", orgId); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // ___________                    ____ ___
 | ||||
| // \__    ___/___ _____    _____ |    |   \______ ___________
 | ||||
| //   |    |_/ __ \\__  \  /     \|    |   /  ___// __ \_  __ \
 | ||||
|  | ||||
| @ -128,6 +128,16 @@ func (u *User) IsOrganization() bool { | ||||
| 	return u.Type == ORGANIZATION | ||||
| } | ||||
| 
 | ||||
| // IsUserOrgOwner returns true if user is in the owner team of given organization.
 | ||||
| func (u *User) IsUserOrgOwner(orgId int64) bool { | ||||
| 	return IsOrganizationOwner(orgId, u.Id) | ||||
| } | ||||
| 
 | ||||
| // IsPublicMember returns true if user public his/her membership in give organization.
 | ||||
| func (u *User) IsPublicMember(orgId int64) bool { | ||||
| 	return IsPublicMembership(orgId, u.Id) | ||||
| } | ||||
| 
 | ||||
| // GetOrganizationCount returns count of membership of organization of user.
 | ||||
| func (u *User) GetOrganizationCount() (int64, error) { | ||||
| 	return x.Where("uid=?", u.Id).Count(new(OrgUser)) | ||||
|  | ||||
| @ -68,7 +68,9 @@ type Context struct { | ||||
| 	Org struct { | ||||
| 		IsOwner      bool | ||||
| 		IsMember     bool | ||||
| 		IsAdminTeam  bool // In owner team or team that has admin permission level.
 | ||||
| 		Organization *models.User | ||||
| 		OrgLink      string | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -181,7 +183,6 @@ func Contexter() macaron.Handler { | ||||
| 			Flash:   f, | ||||
| 			Session: sess, | ||||
| 		} | ||||
| 
 | ||||
| 		// Compute current URL for real-time change language.
 | ||||
| 		link := ctx.Req.RequestURI | ||||
| 		i := strings.Index(link, "?") | ||||
|  | ||||
| @ -13,8 +13,9 @@ import ( | ||||
| func OrgAssignment(redirect bool, args ...bool) macaron.Handler { | ||||
| 	return func(ctx *Context) { | ||||
| 		var ( | ||||
| 			requireMember bool | ||||
| 			requireOwner  bool | ||||
| 			requireMember    bool | ||||
| 			requireOwner     bool | ||||
| 			requireAdminTeam bool | ||||
| 		) | ||||
| 		if len(args) >= 1 { | ||||
| 			requireMember = args[0] | ||||
| @ -22,6 +23,9 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler { | ||||
| 		if len(args) >= 2 { | ||||
| 			requireOwner = args[1] | ||||
| 		} | ||||
| 		if len(args) >= 3 { | ||||
| 			requireAdminTeam = args[2] | ||||
| 		} | ||||
| 
 | ||||
| 		orgName := ctx.Params(":org") | ||||
| 
 | ||||
| @ -43,13 +47,24 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler { | ||||
| 			ctx.Org.IsOwner = ctx.Org.Organization.IsOrgOwner(ctx.User.Id) | ||||
| 			if ctx.Org.IsOwner { | ||||
| 				ctx.Org.IsMember = true | ||||
| 				ctx.Org.IsAdminTeam = true | ||||
| 			} else { | ||||
| 				ctx.Org.IsMember = ctx.Org.Organization.IsOrgMember(ctx.User.Id) | ||||
| 				if ctx.Org.Organization.IsOrgMember(ctx.User.Id) { | ||||
| 					ctx.Org.IsMember = true | ||||
| 					// TODO: ctx.Org.IsAdminTeam
 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if (requireMember && !ctx.Org.IsMember) || (requireOwner && !ctx.Org.IsOwner) { | ||||
| 		if (requireMember && !ctx.Org.IsMember) || | ||||
| 			(requireOwner && !ctx.Org.IsOwner) || | ||||
| 			(requireAdminTeam && !ctx.Org.IsAdminTeam) { | ||||
| 			ctx.Handle(404, "OrgAssignment", err) | ||||
| 			return | ||||
| 		} | ||||
| 		ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam | ||||
| 		ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner | ||||
| 
 | ||||
| 		ctx.Org.OrgLink = "/org/" + ctx.Org.Organization.Name | ||||
| 		ctx.Data["OrgLink"] = ctx.Org.OrgLink | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -146,6 +146,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | ||||
| 		} | ||||
| 		ctx.Repo.GitRepo = gitRepo | ||||
| 		ctx.Repo.RepoLink = "/" + u.Name + "/" + repo.Name | ||||
| 		ctx.Data["RepoLink"] = ctx.Repo.RepoLink | ||||
| 
 | ||||
| 		tags, err := ctx.Repo.GitRepo.GetTags() | ||||
| 		if err != nil { | ||||
| @ -157,7 +158,6 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | ||||
| 		ctx.Data["Title"] = u.Name + "/" + repo.Name | ||||
| 		ctx.Data["Repository"] = repo | ||||
| 		ctx.Data["Owner"] = ctx.Repo.Repository.Owner | ||||
| 		ctx.Data["RepoLink"] = ctx.Repo.RepoLink | ||||
| 		ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner | ||||
| 		ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner | ||||
| 
 | ||||
|  | ||||
| @ -851,6 +851,7 @@ The dashboard page style | ||||
|   margin-left: 1em; | ||||
| } | ||||
| #dashboard-news .push-news .news-content li img { | ||||
|   vertical-align: inherit; | ||||
|   margin-bottom: -2px; | ||||
| } | ||||
| /* | ||||
| @ -1691,6 +1692,30 @@ textarea#issue-add-content { | ||||
| #org-home-header { | ||||
|   min-height: 100px; | ||||
| } | ||||
| #org-header { | ||||
|   height: 48px; | ||||
| } | ||||
| #org-header .org-name { | ||||
|   padding-left: 10px; | ||||
|   font-size: 1.4em; | ||||
|   height: 50px; | ||||
|   line-height: 50px; | ||||
|   margin-bottom: 0; | ||||
| } | ||||
| #org-header > div > .menu-line > li.right > a { | ||||
|   font-size: 1.2em; | ||||
|   color: #444444; | ||||
| } | ||||
| #org-header > div > .menu-line > li.right > a:hover { | ||||
|   background-color: transparent; | ||||
|   color: #d9453d; | ||||
| } | ||||
| #org-header > div > .menu-line > li.right > a .octicon { | ||||
|   margin-right: 6px; | ||||
| } | ||||
| #org-header > div > .menu-line > li.right .current { | ||||
|   border-bottom: 2px solid #D26911; | ||||
| } | ||||
| #org-home-header-info { | ||||
|   padding-top: 10px; | ||||
| } | ||||
| @ -1776,3 +1801,30 @@ textarea#issue-add-content { | ||||
|   margin-bottom: 0; | ||||
|   color: #777; | ||||
| } | ||||
| #org-member-toolbar { | ||||
|   padding: 10px 0; | ||||
| } | ||||
| #org-member-list .org-member-item { | ||||
|   height: 50px; | ||||
|   line-height: 50px; | ||||
|   border-top: 1px solid #eee; | ||||
|   padding: 15px 20px; | ||||
| } | ||||
| #org-member-list .org-member-item .member-name { | ||||
|   padding-left: 15px; | ||||
| } | ||||
| #org-member-list .org-member-item ul { | ||||
|   list-style: none; | ||||
| } | ||||
| #org-member-list .org-member-item ul li { | ||||
|   text-align: center; | ||||
|   display: inline-block; | ||||
| } | ||||
| .invite-box { | ||||
|   padding: 50px 0; | ||||
|   min-height: 130px; | ||||
|   text-align: center; | ||||
| } | ||||
| .invite-box input { | ||||
|   width: 250px; | ||||
| } | ||||
|  | ||||
| @ -251,6 +251,7 @@ The dashboard page style | ||||
|     .news-content li { | ||||
|       margin-left: 1em; | ||||
|       img { | ||||
|         vertical-align: inherit; | ||||
|         margin-bottom: -2px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
| @ -9,6 +9,38 @@ | ||||
| #org-home-header { | ||||
| 	min-height: 100px; | ||||
| } | ||||
| #org-header { | ||||
| 	height: 48px; | ||||
| 	.org-name { | ||||
| 		padding-left: 10px; | ||||
| 		font-size: 1.4em; | ||||
| 		height: 50px; | ||||
| 		line-height: 50px; | ||||
| 		margin-bottom: 0; | ||||
| 	} | ||||
| 	> div { | ||||
| 		> .menu-line { | ||||
| 			> li { | ||||
| 				&.right { | ||||
| 					> a { | ||||
| 						font-size: 1.2em; | ||||
| 						color: @dashboardHeaderLinkColor; | ||||
| 						&:hover { | ||||
| 							background-color: transparent; | ||||
| 							color: @dashboardHeaderLinkHoverColor; | ||||
| 						} | ||||
| 						.octicon { | ||||
| 							margin-right: 6px; | ||||
| 						} | ||||
| 					} | ||||
| 					.current { | ||||
| 						border-bottom: 2px solid #D26911; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| #org-home-header-info { | ||||
| 	padding-top: 10px; | ||||
| 	h2 { | ||||
| @ -93,4 +125,33 @@ | ||||
| 	margin-top: 0; | ||||
| 	margin-bottom: 0; | ||||
| 	color: #777; | ||||
| } | ||||
| #org-member-toolbar { | ||||
| 	padding: 10px 0; | ||||
| } | ||||
| #org-member-list { | ||||
| 	.org-member-item { | ||||
| 		height: 50px; | ||||
| 		line-height: 50px; | ||||
| 		border-top: 1px solid #eee; | ||||
| 		padding: 15px 20px; | ||||
| 		.member-name { | ||||
| 			padding-left: 15px; | ||||
| 		} | ||||
| 		ul { | ||||
| 			list-style: none; | ||||
| 			li { | ||||
| 				text-align: center; | ||||
| 				display: inline-block; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| .invite-box { | ||||
| 	padding: 50px 0; | ||||
| 	min-height: 130px; | ||||
| 	text-align: center; | ||||
| 	input { | ||||
| 		width: 250px; | ||||
| 	} | ||||
| } | ||||
| @ -1,8 +1,8 @@ | ||||
| @import "var"; | ||||
| 
 | ||||
| .label { | ||||
|   padding: 2px 6px; | ||||
|   color: @labelFontColor; | ||||
|     padding: 2px 6px; | ||||
|     color: @labelFontColor; | ||||
| } | ||||
| 
 | ||||
| .label-red { | ||||
| @ -30,7 +30,7 @@ | ||||
| } | ||||
| 
 | ||||
| .label-radius{ | ||||
|   border-radius: .2em; | ||||
|     border-radius: .2em; | ||||
| } | ||||
| 
 | ||||
| .label-link{ | ||||
|  | ||||
| @ -5,10 +5,101 @@ | ||||
| package org | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/Unknwon/com" | ||||
| 
 | ||||
| 	"github.com/gogits/gogs/models" | ||||
| 	"github.com/gogits/gogs/modules/base" | ||||
| 	"github.com/gogits/gogs/modules/log" | ||||
| 	"github.com/gogits/gogs/modules/middleware" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	MEMBERS base.TplName = "org/members" | ||||
| 	INVITE  base.TplName = "org/invite" | ||||
| ) | ||||
| 
 | ||||
| func Members(ctx *middleware.Context) { | ||||
| 	ctx.Data["Title"] = "Organization " + ctx.Params(":org") + " Members" | ||||
| 	ctx.HTML(200, "org/members") | ||||
| 	org := ctx.Org.Organization | ||||
| 	ctx.Data["Title"] = org.Name | ||||
| 	ctx.Data["PageIsOrgMembers"] = true | ||||
| 
 | ||||
| 	if err := org.GetMembers(); err != nil { | ||||
| 		ctx.Handle(500, "GetMembers", err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Data["Members"] = org.Members | ||||
| 
 | ||||
| 	ctx.HTML(200, MEMBERS) | ||||
| } | ||||
| 
 | ||||
| func MembersAction(ctx *middleware.Context) { | ||||
| 	uid := com.StrTo(ctx.Query("uid")).MustInt64() | ||||
| 	if uid == 0 { | ||||
| 		ctx.Redirect(ctx.Org.OrgLink + "/members") | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	org := ctx.Org.Organization | ||||
| 	var err error | ||||
| 	switch ctx.Params(":action") { | ||||
| 	case "private": | ||||
| 		if ctx.User.Id != uid && !ctx.Org.IsOwner { | ||||
| 			ctx.Error(404) | ||||
| 			return | ||||
| 		} | ||||
| 		err = models.ChangeOrgUserStatus(org.Id, uid, false) | ||||
| 	case "public": | ||||
| 		if ctx.User.Id != uid { | ||||
| 			ctx.Error(404) | ||||
| 			return | ||||
| 		} | ||||
| 		err = models.ChangeOrgUserStatus(org.Id, uid, true) | ||||
| 	case "remove": | ||||
| 		if !ctx.Org.IsOwner { | ||||
| 			ctx.Error(404) | ||||
| 			return | ||||
| 		} | ||||
| 		err = org.RemoveMember(uid) | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "Action(%s): %v", ctx.Params(":action"), err) | ||||
| 		ctx.JSON(200, map[string]interface{}{ | ||||
| 			"ok":  false, | ||||
| 			"err": err.Error(), | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Redirect(ctx.Org.OrgLink + "/members") | ||||
| } | ||||
| 
 | ||||
| func Invitation(ctx *middleware.Context) { | ||||
| 	org := ctx.Org.Organization | ||||
| 	ctx.Data["Title"] = org.Name | ||||
| 	ctx.Data["PageIsOrgMembers"] = true | ||||
| 
 | ||||
| 	if ctx.Req.Method == "POST" { | ||||
| 		uname := ctx.Query("uname") | ||||
| 		u, err := models.GetUserByName(uname) | ||||
| 		if err != nil { | ||||
| 			if err == models.ErrUserNotExist { | ||||
| 				ctx.Flash.Error(ctx.Tr("form.user_not_exist")) | ||||
| 				ctx.Redirect(ctx.Org.OrgLink + "/invitations/new") | ||||
| 			} else { | ||||
| 				ctx.Handle(500, " GetUserByName", err) | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		if err = org.AddMember(u.Id); err != nil { | ||||
| 			ctx.Handle(500, " AddMember", err) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		log.Trace("New member added(%s): %s", org.Name, u.Name) | ||||
| 		ctx.Redirect(ctx.Org.OrgLink + "/members") | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.HTML(200, INVITE) | ||||
| } | ||||
|  | ||||
| @ -227,7 +227,7 @@ func Action(ctx *middleware.Context) { | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "repo.Action(%s): %v", ctx.Params(":action"), err) | ||||
| 		log.Error(4, "Action(%s): %v", ctx.Params(":action"), err) | ||||
| 		ctx.JSON(200, map[string]interface{}{ | ||||
| 			"ok":  false, | ||||
| 			"err": err.Error(), | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| 0.4.7.0814 Alpha | ||||
| 0.4.7.0815 Alpha | ||||
							
								
								
									
										16
									
								
								templates/org/header.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								templates/org/header.tmpl
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| <div class="org-header" id="org-header"> | ||||
| 	<div class="container"> | ||||
| 		<a class="text-black left" href="/org/{{.Org.LowerName}}"> | ||||
| 			<img class="avatar-48 left" src="{{.Org.AvatarLink}}?s=100"> | ||||
| 			<span class="org-name">{{.Org.FullName}}</span> | ||||
| 		</a> | ||||
| 		<ul class="menu menu-line container"> | ||||
| 			<li class="right"> | ||||
| 				<a {{if .PageIsOrgTeams}}class="current"{{end}} href="{{.OrgLink}}/teams"><i class="octicon octicon-jersey"></i> {{.i18n.Tr "org.teams"}} <span class="label label-gray label-radius">{{.Org.NumTeams}}</span></a> | ||||
| 			</li> | ||||
| 			<li class="right"> | ||||
| 				<a {{if .PageIsOrgMembers}}class="current"{{end}} href="{{.OrgLink}}/members"><i class="octicon octicon-organization"></i> {{.i18n.Tr "org.people"}} <span class="label label-gray label-radius">{{.Org.NumMembers}}</span></a> | ||||
| 			</li> | ||||
| 		</ul> | ||||
| 	</div> | ||||
| </div> | ||||
| @ -17,7 +17,9 @@ | ||||
| <div class="container"> | ||||
|     <div id="org-home-repo-list" class="left grid-2-3"> | ||||
|         <div class="clear"> | ||||
|         	{{if .IsAdminTeam}} | ||||
|             <a class="btn btn-green btn-large btn-link btn-radius right" href="/repo/create?org={{.Org.Id}}"><i class="octicon octicon-repo-create"></i> {{.i18n.Tr "new_repo"}}</a> | ||||
|         	{{end}} | ||||
|         </div> | ||||
|         <div id="org-repo-list"> | ||||
| 			{{range .Repos}} | ||||
| @ -45,9 +47,11 @@ | ||||
| 	    			<a href="/{{.Name}}"><img src="{{.AvatarLink}}"></a> | ||||
| 	    			{{end}} | ||||
| 	    		</div> | ||||
| 	    		{{if .IsAdminTeam}} | ||||
| 	    		<div class="panel-footer"> | ||||
| 	    			<a class="btn btn-medium btn-blue btn-link btn-radius" href="">{{.i18n.Tr "org.invite_someone"}}</a> | ||||
| 	    			<a class="btn btn-medium btn-blue btn-link btn-radius" href="/org/{{.Org.LowerName}}/invitations/new">{{.i18n.Tr "org.invite_someone"}}</a> | ||||
| 	    		</div> | ||||
| 	    		{{end}} | ||||
| 	    	</div> | ||||
| 	    	<br> | ||||
| 	    	<div class="panel panel-radius"> | ||||
| @ -65,9 +69,12 @@ | ||||
| 		    			{{end}} | ||||
| 	    			</ul> | ||||
| 	    		</div> | ||||
| 	    		{{if .IsOrganizationOwner}} | ||||
| 	    		<div class="panel-footer"> | ||||
| 	    			<a class="btn btn-medium btn-blue btn-link btn-radius" href="/org/{{$.Org.LowerName}}/teams/new">{{.i18n.Tr "org.create_new_team"}}</a> | ||||
| 	    		</div> | ||||
| 
 | ||||
| 	    		{{end}} | ||||
| 	    	</div> | ||||
|     	</div> | ||||
|     </div> | ||||
|  | ||||
							
								
								
									
										15
									
								
								templates/org/invite.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								templates/org/invite.tmpl
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| {{template "ng/base/head" .}} | ||||
| {{template "ng/base/header" .}} | ||||
| {{template "org/header" .}} | ||||
| <div class="container"> | ||||
| 	<div class="invite-box"> | ||||
|     	{{template "ng/base/alert" .}} | ||||
| 		<h3>{{.i18n.Tr "org.members.invite_desc" .Org.FullName}}</h3> | ||||
| 		<form action="{{.OrgLink}}/invitations/new" method="post"> | ||||
| 			{{.CsrfTokenHtml}} | ||||
| 			<input class="ipt ipt-large ipt-radius" name="uname" required> | ||||
| 			<button class="btn btn-blue btn-large btn-radius">{{.i18n.Tr "org.members.invite_now"}}</button> | ||||
| 		</form> | ||||
| 	</div> | ||||
| </div> | ||||
| {{template "ng/base/footer" .}} | ||||
| @ -1,56 +1,43 @@ | ||||
| {{template "base/head" .}} | ||||
| {{template "base/navbar" .}} | ||||
| <div id="body-nav" class="org-nav org-nav-auto"> | ||||
|     <div class="container clearfix"> | ||||
|         <div id="org-nav-wrapper"> | ||||
|             <ul class="nav nav-pills pull-right"> | ||||
|                 <li class="active"><a href="#"><i class="fa fa-users"></i>Members | ||||
|                     <span class="label label-default">5</span></a> | ||||
|                 </li> | ||||
|                 <li><a href="#"><i class="fa fa-tags"></i>Teams | ||||
|                     <span class="label label-default">2</span></a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|             <img class="pull-left org-small-logo" src="https://avatars3.githubusercontent.com/u/6656686?s=140" alt="" width="60"/> | ||||
|             <div id="org-nav-info"> | ||||
|                 <h2 class="org-name">Organization Name</h2> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|     </div> | ||||
| {{template "ng/base/head" .}} | ||||
| {{template "ng/base/header" .}} | ||||
| {{template "org/header" .}} | ||||
| <div class="container"> | ||||
| 	{{template "ng/base/alert" .}} | ||||
| 	<div class="clear" id="org-member-toolbar"> | ||||
| 		{{if .IsAdminTeam}} | ||||
|         <a class="btn btn-green btn-large btn-link btn-radius right" href="{{.OrgLink}}/invitations/new"><i class="octicon octicon-repo-create"></i> {{.i18n.Tr "org.invite_someone"}}</a> | ||||
| 		{{end}} | ||||
| 	</div> | ||||
| 	<div id="org-member-list"> | ||||
| 		{{range .Members}} | ||||
| 		<div class="org-member-item"> | ||||
| 			<img class="avatar-48 left" src="{{.AvatarLink}}?s=100"> | ||||
| 			<a class="text-black" href="/{{.Name}}"><span class="member-name"><strong>{{.FullName}}</strong>({{.Name}})</span></a> | ||||
| 			<ul class="grid-6-12 right"> | ||||
| 				<li class="grid-1-3"> | ||||
| 				{{ $isPublic := .IsPublicMember $.Org.Id}} | ||||
| 				{{if $isPublic}} | ||||
| 					{{$.i18n.Tr "org.members.public"}} | ||||
| 					{{if eq $.SignedUser.Id .Id}}(<a href="{{$.OrgLink}}/members/action/private?uid={{.Id}}">{{$.i18n.Tr "org.members.public_helper"}}</a>){{end}} | ||||
| 				{{else}} | ||||
| 					{{$.i18n.Tr "org.members.private"}} | ||||
| 					{{if eq $.SignedUser.Id .Id}}(<a href="{{$.OrgLink}}/members/action/public?uid={{.Id}}">{{$.i18n.Tr "org.members.private_helper"}}</a>){{end}} | ||||
| 				{{end}} | ||||
| 				</li> | ||||
| 				<li class="grid-1-4">{{if .IsUserOrgOwner $.Org.Id}}<strong>{{$.i18n.Tr "org.members.owner"}}</strong>{{else}}{{$.i18n.Tr "org.members.member"}}{{end}}</li> | ||||
| 				{{if $.IsOrganizationOwner}} | ||||
| 				<li class="grid-1-6 right"> | ||||
| 					<a class="btn btn-red btn-link btn-radius" href="{{$.OrgLink}}/members/action/remove?uid={{.Id}}">{{$.i18n.Tr "org.members.remove"}}</a> | ||||
| 				</li> | ||||
| 				{{if $isPublic}} | ||||
| 				<li class="grid-1-6 right"> | ||||
| 					<a class="btn btn-blue btn-link btn-radius" href="{{$.OrgLink}}/members/action/private?uid={{.Id}}">{{$.i18n.Tr "org.members.conceal"}}</a> | ||||
| 				</li> | ||||
| 				{{end}} | ||||
| 				{{end}} | ||||
| 			</ul> | ||||
| 		</div> | ||||
| 		{{end}} | ||||
| 	</div> | ||||
| </div> | ||||
| <div id="body" class="container"> | ||||
|     <div id="org"> | ||||
|         <div id="org-members"> | ||||
|             <div class="member">  | ||||
|                 <div class="avatar col-md-1"> | ||||
|                     <img src="https://avatars3.githubusercontent.com/u/2142787?s=140" alt=""/> | ||||
|                 </div> | ||||
|                 <div class="name col-md-4"> | ||||
|                     <a href="#"><strong>fuxiaohei</strong><span class="nick">傅小黑</span></a> | ||||
|                 </div> | ||||
|                 <div class="role col-md-2 pull-right"> | ||||
|                     <strong>Member</strong> | ||||
|                 </div> | ||||
|                 <div class="status col-md-1 pull-right"> | ||||
|                     <strong>Public</strong> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="member">  | ||||
|                 <div class="avatar col-md-1"> | ||||
|                     <img src="https://avatars3.githubusercontent.com/u/2142787?s=140" alt=""/> | ||||
|                 </div> | ||||
|                 <div class="name col-md-4"> | ||||
|                     <a href="#"><strong>fuxiaohei</strong><span class="nick">傅小黑</span></a> | ||||
|                 </div> | ||||
|                 <div class="role col-md-2 pull-right"> | ||||
|                     <strong><i class="fa fa-user"></i>Owner</strong> | ||||
|                 </div> | ||||
|                 <div class="status col-md-1 pull-right"> | ||||
|                     <i class="fa fa-lock"></i>Private | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| {{template "base/footer" .}} | ||||
| {{template "ng/base/footer" .}} | ||||
| @ -1,130 +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="{{.Org.AvatarLink}}?s=28" alt="user-avatar" title="username"> | ||||
|                 {{.Org.Name}} | ||||
|             </button> | ||||
|         </div> | ||||
|         <ul class="nav nav-pills pull-right"> | ||||
|             <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"> | ||||
|             <li class="list-group-item active"><a href="#">Options</a></li> | ||||
|         </ul> | ||||
|     </div> | ||||
|     <div id="repo-setting-container" class="col-md-10"> | ||||
|         {{template "base/alert" .}} | ||||
|         <div class="panel panel-default"> | ||||
|             <div class="panel-heading"> | ||||
|                 Organization Options | ||||
|             </div> | ||||
| 
 | ||||
|             <div class="panel-body"> | ||||
|                 <form action="/org/{{.Org.Name}}/settings" method="post" class="form-horizontal"> | ||||
|                     {{.CsrfTokenHtml}} | ||||
|                     <input type="hidden" name="action" value="update"> | ||||
| 
 | ||||
|                     <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="display_name" value="{{.Org.FullName}}" title="" id="org-setting-name"/> | ||||
|                         </div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <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="{{.Org.Email}}" title="" id="org-email" type="email"/> | ||||
|                         </div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <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">{{.Org.Description}}</textarea> | ||||
|                         </div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <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="{{.Org.Website}}" id="org-site"/> | ||||
|                         </div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <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="location" value="{{.Org.Location}}" title="" id="org-location"/> | ||||
|                         </div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <div class="form-group"> | ||||
|                         <div class="col-md-9 col-md-offset-3"> | ||||
|                             <button class="btn btn-primary" type="submit">Save Options</button> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </form> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="panel panel-warning"> | ||||
|             <div class="panel-heading"> | ||||
|                 Danger Zone | ||||
|             </div> | ||||
|             <div class="panel-body"> | ||||
|                 <button type="button" class="btn btn-default pull-right" href="#delete-org-modal" data-toggle="modal"> | ||||
|                     Delete this organization | ||||
|                 </button> | ||||
|                 <dd> | ||||
|                 <dt>Delete this organization</dt> | ||||
|                 <dl>Once you delete this organization and all repositories in, there is no going back. Please be | ||||
|                     certain. | ||||
|                 </dl> | ||||
|                 </dd> | ||||
| 
 | ||||
|                 <div class="modal fade" id="delete-org-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" | ||||
|                      aria-hidden="true"> | ||||
|                     <div class="modal-dialog"> | ||||
|                         <form action="/org/{{.Org.Name}}/settings/delete" method="post" | ||||
|                               class="modal-content"> | ||||
|                             {{.CsrfTokenHtml}} | ||||
|                             <div class="modal-header"> | ||||
|                                 <button type="button" class="close" data-dismiss="modal" | ||||
|                                         aria-hidden="true">×</button> | ||||
|                                 <h4 class="modal-title" id="myModalLabel">Delete organization</h4> | ||||
|                             </div> | ||||
| 
 | ||||
|                             <div class="modal-body"> | ||||
|                                 <div class="form-group"> | ||||
|                                     <label>Make sure your are owner of this organization. Please enter your password.<strong class="text-danger">*</strong></label> | ||||
|                                     <input name="password" class="form-control" type="password" placeholder="Type your account password" required="required"> | ||||
|                                 </div> | ||||
|                             </div> | ||||
| 
 | ||||
|                             <div class="modal-footer"> | ||||
|                                 <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> | ||||
|                                 <button class="btn btn-danger btn-lg">I understand the consequences, delete this | ||||
|                                     organization | ||||
|                                 </button> | ||||
|                             </div> | ||||
|                         </form> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| {{template "base/footer" .}} | ||||
| @ -30,7 +30,7 @@ | ||||
|                             {{ $push := ActionContent2Commits .}} | ||||
|                             {{ $repoLink := .GetRepoLink}} | ||||
|                             {{range $push.Commits}} | ||||
|                             <li><img src="{{AvatarLink .AuthorEmail}}?s=16"> <a href="/{{$repoLink}}/commit/{{.Sha1}}">{{ShortSha .Sha1}}</a> {{.Message}}</li> | ||||
|                             <li><img class="avatar-16" src="{{AvatarLink .AuthorEmail}}?s=16"> <a href="/{{$repoLink}}/commit/{{.Sha1}}">{{ShortSha .Sha1}}</a> {{.Message}}</li> | ||||
|                             {{end}} | ||||
|                         </ul> | ||||
|                     </div> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user