]> source.dussan.org Git - gitea.git/commitdiff
Finish new organization members and invitation page
authorUnknwon <joe2010xtmf@163.com>
Fri, 15 Aug 2014 10:29:41 +0000 (18:29 +0800)
committerUnknwon <joe2010xtmf@163.com>
Fri, 15 Aug 2014 10:29:41 +0000 (18:29 +0800)
24 files changed:
.gopmfile
cmd/web.go
conf/app.ini
conf/locale/locale_en-US.ini
conf/locale/locale_zh-CN.ini
gogs.go
models/org.go
models/user.go
modules/middleware/context.go
modules/middleware/org.go
modules/middleware/repo.go
public/ng/css/gogs.css
public/ng/less/gogs/dashboard.less
public/ng/less/gogs/organization.less
public/ng/less/ui/label.less
routers/org/members.go
routers/repo/repo.go
templates/.VERSION
templates/org/header.tmpl [new file with mode: 0644]
templates/org/home.tmpl
templates/org/invite.tmpl [new file with mode: 0644]
templates/org/members.tmpl
templates/org/settings.tmpl [deleted file]
templates/user/dashboard/dashboard.tmpl

index 235a8a436f42a7039363026f44af863b7cd4568a..25006beb7badc4d97d1c591dfca53aef8330b4a5 100644 (file)
--- a/.gopmfile
+++ b/.gopmfile
@@ -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]
index 03704c64debae6e2eaef1bcacf14e546dd46c6ed..03e5e86066bdb63a2ae0054b8bed3332b71fa37d 100644 (file)
@@ -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.
index ac1c6a3ba99cb5b1656f21573997bdeb30c4b792..99ed628ec1cfa2d590282d5a84636a5b4e899504 100644 (file)
@@ -254,5 +254,5 @@ DRIVER =
 CONN = 
 
 [i18n]
-LANGS = en-US,zh-CN
-NAMES = English,简体中文
+LANGS = en-US,zh-CN,de-DE
+NAMES = English,简体中文,Deutsch
index d7b5c45a02a780c6fcbe87798c245bc66d6dd65d..d44dca081b443a0cab1a0a017d7185cff0ba900b 100644 (file)
@@ -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>
index 2499e121546312cd72657b603f8c5cf5c21ee190..b84aca3d9e8047c21b04fdb2f7e79e7bcc9bd596 100644 (file)
@@ -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>
diff --git a/gogs.go b/gogs.go
index 4a3e353cdf7c31f362ad6317b5ce31c2ea229f50..6a9ded37d50365fef337322eb27a5b420865cf90 100644 (file)
--- a/gogs.go
+++ b/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())
index edae828b54441a3b8d02cd9d420720b2749da49c..b40b313bc37faa3a215ee690eb08e14e86c82cad 100644 (file)
@@ -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()
+}
+
 // ___________                    ____ ___
 // \__    ___/___ _____    _____ |    |   \______ ___________
 //   |    |_/ __ \\__  \  /     \|    |   /  ___// __ \_  __ \
index f4526b51d17b664c7df69aac57a2f9f766fbea81..757c290b9c6c03a8397255b61545e3e72f22bba1 100644 (file)
@@ -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))
index aa1266d6498f555a568324a8051fd2d579a4dd2a..6ce0f6e1f790809a0525d7f1d320a66916d04755 100644 (file)
@@ -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, "?")
index 05316a518f947d4c7bd70e22de78204531f4075f..77e999a328bce54b5d4d76ff1fb219587e71c532 100644 (file)
@@ -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
        }
 }
index a028aab802e53f0fe52d40299982dbf59d9a25e1..3db1932af0be6458f76c4fe6452f511973aaf8f9 100644 (file)
@@ -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
 
index 93a5d2f8c91487fb201a7eba9deb6fee77dcf84d..e7a3a66f2613491ace34c36933d326dbae3d056f 100644 (file)
@@ -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;
+}
index f8838ae6fddc258382a2166adb1c12695d78d2ed..6bf1150ba73a0e37270385d9a7caaeb3a8ce643c 100644 (file)
@@ -251,6 +251,7 @@ The dashboard page style
     .news-content li {
       margin-left: 1em;
       img {
+        vertical-align: inherit;
         margin-bottom: -2px;
       }
     }
index ed8c05397ab98e99995f0c30ff1a0faff621d928..cbdec3132e82fe51ceb7dfe7a39dfbe4473c6031 100644 (file)
@@ -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 {
        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;
+       }
 }
\ No newline at end of file
index 1cf0a81d85b3ea335dedb9ba6c8962bff97b10ce..a2a8a67905797429815fe7678c47ab6c7a7effea 100644 (file)
@@ -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{
index ac278d4e6d22e7f651b5d38fb2e4fe5663cd5952..d98061765aea30cec691772c8684f3203b96f710 100644 (file)
 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)
 }
index 3450ea76f7085ac93daaa256453b54de1241890e..7356f50330d60e42ac3c8c8d37f3003db2f3aacb 100644 (file)
@@ -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(),
index f398e9011070b4ef9532e469c6015f43d929b2a0..906c01dc84b38f461651e515d66f7d018740dc72 100644 (file)
@@ -1 +1 @@
-0.4.7.0814 Alpha
\ No newline at end of file
+0.4.7.0815 Alpha
\ No newline at end of file
diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl
new file mode 100644 (file)
index 0000000..8566d0a
--- /dev/null
@@ -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>
\ No newline at end of file
index 205318b0e9488b47b05d9fbc93128eb550a1de3b..d96624010f23718f2fcf2545f2f6331100b325c8 100644 (file)
@@ -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}}
                                <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">
                                        {{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>
diff --git a/templates/org/invite.tmpl b/templates/org/invite.tmpl
new file mode 100644 (file)
index 0000000..400622f
--- /dev/null
@@ -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" .}}
\ No newline at end of file
index ba14cb4cc98a5b428016e22e8ec3d36105cc5496..bea4340ffb594b3294dea516ffdce9434e549a66 100644 (file)
@@ -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">&nbsp;
-                <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">&nbsp;
-                <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" .}}
\ No newline at end of file
diff --git a/templates/org/settings.tmpl b/templates/org/settings.tmpl
deleted file mode 100644 (file)
index fd0d6a1..0000000
+++ /dev/null
@@ -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">&times;</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" .}}
index c19c177e8355da6175c06c9ddb5cd3c069adff2a..d0789b96d64b11a26b8dfe8d28e552b2f8f67542 100644 (file)
@@ -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>