diff --git a/cmd/web.go b/cmd/web.go index dcfe95003..c49ba1054 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -235,7 +235,7 @@ func runWeb(*cli.Context) { r.Post("/:org/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost) r.Get("/:org/teams/:team/edit", org.EditTeam) - r.Get("/:org/team/:team", org.SingleTeam) + r.Get("/:org/teams/:team", org.SingleTeam) r.Get("/:org/settings", org.Settings) r.Post("/:org/settings", bindIgnErr(auth.OrgSettingForm{}), org.SettingsPost) @@ -264,7 +264,7 @@ func runWeb(*cli.Context) { }, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner) m.Group("/:username/:reponame", func(r *macaron.Router) { - // r.Get("/action/:action", repo.Action) + r.Get("/action/:action", repo.Action) m.Group("/issues", func(r *macaron.Router) { r.Get("/new", repo.CreateIssue) diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index d5adde92b..87329041c 100644 --- a/conf/locale/locale_en-US.ini +++ b/conf/locale/locale_en-US.ini @@ -179,6 +179,13 @@ migrate_type = Migration Type migrate_type_helper = This repository will be a Mirror migrate_repo = Migrate Repository +clone_helper = Need help cloning? Visit Help! +unwatch = Unwatch +watch = Watch +unstar = Unstar +star = Star +fork = Fork + settings = Settings settings.options = Options settings.collaboration = Collaboration @@ -221,6 +228,13 @@ org_name_holder = Organization Name org_name_helper = Great organization names are short and memorable. org_email_helper = Organization's Email receives all notifications and confirmations. create_org = Create Organization +repo_updated = Updated +people = People +invite_someone = Invite Someone +teams = Teams +lower_members = members +lower_repositories = repositories +create_new_team = Create New Team [action] create_repo = created repository %s diff --git a/conf/locale/locale_zh-CN.ini b/conf/locale/locale_zh-CN.ini index c847f7634..2c7bb715f 100644 --- a/conf/locale/locale_zh-CN.ini +++ b/conf/locale/locale_zh-CN.ini @@ -179,6 +179,13 @@ migrate_type = 迁移类型 migrate_type_helper = 本仓库将是 镜像 migrate_repo = 迁移仓库 +clone_helper = 不知道如何操作?访问 此处 查看帮助! +unwatch = 取消关注 +watch = 关注 +unstar = 取消点赞 +star = 点赞 +fork = 派生 + settings = 仓库设置 settings.options = 基本设置 settings.collaboration = 管理协作者 @@ -221,6 +228,13 @@ org_name_holder = 组织名称 org_name_helper = 伟大的组织都有一个简短而寓意深刻的名字。 org_email_helper = 组织的邮箱用于接收所有通知和确认邮件。 create_org = 创建组织 +repo_updated = 最后更新于 +people = 组织成员 +invite_someone = 邀请他人加入 +teams = 组织团队 +lower_members = 名成员 +lower_repositories = 个仓库 +create_new_team = 创建新的团队 [action] create_repo = 创建了仓库 %s diff --git a/gogs.go b/gogs.go index b8a4ec6cf..17179d032 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.4.7.0809 Alpha" +const APP_VER = "0.4.7.0810 Alpha" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/models.go b/models/models.go index af9529f40..bf2586063 100644 --- a/models/models.go +++ b/models/models.go @@ -32,8 +32,9 @@ var ( ) func init() { - tables = append(tables, new(User), new(PublicKey), new(Repository), new(Watch), - new(Action), new(Access), new(Issue), new(Comment), new(Oauth2), new(Follow), + tables = append(tables, new(User), new(PublicKey), + new(Repository), new(Watch), new(Star), new(Action), new(Access), + new(Issue), new(Comment), new(Oauth2), new(Follow), new(Mirror), new(Release), new(LoginSource), new(Webhook), new(IssueUser), new(Milestone), new(Label), new(HookTask), new(Team), new(OrgUser), new(TeamUser), new(UpdateTask), new(Attachment)) diff --git a/models/org.go b/models/org.go index 2efef534b..edae828b5 100644 --- a/models/org.go +++ b/models/org.go @@ -106,6 +106,7 @@ func CreateOrganization(org, owner *User) (*User, error) { // Create default owner team. t := &Team{ OrgId: org.Id, + LowerName: strings.ToLower(OWNER_TEAM), Name: OWNER_TEAM, Authorize: ORG_ADMIN, NumMembers: 1, diff --git a/models/repo.go b/models/repo.go index c2398fe7d..bec3cb212 100644 --- a/models/repo.go +++ b/models/repo.go @@ -137,6 +137,7 @@ type Repository struct { NumTags int `xorm:"-"` IsPrivate bool IsMirror bool + *Mirror `xorm:"-"` IsFork bool `xorm:"NOT NULL DEFAULT false"` IsBare bool IsGoget bool @@ -150,6 +151,11 @@ func (repo *Repository) GetOwner() (err error) { return err } +func (repo *Repository) GetMirror() (err error) { + repo.Mirror, err = GetMirror(repo.Id) + return err +} + // DescriptionHtml does special handles to description and return HTML string. func (repo *Repository) DescriptionHtml() template.HTML { sanitize := func(s string) string { @@ -953,21 +959,33 @@ type Watch struct { } // Watch or unwatch repository. -func WatchRepo(uid, rid int64, watch bool) (err error) { +func WatchRepo(uid, repoId int64, watch bool) (err error) { if watch { - if _, err = x.Insert(&Watch{RepoId: rid, UserId: uid}); err != nil { + if IsWatching(uid, repoId) { + return nil + } + if _, err = x.Insert(&Watch{RepoId: repoId, UserId: uid}); err != nil { return err } - _, err = x.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", rid) + _, err = x.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", repoId) } else { - if _, err = x.Delete(&Watch{0, uid, rid}); err != nil { + if !IsWatching(uid, repoId) { + return nil + } + if _, err = x.Delete(&Watch{0, uid, repoId}); err != nil { return err } - _, err = x.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", rid) + _, err = x.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repoId) } return err } +// IsWatching checks if user has watched given repository. +func IsWatching(uid, rid int64) bool { + has, _ := x.Get(&Watch{0, uid, rid}) + return has +} + // GetWatchers returns all watchers of given repository. func GetWatchers(rid int64) ([]*Watch, error) { watches := make([]*Watch, 0, 10) @@ -1003,9 +1021,37 @@ func NotifyWatchers(act *Action) error { return nil } -// IsWatching checks if user has watched given repository. -func IsWatching(uid, rid int64) bool { - has, _ := x.Get(&Watch{0, uid, rid}) +type Star struct { + Id int64 + Uid int64 `xorm:"UNIQUE(s)"` + RepoId int64 `xorm:"UNIQUE(s)"` +} + +// Star or unstar repository. +func StarRepo(uid, repoId int64, star bool) (err error) { + if star { + if IsStaring(uid, repoId) { + return nil + } + if _, err = x.Insert(&Star{Uid: uid, RepoId: repoId}); err != nil { + return err + } + _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repoId) + } else { + if !IsStaring(uid, repoId) { + return nil + } + if _, err = x.Delete(&Star{0, uid, repoId}); err != nil { + return err + } + _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repoId) + } + return err +} + +// IsStaring checks if user has starred given repository. +func IsStaring(uid, repoId int64) bool { + has, _ := x.Get(&Star{0, uid, repoId}) return has } diff --git a/modules/middleware/auth.go b/modules/middleware/auth.go index 5862787b3..37e3aec45 100644 --- a/modules/middleware/auth.go +++ b/modules/middleware/auth.go @@ -5,7 +5,6 @@ package middleware import ( - "fmt" "net/url" "strings" @@ -44,7 +43,6 @@ func Toggle(options *ToggleOptions) macaron.Handler { } if options.SignInRequire { - fmt.Println(ctx.User.IsActive, setting.Service.RegisterEmailConfirm) if !ctx.IsSigned { // Ignore watch repository operation. if strings.HasSuffix(ctx.Req.RequestURI, "watch") { diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index e1c5c68c7..929850d20 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -246,17 +246,17 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { } // repo is bare and display enable - if displayBare && ctx.Repo.Repository.IsBare { + if ctx.Repo.Repository.IsBare { log.Debug("Bare repository: %s", ctx.Repo.RepoLink) - ctx.HTML(200, "repo/bare") + if displayBare { + ctx.HTML(200, "repo/bare") + } return } if ctx.IsSigned { ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.Id, repo.Id) - } - if ctx.Repo.Repository.IsBare { - return + ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.Id, repo.Id) } ctx.Data["TagName"] = ctx.Repo.TagName diff --git a/public/ng/css/gogs.css b/public/ng/css/gogs.css index 9633ed279..e69e4b663 100644 --- a/public/ng/css/gogs.css +++ b/public/ng/css/gogs.css @@ -40,6 +40,11 @@ img.avatar-48 { height: 48px; vertical-align: middle; } +img.avatar-100 { + width: 100px; + height: 100px; + vertical-align: middle; +} #wrapper { padding: 0; margin: 0 0 -55px 0; @@ -226,6 +231,9 @@ img.avatar-48 { .text-grey { color: #999999; } +.text-black { + color: #444444; +} .markdown { background-color: white; font-size: 16px; @@ -974,6 +982,7 @@ The register and sign-in page style border-left: none; } #repo-clone-help { + clear: both; line-height: 48px; } #repo-clone-zip { @@ -1665,3 +1674,99 @@ textarea#issue-add-content { box-sizing: border-box; height: 120px; } +.org-header { + padding: 16px 0; + background-color: #FFF; + border-bottom: 1px solid #DDD; +} +.org-header img { + padding-right: 10px; +} +#org-home-header { + min-height: 100px; +} +#org-home-header-info { + padding-top: 10px; +} +#org-home-header-info h2 { + font-size: 30px; +} +#org-home-header-info ul { + list-style: none; +} +#org-home-header-info ul li { + float: left; + padding-right: 5px; +} +#org-home-repo-list { + padding: 10px 0; +} +#org-repo-list { + padding: 10px 0; +} +#org-repo-list .org-repo-item { + border-top: 1px solid #eee; + padding: 30px 20px; +} +#org-repo-list .org-repo-item .org-repo-status { + list-style: none; + color: #888; +} +#org-repo-list .org-repo-item .org-repo-status li { + float: left; + margin-right: 6px; +} +#org-repo-list .org-repo-item h2 { + margin-bottom: 5px; +} +#org-repo-list .org-repo-item .org-repo-description { + margin: 0; + font-size: 14px; + color: #666; +} +#org-repo-list .org-repo-item .org-repo-updated { + font-size: 12px; + display: block; + margin: 5px 0 0; + color: #808080; +} +.org-sidebar { + margin: -80px 0 0 20px; +} +.org-sidebar .panel-footer { + padding: .8em 1.2em; +} +#org-member-avatar-group { + padding: 15px; +} +#org-member-avatar-group img { + width: 59px; + height: 59px; + border-radius: 3px; +} +#org-home-team-list { + padding: 0 15px; +} +#org-home-team-list ul { + list-style: none; + padding-top: 10px; +} +#org-home-team-list ul li { + padding: 10px 0; + border-bottom: 1px solid #eee; +} +#org-home-team-list ul li:last-child { + border-bottom: 0; +} +.team-name { + display: block; + font-size: 14px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.team-meta { + margin-top: 0; + margin-bottom: 0; + color: #777; +} diff --git a/public/ng/css/ui.css b/public/ng/css/ui.css index e9e3d3ff8..a60e2c841 100644 --- a/public/ng/css/ui.css +++ b/public/ng/css/ui.css @@ -372,6 +372,10 @@ dt { font-size: 10.8px; padding: .4em .9em; } +.btn-medium { + font-size: 12px; + padding: .4em .9em; +} .btn-large { font-size: 14.4px; } diff --git a/public/ng/less/gogs.less b/public/ng/less/gogs.less index dd458af62..7a9484082 100644 --- a/public/ng/less/gogs.less +++ b/public/ng/less/gogs.less @@ -5,4 +5,5 @@ @import "gogs/sign"; @import "gogs/repository"; @import "gogs/settings"; -@import "gogs/issue"; \ No newline at end of file +@import "gogs/issue"; +@import "gogs/organization"; \ No newline at end of file diff --git a/public/ng/less/gogs/base.less b/public/ng/less/gogs/base.less index 96842f478..1174e8e7a 100644 --- a/public/ng/less/gogs/base.less +++ b/public/ng/less/gogs/base.less @@ -50,6 +50,11 @@ img.avatar-48{ height: 48px; vertical-align: middle; } +img.avatar-100{ + width: 100px; + height: 100px; + vertical-align: middle; +} #wrapper { padding: 0; margin: 0 0 -55px 0; @@ -242,4 +247,7 @@ clear: both; } .text-grey { color: #999999; +} +.text-black { + color: #444444; } \ No newline at end of file diff --git a/public/ng/less/gogs/organization.less b/public/ng/less/gogs/organization.less new file mode 100644 index 000000000..ed8c05397 --- /dev/null +++ b/public/ng/less/gogs/organization.less @@ -0,0 +1,96 @@ +.org-header { + padding: 16px 0; + background-color: #FFF; + border-bottom: 1px solid #DDD; + img { + padding-right: 10px; + } +} +#org-home-header { + min-height: 100px; +} +#org-home-header-info { + padding-top: 10px; + h2 { + font-size: 30px; + } + ul { + list-style: none; + li { + float: left; + padding-right: 5px; + } + } +} +#org-home-repo-list { + padding: 10px 0; +} +#org-repo-list { + padding: 10px 0; + .org-repo-item { + border-top: 1px solid #eee; + padding: 30px 20px; + .org-repo-status { + list-style: none; + color: #888; + li { + float: left; + margin-right: 6px; + } + } + h2 { + margin-bottom: 5px; + } + .org-repo-description { + margin: 0; + font-size: 14px; + color: #666; + } + .org-repo-updated { + font-size: 12px; + display: block; + margin: 5px 0 0; + color: #808080; + } + } +} +.org-sidebar { + margin: -80px 0 0 20px; + .panel-footer { + padding: .8em 1.2em; + } +} +#org-member-avatar-group { + padding: 15px; + img { + width: 59px; + height: 59px; + border-radius: 3px; + } +} +#org-home-team-list { + padding: 0 15px; + ul { + list-style: none; + padding-top: 10px; + li { + padding: 10px 0; + border-bottom: 1px solid #eee; + &:last-child { + border-bottom: 0; + } + } + } +} +.team-name { + display: block; + font-size: 14px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.team-meta { + margin-top: 0; + margin-bottom: 0; + color: #777; +} \ No newline at end of file diff --git a/public/ng/less/gogs/repository.less b/public/ng/less/gogs/repository.less index 2843fac2d..0aa19a04a 100644 --- a/public/ng/less/gogs/repository.less +++ b/public/ng/less/gogs/repository.less @@ -97,6 +97,7 @@ border-top-right-radius: .25em; border-left: none; } #repo-clone-help { + clear: both; line-height: 48px; } #repo-clone-zip { diff --git a/public/ng/less/ui/form.less b/public/ng/less/ui/form.less index 3f530dc39..80d948662 100644 --- a/public/ng/less/ui/form.less +++ b/public/ng/less/ui/form.less @@ -5,14 +5,16 @@ &:hover { } } - .btn-small { font-size: 0.9*@baseFontSize; padding: .4em .9em; } - +.btn-medium { + font-size: @baseFontSize; + padding: .4em .9em; +} .btn-large { - font-size: 1.2*@baseFontSize; + font-size: 1.2*@baseFontSize; } .btn-green { @@ -23,7 +25,6 @@ color: #FFF; } } - .btn-blue { background-color: @btnBlueColor; border: 1px solid @btnBlueColor; diff --git a/routers/org/org.go b/routers/org/org.go index 9b8a13e61..fed1fd5fb 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -19,34 +19,33 @@ const ( ) func Home(ctx *middleware.Context) { - ctx.Data["Title"] = "Organization " + ctx.Params(":org") + ctx.Data["Title"] = ctx.Params(":org") org, err := models.GetUserByName(ctx.Params(":org")) if err != nil { if err == models.ErrUserNotExist { - ctx.Handle(404, "org.Home(GetUserByName)", err) + ctx.Handle(404, "GetUserByName", err) } else { - ctx.Handle(500, "org.Home(GetUserByName)", err) + ctx.Handle(500, "GetUserByName", err) } return } ctx.Data["Org"] = org - ctx.Data["Repos"], err = models.GetRepositories(org.Id, - ctx.IsSigned && org.IsOrgMember(ctx.User.Id)) + ctx.Data["Repos"], err = models.GetRepositories(org.Id, ctx.IsSigned && org.IsOrgMember(ctx.User.Id)) if err != nil { - ctx.Handle(500, "org.Home(GetRepositories)", err) + ctx.Handle(500, "GetRepositories", err) return } if err = org.GetMembers(); err != nil { - ctx.Handle(500, "org.Home(GetMembers)", err) + ctx.Handle(500, "GetMembers", err) return } ctx.Data["Members"] = org.Members if err = org.GetTeams(); err != nil { - ctx.Handle(500, "org.Home(GetTeams)", err) + ctx.Handle(500, "GetTeams", err) return } ctx.Data["Teams"] = org.Teams diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 9b3aca4c3..3450ea76f 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -204,36 +204,42 @@ func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) { ctx.Handle(500, "MigratePost", err) } -// func Action(ctx *middleware.Context, params martini.Params) { -// var err error -// switch params["action"] { -// case "watch": -// err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true) -// case "unwatch": -// err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) -// case "desc": -// if !ctx.Repo.IsOwner { -// ctx.Error(404) -// return -// } - -// ctx.Repo.Repository.Description = ctx.Query("desc") -// ctx.Repo.Repository.Website = ctx.Query("site") -// err = models.UpdateRepository(ctx.Repo.Repository) -// } - -// if err != nil { -// log.Error("repo.Action(%s): %v", params["action"], err) -// ctx.JSON(200, map[string]interface{}{ -// "ok": false, -// "err": err.Error(), -// }) -// return -// } -// ctx.JSON(200, map[string]interface{}{ -// "ok": true, -// }) -// } +func Action(ctx *middleware.Context) { + var err error + switch ctx.Params(":action") { + case "watch": + err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true) + case "unwatch": + err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) + case "star": + err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, true) + case "unstar": + err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) + case "desc": + if !ctx.Repo.IsOwner { + ctx.Error(404) + return + } + + ctx.Repo.Repository.Description = ctx.Query("desc") + ctx.Repo.Repository.Website = ctx.Query("site") + err = models.UpdateRepository(ctx.Repo.Repository) + } + + if err != nil { + log.Error(4, "repo.Action(%s): %v", ctx.Params(":action"), err) + ctx.JSON(200, map[string]interface{}{ + "ok": false, + "err": err.Error(), + }) + return + } + ctx.Redirect(ctx.Repo.RepoLink) + return + ctx.JSON(200, map[string]interface{}{ + "ok": true, + }) +} func Download(ctx *middleware.Context) { ext := "." + ctx.Params(":ext") diff --git a/routers/user/home.go b/routers/user/home.go index 177fa38be..bd33318d3 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -73,6 +73,10 @@ func Dashboard(ctx *middleware.Context) { mirrors := make([]*models.Repository, 0, len(repos)/2) for _, repo := range repos { if repo.IsMirror { + if err = repo.GetMirror(); err != nil { + ctx.Handle(500, "GetMirror: "+repo.Name, err) + return + } mirrors = append(mirrors, repo) } } diff --git a/scripts/start.sh b/scripts/start.sh index 709a10265..dd9a461c4 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -12,4 +12,4 @@ USER=$(whoami) HOME=$(grep "^$USER:" /etc/passwd | cut -d: -f6) export USER HOME PATH -cd "$(dirname $0)" && exec ./gogs web +cd "$(pwd)" && exec ./gogs web diff --git a/templates/.VERSION b/templates/.VERSION index 760339a99..2575ec6bc 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.4.7.0809 Alpha \ No newline at end of file +0.4.7.0810 Alpha \ No newline at end of file diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index b6675d305..205318b0e 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -1,70 +1,75 @@ -{{template "base/head" .}} -{{template "base/navbar" .}} -
-
-
- -
-

{{.Org.FullName}}

- {{if .Org.Description}}

{{.Org.Description}}

{{end}} - -
-
-
+{{template "ng/base/head" .}} +{{template "ng/base/header" .}} +
+
+ +
+

{{.Org.FullName}}

+ {{if .Org.Description}}

{{.Org.Description}}

{{end}} + +
+
-
-
-
- -
-
- {{range .Repos}} -
-
- -
-

{{.Name}}

-

{{.Description}}

-

Updated {{TimeSince .Updated $.Lang}}

-
- {{end}} -
+
+
+ - -
-
- -
- {{range .Members}} - - {{end}} -
-
- +
+ {{range .Repos}} +
+
    +
  • {{.NumStars}}
  • +
  • {{.NumForks}}
  • +
+

{{.Name}}

+

{{.Description}}

+

{{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Updated $.i18n.Lang}}

+
+ {{end}}
+
+
+
+
+ {{.Org.NumMembers}} + {{.i18n.Tr "org.people"}} +
+
+ {{range .Members}} + + {{end}} +
+ +
+
+
+
+ {{.Org.NumTeams}} + {{.i18n.Tr "org.teams"}} +
+
+
    + {{range .Teams}} +
  • + {{.Name}} +

    {{.NumMembers}} {{$.i18n.Tr "org.lower_members"}} · {{.NumRepos}} {{$.i18n.Tr "org.lower_repositories"}}

    +
  • + {{end}} +
+
+ +
+
+
-{{template "base/footer" .}} +{{template "ng/base/footer" .}} \ No newline at end of file diff --git a/templates/org/teams.tmpl b/templates/org/teams.tmpl index decfecf59..90ee209c0 100644 --- a/templates/org/teams.tmpl +++ b/templates/org/teams.tmpl @@ -43,7 +43,7 @@