Merge branch 'master' of github.com:gogits/gogs

Conflicts:
	models/update.go
	routers/repo/http.go
This commit is contained in:
Lunny Xiao 2014-06-28 14:58:59 +08:00
commit 86e2627175
75 changed files with 2034 additions and 661 deletions

View File

@ -75,9 +75,6 @@ There are 5 ways to install Gogs:
The [core team](http://gogs.io/team) of this project. See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors. The [core team](http://gogs.io/team) of this project. See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors.
[![Clone in Koding](http://learn.koding.com/btn/clone_d.png)][koding]
[koding]: https://koding.com/Teamwork?import=https://github.com/gogits/gogs/archive/master.zip&c=git1
## License ## License
This project is under the MIT License. See the [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) file for the full license text. This project is under the MIT License. See the [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) file for the full license text.

View File

@ -66,9 +66,6 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依
本项目的 [开发团队](http://gogs.io/team)。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。 本项目的 [开发团队](http://gogs.io/team)。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。
[![Clone in Koding](http://learn.koding.com/btn/clone_d.png)][koding]
[koding]: https://koding.com/Teamwork?import=https://github.com/gogits/gogs/archive/master.zip&c=git1
## 授权许可 ## 授权许可
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。 本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。

View File

@ -56,19 +56,19 @@ func parseCmd(cmd string) (string, string) {
} }
var ( var (
COMMANDS_READONLY = map[string]int{ COMMANDS_READONLY = map[string]models.AccessType{
"git-upload-pack": models.AU_WRITABLE, "git-upload-pack": models.WRITABLE,
"git upload-pack": models.AU_WRITABLE, "git upload-pack": models.WRITABLE,
"git-upload-archive": models.AU_WRITABLE, "git-upload-archive": models.WRITABLE,
} }
COMMANDS_WRITE = map[string]int{ COMMANDS_WRITE = map[string]models.AccessType{
"git-receive-pack": models.AU_READABLE, "git-receive-pack": models.READABLE,
"git receive-pack": models.AU_READABLE, "git receive-pack": models.READABLE,
} }
) )
func In(b string, sl map[string]int) bool { func In(b string, sl map[string]models.AccessType) bool {
_, e := sl[b] _, e := sl[b]
return e return e
} }
@ -129,7 +129,7 @@ func runServ(k *cli.Context) {
// Access check. // Access check.
switch { switch {
case isWrite: case isWrite:
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.AU_WRITABLE) has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE)
if err != nil { if err != nil {
println("Gogs: internal error:", err) println("Gogs: internal error:", err)
log.GitLogger.Fatal("Fail to check write access:", err) log.GitLogger.Fatal("Fail to check write access:", err)
@ -152,7 +152,7 @@ func runServ(k *cli.Context) {
break break
} }
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.AU_READABLE) has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE)
if err != nil { if err != nil {
println("Gogs: internal error:", err) println("Gogs: internal error:", err)
log.GitLogger.Fatal("Fail to check read access:", err) log.GitLogger.Fatal("Fail to check read access:", err)

View File

@ -42,5 +42,7 @@ func runUpdate(c *cli.Context) {
repoUserName := os.Getenv("repoUserName") repoUserName := os.Getenv("repoUserName")
repoName := os.Getenv("repoName") repoName := os.Getenv("repoName")
models.Update(args[0], args[1], args[2], userName, repoUserName, repoName, userId) if err := models.Update(args[0], args[1], args[2], userName, repoUserName, repoName, userId); err != nil {
log.GitLogger.Fatal(err.Error())
}
} }

View File

@ -188,9 +188,20 @@ func runWeb(*cli.Context) {
reqOwner := middleware.RequireOwner() reqOwner := middleware.RequireOwner()
m.Group("/o", func(r martini.Router) { m.Group("/org", func(r martini.Router) {
r.Get("/create", org.New)
r.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.NewPost)
r.Get("/:org", org.Organization) r.Get("/:org", org.Organization)
}) r.Get("/:org/dashboard", org.Dashboard)
r.Get("/:org/members", org.Members)
// organization teams
r.Get("/:org/teams/:team/edit", org.EditTeam)
r.Get("/:org/teams/new", org.NewTeam)
r.Get("/:org/teams", org.Teams)
r.Get("/:org/settings", org.Settings)
r.Post("/:org/settings", bindIgnErr(auth.OrgSettingForm{}), org.SettingsPost)
}, reqSignIn)
m.Group("/:username/:reponame", func(r martini.Router) { m.Group("/:username/:reponame", func(r martini.Router) {
r.Get("/settings", repo.Setting) r.Get("/settings", repo.Setting)

View File

@ -51,8 +51,8 @@ SECRET_KEY = !#@FDEWREWR&*(
LOGIN_REMEMBER_DAYS = 7 LOGIN_REMEMBER_DAYS = 7
COOKIE_USERNAME = gogs_awesome COOKIE_USERNAME = gogs_awesome
COOKIE_REMEMBER_NAME = gogs_incredible COOKIE_REMEMBER_NAME = gogs_incredible
; Reverse proxy authentication header name of user ID ; Reverse proxy authentication header name of user name
REVERSE_PROXY_AUTHENTICATION_UID = X-WEBAUTH-UID REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
[service] [service]
ACTIVE_CODE_LIVE_MINUTES = 180 ACTIVE_CODE_LIVE_MINUTES = 180

View File

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

View File

@ -11,19 +11,20 @@ import (
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
// Access types. type AccessType int
const ( const (
AU_READABLE = iota + 1 READABLE AccessType = iota + 1
AU_WRITABLE WRITABLE
) )
// Access represents the accessibility of user to repository. // Access represents the accessibility of user to repository.
type Access struct { type Access struct {
Id int64 Id int64
UserName string `xorm:"unique(s)"` UserName string `xorm:"unique(s)"`
RepoName string `xorm:"unique(s)"` // <user name>/<repo name> RepoName string `xorm:"unique(s)"` // <user name>/<repo name>
Mode int `xorm:"unique(s)"` Mode AccessType `xorm:"unique(s)"`
Created time.Time `xorm:"created"` Created time.Time `xorm:"created"`
} }
// AddAccess adds new access record. // AddAccess adds new access record.
@ -59,7 +60,7 @@ func UpdateAccessWithSession(sess *xorm.Session, access *Access) error {
// HasAccess returns true if someone can read or write to given repository. // HasAccess returns true if someone can read or write to given repository.
// The repoName should be in format <username>/<reponame>. // The repoName should be in format <username>/<reponame>.
func HasAccess(uname, repoName string, mode int) (bool, error) { func HasAccess(uname, repoName string, mode AccessType) (bool, error) {
if len(repoName) == 0 { if len(repoName) == 0 {
return false, nil return false, nil
} }

View File

@ -182,14 +182,15 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
} }
// NewRepoAction adds new action for creating repository. // NewRepoAction adds new action for creating repository.
func NewRepoAction(user *User, repo *Repository) (err error) { func NewRepoAction(u *User, repo *Repository) (err error) {
if err = NotifyWatchers(&Action{ActUserId: user.Id, ActUserName: user.Name, ActEmail: user.Email, if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email,
OpType: OP_CREATE_REPO, RepoId: repo.Id, RepoName: repo.Name, IsPrivate: repo.IsPrivate}); err != nil { OpType: OP_CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name,
log.Error("action.NewRepoAction(notify watchers): %d/%s", user.Id, repo.Name) IsPrivate: repo.IsPrivate}); err != nil {
log.Error("action.NewRepoAction(notify watchers): %d/%s", u.Id, repo.Name)
return err return err
} }
log.Trace("action.NewRepoAction: %s/%s", user.LowerName, repo.LowerName) log.Trace("action.NewRepoAction: %s/%s", u.LowerName, repo.LowerName)
return err return err
} }

View File

@ -213,9 +213,9 @@ func GetIssueCountByPoster(uid, rid int64, isClosed bool) int64 {
// IssueUser represents an issue-user relation. // IssueUser represents an issue-user relation.
type IssueUser struct { type IssueUser struct {
Id int64 Id int64
Uid int64 // User ID. Uid int64 `xorm:"INDEX"` // User ID.
IssueId int64 IssueId int64
RepoId int64 RepoId int64 `xorm:"INDEX"`
MilestoneId int64 MilestoneId int64
IsRead bool IsRead bool
IsAssigned bool IsAssigned bool

View File

@ -255,7 +255,7 @@ func LoginUserLdapSource(user *User, name, passwd string, sourceId int64, cfg *L
Email: mail, Email: mail,
} }
return RegisterUser(user) return CreateUser(user)
} }
type loginAuth struct { type loginAuth struct {
@ -359,5 +359,5 @@ func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *S
Passwd: passwd, Passwd: passwd,
Email: name, Email: name,
} }
return RegisterUser(user) return CreateUser(user)
} }

View File

@ -35,7 +35,7 @@ func init() {
tables = append(tables, new(User), new(PublicKey), new(Repository), new(Watch), tables = append(tables, new(User), new(PublicKey), new(Repository), new(Watch),
new(Action), new(Access), new(Issue), new(Comment), new(Oauth2), new(Follow), new(Action), new(Access), new(Issue), new(Comment), new(Oauth2), new(Follow),
new(Mirror), new(Release), new(LoginSource), new(Webhook), new(IssueUser), new(Mirror), new(Release), new(LoginSource), new(Webhook), new(IssueUser),
new(Milestone), new(Label), new(HookTask)) new(Milestone), new(Label), new(HookTask), new(Team), new(OrgUser), new(TeamUser))
} }
func LoadModelsConfig() { func LoadModelsConfig() {

192
models/org.go Normal file
View File

@ -0,0 +1,192 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"strings"
"github.com/gogits/gogs/modules/base"
)
// CreateOrganization creates record of a new organization.
func CreateOrganization(org, owner *User) (*User, error) {
if !IsLegalName(org.Name) {
return nil, ErrUserNameIllegal
}
isExist, err := IsUserExist(org.Name)
if err != nil {
return nil, err
} else if isExist {
return nil, ErrUserAlreadyExist
}
isExist, err = IsEmailUsed(org.Email)
if err != nil {
return nil, err
} else if isExist {
return nil, ErrEmailAlreadyUsed
}
org.LowerName = strings.ToLower(org.Name)
org.FullName = org.Name
org.Avatar = base.EncodeMd5(org.Email)
org.AvatarEmail = org.Email
// No password for organization.
org.NumTeams = 1
org.NumMembers = 1
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return nil, err
}
if _, err = sess.Insert(org); err != nil {
sess.Rollback()
return nil, err
}
// Create default owner team.
t := &Team{
OrgId: org.Id,
Name: OWNER_TEAM,
Authorize: ORG_ADMIN,
NumMembers: 1,
}
if _, err = sess.Insert(t); err != nil {
sess.Rollback()
return nil, err
}
// Add initial creator to organization and owner team.
ou := &OrgUser{
Uid: owner.Id,
OrgId: org.Id,
IsOwner: true,
NumTeam: 1,
}
if _, err = sess.Insert(ou); err != nil {
sess.Rollback()
return nil, err
}
tu := &TeamUser{
Uid: owner.Id,
OrgId: org.Id,
TeamId: t.Id,
}
if _, err = sess.Insert(tu); err != nil {
sess.Rollback()
return nil, err
}
return org, sess.Commit()
}
type AuthorizeType int
const (
ORG_READABLE AuthorizeType = iota + 1
ORG_WRITABLE
ORG_ADMIN
)
const OWNER_TEAM = "Owner"
// Team represents a organization team.
type Team struct {
Id int64
OrgId int64 `xorm:"INDEX"`
Name string
Description string
Authorize AuthorizeType
RepoIds string `xorm:"TEXT"`
NumMembers int
NumRepos int
}
// NewTeam creates a record of new team.
func NewTeam(t *Team) error {
_, err := x.Insert(t)
return err
}
func UpdateTeam(t *Team) error {
if len(t.Description) > 255 {
t.Description = t.Description[:255]
}
_, err := x.Id(t.Id).AllCols().Update(t)
return err
}
// ________ ____ ___
// \_____ \_______ ____ | | \______ ___________
// / | \_ __ \/ ___\| | / ___// __ \_ __ \
// / | \ | \/ /_/ > | /\___ \\ ___/| | \/
// \_______ /__| \___ /|______//____ >\___ >__|
// \/ /_____/ \/ \/
// OrgUser represents an organization-user relation.
type OrgUser struct {
Id int64
Uid int64 `xorm:"INDEX"`
OrgId int64 `xorm:"INDEX"`
IsPublic bool
IsOwner bool
NumTeam int
}
// GetOrgUsersByUserId returns all organization-user relations by user ID.
func GetOrgUsersByUserId(uid int64) ([]*OrgUser, error) {
ous := make([]*OrgUser, 0, 10)
err := x.Where("uid=?", uid).Find(&ous)
return ous, err
}
// GetOrgUsersByOrgId returns all organization-user relations by organization ID.
func GetOrgUsersByOrgId(orgId int64) ([]*OrgUser, error) {
ous := make([]*OrgUser, 0, 10)
err := x.Where("org_id=?", orgId).Find(&ous)
return ous, err
}
func GetOrganizationCount(u *User) (int64, error) {
return x.Where("uid=?", u.Id).Count(new(OrgUser))
}
// ___________ ____ ___
// \__ ___/___ _____ _____ | | \______ ___________
// | |_/ __ \\__ \ / \| | / ___// __ \_ __ \
// | |\ ___/ / __ \| Y Y \ | /\___ \\ ___/| | \/
// |____| \___ >____ /__|_| /______//____ >\___ >__|
// \/ \/ \/ \/ \/
// TeamUser represents an team-user relation.
type TeamUser struct {
Id int64
Uid int64
OrgId int64 `xorm:"INDEX"`
TeamId int64
}
// GetTeamMembers returns all members in given team of organization.
func GetTeamMembers(orgId, teamId int64) ([]*User, error) {
tus := make([]*TeamUser, 0, 10)
err := x.Where("org_id=?", orgId).And("team_id=?", teamId).Find(&tus)
if err != nil {
return nil, err
}
us := make([]*User, len(tus))
for i, tu := range tus {
us[i], err = GetUserById(tu.Uid)
if err != nil {
return nil, err
}
}
return us, nil
}

View File

@ -158,7 +158,7 @@ func IsRepositoryExist(u *User, repoName string) (bool, error) {
} }
var ( var (
illegalEquals = []string{"raw", "install", "api", "avatar", "user", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin"} illegalEquals = []string{"raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin"}
illegalSuffixs = []string{".git"} illegalSuffixs = []string{".git"}
) )
@ -240,7 +240,7 @@ func MirrorUpdate() {
"git", "remote", "update"); err != nil { "git", "remote", "update"); err != nil {
return errors.New("git remote update: " + stderr) return errors.New("git remote update: " + stderr)
} else if err = git.UnpackRefs(repoPath); err != nil { } else if err = git.UnpackRefs(repoPath); err != nil {
return err return errors.New("UnpackRefs: " + err.Error())
} }
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
@ -251,8 +251,8 @@ func MirrorUpdate() {
} }
// MigrateRepository migrates a existing repository from other project hosting. // MigrateRepository migrates a existing repository from other project hosting.
func MigrateRepository(user *User, name, desc string, private, mirror bool, url string) (*Repository, error) { func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) {
repo, err := CreateRepository(user, name, desc, "", "", private, mirror, false) repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -261,11 +261,11 @@ func MigrateRepository(user *User, name, desc string, private, mirror bool, url
tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond())) tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()))
os.MkdirAll(tmpDir, os.ModePerm) os.MkdirAll(tmpDir, os.ModePerm)
repoPath := RepoPath(user.Name, name) repoPath := RepoPath(u.Name, name)
repo.IsBare = false repo.IsBare = false
if mirror { if mirror {
if err = MirrorRepository(repo.Id, user.Name, repo.Name, repoPath, url); err != nil { if err = MirrorRepository(repo.Id, u.Name, repo.Name, repoPath, url); err != nil {
return repo, err return repo, err
} }
repo.IsMirror = true repo.IsMirror = true
@ -454,21 +454,28 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep
return initRepoCommit(tmpDir, user.NewGitSig()) return initRepoCommit(tmpDir, user.NewGitSig())
} }
// CreateRepository creates a repository for given user or orgnaziation. // CreateRepository creates a repository for given user or organization.
func CreateRepository(user *User, name, desc, lang, license string, private, mirror, initReadme bool) (*Repository, error) { func CreateRepository(u *User, name, desc, lang, license string, private, mirror, initReadme bool) (*Repository, error) {
if !IsLegalName(name) { if !IsLegalName(name) {
return nil, ErrRepoNameIllegal return nil, ErrRepoNameIllegal
} }
isExist, err := IsRepositoryExist(user, name) isExist, err := IsRepositoryExist(u, name)
if err != nil { if err != nil {
return nil, err return nil, err
} else if isExist { } else if isExist {
return nil, ErrRepoAlreadyExist return nil, ErrRepoAlreadyExist
} }
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return nil, err
}
repo := &Repository{ repo := &Repository{
OwnerId: user.Id, OwnerId: u.Id,
Owner: u,
Name: name, Name: name,
LowerName: strings.ToLower(name), LowerName: strings.ToLower(name),
Description: desc, Description: desc,
@ -479,67 +486,85 @@ func CreateRepository(user *User, name, desc, lang, license string, private, mir
repo.DefaultBranch = "master" repo.DefaultBranch = "master"
} }
repoPath := RepoPath(user.Name, repo.Name)
sess := x.NewSession()
defer sess.Close()
sess.Begin()
if _, err = sess.Insert(repo); err != nil { if _, err = sess.Insert(repo); err != nil {
if err2 := os.RemoveAll(repoPath); err2 != nil {
log.Error("repo.CreateRepository(repo): %v", err)
return nil, errors.New(fmt.Sprintf(
"delete repo directory %s/%s failed(1): %v", user.Name, repo.Name, err2))
}
sess.Rollback() sess.Rollback()
return nil, err return nil, err
} }
mode := AU_WRITABLE var t *Team // Owner team.
mode := WRITABLE
if mirror { if mirror {
mode = AU_READABLE mode = READABLE
} }
access := Access{ access := &Access{
UserName: user.LowerName, UserName: u.LowerName,
RepoName: strings.ToLower(path.Join(user.Name, repo.Name)), RepoName: strings.ToLower(path.Join(u.Name, repo.Name)),
Mode: mode, Mode: mode,
} }
if _, err = sess.Insert(&access); err != nil { // Give access to all members in owner team.
sess.Rollback() if u.IsOrganization() {
if err2 := os.RemoveAll(repoPath); err2 != nil { t, err = u.GetOwnerTeam()
log.Error("repo.CreateRepository(access): %v", err) if err != nil {
return nil, errors.New(fmt.Sprintf( sess.Rollback()
"delete repo directory %s/%s failed(2): %v", user.Name, repo.Name, err2)) return nil, err
}
us, err := GetTeamMembers(u.Id, t.Id)
if err != nil {
sess.Rollback()
return nil, err
}
for _, u := range us {
access.UserName = u.LowerName
if _, err = sess.Insert(access); err != nil {
sess.Rollback()
return nil, err
}
}
} else {
if _, err = sess.Insert(access); err != nil {
sess.Rollback()
return nil, err
} }
return nil, err
} }
rawSql := "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" rawSql := "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?"
if _, err = sess.Exec(rawSql, user.Id); err != nil { if _, err = sess.Exec(rawSql, u.Id); err != nil {
sess.Rollback() sess.Rollback()
if err2 := os.RemoveAll(repoPath); err2 != nil {
log.Error("repo.CreateRepository(repo count): %v", err)
return nil, errors.New(fmt.Sprintf(
"delete repo directory %s/%s failed(3): %v", user.Name, repo.Name, err2))
}
return nil, err return nil, err
} }
// Update owner team info and count.
if u.IsOrganization() {
t.RepoIds += "$" + base.ToStr(repo.Id) + "|"
t.NumRepos++
if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
sess.Rollback()
return nil, err
}
}
if err = sess.Commit(); err != nil { if err = sess.Commit(); err != nil {
sess.Rollback()
if err2 := os.RemoveAll(repoPath); err2 != nil {
log.Error("repo.CreateRepository(commit): %v", err)
return nil, errors.New(fmt.Sprintf(
"delete repo directory %s/%s failed(3): %v", user.Name, repo.Name, err2))
}
return nil, err return nil, err
} }
if err = WatchRepo(user.Id, repo.Id, true); err != nil { if u.IsOrganization() {
log.Error("repo.CreateRepository(WatchRepo): %v", err) ous, err := GetOrgUsersByOrgId(u.Id)
if err != nil {
log.Error("repo.CreateRepository(GetOrgUsersByOrgId): %v", err)
} else {
for _, ou := range ous {
if err = WatchRepo(ou.Uid, repo.Id, true); err != nil {
log.Error("repo.CreateRepository(WatchRepo): %v", err)
}
}
}
}
if err = WatchRepo(u.Id, repo.Id, true); err != nil {
log.Error("repo.CreateRepository(WatchRepo2): %v", err)
} }
if err = NewRepoAction(user, repo); err != nil { if err = NewRepoAction(u, repo); err != nil {
log.Error("repo.CreateRepository(NewRepoAction): %v", err) log.Error("repo.CreateRepository(NewRepoAction): %v", err)
} }
@ -548,7 +573,13 @@ func CreateRepository(user *User, name, desc, lang, license string, private, mir
return repo, nil return repo, nil
} }
if err = initRepository(repoPath, user, repo, initReadme, lang, license); err != nil { repoPath := RepoPath(u.Name, repo.Name)
if err = initRepository(repoPath, u, repo, initReadme, lang, license); err != nil {
if err2 := os.RemoveAll(repoPath); err2 != nil {
log.Error("repo.CreateRepository(initRepository): %v", err)
return nil, errors.New(fmt.Sprintf(
"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2))
}
return nil, err return nil, err
} }

View File

@ -6,6 +6,7 @@ package models
import ( import (
"container/list" "container/list"
"fmt"
"os/exec" "os/exec"
"strings" "strings"
@ -15,13 +16,13 @@ import (
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
) )
func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName string, userId int64) { func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName string, userId int64) error {
//fmt.Println(refName, oldCommitId, newCommitId) //fmt.Println(refName, oldCommitId, newCommitId)
//fmt.Println(userName, repoUserName, repoName) //fmt.Println(userName, repoUserName, repoName)
isNew := strings.HasPrefix(oldCommitId, "0000000") isNew := strings.HasPrefix(oldCommitId, "0000000")
if isNew && if isNew &&
strings.HasPrefix(newCommitId, "0000000") { strings.HasPrefix(newCommitId, "0000000") {
log.GitLogger.Fatal("old rev and new rev both 000000") return fmt.Errorf("old rev and new rev both 000000")
} }
f := RepoPath(repoUserName, repoName) f := RepoPath(repoUserName, repoName)
@ -33,18 +34,17 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
isDel := strings.HasPrefix(newCommitId, "0000000") isDel := strings.HasPrefix(newCommitId, "0000000")
if isDel { if isDel {
log.GitLogger.Info("del rev", refName, "from", userName+"/"+repoName+".git", "by", userId) log.GitLogger.Info("del rev", refName, "from", userName+"/"+repoName+".git", "by", userId)
return return nil
} }
repo, err := git.OpenRepository(f) repo, err := git.OpenRepository(f)
if err != nil { if err != nil {
log.GitLogger.Fatal("runUpdate.Open repoId: %v", err) return fmt.Errorf("runUpdate.Open repoId: %v", err)
} }
newCommit, err := repo.GetCommit(newCommitId) newCommit, err := repo.GetCommit(newCommitId)
if err != nil { if err != nil {
log.GitLogger.Fatal("runUpdate GetCommit of newCommitId: %v", err) return fmt.Errorf("runUpdate GetCommit of newCommitId: %v", err)
return
} }
var l *list.List var l *list.List
@ -52,28 +52,27 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
if isNew { if isNew {
l, err = newCommit.CommitsBefore() l, err = newCommit.CommitsBefore()
if err != nil { if err != nil {
log.GitLogger.Fatal("Find CommitsBefore erro: %v", err) return fmt.Errorf("Find CommitsBefore erro: %v", err)
} }
} else { } else {
l, err = newCommit.CommitsBeforeUntil(oldCommitId) l, err = newCommit.CommitsBeforeUntil(oldCommitId)
if err != nil { if err != nil {
log.GitLogger.Fatal("Find CommitsBeforeUntil erro: %v", err) return fmt.Errorf("Find CommitsBeforeUntil erro: %v", err)
return
} }
} }
if err != nil { if err != nil {
log.GitLogger.Fatal("runUpdate.Commit repoId: %v", err) return fmt.Errorf("runUpdate.Commit repoId: %v", err)
} }
ru, err := GetUserByName(repoUserName) ru, err := GetUserByName(repoUserName)
if err != nil { if err != nil {
log.GitLogger.Fatal("runUpdate.GetUserByName: %v", err) return fmt.Errorf("runUpdate.GetUserByName: %v", err)
} }
repos, err := GetRepositoryByName(ru.Id, repoName) repos, err := GetRepositoryByName(ru.Id, repoName)
if err != nil { if err != nil {
log.GitLogger.Fatal("runUpdate.GetRepositoryByName userId: %v", err) return fmt.Errorf("runUpdate.GetRepositoryByName userId: %v", err)
} }
// if tags push // if tags push
@ -104,6 +103,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
return return
} }
// if commits push
commits := make([]*base.PushCommit, 0) commits := make([]*base.PushCommit, 0)
var maxCommits = 3 var maxCommits = 3
var actEmail string var actEmail string
@ -126,6 +126,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
//commits = append(commits, []string{lastCommit.Id().String(), lastCommit.Message()}) //commits = append(commits, []string{lastCommit.Id().String(), lastCommit.Message()})
if err = CommitRepoAction(userId, ru.Id, userName, actEmail, if err = CommitRepoAction(userId, ru.Id, userName, actEmail,
repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}); err != nil { repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}); err != nil {
log.GitLogger.Fatal("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err) return fmt.Errorf("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
} }
return nil
} }

View File

@ -21,14 +21,16 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
// User types. type UserType int
const ( const (
UT_INDIVIDUAL = iota + 1 INDIVIDUAL UserType = iota // Historic reason to make it starts at 0.
UT_ORGANIZATION ORGANIZATION
) )
var ( var (
ErrUserOwnRepos = errors.New("User still have ownership of repositories") ErrUserOwnRepos = errors.New("User still have ownership of repositories")
ErrUserHasOrgs = errors.New("User still have membership of organization")
ErrUserAlreadyExist = errors.New("User already exist") ErrUserAlreadyExist = errors.New("User already exist")
ErrUserNotExist = errors.New("User does not exist") ErrUserNotExist = errors.New("User does not exist")
ErrUserNotKeyOwner = errors.New("User does not the owner of public key") ErrUserNotKeyOwner = errors.New("User does not the owner of public key")
@ -50,7 +52,8 @@ type User struct {
LoginType LoginType LoginType LoginType
LoginSource int64 `xorm:"not null default 0"` LoginSource int64 `xorm:"not null default 0"`
LoginName string LoginName string
Type int Type UserType
Orgs []*User `xorm:"-"`
NumFollowers int NumFollowers int
NumFollowings int NumFollowings int
NumStars int NumStars int
@ -65,43 +68,71 @@ type User struct {
Salt string `xorm:"VARCHAR(10)"` Salt string `xorm:"VARCHAR(10)"`
Created time.Time `xorm:"created"` Created time.Time `xorm:"created"`
Updated time.Time `xorm:"updated"` Updated time.Time `xorm:"updated"`
// For organization.
Description string
NumTeams int
NumMembers int
} }
// HomeLink returns the user home page link. // HomeLink returns the user home page link.
func (user *User) HomeLink() string { func (u *User) HomeLink() string {
return "/user/" + user.Name return "/user/" + u.Name
} }
// AvatarLink returns user gravatar link. // AvatarLink returns user gravatar link.
func (user *User) AvatarLink() string { func (u *User) AvatarLink() string {
if setting.DisableGravatar { if setting.DisableGravatar {
return "/img/avatar_default.jpg" return "/img/avatar_default.jpg"
} else if setting.Service.EnableCacheAvatar { } else if setting.Service.EnableCacheAvatar {
return "/avatar/" + user.Avatar return "/avatar/" + u.Avatar
} }
return "//1.gravatar.com/avatar/" + user.Avatar return "//1.gravatar.com/avatar/" + u.Avatar
} }
// NewGitSig generates and returns the signature of given user. // NewGitSig generates and returns the signature of given user.
func (user *User) NewGitSig() *git.Signature { func (u *User) NewGitSig() *git.Signature {
return &git.Signature{ return &git.Signature{
Name: user.Name, Name: u.Name,
Email: user.Email, Email: u.Email,
When: time.Now(), When: time.Now(),
} }
} }
// EncodePasswd encodes password to safe format. // EncodePasswd encodes password to safe format.
func (user *User) EncodePasswd() { func (u *User) EncodePasswd() {
newPasswd := base.PBKDF2([]byte(user.Passwd), []byte(user.Salt), 10000, 50, sha256.New) newPasswd := base.PBKDF2([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New)
user.Passwd = fmt.Sprintf("%x", newPasswd) u.Passwd = fmt.Sprintf("%x", newPasswd)
} }
// Member represents user is member of organization. func (u *User) IsOrganization() bool {
type Member struct { return u.Type == ORGANIZATION
Id int64 }
OrgId int64 `xorm:"unique(member) index"`
UserId int64 `xorm:"unique(member)"` func (u *User) GetOrganizations() error {
ous, err := GetOrgUsersByUserId(u.Id)
if err != nil {
return err
}
u.Orgs = make([]*User, len(ous))
for i, ou := range ous {
u.Orgs[i], err = GetUserById(ou.OrgId)
if err != nil {
return err
}
}
return nil
}
// GetOwnerTeam returns owner team of organization.
func (org *User) GetOwnerTeam() (*Team, error) {
t := &Team{
OrgId: org.Id,
Name: OWNER_TEAM,
}
_, err := x.Get(t)
return t, err
} }
// IsUserExist checks if given user name exist, // IsUserExist checks if given user name exist,
@ -126,49 +157,60 @@ func GetUserSalt() string {
return base.GetRandomString(10) return base.GetRandomString(10)
} }
// RegisterUser creates record of a new user. // CreateUser creates record of a new user.
func RegisterUser(user *User) (*User, error) { func CreateUser(u *User) (*User, error) {
if !IsLegalName(u.Name) {
if !IsLegalName(user.Name) {
return nil, ErrUserNameIllegal return nil, ErrUserNameIllegal
} }
isExist, err := IsUserExist(user.Name) isExist, err := IsUserExist(u.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} else if isExist { } else if isExist {
return nil, ErrUserAlreadyExist return nil, ErrUserAlreadyExist
} }
isExist, err = IsEmailUsed(user.Email) isExist, err = IsEmailUsed(u.Email)
if err != nil { if err != nil {
return nil, err return nil, err
} else if isExist { } else if isExist {
return nil, ErrEmailAlreadyUsed return nil, ErrEmailAlreadyUsed
} }
user.LowerName = strings.ToLower(user.Name) u.LowerName = strings.ToLower(u.Name)
user.Avatar = base.EncodeMd5(user.Email) u.Avatar = base.EncodeMd5(u.Email)
user.AvatarEmail = user.Email u.AvatarEmail = u.Email
user.Rands = GetUserSalt() u.Rands = GetUserSalt()
user.Salt = GetUserSalt() u.Salt = GetUserSalt()
user.EncodePasswd() u.EncodePasswd()
if _, err = x.Insert(user); err != nil {
return nil, err sess := x.NewSession()
} else if err = os.MkdirAll(UserPath(user.Name), os.ModePerm); err != nil { defer sess.Close()
if _, err := x.Id(user.Id).Delete(&User{}); err != nil { if err = sess.Begin(); err != nil {
return nil, errors.New(fmt.Sprintf(
"both create userpath %s and delete table record faild: %v", user.Name, err))
}
return nil, err return nil, err
} }
if user.Id == 1 { if _, err = sess.Insert(u); err != nil {
user.IsAdmin = true sess.Rollback()
user.IsActive = true return nil, err
_, err = x.Id(user.Id).UseBool().Update(user)
} }
return user, err
if err = os.MkdirAll(UserPath(u.Name), os.ModePerm); err != nil {
sess.Rollback()
return nil, err
}
if err = sess.Commit(); err != nil {
return nil, err
}
// Auto-set admin for user whose ID is 1.
if u.Id == 1 {
u.IsAdmin = true
u.IsActive = true
_, err = x.Id(u.Id).UseBool().Update(u)
}
return u, err
} }
// GetUsers returns given number of user objects with offset. // GetUsers returns given number of user objects with offset.
@ -277,51 +319,62 @@ func UpdateUser(u *User) (err error) {
if len(u.Website) > 255 { if len(u.Website) > 255 {
u.Website = u.Website[:255] u.Website = u.Website[:255]
} }
if len(u.Description) > 255 {
u.Description = u.Description[:255]
}
_, err = x.Id(u.Id).AllCols().Update(u) _, err = x.Id(u.Id).AllCols().Update(u)
return err return err
} }
// DeleteUser completely deletes everything of the user. // DeleteUser completely deletes everything of the user.
func DeleteUser(user *User) error { func DeleteUser(u *User) error {
// Check ownership of repository. // Check ownership of repository.
count, err := GetRepositoryCount(user) count, err := GetRepositoryCount(u)
if err != nil { if err != nil {
return errors.New("modesl.GetRepositories: " + err.Error()) return errors.New("modesl.GetRepositories(GetRepositoryCount): " + err.Error())
} else if count > 0 { } else if count > 0 {
return ErrUserOwnRepos return ErrUserOwnRepos
} }
// Check membership of organization.
count, err = GetOrganizationCount(u)
if err != nil {
return errors.New("modesl.GetRepositories(GetOrganizationCount): " + err.Error())
} else if count > 0 {
return ErrUserHasOrgs
}
// TODO: check issues, other repos' commits // TODO: check issues, other repos' commits
// Delete all followers. // Delete all followers.
if _, err = x.Delete(&Follow{FollowId: user.Id}); err != nil { if _, err = x.Delete(&Follow{FollowId: u.Id}); err != nil {
return err return err
} }
// Delete oauth2. // Delete oauth2.
if _, err = x.Delete(&Oauth2{Uid: user.Id}); err != nil { if _, err = x.Delete(&Oauth2{Uid: u.Id}); err != nil {
return err return err
} }
// Delete all feeds. // Delete all feeds.
if _, err = x.Delete(&Action{UserId: user.Id}); err != nil { if _, err = x.Delete(&Action{UserId: u.Id}); err != nil {
return err return err
} }
// Delete all watches. // Delete all watches.
if _, err = x.Delete(&Watch{UserId: user.Id}); err != nil { if _, err = x.Delete(&Watch{UserId: u.Id}); err != nil {
return err return err
} }
// Delete all accesses. // Delete all accesses.
if _, err = x.Delete(&Access{UserName: user.LowerName}); err != nil { if _, err = x.Delete(&Access{UserName: u.LowerName}); err != nil {
return err return err
} }
// Delete all SSH keys. // Delete all SSH keys.
keys := make([]*PublicKey, 0, 10) keys := make([]*PublicKey, 0, 10)
if err = x.Find(&keys, &PublicKey{OwnerId: user.Id}); err != nil { if err = x.Find(&keys, &PublicKey{OwnerId: u.Id}); err != nil {
return err return err
} }
for _, key := range keys { for _, key := range keys {
@ -331,11 +384,11 @@ func DeleteUser(user *User) error {
} }
// Delete user directory. // Delete user directory.
if err = os.RemoveAll(UserPath(user.Name)); err != nil { if err = os.RemoveAll(UserPath(u.Name)); err != nil {
return err return err
} }
_, err = x.Delete(user) _, err = x.Delete(u)
return err return err
} }

57
modules/auth/org.go Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package auth
import (
"net/http"
"reflect"
"github.com/go-martini/martini"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/middleware/binding"
)
type CreateOrgForm struct {
OrgName string `form:"orgname" binding:"Required;AlphaDashDot;MaxSize(30)"`
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
}
func (f *CreateOrgForm) Name(field string) string {
names := map[string]string{
"OrgName": "Organization name",
"Email": "E-mail address",
}
return names[field]
}
func (f *CreateOrgForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
validate(errs, data, f)
}
type OrgSettingForm struct {
DisplayName string `form:"display_name" binding:"Required;MaxSize(100)"`
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
Description string `form:"desc" binding:"MaxSize(255)"`
Website string `form:"site" binding:"Url;MaxSize(100)"`
Location string `form:"location" binding:"MaxSize(50)"`
}
func (f *OrgSettingForm) Name(field string) string {
names := map[string]string{
"DisplayName": "Display name",
"Email": "E-mail address",
"Description": "Description",
"Website": "Website address",
"Location": "Location",
}
return names[field]
}
func (f *OrgSettingForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
validate(errors, data, f)
}

View File

@ -22,9 +22,10 @@ import (
// \/ \/|__| \/ \/ // \/ \/|__| \/ \/
type CreateRepoForm struct { type CreateRepoForm struct {
Uid int64 `form:"uid" binding:"Required"`
RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"` RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"`
Private bool `form:"private"` Private bool `form:"private"`
Description string `form:"desc" binding:"MaxSize(100)"` Description string `form:"desc" binding:"MaxSize(255)"`
Language string `form:"language"` Language string `form:"language"`
License string `form:"license"` License string `form:"license"`
InitReadme bool `form:"initReadme"` InitReadme bool `form:"initReadme"`
@ -47,10 +48,11 @@ type MigrateRepoForm struct {
Url string `form:"url" binding:"Url"` Url string `form:"url" binding:"Url"`
AuthUserName string `form:"auth_username"` AuthUserName string `form:"auth_username"`
AuthPasswd string `form:"auth_password"` AuthPasswd string `form:"auth_password"`
Uid int64 `form:"uid" binding:"Required"`
RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"` RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"`
Mirror bool `form:"mirror"` Mirror bool `form:"mirror"`
Private bool `form:"private"` Private bool `form:"private"`
Description string `form:"desc" binding:"MaxSize(100)"` Description string `form:"desc" binding:"MaxSize(255)"`
} }
func (f *MigrateRepoForm) Name(field string) string { func (f *MigrateRepoForm) Name(field string) string {
@ -69,7 +71,7 @@ func (f *MigrateRepoForm) Validate(errors *binding.Errors, req *http.Request, co
type RepoSettingForm struct { type RepoSettingForm struct {
RepoName string `form:"name" binding:"Required;AlphaDash;MaxSize(100)"` RepoName string `form:"name" binding:"Required;AlphaDash;MaxSize(100)"`
Description string `form:"desc" binding:"MaxSize(100)"` Description string `form:"desc" binding:"MaxSize(255)"`
Website string `form:"site" binding:"Url;MaxSize(100)"` Website string `form:"site" binding:"Url;MaxSize(100)"`
Branch string `form:"branch"` Branch string `form:"branch"`
Interval int `form:"interval"` Interval int `form:"interval"`

View File

@ -25,23 +25,25 @@ func SignedInId(header http.Header, sess session.SessionStore) int64 {
return 0 return 0
} }
var id int64
if setting.Service.EnableReverseProxyAuth { if setting.Service.EnableReverseProxyAuth {
id, _ = base.StrTo(header.Get(setting.ReverseProxyAuthUid)).Int64() webAuthUser := header.Get(setting.ReverseProxyAuthUser)
} if len(webAuthUser) > 0 {
u, err := models.GetUserByName(webAuthUser)
if id <= 0 { if err != nil {
uid := sess.Get("userId") if err != models.ErrUserNotExist {
if uid == nil { log.Error("auth.user.SignedInId(GetUserByName): %v", err)
return 0 }
} return 0
var ok bool }
if id, ok = uid.(int64); !ok { return u.Id
return 0
} }
} }
if id > 0 { uid := sess.Get("userId")
if uid == nil {
return 0
}
if id, ok := uid.(int64); ok {
if _, err := models.GetUserById(id); err != nil { if _, err := models.GetUserById(id); err != nil {
if err != models.ErrUserNotExist { if err != models.ErrUserNotExist {
log.Error("auth.user.SignedInId(GetUserById): %v", err) log.Error("auth.user.SignedInId(GetUserById): %v", err)
@ -91,7 +93,7 @@ func (f *UpdateProfileForm) Name(field string) string {
names := map[string]string{ names := map[string]string{
"UserName": "Username", "UserName": "Username",
"Email": "E-mail address", "Email": "E-mail address",
"Website": "Website", "Website": "Website address",
"Location": "Location", "Location": "Location",
"Avatar": "Gravatar Email", "Avatar": "Gravatar Email",
} }

View File

@ -7,6 +7,7 @@ package base
type ( type (
// Type TmplData represents data in the templates. // Type TmplData represents data in the templates.
TmplData map[string]interface{} TmplData map[string]interface{}
TplName string
ApiJsonErr struct { ApiJsonErr struct {
Message string `json:"message"` Message string `json:"message"`

View File

@ -28,242 +28,241 @@ func bindata_read(data []byte, name string) ([]byte, error) {
func conf_app_ini() ([]byte, error) { func conf_app_ini() ([]byte, error) {
return bindata_read([]byte{ return bindata_read([]byte{
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0xb4, 0x59, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0xb4, 0x59,
0xdd, 0x72, 0xdb, 0xc8, 0xb1, 0xbe, 0xc7, 0x53, 0x8c, 0x79, 0x76, 0xcf, 0xeb, 0x72, 0xdb, 0xc8, 0x95, 0xfe, 0x8f, 0xa7, 0x68, 0x73, 0x67, 0x76,
0xda, 0xa7, 0x24, 0x92, 0x92, 0x8f, 0x65, 0xaf, 0xbc, 0xae, 0x63, 0x8a, 0xec, 0x2d, 0x89, 0xa4, 0xe4, 0xb5, 0xec, 0x91, 0xc7, 0xb5, 0xa6, 0x48,
0x04, 0x25, 0x1c, 0xf3, 0x47, 0x0b, 0x40, 0xf2, 0x2a, 0x2e, 0x15, 0x0a, 0x50, 0xc2, 0x9a, 0x17, 0x0d, 0x00, 0xc9, 0xa3, 0xb8, 0x54, 0x28, 0x08,
0x02, 0x86, 0xe4, 0x44, 0x00, 0x06, 0xc2, 0x0c, 0x45, 0x31, 0x77, 0x79, 0x68, 0x92, 0x1d, 0x01, 0x68, 0x08, 0xdd, 0x14, 0xc5, 0xfc, 0xcb, 0x2b,
0x85, 0x54, 0x9e, 0x26, 0xcf, 0x93, 0x8b, 0x3c, 0x46, 0xbe, 0x1e, 0x00, 0xa4, 0xf2, 0x34, 0x79, 0x9e, 0xfc, 0xc8, 0x63, 0xe4, 0x3b, 0x0d, 0x80,
0x14, 0x28, 0x73, 0xb5, 0xce, 0x5f, 0x25, 0x65, 0x11, 0xf3, 0xd3, 0xd3, 0x02, 0x65, 0x8e, 0xc6, 0xb9, 0x55, 0x52, 0x16, 0xd1, 0xdd, 0xe7, 0xf4,
0xdd, 0xf3, 0xf5, 0xd7, 0xdd, 0xb3, 0xef, 0x59, 0x2f, 0xcf, 0x59, 0x16, 0xb9, 0x7c, 0xe7, 0xd6, 0xf3, 0x9e, 0xf5, 0xf2, 0x9c, 0x65, 0x61, 0xca,
0xa6, 0x9c, 0xe9, 0x45, 0xa8, 0x99, 0x5a, 0xc8, 0x95, 0x62, 0x32, 0x63, 0x99, 0x5e, 0x84, 0x9a, 0xa9, 0x85, 0x5c, 0x29, 0x26, 0x33, 0xc6, 0xef,
0xfc, 0x9e, 0x17, 0x6b, 0x96, 0x87, 0x73, 0x4c, 0x08, 0x9d, 0x70, 0xab, 0x79, 0xb1, 0x66, 0x79, 0x38, 0xc7, 0x86, 0xd0, 0x09, 0xb7, 0x7a, 0xe7,
0x77, 0x7e, 0x1e, 0x4c, 0x7a, 0x63, 0x9b, 0x7d, 0x60, 0xa7, 0x72, 0xae, 0xe7, 0xc1, 0xa4, 0x37, 0xb6, 0xd9, 0x07, 0x76, 0x2a, 0xe7, 0xea, 0x18,
0x8e, 0xf1, 0x2f, 0x3b, 0x15, 0x9a, 0x79, 0xbc, 0xb8, 0x17, 0x51, 0x39, 0xff, 0xb2, 0x53, 0xa1, 0x99, 0xc7, 0x8b, 0x7b, 0x11, 0x95, 0xfb, 0xa3,
0x3f, 0x9a, 0x9e, 0x4e, 0x31, 0x2f, 0xd2, 0x79, 0x67, 0x16, 0x62, 0x54, 0xe9, 0xe9, 0x14, 0xfb, 0x22, 0x9d, 0x77, 0x66, 0x21, 0x56, 0x65, 0xd6,
0x66, 0xed, 0x3c, 0x9b, 0x5b, 0xef, 0x59, 0x7f, 0x11, 0x66, 0x90, 0x84, 0xce, 0xb3, 0xb9, 0xf5, 0x9e, 0xf5, 0x17, 0x61, 0x06, 0x4e, 0x38, 0x2e,
0xe5, 0x62, 0xc6, 0xd6, 0x72, 0xc9, 0x8a, 0x65, 0xc6, 0x12, 0x19, 0x85, 0x66, 0x6c, 0x2d, 0x97, 0xac, 0x58, 0x66, 0x2c, 0x91, 0x51, 0x98, 0x24,
0x49, 0xb2, 0xb6, 0xdc, 0x8b, 0x49, 0x70, 0xe1, 0xd9, 0x2e, 0x76, 0xce, 0x6b, 0xcb, 0xbd, 0x98, 0x04, 0x17, 0x9e, 0xed, 0x82, 0x72, 0x2e, 0x34,
0x85, 0xc6, 0x6a, 0x5b, 0xe8, 0x05, 0x2f, 0x58, 0x2b, 0xe6, 0xf7, 0xad, 0x4e, 0xdb, 0x42, 0x2f, 0x78, 0xc1, 0x5a, 0x31, 0xbf, 0x6f, 0xed, 0xb1,
0x3d, 0xd6, 0xca, 0x0b, 0x19, 0xb7, 0x98, 0xc4, 0x80, 0xe6, 0x4a, 0x63, 0x56, 0x5e, 0xc8, 0xb8, 0xc5, 0x24, 0x16, 0x34, 0x57, 0x1a, 0x2b, 0x31,
0x24, 0xe6, 0xb3, 0x70, 0x99, 0x40, 0x96, 0x2a, 0xd7, 0x18, 0x09, 0xe3, 0x9f, 0x85, 0xcb, 0x04, 0xbc, 0x54, 0x79, 0xc6, 0x70, 0x18, 0x4f, 0x07,
0xe9, 0x80, 0x74, 0xc3, 0xb7, 0x65, 0x7d, 0x29, 0x78, 0x2e, 0x95, 0xd0, 0x24, 0x1b, 0xbe, 0x2d, 0xeb, 0x4b, 0xc1, 0x73, 0xa9, 0x84, 0x96, 0xc5,
0xb2, 0x58, 0x5f, 0x5b, 0xee, 0x74, 0xea, 0x63, 0xc2, 0xf2, 0xfa, 0xae, 0xfa, 0xda, 0x72, 0xa7, 0x53, 0x1f, 0x1b, 0x96, 0xd7, 0x77, 0x9d, 0x73,
0x73, 0xee, 0x07, 0xfe, 0xd5, 0x39, 0xad, 0xbb, 0x09, 0xd5, 0x02, 0x0b, 0x3f, 0xf0, 0xaf, 0xce, 0xe9, 0xdc, 0x4d, 0xa8, 0x16, 0x38, 0xa8, 0x20,
0x15, 0xb4, 0xe7, 0xc5, 0xb5, 0x75, 0xee, 0x4e, 0xfd, 0x69, 0x7f, 0x3a, 0x3d, 0x2f, 0xae, 0xad, 0x73, 0x77, 0xea, 0x4f, 0xfb, 0xd3, 0x11, 0x76,
0xc2, 0xcc, 0x42, 0xeb, 0xdc, 0x1a, 0x4c, 0xc7, 0x3d, 0x67, 0x82, 0x2f, 0x16, 0x5a, 0xe7, 0xd6, 0x60, 0x3a, 0xee, 0x39, 0x13, 0x7c, 0x19, 0x21,
0xa3, 0xe4, 0x42, 0x2a, 0x6d, 0xe4, 0x04, 0x17, 0x2e, 0x2d, 0xf9, 0xfe, 0x17, 0x52, 0x69, 0xc3, 0x27, 0xb8, 0x70, 0xe9, 0xc8, 0xf7, 0x2f, 0xeb,
0x65, 0xbd, 0xfe, 0x95, 0x3a, 0xee, 0x74, 0xbe, 0x7f, 0x59, 0x2e, 0xc7, 0xf3, 0xaf, 0xd4, 0x71, 0xa7, 0xf3, 0xfd, 0xcb, 0xf2, 0x38, 0x3e, 0xbe,
0xc7, 0xf7, 0x2f, 0xcf, 0x7c, 0xff, 0x3c, 0x38, 0x9f, 0xba, 0xfe, 0x2b, 0x7f, 0x79, 0xe6, 0xfb, 0xe7, 0xc1, 0xf9, 0xd4, 0xf5, 0x5f, 0xa9, 0x8e,
0xd5, 0xb1, 0xcc, 0x47, 0x6f, 0x30, 0x20, 0xdb, 0xac, 0xcd, 0x0c, 0x3e, 0x65, 0x3e, 0x7a, 0x83, 0x01, 0xe9, 0x66, 0x6d, 0x76, 0xf0, 0xf1, 0xba,
0x5e, 0x77, 0xbb, 0x5d, 0xcb, 0xf3, 0xce, 0xea, 0xef, 0xc3, 0x43, 0xd8, 0xdb, 0xed, 0x5a, 0x9e, 0x77, 0x56, 0x7f, 0x1f, 0x1e, 0x42, 0xef, 0x81,
0x3d, 0x10, 0x2a, 0xbc, 0x49, 0x38, 0xeb, 0x0f, 0x26, 0xe4, 0xff, 0x8c, 0x50, 0xe1, 0x4d, 0xc2, 0x59, 0x7f, 0x30, 0x21, 0xfb, 0x67, 0x4c, 0x64,
0x89, 0xac, 0xb6, 0x3e, 0x95, 0x31, 0xb7, 0xa6, 0xc3, 0xe1, 0xc8, 0x99, 0xb5, 0xf6, 0xa9, 0x8c, 0xb9, 0x35, 0x1d, 0x0e, 0x47, 0xce, 0xc4, 0xae,
0xd8, 0xb5, 0xa9, 0xb3, 0x30, 0x51, 0xdc, 0x1a, 0x38, 0x5e, 0xef, 0x64, 0x55, 0x9d, 0x85, 0x89, 0xe2, 0xd6, 0xc0, 0xf1, 0x7a, 0x27, 0x23, 0x3b,
0x64, 0x07, 0xee, 0xf4, 0xc2, 0xb7, 0x5d, 0xba, 0x82, 0xcd, 0xd4, 0x7b, 0x70, 0xa7, 0x17, 0xbe, 0xed, 0x92, 0x0b, 0x36, 0x5b, 0xef, 0xd9, 0x29,
0x76, 0xca, 0x33, 0x5e, 0x84, 0x9a, 0x33, 0xa5, 0x79, 0xae, 0x8e, 0x31, 0xcf, 0x78, 0x11, 0x6a, 0xce, 0x94, 0xe6, 0xb9, 0x3a, 0xc6, 0xca, 0x77,
0xf2, 0x1d, 0x8b, 0x62, 0x5c, 0xab, 0x5e, 0x74, 0xb4, 0xec, 0xcc, 0x71, 0x2c, 0x8a, 0xe1, 0x56, 0xbd, 0xe8, 0x68, 0xd9, 0x99, 0xc3, 0x91, 0x9d,
0x91, 0x9d, 0x68, 0xa9, 0xb4, 0x4c, 0x3b, 0x64, 0xb6, 0x32, 0x0b, 0xe6, 0x68, 0xa9, 0xb4, 0x4c, 0x3b, 0xa4, 0xb6, 0x32, 0x07, 0xe6, 0xd2, 0xb8,
0xd2, 0x5c, 0xcf, 0x77, 0xa7, 0x53, 0x32, 0xb9, 0xa3, 0x8a, 0xa8, 0x93, 0xe7, 0xbb, 0xd3, 0x29, 0xa9, 0xdc, 0x51, 0x45, 0xd4, 0xc9, 0x6f, 0xe7,
0xdf, 0xce, 0x3b, 0x51, 0xb1, 0xce, 0xb1, 0x47, 0x27, 0xaa, 0x33, 0xaf, 0x9d, 0xa8, 0x58, 0xe7, 0xa0, 0xd1, 0x89, 0xea, 0xcc, 0x2b, 0xb6, 0x41,
0xc4, 0x06, 0x11, 0x2f, 0x74, 0x1b, 0xeb, 0xf7, 0xa3, 0xf0, 0x83, 0x2e, 0xc4, 0x0b, 0xdd, 0xc6, 0xf9, 0xfd, 0x28, 0xfc, 0xa0, 0x8b, 0x25, 0x67,
0x96, 0x9c, 0xed, 0xc7, 0x4b, 0x4c, 0x08, 0x99, 0x7d, 0x78, 0xf7, 0xf6, 0xfb, 0xf1, 0x12, 0x1b, 0x42, 0x66, 0x1f, 0xde, 0xbd, 0x3d, 0xea, 0x2e,
0xa8, 0xbb, 0xe8, 0xa6, 0x5d, 0xc5, 0xf6, 0xc9, 0x7d, 0x1f, 0xd2, 0x35, 0xba, 0x69, 0x57, 0xb1, 0x7d, 0x32, 0xdf, 0x87, 0x74, 0x4d, 0x7f, 0xda,
0xfd, 0x69, 0xf3, 0x87, 0x30, 0xcd, 0x13, 0xde, 0x8e, 0x64, 0x6a, 0xf5, 0xfc, 0x21, 0x4c, 0xf3, 0x84, 0xb7, 0x23, 0x99, 0x5a, 0x7d, 0xdb, 0xf5,
0x6d, 0xd7, 0x0f, 0x86, 0xce, 0x88, 0x8c, 0x69, 0x6a, 0xd1, 0x31, 0x62, 0x83, 0xa1, 0x33, 0x22, 0x65, 0x9a, 0x52, 0x74, 0x0c, 0xdb, 0x9c, 0xa7,
0x73, 0x9e, 0x5a, 0x9f, 0xec, 0xab, 0x9d, 0x0b, 0x6e, 0xf9, 0xda, 0xcc, 0xd6, 0x27, 0xfb, 0x6a, 0xe7, 0x81, 0x5b, 0xbe, 0x36, 0xfb, 0xef, 0xd9,
0xbf, 0x67, 0x17, 0x79, 0x0e, 0xa8, 0x24, 0x70, 0x57, 0xc2, 0xe4, 0x8c, 0x45, 0x9e, 0x03, 0x2a, 0x09, 0xcc, 0x95, 0x30, 0x39, 0x63, 0x9a, 0x83,
0x69, 0x0e, 0xe9, 0x64, 0x70, 0x98, 0xc5, 0x30, 0x1a, 0xaa, 0x44, 0x6c, 0x3b, 0x29, 0x1c, 0x66, 0x31, 0x94, 0x86, 0x28, 0x11, 0x9b, 0x09, 0xd8,
0x26, 0xe0, 0x53, 0x32, 0x19, 0xcb, 0x1b, 0xd0, 0x01, 0xc6, 0xcc, 0x28, 0x94, 0x54, 0xc6, 0xf1, 0x06, 0x74, 0x80, 0x31, 0xb3, 0xca, 0x56, 0x00,
0x5b, 0x01, 0x6c, 0xdc, 0x80, 0x9a, 0x86, 0xf9, 0x03, 0x8f, 0x96, 0x9a, 0x1b, 0x37, 0xa0, 0xa6, 0x65, 0xfe, 0xc0, 0xa3, 0xa5, 0xe6, 0xb1, 0xe5,
0xc7, 0x96, 0xe7, 0xf7, 0x7c, 0xa7, 0x1f, 0x98, 0x6b, 0x3f, 0xef, 0xf9, 0xf9, 0x3d, 0xdf, 0xe9, 0x07, 0xc6, 0xed, 0xe7, 0x3d, 0xff, 0x8c, 0x5c,
0x67, 0x74, 0x85, 0xd6, 0x97, 0x38, 0xd4, 0x21, 0xb0, 0xc3, 0xaf, 0x1b, 0x68, 0x7d, 0x89, 0x43, 0x1d, 0x02, 0x3b, 0xfc, 0xba, 0x81, 0xd3, 0x74,
0x38, 0x4d, 0xd7, 0xea, 0x2e, 0x31, 0x48, 0x85, 0x85, 0xf3, 0x82, 0xab, 0xad, 0xee, 0x12, 0x83, 0x54, 0x68, 0x38, 0x2f, 0xb8, 0x2a, 0xd1, 0x8a,
0x12, 0xad, 0x18, 0x14, 0x9a, 0xbf, 0xc6, 0x84, 0xd0, 0x3f, 0x28, 0x82, 0x45, 0xa1, 0xf9, 0x6b, 0x6c, 0x08, 0xfd, 0x83, 0x22, 0xd8, 0x17, 0x2c,
0x7d, 0xc1, 0xa2, 0x85, 0xa4, 0x60, 0x19, 0x9c, 0xd4, 0x38, 0x34, 0x7b, 0x5a, 0x48, 0x0a, 0x96, 0xc1, 0x49, 0x8d, 0x43, 0x43, 0x6b, 0x9d, 0x4d,
0xad, 0xb3, 0xa9, 0x47, 0x28, 0x38, 0x38, 0x7c, 0xdb, 0xee, 0xe2, 0x7f, 0x3d, 0x42, 0xc1, 0xc1, 0xe1, 0xdb, 0x76, 0x17, 0xff, 0x3b, 0x38, 0x7e,
0x07, 0xc7, 0xaf, 0x5f, 0x77, 0x8f, 0xac, 0x2a, 0xdc, 0xe8, 0x96, 0xac, 0xfd, 0xba, 0x7b, 0x64, 0x55, 0xe1, 0x46, 0x5e, 0xb2, 0xaa, 0x00, 0x29,
0x2a, 0x40, 0x0a, 0x29, 0xb5, 0x75, 0xde, 0xf3, 0xbc, 0xcf, 0x03, 0xf6, 0xa4, 0xd4, 0xd6, 0x79, 0xcf, 0xf3, 0x3e, 0x0f, 0xd8, 0x07, 0x88, 0x30,
0x01, 0x2a, 0x0c, 0xe9, 0xa0, 0xc6, 0xb1, 0x59, 0xb2, 0xde, 0x63, 0xbc, 0xa4, 0x8b, 0x1a, 0xd7, 0x66, 0xc9, 0x7a, 0x8f, 0xf1, 0x3a, 0x7e, 0x4a,
0x8e, 0x9f, 0x12, 0x4f, 0xa4, 0x59, 0xc1, 0xef, 0x96, 0xa2, 0xe0, 0xa5, 0x3c, 0x91, 0x64, 0x05, 0xbf, 0x5b, 0x8a, 0x82, 0x97, 0x82, 0x01, 0xf1,
0x62, 0x40, 0xbc, 0x98, 0xad, 0xf7, 0x67, 0xcb, 0x24, 0x69, 0x01, 0x84, 0x62, 0xb6, 0xde, 0x9f, 0x2d, 0x93, 0xa4, 0x05, 0x10, 0x8e, 0x36, 0xb1,
0xa3, 0x4d, 0xec, 0x94, 0xeb, 0x6b, 0xb1, 0xb5, 0xfe, 0x46, 0xaa, 0x55, 0x53, 0x9e, 0xaf, 0xd9, 0xd6, 0xf2, 0x1b, 0xae, 0x56, 0x65, 0x02, 0xd2,
0xb9, 0x80, 0xec, 0x37, 0xb8, 0x69, 0xc7, 0x37, 0x70, 0x47, 0x18, 0xa7, 0xdf, 0xe0, 0xa6, 0x1d, 0xdf, 0xc0, 0x1c, 0x61, 0x9c, 0x8a, 0xec, 0xda,
0x22, 0xbb, 0x36, 0x81, 0x14, 0x2d, 0x0b, 0xa1, 0x11, 0x6f, 0xce, 0x04, 0x04, 0x52, 0xb4, 0x2c, 0x84, 0x46, 0xbc, 0x39, 0x13, 0x58, 0x6e, 0x34,
0x9e, 0x1b, 0x8d, 0x80, 0xc4, 0xfe, 0xa7, 0x06, 0x14, 0x5f, 0xbc, 0xe8, 0x02, 0x12, 0xfb, 0x9f, 0x1a, 0x50, 0x7c, 0xf1, 0xa2, 0x7f, 0xd6, 0x9b,
0x9f, 0xf5, 0x26, 0xa7, 0x36, 0xf3, 0xcf, 0x1c, 0x8f, 0xf9, 0x53, 0xf6, 0x9c, 0xda, 0xcc, 0x3f, 0x73, 0x3c, 0xe6, 0x4f, 0xd9, 0x27, 0xdb, 0x3e,
0xc9, 0xb6, 0xcf, 0xd9, 0xd5, 0xf4, 0xc2, 0x65, 0xc6, 0xb6, 0x41, 0xcf, 0x67, 0x57, 0xd3, 0x0b, 0x97, 0x19, 0xdd, 0x06, 0x3d, 0xbf, 0xc7, 0xbc,
0xef, 0x31, 0xaf, 0x37, 0xb4, 0x5f, 0xbc, 0xb0, 0x3c, 0xbb, 0xef, 0xda, 0xde, 0xd0, 0x7e, 0xf1, 0xc2, 0xf2, 0xec, 0xbe, 0x6b, 0xfb, 0x01, 0xbc,
0x7e, 0x80, 0xdb, 0x87, 0x80, 0x17, 0xff, 0xf5, 0x71, 0x38, 0xb0, 0x3f, 0x0f, 0x06, 0x2f, 0xfe, 0xeb, 0xe3, 0x70, 0x60, 0x7f, 0x76, 0xf1, 0xff,
0xbb, 0xf8, 0xff, 0x7f, 0xff, 0xcf, 0x4b, 0x48, 0xea, 0x2d, 0xb5, 0xdc, 0xff, 0xfe, 0x9f, 0x97, 0xe0, 0xd4, 0x5b, 0x6a, 0xb9, 0x9f, 0xc8, 0x39,
0x4f, 0xe4, 0x1c, 0xd1, 0x51, 0xf0, 0x94, 0xa7, 0x37, 0xb0, 0x35, 0x0e, 0xa2, 0xa3, 0xe0, 0x29, 0x4f, 0x6f, 0xa0, 0x6b, 0x1c, 0xae, 0x95, 0x05,
0xd7, 0xca, 0x02, 0xf6, 0x9d, 0x49, 0xe0, 0xda, 0x63, 0x7b, 0x7c, 0x82, 0xec, 0x3b, 0x93, 0xc0, 0xb5, 0xc7, 0xf6, 0xf8, 0x04, 0xa1, 0x30, 0xe8,
0x50, 0x18, 0xf4, 0xae, 0x3c, 0xec, 0x7f, 0x6b, 0xf5, 0xa7, 0xd3, 0x4f, 0x5d, 0x79, 0xa0, 0x7f, 0x6b, 0xf5, 0xa7, 0xd3, 0x4f, 0x8e, 0x6d, 0x72,
0x8e, 0x6d, 0x38, 0xa6, 0xe1, 0xd2, 0x20, 0x5c, 0x71, 0x25, 0x53, 0x5e, 0x4c, 0xc3, 0xa4, 0x41, 0xb8, 0xe2, 0x4a, 0xa6, 0xbc, 0xde, 0xde, 0xd0,
0x4f, 0x6f, 0xf6, 0x35, 0xd7, 0x88, 0x2c, 0x2a, 0x78, 0x2c, 0x4a, 0xaf, 0x35, 0xcf, 0x88, 0x2c, 0x2a, 0x78, 0x2c, 0x4a, 0xab, 0xb8, 0x94, 0x14,
0xb8, 0x44, 0x8a, 0x0a, 0xa8, 0x29, 0xe4, 0xc3, 0x9a, 0x85, 0x4b, 0x78, 0x15, 0x50, 0x53, 0xc8, 0x87, 0x35, 0x0b, 0x97, 0xb0, 0x72, 0x06, 0x80,
0x39, 0x03, 0xc0, 0x0c, 0xde, 0xd9, 0x82, 0x87, 0x31, 0x14, 0x31, 0x54, 0x19, 0xbc, 0xb3, 0x05, 0x0f, 0x63, 0x08, 0x62, 0x52, 0x29, 0x80, 0xb8,
0x0a, 0x20, 0x2e, 0xc1, 0x2c, 0xcc, 0x19, 0x58, 0xae, 0x7d, 0x69, 0xbb, 0x54, 0xd5, 0x87, 0xe5, 0xda, 0x97, 0xb6, 0xeb, 0xd9, 0x01, 0x52, 0xc6,
0x9e, 0x1d, 0x80, 0x30, 0x7e, 0xb9, 0x0a, 0x7a, 0x17, 0xfe, 0x99, 0x3d, 0x2f, 0x57, 0x41, 0xef, 0xc2, 0x3f, 0xb3, 0x27, 0x00, 0x16, 0xc0, 0x35,
0x01, 0xac, 0x00, 0xad, 0x29, 0x58, 0xcf, 0xc1, 0x2d, 0xb2, 0x5f, 0xf6, 0xdd, 0xe4, 0xbd, 0x5f, 0xf6, 0x3f, 0xdb, 0x27, 0xb4, 0xb5, 0x4f, 0x0b,
0x3f, 0xdb, 0x27, 0x34, 0xb3, 0x8f, 0xef, 0x8a, 0x93, 0x00, 0x92, 0x6b, 0x55, 0x5e, 0x02, 0x50, 0xae, 0xad, 0x5e, 0xdf, 0x77, 0x2e, 0xed, 0xa0,
0xab, 0xd7, 0xf7, 0x9d, 0x4b, 0x3b, 0xe8, 0xe3, 0x76, 0x82, 0x11, 0xfd, 0x0f, 0x0f, 0x05, 0x23, 0xfa, 0x35, 0x76, 0x26, 0x08, 0x74, 0x52, 0xec,
0x1a, 0x3b, 0x13, 0x04, 0x39, 0x19, 0x75, 0xf0, 0xae, 0x0b, 0xd1, 0x9e, 0xe0, 0x5d, 0x17, 0xcc, 0x3d, 0x9b, 0xe0, 0x49, 0x80, 0xf8, 0xd5, 0x43,
0x4d, 0xd0, 0x24, 0x30, 0xfc, 0xea, 0x22, 0x44, 0x08, 0x69, 0x92, 0x71, 0x88, 0x12, 0x23, 0x0d, 0xe7, 0x31, 0xd3, 0x92, 0x21, 0x2d, 0xcf, 0x44,
0x1e, 0x33, 0x2d, 0x19, 0x28, 0x79, 0x26, 0x8a, 0x94, 0xf1, 0xfd, 0x34, 0x91, 0x32, 0xbe, 0x9f, 0x86, 0x22, 0x61, 0x33, 0xf8, 0xba, 0xe0, 0x73,
0x14, 0x09, 0x9b, 0xe1, 0x9e, 0x0b, 0x3e, 0x17, 0x4a, 0x97, 0x51, 0x0b, 0xa1, 0x74, 0x19, 0xb9, 0xe0, 0x79, 0xea, 0x78, 0x94, 0x4b, 0x6c, 0x24,
0x99, 0xa7, 0x8e, 0x47, 0x3c, 0x62, 0x83, 0xd0, 0x46, 0x90, 0x3a, 0x19, 0xb5, 0x11, 0xb8, 0x4e, 0x86, 0x8e, 0x3b, 0x6e, 0xb8, 0x72, 0x20, 0xb9,
0x3a, 0xee, 0xb8, 0x71, 0x8d, 0x03, 0xc9, 0x15, 0xcb, 0xa4, 0x66, 0xa0, 0x62, 0x99, 0xd4, 0x0c, 0xe9, 0x5b, 0xae, 0x2a, 0x62, 0x5c, 0x40, 0x31,
0x6e, 0xb9, 0xaa, 0x36, 0xe3, 0x00, 0x8a, 0x37, 0x03, 0x06, 0x06, 0x87, 0x67, 0x00, 0xc1, 0x60, 0x34, 0x13, 0x84, 0x51, 0x24, 0x97, 0x99, 0x2e,
0x99, 0x00, 0x8c, 0x22, 0xb9, 0xcc, 0x74, 0x09, 0x9e, 0x0d, 0x49, 0x19, 0x01, 0xb4, 0x49, 0x54, 0x86, 0xbd, 0x6b, 0xf4, 0x6f, 0x30, 0x35, 0x22,
0xf1, 0xae, 0xb1, 0xbe, 0x21, 0xd4, 0xa8, 0x98, 0x22, 0xc0, 0x99, 0x12, 0xa6, 0x08, 0x72, 0xa6, 0xc4, 0xdc, 0xa4, 0x3e, 0x88, 0x7a, 0x2f, 0xf8,
0x73, 0x43, 0x7b, 0x50, 0xf5, 0x5e, 0xf0, 0x15, 0xc4, 0xae, 0xf5, 0x42, 0x0a, 0x6c, 0xd7, 0x7a, 0x21, 0xb2, 0x79, 0x1b, 0x92, 0xfd, 0x7c, 0xe1,
0x64, 0xf3, 0x36, 0x34, 0xfb, 0xf9, 0xc2, 0x71, 0xed, 0xc0, 0x73, 0x4e, 0xb8, 0x76, 0xe0, 0x39, 0xa7, 0x13, 0x78, 0xfa, 0xd2, 0xb1, 0x3f, 0x37,
0x27, 0xb8, 0xe5, 0x4b, 0xc7, 0xfe, 0xdc, 0x90, 0xd0, 0x0f, 0x23, 0x84, 0x38, 0xf4, 0xc3, 0x08, 0x21, 0x1d, 0xde, 0x03, 0xa1, 0x90, 0x45, 0xb1,
0x73, 0x78, 0x0f, 0x74, 0x42, 0x17, 0xc5, 0x72, 0x11, 0xe9, 0x65, 0xc1, 0x5c, 0x44, 0x7a, 0x59, 0x70, 0xcb, 0x9e, 0x98, 0x7b, 0xfb, 0xbd, 0xfe,
0x2d, 0x7b, 0x62, 0xce, 0xed, 0xf7, 0xfa, 0x67, 0x76, 0xd0, 0xbb, 0x04, 0x99, 0x1d, 0xf4, 0x2e, 0x81, 0x33, 0xb7, 0x41, 0x35, 0x26, 0x1b, 0x40,
0xc6, 0xdc, 0xc6, 0xae, 0x31, 0xf9, 0x00, 0xc6, 0x88, 0x59, 0x75, 0x8b, 0x19, 0x31, 0xab, 0x3c, 0x59, 0x9f, 0x9f, 0x4c, 0x7d, 0x67, 0x78, 0x15,
0xf5, 0xfa, 0xc9, 0xd4, 0x77, 0x86, 0x57, 0x01, 0xf9, 0xa0, 0xb9, 0x5c, 0x90, 0x0d, 0x9a, 0xc7, 0x25, 0x72, 0x45, 0xcc, 0x35, 0xa8, 0x8e, 0x4d,
0x82, 0x27, 0x62, 0xae, 0xb1, 0xeb, 0xd8, 0xa4, 0x09, 0x22, 0x7f, 0xa4, 0xa9, 0xa0, 0x02, 0x80, 0xb2, 0xb5, 0x58, 0xde, 0x50, 0x4e, 0xa3, 0xd0,
0xac, 0xc5, 0xf2, 0x86, 0xf8, 0x8c, 0xc2, 0x42, 0x68, 0x55, 0xb2, 0xaa, 0x10, 0x5a, 0x95, 0x99, 0x55, 0x28, 0xb5, 0xe4, 0xaa, 0x73, 0x70, 0xf4,
0x50, 0x6a, 0xc9, 0x55, 0xe7, 0xe0, 0xe8, 0x4d, 0x2d, 0xf3, 0x39, 0x24, 0xa6, 0xe6, 0xf9, 0x1c, 0x16, 0x36, 0x97, 0x58, 0x5f, 0x56, 0xfc, 0x66,
0x6c, 0x0e, 0xb1, 0xbe, 0xac, 0xf8, 0xcd, 0x42, 0xca, 0x5b, 0xe2, 0x97, 0x21, 0xe5, 0x2d, 0xe5, 0x98, 0x7e, 0x01, 0x6c, 0xe9, 0x50, 0xdd, 0xc2,
0x7e, 0x01, 0x5c, 0xe9, 0x50, 0xdd, 0xc2, 0x23, 0xf0, 0xf1, 0x7d, 0x98, 0x22, 0xb0, 0xf1, 0x7d, 0x98, 0x90, 0x69, 0x60, 0x63, 0xe4, 0x28, 0x65,
0x90, 0x6b, 0xe0, 0x63, 0xf0, 0x93, 0xb2, 0xfc, 0x9e, 0xf7, 0x29, 0x70, 0xf9, 0x3d, 0xef, 0x53, 0xe0, 0x4c, 0xe0, 0xac, 0xcb, 0x1e, 0x49, 0x79,
0x26, 0xb8, 0xac, 0xcb, 0x1e, 0x69, 0x79, 0x40, 0xb7, 0xc3, 0x13, 0x01, 0x40, 0xde, 0xe1, 0x89, 0x00, 0x4e, 0x51, 0xb6, 0x53, 0x2e, 0x97, 0x9a,
0x8c, 0x22, 0x65, 0xa7, 0x5c, 0x2e, 0x35, 0x2d, 0x47, 0x60, 0xca, 0x2c, 0x8e, 0x23, 0x38, 0x65, 0x16, 0x2b, 0x6b, 0x60, 0x13, 0x3a, 0xdc, 0xc0,
0x56, 0xd6, 0xc0, 0x26, 0x74, 0xb8, 0x81, 0xef, 0x8c, 0x6d, 0xa4, 0x0a, 0x77, 0xc6, 0x36, 0xca, 0x05, 0x08, 0xde, 0xe0, 0x36, 0x42, 0x01, 0xd5,
0x6c, 0x78, 0x83, 0xd3, 0x08, 0x05, 0x94, 0xff, 0x4a, 0x1d, 0x07, 0x0d, 0xc0, 0x52, 0xc6, 0x41, 0x43, 0xd9, 0x93, 0xe5, 0x6c, 0x66, 0xb2, 0x6b,
0x63, 0x4f, 0x96, 0xb3, 0x99, 0x61, 0xd6, 0x6c, 0x0e, 0x8e, 0x04, 0xa2, 0x36, 0x47, 0x9e, 0x04, 0xaa, 0x23, 0xd4, 0xf1, 0x8c, 0x27, 0x7b, 0xec,
0x23, 0xe4, 0xf0, 0x8c, 0x27, 0x7b, 0xec, 0x96, 0xf3, 0x9c, 0x52, 0x39, 0x96, 0xf3, 0x9c, 0xca, 0x39, 0xcc, 0x2c, 0x4c, 0x36, 0xad, 0xea, 0x7a,
0xdc, 0x2c, 0x0c, 0x93, 0x56, 0x39, 0x3d, 0x96, 0xd9, 0x0f, 0x9a, 0xdd, 0x2c, 0xb3, 0x1f, 0x34, 0xbb, 0xcd, 0x00, 0x8b, 0x15, 0xf5, 0x13, 0x66,
0x66, 0x80, 0xc5, 0x8a, 0x6a, 0x09, 0x33, 0xd9, 0x46, 0x30, 0x4f, 0x06, 0xb3, 0x8d, 0x80, 0x9e, 0x0c, 0x82, 0x93, 0x8b, 0xe1, 0x90, 0x2a, 0x94,
0xc1, 0xc9, 0xc5, 0x70, 0x48, 0xd9, 0xc9, 0x26, 0x53, 0x0f, 0x08, 0x96, 0x4d, 0xaa, 0x1e, 0x10, 0x2c, 0x27, 0x14, 0x2c, 0xc8, 0x3a, 0x48, 0xd9,
0x13, 0x0a, 0x14, 0x30, 0x0e, 0xe8, 0x7a, 0x0d, 0x6c, 0x92, 0x61, 0x74, 0x6b, 0x60, 0x93, 0x14, 0x23, 0x6f, 0x94, 0x0d, 0x87, 0x77, 0x71, 0xf2,
0x1b, 0x65, 0xb1, 0xe1, 0x5d, 0x9c, 0xfc, 0xbf, 0xdd, 0xf7, 0x4d, 0xaa, 0xff, 0x76, 0xdf, 0x37, 0xe5, 0xb6, 0x6e, 0x3e, 0x5e, 0xa9, 0xda, 0x63,
0xad, 0x0b, 0x8f, 0x57, 0xaa, 0xbe, 0xb1, 0x32, 0x69, 0x53, 0x7a, 0x4b, 0x65, 0xe1, 0xa6, 0x12, 0x97, 0x1a, 0x57, 0xa8, 0x54, 0xe7, 0xed, 0x39,
0xcd, 0x55, 0xa8, 0x54, 0xe7, 0xed, 0x39, 0xfd, 0xa6, 0x6b, 0x38, 0x7e, 0xfd, 0x26, 0x37, 0x1c, 0xbf, 0x79, 0xf7, 0x16, 0x7b, 0x3f, 0xff, 0x5c,
0xf3, 0xee, 0x2d, 0xe6, 0x7e, 0xfe, 0xb9, 0x9a, 0xb8, 0xbb, 0x33, 0xa3, 0x6d, 0xdc, 0xdd, 0x99, 0xd5, 0xc3, 0x37, 0x75, 0xa6, 0xad, 0xd9, 0xcc,
0x87, 0x6f, 0x6a, 0x96, 0xad, 0xc5, 0xcc, 0x0a, 0x99, 0x02, 0xb3, 0x31, 0x0a, 0x99, 0x02, 0xb3, 0x31, 0xb2, 0xa7, 0xb2, 0x86, 0xee, 0x74, 0xfc,
0x98, 0x53, 0x59, 0x43, 0x77, 0x3a, 0x7e, 0x9c, 0x83, 0xe1, 0x26, 0x80, 0xb8, 0x07, 0xc5, 0x37, 0x41, 0x6c, 0xa0, 0x9d, 0x87, 0x4a, 0xad, 0x64,
0x4d, 0x34, 0x13, 0xb4, 0xf3, 0x50, 0xa9, 0x95, 0x2c, 0xe2, 0x9a, 0x87, 0x11, 0xd7, 0xb9, 0x78, 0x93, 0x87, 0xa9, 0x2e, 0x48, 0x4a, 0x07, 0x5f,
0x37, 0x1c, 0x4c, 0x39, 0x41, 0x12, 0x15, 0x7c, 0xed, 0xc3, 0x6a, 0xa2, 0xdb, 0xb0, 0xda, 0x68, 0x97, 0x08, 0xf9, 0x7a, 0xbf, 0x3f, 0x72, 0x80,
0x5d, 0x22, 0xe4, 0xeb, 0xf9, 0xfe, 0xc8, 0x01, 0x02, 0x02, 0xc3, 0x01, 0x80, 0xc0, 0x31, 0x5c, 0xaa, 0x8f, 0x32, 0xfb, 0x95, 0x2d, 0xcb, 0xf4,
0xf5, 0x47, 0xc9, 0x7c, 0x65, 0xb9, 0x32, 0x3d, 0x37, 0x51, 0x5c, 0x03, 0xdc, 0x44, 0x71, 0x0d, 0xb4, 0x30, 0x17, 0xed, 0x06, 0xd8, 0x48, 0x3e,
0x2d, 0xcc, 0x45, 0xbb, 0x01, 0x36, 0xd2, 0xcf, 0x22, 0x14, 0x55, 0x35, 0x8b, 0x50, 0x54, 0xf5, 0x25, 0x3b, 0xf0, 0x68, 0xf2, 0x64, 0xc7, 0x08,
0xc9, 0x0e, 0x3c, 0x1a, 0x8e, 0xec, 0x18, 0x25, 0x3a, 0xf4, 0x8f, 0x2c, 0xd1, 0xa1, 0x7f, 0x64, 0x21, 0xfe, 0xc0, 0x2d, 0x7f, 0xfa, 0xc9, 0x9e,
0xc4, 0x1f, 0xb8, 0xe5, 0x4f, 0x3f, 0xd9, 0x93, 0x6f, 0xdc, 0x14, 0x45, 0x7c, 0x23, 0x51, 0x14, 0xc1, 0x36, 0x81, 0x96, 0xb7, 0x3c, 0xb3, 0x4c,
0xf0, 0x4d, 0xa0, 0xe5, 0x2d, 0xcf, 0x2c, 0x53, 0x4e, 0x68, 0x16, 0x25, 0x4b, 0xa1, 0x59, 0x94, 0x08, 0x64, 0x3e, 0x26, 0xe2, 0xb2, 0xcc, 0x72,
0x02, 0xac, 0xc7, 0x44, 0x5c, 0xa6, 0x58, 0x8e, 0x70, 0xd7, 0xc6, 0x95, 0x84, 0xbb, 0x36, 0xa6, 0xc4, 0x7e, 0xcd, 0x0e, 0x88, 0x53, 0x12, 0x85,
0x98, 0xaf, 0xc5, 0x01, 0x71, 0x4a, 0x22, 0xc9, 0xc7, 0x94, 0x96, 0x25, 0x3e, 0xa6, 0xd2, 0x2c, 0x51, 0xa4, 0x15, 0x1a, 0x05, 0x39, 0x2f, 0x4b,
0x12, 0xb4, 0x42, 0x91, 0x20, 0xe7, 0x65, 0xda, 0xef, 0x80, 0x3e, 0x7f, 0x7f, 0x07, 0x29, 0xf4, 0xf7, 0x3c, 0xd2, 0x1b, 0xf3, 0x98, 0x9d, 0x7f,
0xcf, 0x23, 0xbd, 0x71, 0x8f, 0x99, 0xf9, 0x97, 0xdd, 0xb3, 0x5a, 0xad, 0xd9, 0x3c, 0xab, 0xd5, 0xaa, 0x62, 0x05, 0x43, 0x29, 0x73, 0x91, 0xd1,
0x2a, 0x51, 0x70, 0x94, 0x32, 0x07, 0x19, 0x1b, 0xc8, 0x4f, 0x22, 0x9b, 0x81, 0xec, 0x24, 0xb2, 0x99, 0x6c, 0x73, 0x83, 0xaf, 0x6f, 0x3e, 0x0e,
0xc9, 0x36, 0x37, 0xf8, 0xfa, 0xe6, 0xe5, 0xd0, 0x92, 0x0a, 0x87, 0x5d, 0x29, 0xa9, 0x79, 0xd8, 0x65, 0xe2, 0x2a, 0xb5, 0x6d, 0x29, 0x25, 0x4b,
0x2e, 0xae, 0xa8, 0x6d, 0xcb, 0x28, 0x59, 0xba, 0xec, 0xd0, 0x48, 0xd9, 0x93, 0x1d, 0x1a, 0x2e, 0x3b, 0x6d, 0xfc, 0x2c, 0x55, 0x65, 0xe2, 0xca,
0xe9, 0xe3, 0x67, 0x77, 0x55, 0x2e, 0xae, 0x5c, 0x72, 0x77, 0xf7, 0x4f, 0x24, 0x77, 0x77, 0xff, 0xb4, 0x39, 0x90, 0x96, 0x0d, 0xf8, 0xd9, 0x5f,
0xbb, 0x03, 0xb4, 0x6c, 0xc0, 0xcf, 0xfe, 0xfa, 0x97, 0x3f, 0xfd, 0xed, 0xff, 0xf2, 0xa7, 0xbf, 0xfd, 0xf1, 0xcf, 0x54, 0x32, 0x77, 0x60, 0xa4,
0x8f, 0x7f, 0xa6, 0x74, 0xb9, 0x03, 0x23, 0x45, 0x98, 0x2f, 0xaa, 0xc0, 0x08, 0xf3, 0x45, 0x15, 0x18, 0x95, 0x04, 0xed, 0x6e, 0x03, 0x22, 0xef,
0xa8, 0x34, 0x68, 0x77, 0x1b, 0x10, 0x79, 0xcf, 0x76, 0x82, 0x64, 0xe7, 0xd9, 0x4e, 0x90, 0xec, 0xa4, 0x2a, 0x25, 0x07, 0x05, 0xcf, 0x22, 0x02,
0xae, 0x52, 0x73, 0xec, 0xe0, 0x59, 0x44, 0xc0, 0x58, 0x71, 0x71, 0x23, 0xc6, 0x8a, 0x8b, 0x1b, 0xb9, 0xcb, 0x6a, 0xc0, 0x41, 0xd6, 0xd6, 0x35,
0x77, 0x79, 0x0d, 0x38, 0xc8, 0xda, 0xba, 0xde, 0x1f, 0xcd, 0xc5, 0xfe, 0x7d, 0x34, 0x17, 0xfb, 0x37, 0x35, 0xd0, 0x0e, 0x7f, 0x03, 0x9e, 0xcf,
0x4d, 0x0d, 0xb4, 0xc3, 0xdf, 0x80, 0xe7, 0xf3, 0x5b, 0xb7, 0x40, 0x5a, 0x93, 0x6e, 0x81, 0xb4, 0xb2, 0xa0, 0x5e, 0x09, 0xad, 0x77, 0x25, 0xb6,
0x79, 0x50, 0xaf, 0x84, 0xd6, 0xbb, 0x88, 0xed, 0x1f, 0x70, 0xe3, 0xae, 0x7f, 0xc0, 0x8c, 0xbb, 0x3c, 0x8f, 0x18, 0xac, 0x58, 0x3f, 0x5a, 0xe1,
0x9b, 0x47, 0x0c, 0x56, 0xa2, 0x1f, 0xbd, 0xf0, 0x1b, 0xca, 0xff, 0xca, 0x37, 0x84, 0xff, 0x15, 0x9a, 0x5d, 0x52, 0x1b, 0xdb, 0xfd, 0x27, 0x64,
0x9e, 0x5d, 0x5a, 0x1b, 0xdf, 0xfd, 0x27, 0x74, 0x36, 0x82, 0x1b, 0xf7, 0x36, 0x8c, 0x1b, 0x7e, 0xfb, 0x06, 0x91, 0xbf, 0x26, 0xd9, 0x96, 0x38,
0xf6, 0x0d, 0x2a, 0x7f, 0xbd, 0x65, 0x5b, 0xe3, 0x88, 0x32, 0xee, 0x56, 0xa2, 0x8a, 0xbb, 0xd5, 0x09, 0xf3, 0x14, 0x33, 0x57, 0xd9, 0x70, 0x22,
0x15, 0xcc, 0x53, 0xf4, 0x5b, 0x65, 0xb1, 0x09, 0x5e, 0xc7, 0x0f, 0x59, 0xaf, 0xe3, 0x87, 0x2c, 0x57, 0xcd, 0xc9, 0x27, 0xa3, 0x5b, 0x75, 0xd8,
0x8e, 0x9a, 0x95, 0x4f, 0xda, 0xb6, 0x6a, 0xb1, 0xd5, 0x1b, 0xf4, 0xce, 0xea, 0x0d, 0x7a, 0xe7, 0xbe, 0xc9, 0xa8, 0xe5, 0x4a, 0xdd, 0x7f, 0x56,
0x7d, 0xc3, 0xa8, 0xe5, 0x48, 0x5d, 0x7b, 0x56, 0xf3, 0x55, 0x41, 0x7b, 0xfb, 0x55, 0x53, 0x7b, 0xda, 0xdf, 0xaa, 0x80, 0x55, 0x49, 0xdb, 0xe2,
0xda, 0xdf, 0xca, 0x80, 0x55, 0x4a, 0xdb, 0x92, 0x78, 0xd4, 0xb5, 0x1a, 0x78, 0xd4, 0xb5, 0x1a, 0xb5, 0xf0, 0xa8, 0x5b, 0x33, 0x2a, 0x65, 0x31,
0xb9, 0xf0, 0xa8, 0x5b, 0x0b, 0x2a, 0x75, 0x31, 0x5c, 0xd5, 0xd4, 0x05, 0xb9, 0xaa, 0x29, 0x0b, 0x18, 0x64, 0xc8, 0x41, 0xa6, 0x79, 0x43, 0x07,
0x02, 0x32, 0x70, 0x90, 0x29, 0xdc, 0x50, 0x3d, 0x6f, 0xd2, 0xc0, 0x7b, 0xbd, 0x29, 0x03, 0xef, 0x99, 0x21, 0x38, 0x66, 0xad, 0xe3, 0xa3, 0xee,
0x66, 0x36, 0x1c, 0xb3, 0xd6, 0xf1, 0x51, 0xf7, 0xf5, 0x8f, 0x2d, 0x0c, 0xeb, 0x1f, 0x5b, 0x58, 0xa8, 0xa9, 0xb0, 0xf6, 0xd8, 0xa3, 0x1f, 0x1c,
0xd4, 0xbb, 0x30, 0xf6, 0x58, 0x9f, 0x1f, 0x1c, 0x1c, 0x1e, 0x1c, 0xb4, 0x1c, 0x1e, 0x1c, 0xb4, 0xaa, 0x8a, 0x62, 0x7a, 0x36, 0xa5, 0xc0, 0x6c,
0xaa, 0x8c, 0x62, 0x6a, 0x36, 0xa5, 0x20, 0x6c, 0xb7, 0x3f, 0x88, 0x47, 0xb7, 0x3d, 0x28, 0x8f, 0x3c, 0xda, 0xa5, 0x34, 0x4b, 0x35, 0x36, 0xec,
0x1e, 0xfd, 0x52, 0xba, 0xa5, 0x6a, 0x19, 0x76, 0xf9, 0x04, 0x05, 0xc2, 0xb2, 0x09, 0x1a, 0x84, 0x4b, 0x67, 0x60, 0x8c, 0x62, 0x32, 0xd0, 0x7b,
0xa5, 0x33, 0x30, 0x4e, 0x31, 0x0c, 0xf4, 0x9e, 0x9d, 0x17, 0xf2, 0x5e, 0x76, 0x5e, 0xc8, 0x7b, 0x41, 0x1d, 0xa6, 0x69, 0xdf, 0xe6, 0x4c, 0xe6,
0x50, 0x75, 0x69, 0xca, 0xb7, 0x39, 0x93, 0x39, 0x69, 0xae, 0x4a, 0xe5, 0x24, 0xb9, 0x2a, 0x85, 0x03, 0xcd, 0xb1, 0xe9, 0xc8, 0x16, 0xe1, 0x3d,
0xb0, 0xe7, 0xd8, 0x54, 0x64, 0x8b, 0xf0, 0x9e, 0x92, 0xd5, 0xba, 0x5e, 0x15, 0xab, 0x75, 0x7d, 0x6a, 0xcd, 0x69, 0xa0, 0x26, 0x16, 0xa8, 0x84,
0xb5, 0xe6, 0xd4, 0x4c, 0x93, 0x08, 0x64, 0xc2, 0x52, 0xbf, 0xc7, 0x5e, 0xa5, 0x7c, 0x8f, 0xf3, 0x10, 0x26, 0x85, 0xf6, 0xbc, 0x8d, 0x39, 0x81,
0x08, 0x5d, 0x42, 0x7b, 0xde, 0x46, 0x8f, 0x40, 0xf5, 0x7c, 0x35, 0xab, 0x7a, 0xfa, 0x6a, 0x57, 0xb5, 0x1e, 0xf5, 0xaf, 0x78, 0x24, 0xe2, 0x96,
0x5a, 0x8f, 0xf6, 0x57, 0x32, 0x12, 0x71, 0xcb, 0xcb, 0xa1, 0x2a, 0xeb, 0x97, 0x4b, 0x55, 0xd5, 0x35, 0x96, 0xda, 0x63, 0xb9, 0x94, 0x89, 0x07,
0x1a, 0x4f, 0xed, 0xb1, 0x5c, 0xca, 0xc4, 0x03, 0x7c, 0xf6, 0x36, 0x99, 0xf8, 0xec, 0x6d, 0x2a, 0x63, 0xcd, 0xf0, 0xd1, 0x46, 0x47, 0xaf, 0xdf,
0xb1, 0x16, 0xf8, 0xe8, 0xa3, 0xa3, 0xd7, 0x6f, 0x7f, 0xdc, 0x3b, 0xe8, 0xfe, 0xb8, 0x77, 0xd0, 0xed, 0xee, 0x85, 0x18, 0xc6, 0x1e, 0x04, 0x37,
0x76, 0xf7, 0x42, 0x34, 0x62, 0x0f, 0x82, 0x1b, 0x67, 0x92, 0xdd, 0xc7, 0xc6, 0x24, 0xbd, 0x8f, 0xd1, 0x5f, 0xef, 0xe3, 0xef, 0x7e, 0x5c, 0x50,
0xa8, 0xad, 0xf7, 0xf1, 0x77, 0x3f, 0x2e, 0xa8, 0x5a, 0xe9, 0x98, 0x41, 0xb7, 0xd2, 0x31, 0x8b, 0x2c, 0x56, 0x59, 0x7d, 0x2b, 0xda, 0x51, 0xf4,
0x16, 0xab, 0xac, 0x3e, 0x15, 0xe5, 0x28, 0x6a, 0xbe, 0x5a, 0x22, 0xf5, 0x7c, 0x35, 0x47, 0x9a, 0x7b, 0x8e, 0xeb, 0x6b, 0x3e, 0xd6, 0xc2, 0x06,
0x3c, 0xc7, 0xf5, 0x31, 0x1f, 0x6b, 0x65, 0x03, 0x6d, 0x7a, 0x9b, 0x8d, 0xda, 0xcc, 0x37, 0x1b, 0x6b, 0x95, 0xbd, 0xea, 0x69, 0x3d, 0xa6, 0xd4,
0xb7, 0xca, 0x5a, 0xf5, 0xb4, 0x6e, 0x51, 0x6a, 0x93, 0x70, 0xa6, 0x57, 0x2a, 0xe1, 0x4e, 0xaf, 0xd2, 0x3d, 0x42, 0x5b, 0x25, 0x78, 0xd9, 0x98,
0xd9, 0x1e, 0xa1, 0xac, 0x12, 0xdc, 0xe4, 0xf4, 0xba, 0xe6, 0xaf, 0x4a, 0x57, 0x7d, 0x7f, 0xd5, 0xee, 0x8b, 0x80, 0xf4, 0x0c, 0xca, 0xfe, 0x0d,
0x7d, 0x11, 0x90, 0x9d, 0x41, 0x59, 0xbf, 0x61, 0x87, 0x53, 0x16, 0x34, 0x14, 0x4e, 0xd9, 0xd0, 0xa0, 0x16, 0x6c, 0x0c, 0x07, 0xd8, 0x99, 0xe8,
0xc8, 0x05, 0x1b, 0xc7, 0x01, 0x76, 0x26, 0x3a, 0x2a, 0x44, 0x36, 0xee, 0xa8, 0x10, 0xd9, 0xf0, 0x5b, 0x15, 0xa3, 0x25, 0x43, 0x84, 0xe5, 0x85,
0xad, 0x8a, 0xd1, 0x52, 0x20, 0xc2, 0xf2, 0xc2, 0xb5, 0x1b, 0x65, 0x94, 0x6b, 0x37, 0xda, 0x28, 0x3b, 0x33, 0x63, 0xbd, 0xa2, 0xca, 0x69, 0xee,
0x9d, 0x99, 0x96, 0x5e, 0x51, 0xe6, 0x34, 0xe7, 0x6f, 0xed, 0xa5, 0x9e, 0xdf, 0xa2, 0xa5, 0xb9, 0xb9, 0xee, 0x0f, 0xa9, 0x99, 0x2f, 0xb9, 0x80,
0xb9, 0xae, 0x0f, 0xa9, 0x98, 0x2f, 0xa5, 0x60, 0xbb, 0x99, 0x78, 0x54, 0xdc, 0x6c, 0x3c, 0x8a, 0x8e, 0x00, 0xa0, 0x96, 0x6e, 0x13, 0x05, 0x5b,
0x1d, 0x01, 0x40, 0x25, 0xdd, 0x26, 0x0a, 0xb6, 0x84, 0xbc, 0x3b, 0xfa, 0x4c, 0xde, 0x1d, 0xfd, 0x6f, 0xb7, 0x6b, 0x9d, 0xf6, 0x37, 0xcd, 0xa0,
0xdf, 0x6e, 0xd7, 0x3a, 0xed, 0x6f, 0x8a, 0x41, 0x53, 0xe3, 0x41, 0x48, 0xe9, 0xf1, 0xc0, 0xa4, 0xdc, 0x78, 0xe4, 0x92, 0x88, 0x19, 0x37, 0x7c,
0x39, 0xf1, 0x28, 0x25, 0x11, 0x33, 0x6e, 0xe4, 0xec, 0xd8, 0xee, 0xd9, 0x76, 0x90, 0x7b, 0xb6, 0xe7, 0xd1, 0x50, 0x32, 0x72, 0x86, 0xf6, 0x53,
0x9e, 0x47, 0x2d, 0xc9, 0xc8, 0x19, 0xda, 0x4f, 0xf7, 0x6f, 0x7c, 0x10, 0xfa, 0x8d, 0x0d, 0x62, 0x60, 0x4c, 0x2d, 0xd8, 0x6c, 0x99, 0x45, 0x7b,
0x03, 0x63, 0x6a, 0xc1, 0x66, 0xcb, 0x2c, 0xda, 0xdb, 0xe0, 0x5c, 0x2d, 0x1b, 0x9c, 0xab, 0x45, 0x78, 0x40, 0xe8, 0xc6, 0xdf, 0xc3, 0x37, 0x47,
0xc2, 0x03, 0x42, 0x37, 0xfe, 0x1e, 0xbe, 0x39, 0xaa, 0xe0, 0x1d, 0xbf, 0x15, 0xbc, 0xe3, 0x37, 0xad, 0xe6, 0x1d, 0x74, 0x66, 0x73, 0x85, 0x33,
0x69, 0x35, 0xcf, 0xa0, 0x35, 0x9b, 0x23, 0x9c, 0x41, 0x70, 0xd6, 0xf3, 0x08, 0xce, 0x7a, 0xde, 0xd9, 0xf0, 0x62, 0xd2, 0xc7, 0x25, 0x66, 0xeb,
0xce, 0x86, 0x17, 0x93, 0x3e, 0x0e, 0x31, 0x53, 0x8f, 0x3a, 0x9a, 0x03, 0x51, 0x46, 0x73, 0x01, 0x46, 0xfc, 0x2d, 0x11, 0xc9, 0x11, 0x05, 0x42,
0xd0, 0xde, 0x6f, 0xa9, 0x48, 0x17, 0x51, 0x20, 0x84, 0x51, 0xaf, 0x95, 0x18, 0xfd, 0x5a, 0x09, 0x8d, 0xa7, 0xbc, 0xcc, 0xb4, 0x88, 0x30, 0xac,
0xd0, 0x78, 0x2a, 0xcb, 0x74, 0x8a, 0x08, 0xc3, 0xaa, 0xec, 0xa7, 0x30, 0xda, 0x7e, 0x0a, 0x43, 0x9f, 0x46, 0xfc, 0x24, 0x8c, 0x38, 0xcd, 0x12,
0xf4, 0xa9, 0xbd, 0x4f, 0xc2, 0x88, 0x53, 0x2f, 0x51, 0x8d, 0x1b, 0x68, 0xd5, 0xba, 0x81, 0xc6, 0xe3, 0x8c, 0x5c, 0x22, 0xba, 0x94, 0xf8, 0x4e,
0x3c, 0xf6, 0xc7, 0x25, 0xa2, 0x4b, 0x8d, 0xef, 0x44, 0x26, 0x96, 0x4f, 0x64, 0x62, 0xf9, 0x24, 0x20, 0xab, 0x7d, 0x5c, 0xe6, 0x5e, 0x3a, 0x7d,
0x02, 0xb2, 0x9a, 0xc7, 0x61, 0xee, 0xa5, 0xd3, 0x27, 0x8f, 0x54, 0x95, 0xb2, 0x48, 0xd5, 0x79, 0xd6, 0xe3, 0xcc, 0xa9, 0xfb, 0x64, 0xa4, 0xb0,
0x67, 0xdd, 0xce, 0x9c, 0xba, 0x4f, 0x5a, 0x0a, 0xeb, 0x0b, 0xca, 0xa7, 0xbe, 0xa0, 0x7d, 0x2a, 0x9f, 0x9d, 0xaa, 0x77, 0x83, 0x46, 0x42, 0xa8,
0xf2, 0xc9, 0xa9, 0x7a, 0x33, 0x68, 0x10, 0x42, 0x55, 0x15, 0x35, 0x19, 0xba, 0xa2, 0x66, 0x46, 0xa0, 0x34, 0x64, 0x6c, 0x87, 0x46, 0xb5, 0x94,
0x81, 0x68, 0xc8, 0xf8, 0x0e, 0x85, 0x6a, 0xa9, 0x47, 0xfd, 0xbe, 0xf0, 0xa3, 0x7e, 0x63, 0x78, 0x22, 0x4a, 0x4d, 0x5b, 0x0e, 0x4b, 0x80, 0x52,
0x44, 0x95, 0x7a, 0x6f, 0xd9, 0x2c, 0x01, 0x4a, 0x69, 0x1a, 0x92, 0x61, 0x9a, 0x86, 0xa4, 0x98, 0xe2, 0x79, 0x68, 0x1e, 0x79, 0x52, 0x9c, 0x14,
0x8a, 0xe7, 0xa1, 0x79, 0xe0, 0x49, 0xb1, 0x52, 0xe4, 0x40, 0x1a, 0xbd, 0x39, 0x90, 0x46, 0xaf, 0x45, 0xaa, 0x0e, 0x9d, 0x8a, 0x6c, 0xcf, 0xc4,
0x14, 0xa9, 0x3a, 0x74, 0xaa, 0x6d, 0x7b, 0x26, 0xee, 0x5b, 0x56, 0xd5, 0x7d, 0xcb, 0xaa, 0x66, 0xfd, 0x6a, 0xf5, 0xdf, 0xd9, 0xe4, 0x3f, 0xe9,
0xe7, 0x57, 0xa3, 0xff, 0xce, 0x22, 0xff, 0x49, 0x7d, 0xdf, 0x35, 0xb8, 0xef, 0xbb, 0x06, 0x37, 0xb5, 0xe2, 0x7e, 0x01, 0x37, 0x90, 0x9a, 0x03,
0xa9, 0x0d, 0xf7, 0x0b, 0x5c, 0x03, 0x99, 0x39, 0xe0, 0x37, 0xcb, 0x39, 0x7e, 0xb3, 0x9c, 0xd3, 0x0f, 0x07, 0x1d, 0x16, 0xfd, 0xfd, 0x1c, 0x16,
0xfd, 0x70, 0x50, 0x61, 0xd1, 0xdf, 0xcf, 0x61, 0x61, 0xec, 0xb7, 0x8b, 0x46, 0x7f, 0xbb, 0x28, 0x64, 0x41, 0x3f, 0xfa, 0x85, 0xa0, 0xa9, 0xfa,
0x42, 0x16, 0xf4, 0xa3, 0x5f, 0x08, 0xea, 0xa8, 0x9f, 0x52, 0x63, 0x29, 0x69, 0x6a, 0x2c, 0x39, 0x58, 0x23, 0x8c, 0x50, 0x94, 0xde, 0xcd, 0xa7,
0xc1, 0x1a, 0xa1, 0x85, 0x22, 0x7a, 0x37, 0x9f, 0x56, 0x4d, 0xf1, 0xb5, 0x55, 0xa7, 0xf8, 0xda, 0x36, 0x46, 0xf5, 0x72, 0xde, 0x24, 0x37, 0xb4,
0x6f, 0x8c, 0xe9, 0x65, 0xbf, 0x49, 0xd7, 0xd0, 0xae, 0xc6, 0xaf, 0x37, 0xab, 0xf5, 0xeb, 0x0d, 0xd9, 0x86, 0xc2, 0x58, 0xe3, 0xe9, 0x71, 0x5a,
0xdb, 0x36, 0x3b, 0x8c, 0x37, 0x9e, 0x2e, 0xa7, 0xc1, 0xc6, 0x5a, 0x7a, 0x6c, 0x9c, 0xa5, 0xa7, 0xa7, 0x3a, 0x3f, 0x60, 0xbb, 0x7c, 0xf7, 0xc0,
0x76, 0xaa, 0xf9, 0x01, 0xd3, 0xe5, 0x9b, 0x07, 0x7e, 0x18, 0x68, 0xd1, 0x0f, 0x03, 0x2d, 0x7a, 0x2b, 0x32, 0x81, 0xad, 0xe8, 0x29, 0x40, 0xa6,
0x3b, 0x91, 0x09, 0x6c, 0x45, 0xcf, 0x00, 0x32, 0xc5, 0x0d, 0xc4, 0xb4, 0xf0, 0x40, 0x4c, 0xa7, 0x58, 0x21, 0x35, 0x7e, 0xbf, 0x54, 0xa8, 0xf7,
0x8a, 0x15, 0x52, 0xe3, 0xf7, 0x4b, 0x85, 0x7c, 0x1f, 0x19, 0x87, 0xce, 0x91, 0x31, 0xe8, 0x4c, 0xd2, 0x9c, 0x0c, 0xc8, 0xd6, 0x49, 0xfb, 0xd5,
0x24, 0xf5, 0xc9, 0x80, 0x6c, 0x4d, 0xda, 0xaf, 0xbe, 0x26, 0x80, 0xd1, 0xd7, 0x09, 0x60, 0x34, 0x3d, 0x0d, 0xdc, 0xa9, 0xdf, 0xf3, 0x1b, 0x91,
0xf4, 0x34, 0x70, 0xa7, 0x7e, 0xcf, 0x6f, 0x44, 0xfe, 0x38, 0x7c, 0x40, 0x3f, 0x0e, 0x1f, 0x10, 0xaf, 0x19, 0xd2, 0xd5, 0xd2, 0x3c, 0x72, 0x80,
0xbc, 0x66, 0xa0, 0xab, 0xa5, 0x79, 0xe0, 0x80, 0x28, 0x05, 0x29, 0xb8, 0x95, 0x02, 0x17, 0x38, 0x98, 0xe4, 0xdc, 0xe2, 0x61, 0xcc, 0x0d, 0x83,
0x60, 0xd2, 0x73, 0x4b, 0x86, 0x71, 0x37, 0x1c, 0x3e, 0xee, 0xfd, 0x12, 0x8f, 0x7b, 0xbf, 0x04, 0xf4, 0x46, 0xe8, 0xd5, 0x2e, 0x30, 0x4e, 0x20,
0xd0, 0xfb, 0xa0, 0x57, 0x5f, 0x81, 0xb9, 0x04, 0x12, 0xa4, 0xc0, 0xd4, 0x46, 0x0a, 0x99, 0x1a, 0x81, 0x26, 0x66, 0xfa, 0x39, 0x3e, 0x87, 0xef,
0x08, 0x34, 0x31, 0xd3, 0xcf, 0xc9, 0x39, 0x7c, 0x87, 0x74, 0x12, 0x66, 0x50, 0x4e, 0xc2, 0x0c, 0x0c, 0xd9, 0x4f, 0x3f, 0xe1, 0x6b, 0x8f, 0x21,
0x10, 0xc8, 0x7e, 0xfa, 0x09, 0x5f, 0x7b, 0x0c, 0xf1, 0x3c, 0x3e, 0x31, 0x9e, 0xc7, 0x27, 0x86, 0xaf, 0xe7, 0xfc, 0x0e, 0x19, 0xea, 0xcc, 0x19,
0x72, 0x3d, 0xe7, 0x77, 0x60, 0xa8, 0x33, 0x67, 0x68, 0x1e, 0x2b, 0xdf, 0x9a, 0x07, 0xcb, 0x77, 0x26, 0x60, 0xe7, 0x29, 0xf5, 0x7b, 0xa4, 0x75,
0x99, 0x80, 0x9d, 0xa7, 0x54, 0xef, 0x91, 0xd5, 0x31, 0x2a, 0xeb, 0xf5, 0x8c, 0xce, 0x7a, 0xfd, 0xb5, 0x5e, 0x03, 0x8c, 0xcf, 0x57, 0x5f, 0x69,
0xd7, 0x76, 0x0d, 0xd0, 0x3e, 0x5f, 0x7d, 0x65, 0x99, 0xfd, 0x90, 0x0b, 0x66, 0x3f, 0xe4, 0x02, 0x15, 0xc5, 0x3c, 0xdb, 0x90, 0x38, 0xc4, 0x80,
0x64, 0x14, 0xf3, 0x64, 0x43, 0xea, 0x90, 0x00, 0xd2, 0xe5, 0x65, 0xcc, 0x64, 0x79, 0x19, 0xf3, 0x84, 0xd3, 0xc3, 0xc1, 0x8c, 0xde, 0x13, 0x52,
0x13, 0x4e, 0x0f, 0x07, 0x33, 0x7a, 0x4f, 0x48, 0xa1, 0x36, 0xad, 0xd8, 0x88, 0x4d, 0x27, 0xb6, 0xcd, 0xf5, 0xd6, 0x08, 0xb3, 0x79, 0xdc, 0x69,
0x76, 0xd7, 0x5b, 0xa3, 0xcc, 0xe6, 0x61, 0xa7, 0x81, 0x80, 0x6c, 0xd7, 0x20, 0x20, 0xdb, 0xe5, 0xfe, 0xac, 0xe1, 0x4f, 0x7a, 0xc2, 0xa9, 0xaa,
0xf5, 0x67, 0x8d, 0xfb, 0xa4, 0xe7, 0x9b, 0x2a, 0xeb, 0x97, 0x29, 0x9f, 0x7e, 0x59, 0xf2, 0xe9, 0xed, 0xa3, 0x7c, 0xe9, 0xae, 0x0c, 0x92, 0x22,
0xde, 0x3e, 0xca, 0x57, 0xee, 0xca, 0x21, 0x29, 0x28, 0x28, 0x9c, 0xf3, 0x05, 0x85, 0x73, 0xbe, 0x23, 0xb9, 0xbb, 0x36, 0x8a, 0xcb, 0x04, 0x03,
0x1d, 0xe4, 0xee, 0xda, 0x48, 0x2e, 0x13, 0x34, 0xa4, 0x01, 0x28, 0x67, 0x69, 0x80, 0x94, 0x33, 0xf6, 0x9a, 0xaf, 0xac, 0x3e, 0xe8, 0x11, 0x87,
0xec, 0x35, 0x5f, 0x58, 0x7d, 0xec, 0x47, 0x1c, 0x16, 0x1b, 0xd9, 0xab, 0xc5, 0x86, 0xf7, 0x6a, 0xc1, 0xb3, 0x66, 0x7b, 0x01, 0x26, 0x09, 0xae,
0x05, 0xcf, 0x9a, 0xe5, 0x05, 0x84, 0x24, 0x38, 0xee, 0x39, 0xa9, 0xcd, 0x7b, 0x8e, 0x6b, 0xb3, 0x5c, 0x54, 0x21, 0xa3, 0xa3, 0x9c, 0xc2, 0x61,
0x74, 0x51, 0x85, 0x8c, 0x8e, 0x72, 0x0a, 0x87, 0x65, 0x26, 0x1e, 0x4a, 0x99, 0x89, 0x87, 0x32, 0x2f, 0x2c, 0xe3, 0xfc, 0x49, 0x4c, 0xd0, 0x91,
0x5e, 0x58, 0xc6, 0xf9, 0x93, 0x98, 0xa0, 0x25, 0xcd, 0x77, 0x6b, 0x7c, 0xe6, 0xdb, 0x35, 0xbe, 0xc1, 0xe0, 0xac, 0xd9, 0xcd, 0xd4, 0xaf, 0xcf,
0x43, 0xc0, 0x59, 0xb3, 0x9a, 0xa9, 0x5f, 0x9e, 0x37, 0x2f, 0x7a, 0x86, 0x9b, 0x57, 0x3d, 0x93, 0x66, 0x9e, 0xd8, 0x89, 0x16, 0xb7, 0xec, 0xf4,
0x66, 0x9e, 0xf8, 0x89, 0x06, 0xb7, 0xfc, 0xf4, 0x5c, 0x67, 0xbe, 0xad, 0xdc, 0x64, 0xbe, 0x2d, 0xc2, 0x40, 0x84, 0xf3, 0x0c, 0x17, 0x8a, 0xa8,
0xc2, 0x40, 0x84, 0xf3, 0x0c, 0x07, 0x8a, 0xa8, 0x76, 0x5e, 0xd9, 0x54, 0x36, 0x5e, 0x39, 0x54, 0x9b, 0x34, 0xd9, 0x6a, 0x4c, 0xf1, 0xcf, 0x1e,
0x1b, 0x9a, 0x6c, 0x35, 0xba, 0xf8, 0x67, 0x17, 0x3e, 0x69, 0xeb, 0xb7, 0x7c, 0x32, 0xd6, 0x6f, 0x4f, 0xe9, 0xdf, 0x3e, 0x89, 0x97, 0x1e, 0xe6,
0xbb, 0xf4, 0x6f, 0xef, 0xc4, 0xcb, 0x1b, 0xe6, 0x54, 0x51, 0x80, 0xff, 0xd4, 0x51, 0x20, 0xff, 0x45, 0x61, 0xc6, 0x6e, 0x48, 0x4d, 0x4e, 0xe6,
0xa2, 0x30, 0x63, 0x37, 0x64, 0x26, 0x27, 0xf7, 0xa1, 0x48, 0xe2, 0x15, 0x43, 0x93, 0xc4, 0xab, 0x9c, 0xf8, 0xa5, 0x75, 0xf0, 0xb1, 0xf1, 0x10,
0x27, 0x7e, 0x69, 0x1d, 0x7c, 0x6c, 0x3c, 0x42, 0xb7, 0xf6, 0x5a, 0x87, 0xdd, 0xda, 0x6b, 0x1d, 0x6e, 0x7d, 0x5f, 0x93, 0x5f, 0x6c, 0x7a, 0x2a,
0x5b, 0xdf, 0xd7, 0x74, 0x2f, 0x36, 0x3d, 0x95, 0x78, 0x4d, 0xd7, 0x6d, 0xf1, 0x9a, 0xa6, 0xdb, 0xe4, 0xe5, 0xa7, 0xe6, 0x7b, 0x7c, 0x14, 0x6e,
0x78, 0xf9, 0xa9, 0xfb, 0x1e, 0x1f, 0x84, 0x1b, 0x2e, 0xdc, 0x7e, 0x19, 0x98, 0x70, 0xfb, 0x75, 0x98, 0x6d, 0x3d, 0xd4, 0x5a, 0x03, 0x97, 0xb8,
0x66, 0x5b, 0x8f, 0xb4, 0xd6, 0xc0, 0x25, 0xe9, 0xe5, 0xc2, 0x13, 0xec, 0x97, 0x07, 0x4f, 0x40, 0x19, 0xd3, 0x7f, 0x73, 0x79, 0x90, 0x45, 0x5a,
0x8c, 0xe9, 0xbf, 0xb7, 0x3c, 0xc8, 0x22, 0x2d, 0x35, 0x3c, 0x36, 0x8f, 0x4a, 0x78, 0x6c, 0x1e, 0x7a, 0x8f, 0xe9, 0x9f, 0x8f, 0x9b, 0xff, 0x02,
0xbc, 0xc7, 0xf4, 0xcf, 0xc7, 0xcd, 0x7f, 0x7d, 0x30, 0xf4, 0xf3, 0x7f, 0x61, 0xd2, 0xcf, 0xff, 0x21, 0x3b, 0x17, 0xe8, 0x24, 0x3e, 0x2c, 0xf5,
0x60, 0xe7, 0x02, 0x95, 0xc4, 0x87, 0xa5, 0x9e, 0xbd, 0xb3, 0x08, 0x3c, 0xec, 0x9d, 0x45, 0xe0, 0x21, 0x26, 0x7f, 0x0f, 0x00, 0x00, 0xff, 0xff,
0x24, 0xe4, 0xef, 0x01, 0x00, 0x00, 0xff, 0xff, 0xca, 0xc7, 0x79, 0x5b, 0xc9, 0x2e, 0x07, 0x65, 0xc7, 0x19, 0x00, 0x00,
0xc3, 0x19, 0x00, 0x00,
}, },
"conf/app.ini", "conf/app.ini",
) )

View File

@ -17,6 +17,15 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const (
AUTH_ACTIVE base.TplName = "mail/auth/active"
AUTH_REGISTER_SUCCESS base.TplName = "mail/auth/register_success"
AUTH_RESET_PASSWORD base.TplName = "mail/auth/reset_passwd"
NOTIFY_COLLABORATOR base.TplName = "mail/notify/collaborator"
NOTIFY_MENTION base.TplName = "mail/notify/mention"
)
// Create New mail message use MailFrom and MailUser // Create New mail message use MailFrom and MailUser
func NewMailMessageFrom(To []string, from, subject, body string) Message { func NewMailMessageFrom(To []string, from, subject, body string) Message {
msg := NewHtmlMessage(To, from, subject, body) msg := NewHtmlMessage(To, from, subject, body)
@ -61,7 +70,7 @@ func SendRegisterMail(r *middleware.Render, u *models.User) {
data := GetMailTmplData(u) data := GetMailTmplData(u)
data["Code"] = code data["Code"] = code
body, err := r.HTMLString("mail/auth/register_success", data) body, err := r.HTMLString(string(AUTH_REGISTER_SUCCESS), data)
if err != nil { if err != nil {
log.Error("mail.SendRegisterMail(fail to render): %v", err) log.Error("mail.SendRegisterMail(fail to render): %v", err)
return return
@ -81,7 +90,7 @@ func SendActiveMail(r *middleware.Render, u *models.User) {
data := GetMailTmplData(u) data := GetMailTmplData(u)
data["Code"] = code data["Code"] = code
body, err := r.HTMLString("mail/auth/active_email", data) body, err := r.HTMLString(string(AUTH_ACTIVE), data)
if err != nil { if err != nil {
log.Error("mail.SendActiveMail(fail to render): %v", err) log.Error("mail.SendActiveMail(fail to render): %v", err)
return return
@ -101,7 +110,7 @@ func SendResetPasswdMail(r *middleware.Render, u *models.User) {
data := GetMailTmplData(u) data := GetMailTmplData(u)
data["Code"] = code data["Code"] = code
body, err := r.HTMLString("mail/auth/reset_passwd", data) body, err := r.HTMLString(string(AUTH_RESET_PASSWORD), data)
if err != nil { if err != nil {
log.Error("mail.SendResetPasswdMail(fail to render): %v", err) log.Error("mail.SendResetPasswdMail(fail to render): %v", err)
return return
@ -161,7 +170,7 @@ func SendIssueMentionMail(r *middleware.Render, u, owner *models.User,
data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index) data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index)
data["Subject"] = subject data["Subject"] = subject
body, err := r.HTMLString("mail/notify/mention", data) body, err := r.HTMLString(string(NOTIFY_MENTION), data)
if err != nil { if err != nil {
return fmt.Errorf("mail.SendIssueMentionMail(fail to render): %v", err) return fmt.Errorf("mail.SendIssueMentionMail(fail to render): %v", err)
} }
@ -182,7 +191,7 @@ func SendCollaboratorMail(r *middleware.Render, u, owner *models.User,
data["RepoLink"] = path.Join(owner.Name, repo.Name) data["RepoLink"] = path.Join(owner.Name, repo.Name)
data["Subject"] = subject data["Subject"] = subject
body, err := r.HTMLString("mail/notify/collaborator", data) body, err := r.HTMLString(string(NOTIFY_COLLABORATOR), data)
if err != nil { if err != nil {
return fmt.Errorf("mail.SendCollaboratorMail(fail to render): %v", err) return fmt.Errorf("mail.SendCollaboratorMail(fail to render): %v", err)
} }

View File

@ -104,12 +104,12 @@ func (ctx *Context) HasError() bool {
} }
// HTML calls render.HTML underlying but reduce one argument. // HTML calls render.HTML underlying but reduce one argument.
func (ctx *Context) HTML(status int, name string, htmlOpt ...HTMLOptions) { func (ctx *Context) HTML(status int, name base.TplName, htmlOpt ...HTMLOptions) {
ctx.Render.HTML(status, name, ctx.Data, htmlOpt...) ctx.Render.HTML(status, string(name), ctx.Data, htmlOpt...)
} }
// RenderWithErr used for page has form validation but need to prompt error to users. // RenderWithErr used for page has form validation but need to prompt error to users.
func (ctx *Context) RenderWithErr(msg, tpl string, form auth.Form) { func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form auth.Form) {
if form != nil { if form != nil {
auth.AssignForm(form, ctx.Data) auth.AssignForm(form, ctx.Data)
} }
@ -133,7 +133,7 @@ func (ctx *Context) Handle(status int, title string, err error) {
case 500: case 500:
ctx.Data["Title"] = "Internal Server Error" ctx.Data["Title"] = "Internal Server Error"
} }
ctx.HTML(status, fmt.Sprintf("status/%d", status)) ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
} }
func (ctx *Context) Debug(msg string, args ...interface{}) { func (ctx *Context) Debug(msg string, args ...interface{}) {

View File

@ -46,7 +46,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
// Collaborators who have write access can be seen as owners. // Collaborators who have write access can be seen as owners.
if ctx.IsSigned { if ctx.IsSigned {
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.AU_WRITABLE) ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE)
if err != nil { if err != nil {
ctx.Handle(500, "RepoAssignment(HasAccess)", err) ctx.Handle(500, "RepoAssignment(HasAccess)", err)
return return
@ -107,7 +107,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
return return
} }
hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.AU_READABLE) hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE)
if err != nil { if err != nil {
ctx.Handle(500, "RepoAssignment(HasAccess)", err) ctx.Handle(500, "RepoAssignment(HasAccess)", err)
return return

View File

@ -47,12 +47,12 @@ var (
StaticRootPath string StaticRootPath string
// Security settings. // Security settings.
InstallLock bool InstallLock bool
SecretKey string SecretKey string
LogInRememberDays int LogInRememberDays int
CookieUserName string CookieUserName string
CookieRememberName string CookieRememberName string
ReverseProxyAuthUid string ReverseProxyAuthUser string
// Webhook settings. // Webhook settings.
WebhookTaskInterval int WebhookTaskInterval int
@ -164,7 +164,7 @@ func NewConfigContext() {
LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS") LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS")
CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME") CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME")
CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME")
ReverseProxyAuthUid = Cfg.MustValue("security", "REVERSE_PROXY_AUTHENTICATION_UID", "X-WEBAUTH-UID") ReverseProxyAuthUser = Cfg.MustValue("security", "REVERSE_PROXY_AUTHENTICATION_USER", "X-WEBAUTH-USER")
RunUser = Cfg.MustValue("", "RUN_USER") RunUser = Cfg.MustValue("", "RUN_USER")
curUser := os.Getenv("USER") curUser := os.Getenv("USER")

View File

@ -257,6 +257,9 @@ html, body {
.card .btn { .card .btn {
cursor: pointer; cursor: pointer;
}
.card .btn-primary {
margin-right: 1.2em; margin-right: 1.2em;
} }
@ -372,7 +375,7 @@ html, body {
/* gogits repo create */ /* gogits repo create */
#repo-create { #repo-create, #org-create, #org-teams-create, #org-teams-edit {
width: 800px; width: 800px;
} }
@ -638,7 +641,7 @@ html, body {
margin: 0 .5em; margin: 0 .5em;
} }
#dashboard-switch .btn { #dashboard-switch .btn, #repo-owner-switch .btn {
height: 40px; height: 40px;
} }
@ -647,7 +650,7 @@ html, body {
margin-right: 18px; margin-right: 18px;
} }
#dashboard-switch .dropdown-menu { #dashboard-switch .dropdown-menu,#repo-owner-switch .dropdown-menu {
padding: 0; padding: 0;
} }
@ -662,6 +665,14 @@ html, body {
padding: .8em 1.2em; padding: .8em 1.2em;
} }
#dashboard-switch-menu > li > a:hover {
text-decoration: none;
}
#dashboard-switch-menu > li > a img, #dashboard-switch button img {
margin-right: 6px;
}
#dashboard-switch-menu > li { #dashboard-switch-menu > li {
border-bottom: 1px solid #eaeaea; border-bottom: 1px solid #eaeaea;
} }
@ -1864,16 +1875,44 @@ html, body {
padding: 16px 0; padding: 16px 0;
} }
#body-nav.org-nav.org-nav-auto {
height: auto;
}
.org-nav > .container {
padding-left: 0;
padding-left: 0;
}
.org-nav .org-logo { .org-nav .org-logo {
margin-right: 16px; margin-right: 16px;
width: 100px; width: 100px;
height: 100px; height: 100px;
} }
.org-nav .org-small-logo {
margin-right: 16px;
width: 50px;
height: 50px;
}
.org-nav .org-name { .org-nav .org-name {
margin-top: 0; margin-top: 0;
} }
.org-nav-auto .org-name {
font-size: 1.4em;
line-height: 48px;
}
#body-nav.org-nav-auto .nav {
margin-top: 6px;
}
#body-nav.org-nav-auto .nav a:hover {
text-decoration: none;
}
.org-description { .org-description {
font-size: 16px; font-size: 16px;
} }
@ -1894,6 +1933,10 @@ html, body {
margin-left: 0; margin-left: 0;
} }
.org-main {
padding-left: 0;
}
.org-sidebar { .org-sidebar {
margin-top: -100px; margin-top: -100px;
} }
@ -1948,3 +1991,71 @@ html, body {
.org-team a:hover .org-team-name { .org-team a:hover .org-team-name {
color: #0079bc !important; color: #0079bc !important;
} }
#org-members {
margin-right: 30px;
}
#org-members .member .avatar img {
width: 50px;
height: 50px;
}
#org-members .member {
padding-bottom: 20px;
margin-bottom: 20px;
border-bottom: 1px solid #DDD;
height: 70px;
}
#org-members .member .name {
padding-top: 4px;
}
#org-members .member .nick {
display: block;
color: #888;
}
#org-members .member .name a {
color: #444;
}
#org-members .member .name strong {
font-size: 1.2em;
}
#org-members .status, #org-members .role {
line-height: 48px;
text-align: right;
}
#org-teams .org-team .panel-heading {
margin-top: 0;
}
#org-teams .org-team .panel-heading a {
color: #444;
}
#org-teams .org-team-members {
margin-top: 18px;
}
#org-teams .org-team-members img {
width: 40px;
height: 40px;
margin-right: 12px;
}
#org-teams .org-team-members a {
display: inline-block;
}
#org-teams .org-team .panel-footer {
height: 60px;
}
#org-teams .org-team {
border-bottom: none;
}

View File

@ -758,6 +758,27 @@ function initRepoSetting() {
}); });
} }
function initRepoCreating() {
// owner switch menu click
(function () {
$('#repo-owner-switch .dropdown-menu').on("click", "li", function () {
var uid = $(this).data('uid');
// set to input
$('#repo-owner-id').val(uid);
// set checked class
if (!$(this).hasClass("checked")) {
$(this).parent().find(".checked").removeClass("checked");
$(this).addClass("checked");
}
// set button group to show clicked owner
$('#repo-owner-avatar').attr("src",$(this).find('img').attr("src"));
$('#repo-owner-name').text($(this).text().trim());
console.log("set repo owner to uid :",uid,$(this).text().trim());
});
}());
console.log("init repo-creating scripts");
}
(function ($) { (function ($) {
$(function () { $(function () {
initCore(); initCore();
@ -780,6 +801,9 @@ function initRepoSetting() {
if ($('#repo-setting-container').length) { if ($('#repo-setting-container').length) {
initRepoSetting(); initRepoSetting();
} }
if ($('#repo-create').length) {
initRepoCreating();
}
}); });
})(jQuery); })(jQuery);

View File

@ -20,6 +20,16 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const (
DASHBOARD base.TplName = "admin/dashboard"
USERS base.TplName = "admin/users"
REPOS base.TplName = "admin/repos"
AUTHS base.TplName = "admin/auths"
CONFIG base.TplName = "admin/config"
MONITOR_PROCESS base.TplName = "admin/monitor/process"
MONITOR_CRON base.TplName = "admin/monitor/cron"
)
var startTime = time.Now() var startTime = time.Now()
var sysStatus struct { var sysStatus struct {
@ -140,7 +150,7 @@ func Dashboard(ctx *middleware.Context) {
ctx.Data["Stats"] = models.GetStatistic() ctx.Data["Stats"] = models.GetStatistic()
updateSystemStatus() updateSystemStatus()
ctx.Data["SysStatus"] = sysStatus ctx.Data["SysStatus"] = sysStatus
ctx.HTML(200, "admin/dashboard") ctx.HTML(200, DASHBOARD)
} }
func Users(ctx *middleware.Context) { func Users(ctx *middleware.Context) {
@ -150,10 +160,10 @@ func Users(ctx *middleware.Context) {
var err error var err error
ctx.Data["Users"], err = models.GetUsers(200, 0) ctx.Data["Users"], err = models.GetUsers(200, 0)
if err != nil { if err != nil {
ctx.Handle(500, "admin.Users", err) ctx.Handle(500, "admin.Users(GetUsers)", err)
return return
} }
ctx.HTML(200, "admin/users") ctx.HTML(200, USERS)
} }
func Repositories(ctx *middleware.Context) { func Repositories(ctx *middleware.Context) {
@ -166,7 +176,7 @@ func Repositories(ctx *middleware.Context) {
ctx.Handle(500, "admin.Repositories", err) ctx.Handle(500, "admin.Repositories", err)
return return
} }
ctx.HTML(200, "admin/repos") ctx.HTML(200, REPOS)
} }
func Auths(ctx *middleware.Context) { func Auths(ctx *middleware.Context) {
@ -179,7 +189,7 @@ func Auths(ctx *middleware.Context) {
ctx.Handle(500, "admin.Auths", err) ctx.Handle(500, "admin.Auths", err)
return return
} }
ctx.HTML(200, "admin/auths") ctx.HTML(200, AUTHS)
} }
func Config(ctx *middleware.Context) { func Config(ctx *middleware.Context) {
@ -196,7 +206,7 @@ func Config(ctx *middleware.Context) {
ctx.Data["StaticRootPath"] = setting.StaticRootPath ctx.Data["StaticRootPath"] = setting.StaticRootPath
ctx.Data["LogRootPath"] = setting.LogRootPath ctx.Data["LogRootPath"] = setting.LogRootPath
ctx.Data["ScriptType"] = setting.ScriptType ctx.Data["ScriptType"] = setting.ScriptType
ctx.Data["ReverseProxyAuthUid"] = setting.ReverseProxyAuthUid ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser
ctx.Data["Service"] = setting.Service ctx.Data["Service"] = setting.Service
@ -235,7 +245,7 @@ func Config(ctx *middleware.Context) {
} }
ctx.Data["Loggers"] = loggers ctx.Data["Loggers"] = loggers
ctx.HTML(200, "admin/config") ctx.HTML(200, CONFIG)
} }
func Monitor(ctx *middleware.Context) { func Monitor(ctx *middleware.Context) {
@ -247,11 +257,10 @@ func Monitor(ctx *middleware.Context) {
case "process": case "process":
ctx.Data["PageIsMonitorProcess"] = true ctx.Data["PageIsMonitorProcess"] = true
ctx.Data["Processes"] = process.Processes ctx.Data["Processes"] = process.Processes
ctx.HTML(200, "admin/monitor/process") ctx.HTML(200, MONITOR_PROCESS)
default: default:
ctx.Data["PageIsMonitorCron"] = true ctx.Data["PageIsMonitorCron"] = true
ctx.Data["Entries"] = cron.ListEntries() ctx.Data["Entries"] = cron.ListEntries()
ctx.HTML(200, "admin/monitor/cron") ctx.HTML(200, MONITOR_CRON)
} }
} }

View File

@ -18,12 +18,17 @@ import (
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
) )
const (
AUTH_NEW base.TplName = "admin/auth/new"
AUTH_EDIT base.TplName = "admin/auth/edit"
)
func NewAuthSource(ctx *middleware.Context) { func NewAuthSource(ctx *middleware.Context) {
ctx.Data["Title"] = "New Authentication" ctx.Data["Title"] = "New Authentication"
ctx.Data["PageIsAuths"] = true ctx.Data["PageIsAuths"] = true
ctx.Data["LoginTypes"] = models.LoginTypes ctx.Data["LoginTypes"] = models.LoginTypes
ctx.Data["SMTPAuths"] = models.SMTPAuths ctx.Data["SMTPAuths"] = models.SMTPAuths
ctx.HTML(200, "admin/auths/new") ctx.HTML(200, AUTH_NEW)
} }
func NewAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) { func NewAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
@ -33,7 +38,7 @@ func NewAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
ctx.Data["SMTPAuths"] = models.SMTPAuths ctx.Data["SMTPAuths"] = models.SMTPAuths
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "admin/auths/new") ctx.HTML(200, AUTH_NEW)
return return
} }
@ -74,7 +79,7 @@ func NewAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
} }
if err := models.CreateSource(source); err != nil { if err := models.CreateSource(source); err != nil {
ctx.Handle(500, "admin.auths.NewAuth", err) ctx.Handle(500, "admin.auths.NewAuth(CreateSource)", err)
return return
} }
@ -97,11 +102,11 @@ func EditAuthSource(ctx *middleware.Context, params martini.Params) {
} }
u, err := models.GetLoginSourceById(id) u, err := models.GetLoginSourceById(id)
if err != nil { if err != nil {
ctx.Handle(500, "admin.user.EditUser", err) ctx.Handle(500, "admin.user.EditUser(GetLoginSourceById)", err)
return return
} }
ctx.Data["Source"] = u ctx.Data["Source"] = u
ctx.HTML(200, "admin/auths/edit") ctx.HTML(200, AUTH_EDIT)
} }
func EditAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) { func EditAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
@ -111,7 +116,7 @@ func EditAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
ctx.Data["SMTPAuths"] = models.SMTPAuths ctx.Data["SMTPAuths"] = models.SMTPAuths
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "admin/auths/edit") ctx.HTML(200, AUTH_EDIT)
return return
} }
@ -153,7 +158,7 @@ func EditAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
} }
if err := models.UpdateSource(&u); err != nil { if err := models.UpdateSource(&u); err != nil {
ctx.Handle(500, "admin.auths.EditAuth", err) ctx.Handle(500, "admin.auths.EditAuth(UpdateSource)", err)
return return
} }
@ -175,7 +180,7 @@ func DeleteAuthSource(ctx *middleware.Context, params martini.Params) {
a, err := models.GetLoginSourceById(id) a, err := models.GetLoginSourceById(id)
if err != nil { if err != nil {
ctx.Handle(500, "admin.auths.DeleteAuth", err) ctx.Handle(500, "admin.auths.DeleteAuth(GetLoginSourceById)", err)
return return
} }
@ -185,7 +190,7 @@ func DeleteAuthSource(ctx *middleware.Context, params martini.Params) {
ctx.Flash.Error("This authentication still has used by some users, you should move them and then delete again.") ctx.Flash.Error("This authentication still has used by some users, you should move them and then delete again.")
ctx.Redirect("/admin/auths/" + params["authid"]) ctx.Redirect("/admin/auths/" + params["authid"])
default: default:
ctx.Handle(500, "admin.auths.DeleteAuth", err) ctx.Handle(500, "admin.auths.DeleteAuth(DelLoginSource)", err)
} }
return return
} }

View File

@ -5,8 +5,6 @@
package admin package admin
import ( import (
"fmt"
"strconv"
"strings" "strings"
"github.com/go-martini/martini" "github.com/go-martini/martini"
@ -18,16 +16,21 @@ import (
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
) )
const (
USER_NEW base.TplName = "admin/user/new"
USER_EDIT base.TplName = "admin/user/edit"
)
func NewUser(ctx *middleware.Context) { func NewUser(ctx *middleware.Context) {
ctx.Data["Title"] = "New Account" ctx.Data["Title"] = "New Account"
ctx.Data["PageIsUsers"] = true ctx.Data["PageIsUsers"] = true
auths, err := models.GetAuths() auths, err := models.GetAuths()
if err != nil { if err != nil {
ctx.Handle(500, "admin.user.NewUser", err) ctx.Handle(500, "admin.user.NewUser(GetAuths)", err)
return return
} }
ctx.Data["LoginSources"] = auths ctx.Data["LoginSources"] = auths
ctx.HTML(200, "admin/users/new") ctx.HTML(200, USER_NEW)
} }
func NewUserPost(ctx *middleware.Context, form auth.RegisterForm) { func NewUserPost(ctx *middleware.Context, form auth.RegisterForm) {
@ -35,7 +38,7 @@ func NewUserPost(ctx *middleware.Context, form auth.RegisterForm) {
ctx.Data["PageIsUsers"] = true ctx.Data["PageIsUsers"] = true
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "admin/users/new") ctx.HTML(200, USER_NEW)
return return
} }
@ -55,25 +58,25 @@ func NewUserPost(ctx *middleware.Context, form auth.RegisterForm) {
} }
if len(form.LoginType) > 0 { if len(form.LoginType) > 0 {
// NOTE: need rewrite.
fields := strings.Split(form.LoginType, "-") fields := strings.Split(form.LoginType, "-")
tp, _ := strconv.Atoi(fields[0]) tp, _ := base.StrTo(fields[0]).Int()
u.LoginType = models.LoginType(tp) u.LoginType = models.LoginType(tp)
u.LoginSource, _ = strconv.ParseInt(fields[1], 10, 64) u.LoginSource, _ = base.StrTo(fields[1]).Int64()
u.LoginName = form.LoginName u.LoginName = form.LoginName
fmt.Println(u.LoginType, u.LoginSource, u.LoginName)
} }
var err error var err error
if u, err = models.RegisterUser(u); err != nil { if u, err = models.CreateUser(u); err != nil {
switch err { switch err {
case models.ErrUserAlreadyExist: case models.ErrUserAlreadyExist:
ctx.RenderWithErr("Username has been already taken", "admin/users/new", &form) ctx.RenderWithErr("Username has been already taken", USER_NEW, &form)
case models.ErrEmailAlreadyUsed: case models.ErrEmailAlreadyUsed:
ctx.RenderWithErr("E-mail address has been already used", "admin/users/new", &form) ctx.RenderWithErr("E-mail address has been already used", USER_NEW, &form)
case models.ErrUserNameIllegal: case models.ErrUserNameIllegal:
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "admin/users/new", &form) ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), USER_NEW, &form)
default: default:
ctx.Handle(500, "admin.user.NewUser", err) ctx.Handle(500, "admin.user.NewUser(CreateUser)", err)
} }
return return
} }
@ -96,18 +99,18 @@ func EditUser(ctx *middleware.Context, params martini.Params) {
u, err := models.GetUserById(int64(uid)) u, err := models.GetUserById(int64(uid))
if err != nil { if err != nil {
ctx.Handle(500, "admin.user.EditUser", err) ctx.Handle(500, "admin.user.EditUser(GetUserById)", err)
return return
} }
ctx.Data["User"] = u ctx.Data["User"] = u
auths, err := models.GetAuths() auths, err := models.GetAuths()
if err != nil { if err != nil {
ctx.Handle(500, "admin.user.NewUser", err) ctx.Handle(500, "admin.user.NewUser(GetAuths)", err)
return return
} }
ctx.Data["LoginSources"] = auths ctx.Data["LoginSources"] = auths
ctx.HTML(200, "admin/users/edit") ctx.HTML(200, USER_EDIT)
} }
func EditUserPost(ctx *middleware.Context, params martini.Params, form auth.AdminEditUserForm) { func EditUserPost(ctx *middleware.Context, params martini.Params, form auth.AdminEditUserForm) {
@ -116,13 +119,18 @@ func EditUserPost(ctx *middleware.Context, params martini.Params, form auth.Admi
uid, err := base.StrTo(params["userid"]).Int() uid, err := base.StrTo(params["userid"]).Int()
if err != nil { if err != nil {
ctx.Handle(404, "admin.user.EditUser", err) ctx.Handle(404, "admin.user.EditUserPost", err)
return return
} }
u, err := models.GetUserById(int64(uid)) u, err := models.GetUserById(int64(uid))
if err != nil { if err != nil {
ctx.Handle(500, "admin.user.EditUser", err) ctx.Handle(500, "admin.user.EditUserPost(GetUserById)", err)
return
}
if ctx.HasError() {
ctx.HTML(200, USER_EDIT)
return return
} }
@ -134,7 +142,7 @@ func EditUserPost(ctx *middleware.Context, params martini.Params, form auth.Admi
u.IsActive = form.Active u.IsActive = form.Active
u.IsAdmin = form.Admin u.IsAdmin = form.Admin
if err := models.UpdateUser(u); err != nil { if err := models.UpdateUser(u); err != nil {
ctx.Handle(500, "admin.user.EditUser", err) ctx.Handle(500, "admin.user.EditUserPost(UpdateUser)", err)
return return
} }
log.Trace("%s User profile updated by admin(%s): %s", ctx.Req.RequestURI, log.Trace("%s User profile updated by admin(%s): %s", ctx.Req.RequestURI,
@ -152,13 +160,13 @@ func DeleteUser(ctx *middleware.Context, params martini.Params) {
//log.Info("delete") //log.Info("delete")
uid, err := base.StrTo(params["userid"]).Int() uid, err := base.StrTo(params["userid"]).Int()
if err != nil { if err != nil {
ctx.Handle(404, "admin.user.EditUser", err) ctx.Handle(404, "admin.user.DeleteUser", err)
return return
} }
u, err := models.GetUserById(int64(uid)) u, err := models.GetUserById(int64(uid))
if err != nil { if err != nil {
ctx.Handle(500, "admin.user.EditUser", err) ctx.Handle(500, "admin.user.DeleteUser(GetUserById)", err)
return return
} }

View File

@ -6,11 +6,16 @@ package routers
import ( import (
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/routers/user" "github.com/gogits/gogs/routers/user"
) )
const (
HOME base.TplName = "home"
)
func Home(ctx *middleware.Context) { func Home(ctx *middleware.Context) {
if ctx.IsSigned { if ctx.IsSigned {
user.Dashboard(ctx) user.Dashboard(ctx)
@ -40,7 +45,7 @@ func Home(ctx *middleware.Context) {
} }
} }
ctx.Data["Repos"] = repos ctx.Data["Repos"] = repos
ctx.HTML(200, "home") ctx.HTML(200, HOME)
} }
func NotFound(ctx *middleware.Context) { func NotFound(ctx *middleware.Context) {

View File

@ -8,6 +8,7 @@ import (
"github.com/go-martini/martini" "github.com/go-martini/martini"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
@ -22,5 +23,5 @@ func TemplatePreview(ctx *middleware.Context, params martini.Params) {
ctx.Data["ActiveCodeLives"] = setting.Service.ActiveCodeLives / 60 ctx.Data["ActiveCodeLives"] = setting.Service.ActiveCodeLives / 60
ctx.Data["ResetPwdCodeLives"] = setting.Service.ResetPwdCodeLives / 60 ctx.Data["ResetPwdCodeLives"] = setting.Service.ResetPwdCodeLives / 60
ctx.Data["CurDbValue"] = "" ctx.Data["CurDbValue"] = ""
ctx.HTML(200, params["_1"]) ctx.HTML(200, base.TplName(params["_1"]))
} }

View File

@ -26,6 +26,10 @@ import (
"github.com/gogits/gogs/modules/social" "github.com/gogits/gogs/modules/social"
) )
const (
INSTALL base.TplName = "install"
)
func checkRunMode() { func checkRunMode() {
switch setting.Cfg.MustValue("", "RUN_MODE") { switch setting.Cfg.MustValue("", "RUN_MODE") {
case "prod": case "prod":
@ -72,6 +76,7 @@ func renderDbOption(ctx *middleware.Context) {
ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"} ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"}
} }
// @router /install [get]
func Install(ctx *middleware.Context, form auth.InstallForm) { func Install(ctx *middleware.Context, form auth.InstallForm) {
if setting.InstallLock { if setting.InstallLock {
ctx.Handle(404, "install.Install", errors.New("Installation is prohibited")) ctx.Handle(404, "install.Install", errors.New("Installation is prohibited"))
@ -119,12 +124,12 @@ func Install(ctx *middleware.Context, form auth.InstallForm) {
ctx.Data["CurDbOption"] = curDbOp ctx.Data["CurDbOption"] = curDbOp
auth.AssignForm(form, ctx.Data) auth.AssignForm(form, ctx.Data)
ctx.HTML(200, "install") ctx.HTML(200, INSTALL)
} }
func InstallPost(ctx *middleware.Context, form auth.InstallForm) { func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
if setting.InstallLock { if setting.InstallLock {
ctx.Handle(404, "install.Install", errors.New("Installation is prohibited")) ctx.Handle(404, "install.InstallPost", errors.New("Installation is prohibited"))
return return
} }
@ -135,12 +140,12 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
ctx.Data["CurDbOption"] = form.Database ctx.Data["CurDbOption"] = form.Database
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "install") ctx.HTML(200, INSTALL)
return return
} }
if _, err := exec.LookPath("git"); err != nil { if _, err := exec.LookPath("git"); err != nil {
ctx.RenderWithErr("Fail to test 'git' command: "+err.Error(), "install", &form) ctx.RenderWithErr("Fail to test 'git' command: "+err.Error(), INSTALL, &form)
return return
} }
@ -158,18 +163,19 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
// Set test engine. // Set test engine.
var x *xorm.Engine var x *xorm.Engine
if err := models.NewTestEngine(x); err != nil { if err := models.NewTestEngine(x); err != nil {
// NOTE: should use core.QueryDriver (github.com/go-xorm/core)
if strings.Contains(err.Error(), `Unknown database type: sqlite3`) { if strings.Contains(err.Error(), `Unknown database type: sqlite3`) {
ctx.RenderWithErr("Your release version does not support SQLite3, please download the official binary version "+ ctx.RenderWithErr("Your release version does not support SQLite3, please download the official binary version "+
"from http://gogs.io/docs/installation/install_from_binary.md, NOT the gobuild version.", "install", &form) "from http://gogs.io/docs/installation/install_from_binary.md, NOT the gobuild version.", INSTALL, &form)
} else { } else {
ctx.RenderWithErr("Database setting is not correct: "+err.Error(), "install", &form) ctx.RenderWithErr("Database setting is not correct: "+err.Error(), INSTALL, &form)
} }
return return
} }
// Test repository root path. // Test repository root path.
if err := os.MkdirAll(form.RepoRootPath, os.ModePerm); err != nil { if err := os.MkdirAll(form.RepoRootPath, os.ModePerm); err != nil {
ctx.RenderWithErr("Repository root path is invalid: "+err.Error(), "install", &form) ctx.RenderWithErr("Repository root path is invalid: "+err.Error(), INSTALL, &form)
return return
} }
@ -180,7 +186,7 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
} }
// Does not check run user when the install lock is off. // Does not check run user when the install lock is off.
if form.RunUser != curUser { if form.RunUser != curUser {
ctx.RenderWithErr("Run user isn't the current user: "+form.RunUser+" -> "+curUser, "install", &form) ctx.RenderWithErr("Run user isn't the current user: "+form.RunUser+" -> "+curUser, INSTALL, &form)
return return
} }
@ -214,18 +220,18 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
os.MkdirAll("custom/conf", os.ModePerm) os.MkdirAll("custom/conf", os.ModePerm)
if err := goconfig.SaveConfigFile(setting.Cfg, path.Join(setting.CustomPath, "conf/app.ini")); err != nil { if err := goconfig.SaveConfigFile(setting.Cfg, path.Join(setting.CustomPath, "conf/app.ini")); err != nil {
ctx.RenderWithErr("Fail to save configuration: "+err.Error(), "install", &form) ctx.RenderWithErr("Fail to save configuration: "+err.Error(), INSTALL, &form)
return return
} }
GlobalInit() GlobalInit()
// Create admin account. // Create admin account.
if _, err := models.RegisterUser(&models.User{Name: form.AdminName, Email: form.AdminEmail, Passwd: form.AdminPasswd, if _, err := models.CreateUser(&models.User{Name: form.AdminName, Email: form.AdminEmail, Passwd: form.AdminPasswd,
IsAdmin: true, IsActive: true}); err != nil { IsAdmin: true, IsActive: true}); err != nil {
if err != models.ErrUserAlreadyExist { if err != models.ErrUserAlreadyExist {
setting.InstallLock = false setting.InstallLock = false
ctx.RenderWithErr("Admin account setting is invalid: "+err.Error(), "install", &form) ctx.RenderWithErr("Admin account setting is invalid: "+err.Error(), INSTALL, &form)
return return
} }
log.Info("Admin account already exist") log.Info("Admin account already exist")

View File

@ -1,11 +1,162 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package org package org
import ( import (
"github.com/go-martini/martini" "github.com/go-martini/martini"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
"github.com/gogits/gogs/routers/user"
)
const (
NEW base.TplName = "org/new"
SETTINGS base.TplName = "org/settings"
) )
func Organization(ctx *middleware.Context, params martini.Params) { func Organization(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Organization Name" + params["org"] ctx.Data["Title"] = "Organization " + params["org"]
ctx.HTML(200, "org/org") ctx.HTML(200, "org/org")
} }
func Members(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Organization " + params["org"] + " Members"
ctx.HTML(200, "org/members")
}
func New(ctx *middleware.Context) {
ctx.Data["Title"] = "Create An Organization"
ctx.HTML(200, NEW)
}
func NewPost(ctx *middleware.Context, form auth.CreateOrgForm) {
ctx.Data["Title"] = "Create An Organization"
if ctx.HasError() {
ctx.HTML(200, NEW)
return
}
org := &models.User{
Name: form.OrgName,
Email: form.Email,
IsActive: true, // NOTE: may need to set false when require e-mail confirmation.
Type: models.ORGANIZATION,
}
var err error
if org, err = models.CreateOrganization(org, ctx.User); err != nil {
switch err {
case models.ErrUserAlreadyExist:
ctx.Data["Err_OrgName"] = true
ctx.RenderWithErr("Organization name has been already taken", NEW, &form)
case models.ErrEmailAlreadyUsed:
ctx.Data["Err_Email"] = true
ctx.RenderWithErr("E-mail address has been already used", NEW, &form)
case models.ErrUserNameIllegal:
ctx.Data["Err_OrgName"] = true
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), NEW, &form)
default:
ctx.Handle(500, "user.NewPost(CreateUser)", err)
}
return
}
log.Trace("%s Organization created: %s", ctx.Req.RequestURI, org.Name)
ctx.Redirect("/org/" + form.OrgName + "/dashboard")
}
func Dashboard(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Dashboard"
ctx.Data["PageIsUserDashboard"] = true
ctx.Data["PageIsOrgDashboard"] = true
org, err := models.GetUserByName(params["org"])
if err != nil {
if err == models.ErrUserNotExist {
ctx.Handle(404, "org.Dashboard(GetUserByName)", err)
} else {
ctx.Handle(500, "org.Dashboard(GetUserByName)", err)
}
return
}
if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "home.Dashboard(GetOrganizations)", err)
return
}
ctx.Data["Orgs"] = ctx.User.Orgs
ctx.Data["ContextUser"] = org
ctx.Data["MyRepos"], err = models.GetRepositories(org.Id, true)
if err != nil {
ctx.Handle(500, "org.Dashboard(GetRepositories)", err)
return
}
actions, err := models.GetFeeds(org.Id, 0, false)
if err != nil {
ctx.Handle(500, "org.Dashboard(GetFeeds)", err)
return
}
ctx.Data["Feeds"] = actions
ctx.HTML(200, user.DASHBOARD)
}
func Settings(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Settings"
org, err := models.GetUserByName(params["org"])
if err != nil {
if err == models.ErrUserNotExist {
ctx.Handle(404, "org.Settings(GetUserByName)", err)
} else {
ctx.Handle(500, "org.Settings(GetUserByName)", err)
}
return
}
ctx.Data["Org"] = org
ctx.HTML(200, SETTINGS)
}
func SettingsPost(ctx *middleware.Context, params martini.Params, form auth.OrgSettingForm) {
ctx.Data["Title"] = "Settings"
org, err := models.GetUserByName(params["org"])
if err != nil {
if err == models.ErrUserNotExist {
ctx.Handle(404, "org.SettingsPost(GetUserByName)", err)
} else {
ctx.Handle(500, "org.SettingsPost(GetUserByName)", err)
}
return
}
ctx.Data["Org"] = org
if ctx.HasError() {
ctx.HTML(200, SETTINGS)
return
}
org.FullName = form.DisplayName
org.Email = form.Email
org.Description = form.Description
org.Website = form.Website
org.Location = form.Location
if err = models.UpdateUser(org); err != nil {
ctx.Handle(500, "org.SettingsPost(UpdateUser)", err)
return
}
log.Trace("%s Organization setting updated: %s", ctx.Req.RequestURI, org.LowerName)
ctx.Flash.Success("Organization profile has been successfully updated.")
ctx.Redirect("/org/" + org.Name + "/settings")
}

21
routers/org/teams.go Normal file
View File

@ -0,0 +1,21 @@
package org
import (
"github.com/go-martini/martini"
"github.com/gogits/gogs/modules/middleware"
)
func Teams(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Organization "+params["org"]+" Teams"
ctx.HTML(200, "org/teams")
}
func NewTeam(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Organization "+params["org"]+" New Team"
ctx.HTML(200, "org/new_team")
}
func EditTeam(ctx *middleware.Context, params martini.Params){
ctx.Data["Title"] = "Organization "+params["org"]+" Edit Team"
ctx.HTML(200,"org/edit_team")
}

View File

@ -7,22 +7,27 @@ package repo
import ( import (
"github.com/go-martini/martini" "github.com/go-martini/martini"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
) )
const (
BRANCH base.TplName = "repo/branch"
)
func Branches(ctx *middleware.Context, params martini.Params) { func Branches(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Branches" ctx.Data["Title"] = "Branches"
ctx.Data["IsRepoToolbarBranches"] = true ctx.Data["IsRepoToolbarBranches"] = true
brs, err := ctx.Repo.GitRepo.GetBranches() brs, err := ctx.Repo.GitRepo.GetBranches()
if err != nil { if err != nil {
ctx.Handle(500, "repo.Branches", err) ctx.Handle(500, "repo.Branches(GetBranches)", err)
return return
} else if len(brs) == 0 { } else if len(brs) == 0 {
ctx.Handle(404, "repo.Branches", nil) ctx.Handle(404, "repo.Branches(GetBranches)", nil)
return return
} }
ctx.Data["Branches"] = brs ctx.Data["Branches"] = brs
ctx.HTML(200, "repo/branches") ctx.HTML(200, BRANCH)
} }

View File

@ -14,6 +14,11 @@ import (
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
) )
const (
COMMITS base.TplName = "repo/commits"
DIFF base.TplName = "repo/diff"
)
func Commits(ctx *middleware.Context, params martini.Params) { func Commits(ctx *middleware.Context, params martini.Params) {
ctx.Data["IsRepoToolbarCommits"] = true ctx.Data["IsRepoToolbarCommits"] = true
@ -22,10 +27,10 @@ func Commits(ctx *middleware.Context, params martini.Params) {
brs, err := ctx.Repo.GitRepo.GetBranches() brs, err := ctx.Repo.GitRepo.GetBranches()
if err != nil { if err != nil {
ctx.Handle(500, "repo.Commits", err) ctx.Handle(500, "repo.Commits(GetBranches)", err)
return return
} else if len(brs) == 0 { } else if len(brs) == 0 {
ctx.Handle(404, "repo.Commits", nil) ctx.Handle(404, "repo.Commits(GetBranches)", nil)
return return
} }
@ -61,7 +66,43 @@ func Commits(ctx *middleware.Context, params martini.Params) {
ctx.Data["CommitCount"] = commitsCount ctx.Data["CommitCount"] = commitsCount
ctx.Data["LastPageNum"] = lastPage ctx.Data["LastPageNum"] = lastPage
ctx.Data["NextPageNum"] = nextPage ctx.Data["NextPageNum"] = nextPage
ctx.HTML(200, "repo/commits") ctx.HTML(200, COMMITS)
}
func SearchCommits(ctx *middleware.Context, params martini.Params) {
ctx.Data["IsSearchPage"] = true
ctx.Data["IsRepoToolbarCommits"] = true
keyword := ctx.Query("q")
if len(keyword) == 0 {
ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchName)
return
}
userName := params["username"]
repoName := params["reponame"]
brs, err := ctx.Repo.GitRepo.GetBranches()
if err != nil {
ctx.Handle(500, "repo.SearchCommits(GetBranches)", err)
return
} else if len(brs) == 0 {
ctx.Handle(404, "repo.SearchCommits(GetBranches)", nil)
return
}
commits, err := ctx.Repo.Commit.SearchCommits(keyword)
if err != nil {
ctx.Handle(500, "repo.SearchCommits(SearchCommits)", err)
return
}
ctx.Data["Keyword"] = keyword
ctx.Data["Username"] = userName
ctx.Data["Reponame"] = repoName
ctx.Data["CommitCount"] = commits.Len()
ctx.Data["Commits"] = commits
ctx.HTML(200, COMMITS)
} }
func Diff(ctx *middleware.Context, params martini.Params) { func Diff(ctx *middleware.Context, params martini.Params) {
@ -75,7 +116,7 @@ func Diff(ctx *middleware.Context, params martini.Params) {
diff, err := models.GetDiff(models.RepoPath(userName, repoName), commitId) diff, err := models.GetDiff(models.RepoPath(userName, repoName), commitId)
if err != nil { if err != nil {
ctx.Handle(404, "repo.Diff", err) ctx.Handle(404, "repo.Diff(GetDiff)", err)
return return
} }
@ -119,43 +160,7 @@ func Diff(ctx *middleware.Context, params martini.Params) {
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
ctx.Data["SourcePath"] = "/" + path.Join(userName, repoName, "src", commitId) ctx.Data["SourcePath"] = "/" + path.Join(userName, repoName, "src", commitId)
ctx.Data["RawPath"] = "/" + path.Join(userName, repoName, "raw", commitId) ctx.Data["RawPath"] = "/" + path.Join(userName, repoName, "raw", commitId)
ctx.HTML(200, "repo/diff") ctx.HTML(200, DIFF)
}
func SearchCommits(ctx *middleware.Context, params martini.Params) {
ctx.Data["IsSearchPage"] = true
ctx.Data["IsRepoToolbarCommits"] = true
keyword := ctx.Query("q")
if len(keyword) == 0 {
ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchName)
return
}
userName := params["username"]
repoName := params["reponame"]
brs, err := ctx.Repo.GitRepo.GetBranches()
if err != nil {
ctx.Handle(500, "repo.SearchCommits(GetBranches)", err)
return
} else if len(brs) == 0 {
ctx.Handle(404, "repo.SearchCommits(GetBranches)", nil)
return
}
commits, err := ctx.Repo.Commit.SearchCommits(keyword)
if err != nil {
ctx.Handle(500, "repo.SearchCommits(SearchCommits)", err)
return
}
ctx.Data["Keyword"] = keyword
ctx.Data["Username"] = userName
ctx.Data["Reponame"] = repoName
ctx.Data["CommitCount"] = commits.Len()
ctx.Data["Commits"] = commits
ctx.HTML(200, "repo/commits")
} }
func FileHistory(ctx *middleware.Context, params martini.Params) { func FileHistory(ctx *middleware.Context, params martini.Params) {
@ -184,8 +189,7 @@ func FileHistory(ctx *middleware.Context, params martini.Params) {
if err != nil { if err != nil {
ctx.Handle(500, "repo.FileHistory(GetCommitsCount)", err) ctx.Handle(500, "repo.FileHistory(GetCommitsCount)", err)
return return
} } else if commitsCount == 0 {
if commitsCount == 0 {
ctx.Handle(404, "repo.FileHistory", nil) ctx.Handle(404, "repo.FileHistory", nil)
return return
} }
@ -217,5 +221,5 @@ func FileHistory(ctx *middleware.Context, params martini.Params) {
ctx.Data["CommitCount"] = commitsCount ctx.Data["CommitCount"] = commitsCount
ctx.Data["LastPageNum"] = lastPage ctx.Data["LastPageNum"] = lastPage
ctx.Data["NextPageNum"] = nextPage ctx.Data["NextPageNum"] = nextPage
ctx.HTML(200, "repo/commits") ctx.HTML(200, COMMITS)
} }

View File

@ -107,9 +107,9 @@ func Http(ctx *middleware.Context, params martini.Params) {
} }
if !isPublicPull { if !isPublicPull {
var tp = models.AU_WRITABLE var tp = models.WRITABLE
if isPull { if isPull {
tp = models.AU_READABLE tp = models.READABLE
} }
has, err := models.HasAccess(authUsername, username+"/"+reponame, tp) has, err := models.HasAccess(authUsername, username+"/"+reponame, tp)
@ -117,8 +117,8 @@ func Http(ctx *middleware.Context, params martini.Params) {
ctx.Handle(401, "no basic auth and digit auth", nil) ctx.Handle(401, "no basic auth and digit auth", nil)
return return
} else if !has { } else if !has {
if tp == models.AU_READABLE { if tp == models.READABLE {
has, err = models.HasAccess(authUsername, username+"/"+reponame, models.AU_WRITABLE) has, err = models.HasAccess(authUsername, username+"/"+reponame, models.WRITABLE)
if err != nil || !has { if err != nil || !has {
ctx.Handle(401, "no basic auth and digit auth", nil) ctx.Handle(401, "no basic auth and digit auth", nil)
return return

View File

@ -22,6 +22,16 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const (
ISSUES base.TplName = "repo/issue/list"
ISSUE_CREATE base.TplName = "repo/issue/create"
ISSUE_VIEW base.TplName = "repo/issue/view"
MILESTONE base.TplName = "repo/issue/milestone"
MILESTONE_NEW base.TplName = "repo/issue/milestone_new"
MILESTONE_EDIT base.TplName = "repo/issue/milestone_edit"
)
func Issues(ctx *middleware.Context) { func Issues(ctx *middleware.Context) {
ctx.Data["Title"] = "Issues" ctx.Data["Title"] = "Issues"
ctx.Data["IsRepoToolbarIssues"] = true ctx.Data["IsRepoToolbarIssues"] = true
@ -134,7 +144,7 @@ func Issues(ctx *middleware.Context) {
} else { } else {
ctx.Data["ShowCount"] = issueStats.OpenCount ctx.Data["ShowCount"] = issueStats.OpenCount
} }
ctx.HTML(200, "issue/list") ctx.HTML(200, ISSUES)
} }
func CreateIssue(ctx *middleware.Context, params martini.Params) { func CreateIssue(ctx *middleware.Context, params martini.Params) {
@ -161,7 +171,7 @@ func CreateIssue(ctx *middleware.Context, params martini.Params) {
return return
} }
ctx.Data["Collaborators"] = us ctx.Data["Collaborators"] = us
ctx.HTML(200, "issue/create") ctx.HTML(200, ISSUE_CREATE)
} }
func CreateIssuePost(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) { func CreateIssuePost(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) {
@ -190,7 +200,7 @@ func CreateIssuePost(ctx *middleware.Context, params martini.Params, form auth.C
ctx.Data["Collaborators"] = us ctx.Data["Collaborators"] = us
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "issue/create") ctx.HTML(200, ISSUE_CREATE)
return return
} }
@ -392,7 +402,7 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner || (ctx.IsSigned && issue.PosterId == ctx.User.Id) ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner || (ctx.IsSigned && issue.PosterId == ctx.User.Id)
ctx.Data["IsRepoToolbarIssues"] = true ctx.Data["IsRepoToolbarIssues"] = true
ctx.Data["IsRepoToolbarIssuesList"] = false ctx.Data["IsRepoToolbarIssuesList"] = false
ctx.HTML(200, "issue/view") ctx.HTML(200, ISSUE_VIEW)
} }
func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) { func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) {
@ -794,14 +804,14 @@ func Milestones(ctx *middleware.Context) {
} else { } else {
ctx.Data["State"] = "open" ctx.Data["State"] = "open"
} }
ctx.HTML(200, "issue/milestone") ctx.HTML(200, MILESTONE)
} }
func NewMilestone(ctx *middleware.Context) { func NewMilestone(ctx *middleware.Context) {
ctx.Data["Title"] = "New Milestone" ctx.Data["Title"] = "New Milestone"
ctx.Data["IsRepoToolbarIssues"] = true ctx.Data["IsRepoToolbarIssues"] = true
ctx.Data["IsRepoToolbarIssuesList"] = true ctx.Data["IsRepoToolbarIssuesList"] = true
ctx.HTML(200, "issue/milestone_new") ctx.HTML(200, MILESTONE_NEW)
} }
func NewMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm) { func NewMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm) {
@ -809,6 +819,11 @@ func NewMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm) {
ctx.Data["IsRepoToolbarIssues"] = true ctx.Data["IsRepoToolbarIssues"] = true
ctx.Data["IsRepoToolbarIssuesList"] = true ctx.Data["IsRepoToolbarIssuesList"] = true
if ctx.HasError() {
ctx.HTML(200, MILESTONE_NEW)
return
}
var deadline time.Time var deadline time.Time
var err error var err error
if len(form.Deadline) == 0 { if len(form.Deadline) == 0 {
@ -890,7 +905,7 @@ func UpdateMilestone(ctx *middleware.Context, params martini.Params) {
} }
ctx.Data["Milestone"] = mile ctx.Data["Milestone"] = mile
ctx.HTML(200, "issue/milestone_edit") ctx.HTML(200, MILESTONE_EDIT)
} }
func UpdateMilestonePost(ctx *middleware.Context, params martini.Params, form auth.CreateMilestoneForm) { func UpdateMilestonePost(ctx *middleware.Context, params martini.Params, form auth.CreateMilestoneForm) {
@ -914,6 +929,11 @@ func UpdateMilestonePost(ctx *middleware.Context, params martini.Params, form au
return return
} }
if ctx.HasError() {
ctx.HTML(200, MILESTONE_EDIT)
return
}
var deadline time.Time var deadline time.Time
if len(form.Deadline) == 0 { if len(form.Deadline) == 0 {
form.Deadline = "12/31/9999" form.Deadline = "12/31/9999"

View File

@ -7,10 +7,15 @@ package repo
import ( import (
"github.com/go-martini/martini" "github.com/go-martini/martini"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
) )
const (
PULLS base.TplName = "repo/pulls"
)
func Pulls(ctx *middleware.Context, params martini.Params) { func Pulls(ctx *middleware.Context, params martini.Params) {
ctx.Data["IsRepoToolbarPulls"] = true ctx.Data["IsRepoToolbarPulls"] = true
ctx.HTML(200, "repo/pulls") ctx.HTML(200, PULLS)
} }

View File

@ -14,6 +14,12 @@ import (
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
) )
const (
RELEASES base.TplName = "repo/release/list"
RELEASE_NEW base.TplName = "repo/release/new"
RELEASE_EDIT base.TplName = "repo/release/edit"
)
func Releases(ctx *middleware.Context) { func Releases(ctx *middleware.Context) {
ctx.Data["Title"] = "Releases" ctx.Data["Title"] = "Releases"
ctx.Data["IsRepoToolbarReleases"] = true ctx.Data["IsRepoToolbarReleases"] = true
@ -100,7 +106,7 @@ func Releases(ctx *middleware.Context) {
} }
models.SortReleases(tags) models.SortReleases(tags)
ctx.Data["Releases"] = tags ctx.Data["Releases"] = tags
ctx.HTML(200, "release/list") ctx.HTML(200, RELEASES)
} }
func NewRelease(ctx *middleware.Context) { func NewRelease(ctx *middleware.Context) {
@ -112,7 +118,7 @@ func NewRelease(ctx *middleware.Context) {
ctx.Data["Title"] = "New Release" ctx.Data["Title"] = "New Release"
ctx.Data["IsRepoToolbarReleases"] = true ctx.Data["IsRepoToolbarReleases"] = true
ctx.Data["IsRepoReleaseNew"] = true ctx.Data["IsRepoReleaseNew"] = true
ctx.HTML(200, "release/new") ctx.HTML(200, RELEASE_NEW)
} }
func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) { func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
@ -126,7 +132,7 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
ctx.Data["IsRepoReleaseNew"] = true ctx.Data["IsRepoReleaseNew"] = true
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "release/new") ctx.HTML(200, RELEASE_NEW)
return return
} }
@ -187,7 +193,7 @@ func EditRelease(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Edit Release" ctx.Data["Title"] = "Edit Release"
ctx.Data["IsRepoToolbarReleases"] = true ctx.Data["IsRepoToolbarReleases"] = true
ctx.HTML(200, "release/edit") ctx.HTML(200, RELEASE_EDIT)
} }
func EditReleasePost(ctx *middleware.Context, params martini.Params, form auth.EditReleaseForm) { func EditReleasePost(ctx *middleware.Context, params martini.Params, form auth.EditReleaseForm) {
@ -208,6 +214,11 @@ func EditReleasePost(ctx *middleware.Context, params martini.Params, form auth.E
} }
ctx.Data["Release"] = rel ctx.Data["Release"] = rel
if ctx.HasError() {
ctx.HTML(200, RELEASE_EDIT)
return
}
ctx.Data["Title"] = "Edit Release" ctx.Data["Title"] = "Edit Release"
ctx.Data["IsRepoToolbarReleases"] = true ctx.Data["IsRepoToolbarReleases"] = true

View File

@ -25,12 +25,25 @@ import (
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
) )
const (
CREATE base.TplName = "repo/create"
MIGRATE base.TplName = "repo/migrate"
SINGLE base.TplName = "repo/single"
)
func Create(ctx *middleware.Context) { func Create(ctx *middleware.Context) {
ctx.Data["Title"] = "Create repository" ctx.Data["Title"] = "Create repository"
ctx.Data["PageIsNewRepo"] = true ctx.Data["PageIsNewRepo"] = true
ctx.Data["LanguageIgns"] = models.LanguageIgns ctx.Data["LanguageIgns"] = models.LanguageIgns
ctx.Data["Licenses"] = models.Licenses ctx.Data["Licenses"] = models.Licenses
ctx.HTML(200, "repo/create")
if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "home.Dashboard(GetOrganizations)", err)
return
}
ctx.Data["Orgs"] = ctx.User.Orgs
ctx.HTML(200, CREATE)
} }
func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) { func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
@ -39,76 +52,125 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
ctx.Data["LanguageIgns"] = models.LanguageIgns ctx.Data["LanguageIgns"] = models.LanguageIgns
ctx.Data["Licenses"] = models.Licenses ctx.Data["Licenses"] = models.Licenses
if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "home.CreatePost(GetOrganizations)", err)
return
}
ctx.Data["Orgs"] = ctx.User.Orgs
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "repo/create") ctx.HTML(200, CREATE)
return return
} }
repo, err := models.CreateRepository(ctx.User, form.RepoName, form.Description, u := ctx.User
// Not equal means current user is an organization.
if u.Id != form.Uid {
var err error
u, err = models.GetUserById(form.Uid)
if err != nil {
if err == models.ErrUserNotExist {
ctx.Handle(404, "home.CreatePost(GetUserById)", err)
} else {
ctx.Handle(500, "home.CreatePost(GetUserById)", err)
}
return
}
}
repo, err := models.CreateRepository(u, form.RepoName, form.Description,
form.Language, form.License, form.Private, false, form.InitReadme) form.Language, form.License, form.Private, false, form.InitReadme)
if err == nil { if err == nil {
log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName) log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, u.LowerName, form.RepoName)
ctx.Redirect("/" + ctx.User.Name + "/" + form.RepoName) ctx.Redirect("/" + u.Name + "/" + form.RepoName)
return return
} else if err == models.ErrRepoAlreadyExist { } else if err == models.ErrRepoAlreadyExist {
ctx.RenderWithErr("Repository name has already been used", "repo/create", &form) ctx.RenderWithErr("Repository name has already been used", CREATE, &form)
return return
} else if err == models.ErrRepoNameIllegal { } else if err == models.ErrRepoNameIllegal {
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "repo/create", &form) ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), CREATE, &form)
return return
} }
if repo != nil { if repo != nil {
if errDelete := models.DeleteRepository(ctx.User.Id, repo.Id, ctx.User.Name); errDelete != nil { if errDelete := models.DeleteRepository(u.Id, repo.Id, u.Name); errDelete != nil {
log.Error("repo.MigratePost(CreatePost): %v", errDelete) log.Error("repo.CreatePost(DeleteRepository): %v", errDelete)
} }
} }
ctx.Handle(500, "repo.Create", err) ctx.Handle(500, "repo.CreatePost(CreateRepository)", err)
} }
func Migrate(ctx *middleware.Context) { func Migrate(ctx *middleware.Context) {
ctx.Data["Title"] = "Migrate repository" ctx.Data["Title"] = "Migrate repository"
ctx.Data["PageIsNewRepo"] = true ctx.Data["PageIsNewRepo"] = true
ctx.HTML(200, "repo/migrate")
if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "home.Migrate(GetOrganizations)", err)
return
}
ctx.Data["Orgs"] = ctx.User.Orgs
ctx.HTML(200, MIGRATE)
} }
func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) { func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
ctx.Data["Title"] = "Migrate repository" ctx.Data["Title"] = "Migrate repository"
ctx.Data["PageIsNewRepo"] = true ctx.Data["PageIsNewRepo"] = true
if ctx.HasError() { if err := ctx.User.GetOrganizations(); err != nil {
ctx.HTML(200, "repo/migrate") ctx.Handle(500, "home.MigratePost(GetOrganizations)", err)
return return
} }
ctx.Data["Orgs"] = ctx.User.Orgs
if ctx.HasError() {
ctx.HTML(200, MIGRATE)
return
}
u := ctx.User
// Not equal means current user is an organization.
if u.Id != form.Uid {
var err error
u, err = models.GetUserById(form.Uid)
if err != nil {
if err == models.ErrUserNotExist {
ctx.Handle(404, "home.MigratePost(GetUserById)", err)
} else {
ctx.Handle(500, "home.MigratePost(GetUserById)", err)
}
return
}
}
authStr := strings.Replace(fmt.Sprintf("://%s:%s", authStr := strings.Replace(fmt.Sprintf("://%s:%s",
form.AuthUserName, form.AuthPasswd), "@", "%40", -1) form.AuthUserName, form.AuthPasswd), "@", "%40", -1)
url := strings.Replace(form.Url, "://", authStr+"@", 1) url := strings.Replace(form.Url, "://", authStr+"@", 1)
repo, err := models.MigrateRepository(ctx.User, form.RepoName, form.Description, form.Private, repo, err := models.MigrateRepository(u, form.RepoName, form.Description, form.Private,
form.Mirror, url) form.Mirror, url)
if err == nil { if err == nil {
log.Trace("%s Repository migrated: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName) log.Trace("%s Repository migrated: %s/%s", ctx.Req.RequestURI, u.LowerName, form.RepoName)
ctx.Redirect("/" + ctx.User.Name + "/" + form.RepoName) ctx.Redirect("/" + u.Name + "/" + form.RepoName)
return return
} else if err == models.ErrRepoAlreadyExist { } else if err == models.ErrRepoAlreadyExist {
ctx.RenderWithErr("Repository name has already been used", "repo/migrate", &form) ctx.RenderWithErr("Repository name has already been used", MIGRATE, &form)
return return
} else if err == models.ErrRepoNameIllegal { } else if err == models.ErrRepoNameIllegal {
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "repo/migrate", &form) ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), MIGRATE, &form)
return return
} }
if repo != nil { if repo != nil {
if errDelete := models.DeleteRepository(ctx.User.Id, repo.Id, ctx.User.Name); errDelete != nil { if errDelete := models.DeleteRepository(u.Id, repo.Id, u.Name); errDelete != nil {
log.Error("repo.MigratePost(DeleteRepository): %v", errDelete) log.Error("repo.MigratePost(DeleteRepository): %v", errDelete)
} }
} }
if strings.Contains(err.Error(), "Authentication failed") { if strings.Contains(err.Error(), "Authentication failed") {
ctx.RenderWithErr(err.Error(), "repo/migrate", &form) ctx.RenderWithErr(err.Error(), MIGRATE, &form)
return return
} }
ctx.Handle(500, "repo.Migrate", err) ctx.Handle(500, "repo.Migrate(MigrateRepository)", err)
} }
func Single(ctx *middleware.Context, params martini.Params) { func Single(ctx *middleware.Context, params martini.Params) {
@ -291,7 +353,7 @@ func Single(ctx *middleware.Context, params martini.Params) {
ctx.Data["Treenames"] = treenames ctx.Data["Treenames"] = treenames
ctx.Data["TreePath"] = treePath ctx.Data["TreePath"] = treePath
ctx.Data["BranchLink"] = branchLink ctx.Data["BranchLink"] = branchLink
ctx.HTML(200, "repo/single") ctx.HTML(200, SINGLE)
} }
func basicEncode(username, password string) string { func basicEncode(username, password string) string {
@ -318,7 +380,7 @@ func basicDecode(encoded string) (user string, name string, err error) {
func authRequired(ctx *middleware.Context) { func authRequired(ctx *middleware.Context) {
ctx.ResponseWriter.Header().Set("WWW-Authenticate", "Basic realm=\".\"") ctx.ResponseWriter.Header().Set("WWW-Authenticate", "Basic realm=\".\"")
ctx.Data["ErrorMsg"] = "no basic auth and digit auth" ctx.Data["ErrorMsg"] = "no basic auth and digit auth"
ctx.HTML(401, fmt.Sprintf("status/401")) ctx.HTML(401, base.TplName("status/401"))
} }
func Action(ctx *middleware.Context, params martini.Params) { func Action(ctx *middleware.Context, params martini.Params) {

View File

@ -20,10 +20,19 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const (
SETTING base.TplName = "repo/setting"
COLLABORATION base.TplName = "repo/collaboration"
HOOKS base.TplName = "repo/hooks"
HOOK_ADD base.TplName = "repo/hook_add"
HOOK_EDIT base.TplName = "repo/hook_edit"
)
func Setting(ctx *middleware.Context) { func Setting(ctx *middleware.Context) {
ctx.Data["IsRepoToolbarSetting"] = true ctx.Data["IsRepoToolbarSetting"] = true
ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - settings" ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - settings"
ctx.HTML(200, "repo/setting") ctx.HTML(200, SETTING)
} }
func SettingPost(ctx *middleware.Context, form auth.RepoSettingForm) { func SettingPost(ctx *middleware.Context, form auth.RepoSettingForm) {
@ -32,7 +41,7 @@ func SettingPost(ctx *middleware.Context, form auth.RepoSettingForm) {
switch ctx.Query("action") { switch ctx.Query("action") {
case "update": case "update":
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "repo/setting") ctx.HTML(200, SETTING)
return return
} }
@ -44,7 +53,7 @@ func SettingPost(ctx *middleware.Context, form auth.RepoSettingForm) {
ctx.Handle(500, "setting.SettingPost(update: check existence)", err) ctx.Handle(500, "setting.SettingPost(update: check existence)", err)
return return
} else if isExist { } else if isExist {
ctx.RenderWithErr("Repository name has been taken in your repositories.", "repo/setting", nil) ctx.RenderWithErr("Repository name has been taken in your repositories.", SETTING, nil)
return return
} else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil { } else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil {
ctx.Handle(500, "setting.SettingPost(change repository name)", err) ctx.Handle(500, "setting.SettingPost(change repository name)", err)
@ -84,7 +93,7 @@ func SettingPost(ctx *middleware.Context, form auth.RepoSettingForm) {
ctx.Redirect(fmt.Sprintf("/%s/%s/settings", ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)) ctx.Redirect(fmt.Sprintf("/%s/%s/settings", ctx.Repo.Owner.Name, ctx.Repo.Repository.Name))
case "transfer": case "transfer":
if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") { if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
ctx.RenderWithErr("Please make sure you entered repository name is correct.", "repo/setting", nil) ctx.RenderWithErr("Please make sure you entered repository name is correct.", SETTING, nil)
return return
} else if ctx.Repo.Repository.IsMirror { } else if ctx.Repo.Repository.IsMirror {
ctx.Error(404) ctx.Error(404)
@ -98,7 +107,7 @@ func SettingPost(ctx *middleware.Context, form auth.RepoSettingForm) {
ctx.Handle(500, "setting.SettingPost(transfer: check existence)", err) ctx.Handle(500, "setting.SettingPost(transfer: check existence)", err)
return return
} else if !isExist { } else if !isExist {
ctx.RenderWithErr("Please make sure you entered owner name is correct.", "repo/setting", nil) ctx.RenderWithErr("Please make sure you entered owner name is correct.", SETTING, nil)
return return
} else if err = models.TransferOwnership(ctx.User, newOwner, ctx.Repo.Repository); err != nil { } else if err = models.TransferOwnership(ctx.User, newOwner, ctx.Repo.Repository); err != nil {
ctx.Handle(500, "setting.SettingPost(transfer repository)", err) ctx.Handle(500, "setting.SettingPost(transfer repository)", err)
@ -109,7 +118,7 @@ func SettingPost(ctx *middleware.Context, form auth.RepoSettingForm) {
ctx.Redirect("/") ctx.Redirect("/")
case "delete": case "delete":
if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") { if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
ctx.RenderWithErr("Please make sure you entered repository name is correct.", "repo/setting", nil) ctx.RenderWithErr("Please make sure you entered repository name is correct.", SETTING, nil)
return return
} }
@ -156,7 +165,7 @@ func Collaboration(ctx *middleware.Context) {
} }
ctx.Data["Collaborators"] = us ctx.Data["Collaborators"] = us
ctx.HTML(200, "repo/collaboration") ctx.HTML(200, COLLABORATION)
} }
func CollaborationPost(ctx *middleware.Context) { func CollaborationPost(ctx *middleware.Context) {
@ -166,7 +175,7 @@ func CollaborationPost(ctx *middleware.Context) {
ctx.Redirect(ctx.Req.RequestURI) ctx.Redirect(ctx.Req.RequestURI)
return return
} }
has, err := models.HasAccess(name, repoLink, models.AU_WRITABLE) has, err := models.HasAccess(name, repoLink, models.WRITABLE)
if err != nil { if err != nil {
ctx.Handle(500, "setting.CollaborationPost(HasAccess)", err) ctx.Handle(500, "setting.CollaborationPost(HasAccess)", err)
return return
@ -187,7 +196,7 @@ func CollaborationPost(ctx *middleware.Context) {
} }
if err = models.AddAccess(&models.Access{UserName: name, RepoName: repoLink, if err = models.AddAccess(&models.Access{UserName: name, RepoName: repoLink,
Mode: models.AU_WRITABLE}); err != nil { Mode: models.WRITABLE}); err != nil {
ctx.Handle(500, "setting.CollaborationPost(AddAccess)", err) ctx.Handle(500, "setting.CollaborationPost(AddAccess)", err)
return return
} }
@ -226,13 +235,13 @@ func WebHooks(ctx *middleware.Context) {
} }
ctx.Data["Webhooks"] = ws ctx.Data["Webhooks"] = ws
ctx.HTML(200, "repo/hooks") ctx.HTML(200, HOOKS)
} }
func WebHooksAdd(ctx *middleware.Context) { func WebHooksAdd(ctx *middleware.Context) {
ctx.Data["IsRepoToolbarWebHooks"] = true ctx.Data["IsRepoToolbarWebHooks"] = true
ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook" ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook"
ctx.HTML(200, "repo/hooks_add") ctx.HTML(200, HOOK_ADD)
} }
func WebHooksAddPost(ctx *middleware.Context, form auth.NewWebhookForm) { func WebHooksAddPost(ctx *middleware.Context, form auth.NewWebhookForm) {
@ -240,7 +249,7 @@ func WebHooksAddPost(ctx *middleware.Context, form auth.NewWebhookForm) {
ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook" ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook"
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "repo/hooks_add") ctx.HTML(200, HOOK_ADD)
return return
} }
@ -293,40 +302,46 @@ func WebHooksEdit(ctx *middleware.Context, params martini.Params) {
w.GetEvent() w.GetEvent()
ctx.Data["Webhook"] = w ctx.Data["Webhook"] = w
ctx.HTML(200, "repo/hooks_edit") ctx.HTML(200, HOOK_EDIT)
} }
func WebHooksEditPost(ctx *middleware.Context, params martini.Params, form auth.NewWebhookForm) { func WebHooksEditPost(ctx *middleware.Context, params martini.Params, form auth.NewWebhookForm) {
ctx.Data["IsRepoToolbarWebHooks"] = true ctx.Data["IsRepoToolbarWebHooks"] = true
ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhook" ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhook"
if ctx.HasError() {
ctx.HTML(200, "repo/hooks_add")
return
}
hookId, _ := base.StrTo(params["id"]).Int64() hookId, _ := base.StrTo(params["id"]).Int64()
if hookId == 0 { if hookId == 0 {
ctx.Handle(404, "setting.WebHooksEditPost", nil) ctx.Handle(404, "setting.WebHooksEditPost", nil)
return return
} }
w, err := models.GetWebhookById(hookId)
if err != nil {
if err == models.ErrWebhookNotExist {
ctx.Handle(404, "setting.WebHooksEditPost(GetWebhookById)", nil)
} else {
ctx.Handle(500, "setting.WebHooksEditPost(GetWebhookById)", err)
}
return
}
if ctx.HasError() {
ctx.HTML(200, HOOK_EDIT)
return
}
ct := models.JSON ct := models.JSON
if form.ContentType == "2" { if form.ContentType == "2" {
ct = models.FORM ct = models.FORM
} }
w := &models.Webhook{ w.Url = form.Url
Id: hookId, w.ContentType = ct
RepoId: ctx.Repo.Repository.Id, w.Secret = form.Secret
Url: form.Url, w.HookEvent = &models.HookEvent{
ContentType: ct, PushOnly: form.PushOnly,
Secret: form.Secret,
HookEvent: &models.HookEvent{
PushOnly: form.PushOnly,
},
IsActive: form.Active,
} }
w.IsActive = form.Active
if err := w.UpdateEvent(); err != nil { if err := w.UpdateEvent(); err != nil {
ctx.Handle(500, "setting.WebHooksEditPost(UpdateEvent)", err) ctx.Handle(500, "setting.WebHooksEditPost(UpdateEvent)", err)
return return

View File

@ -17,10 +17,25 @@ import (
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
) )
const (
DASHBOARD base.TplName = "user/dashboard"
PROFILE base.TplName = "user/profile"
ISSUES base.TplName = "user/issues"
PULLS base.TplName = "user/pulls"
STARS base.TplName = "user/stars"
)
func Dashboard(ctx *middleware.Context) { func Dashboard(ctx *middleware.Context) {
ctx.Data["Title"] = "Dashboard" ctx.Data["Title"] = "Dashboard"
ctx.Data["PageIsUserDashboard"] = true ctx.Data["PageIsUserDashboard"] = true
if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "home.Dashboard(GetOrganizations)", err)
return
}
ctx.Data["Orgs"] = ctx.User.Orgs
ctx.Data["ContextUser"] = ctx.User
var err error var err error
ctx.Data["MyRepos"], err = models.GetRepositories(ctx.User.Id, true) ctx.Data["MyRepos"], err = models.GetRepositories(ctx.User.Id, true)
if err != nil { if err != nil {
@ -45,21 +60,21 @@ func Dashboard(ctx *middleware.Context) {
for _, act := range actions { for _, act := range actions {
if act.IsPrivate { if act.IsPrivate {
if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName, if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
models.AU_READABLE); !has { models.READABLE); !has {
continue continue
} }
} }
feeds = append(feeds, act) feeds = append(feeds, act)
} }
ctx.Data["Feeds"] = feeds ctx.Data["Feeds"] = feeds
ctx.HTML(200, "user/dashboard") ctx.HTML(200, DASHBOARD)
} }
func Profile(ctx *middleware.Context, params martini.Params) { func Profile(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Profile" ctx.Data["Title"] = "Profile"
ctx.Data["PageIsUserProfile"] = true ctx.Data["PageIsUserProfile"] = true
user, err := models.GetUserByName(params["username"]) u, err := models.GetUserByName(params["username"])
if err != nil { if err != nil {
if err == models.ErrUserNotExist { if err == models.ErrUserNotExist {
ctx.Handle(404, "user.Profile(GetUserByName)", err) ctx.Handle(404, "user.Profile(GetUserByName)", err)
@ -68,26 +83,30 @@ func Profile(ctx *middleware.Context, params martini.Params) {
} }
return return
} }
ctx.Data["Owner"] = user // For security reason, hide e-mail address for anonymous visitors.
if !ctx.IsSigned {
u.Email = ""
}
ctx.Data["Owner"] = u
tab := ctx.Query("tab") tab := ctx.Query("tab")
ctx.Data["TabName"] = tab ctx.Data["TabName"] = tab
switch tab { switch tab {
case "activity": case "activity":
ctx.Data["Feeds"], err = models.GetFeeds(user.Id, 0, true) ctx.Data["Feeds"], err = models.GetFeeds(u.Id, 0, true)
if err != nil { if err != nil {
ctx.Handle(500, "user.Profile(GetFeeds)", err) ctx.Handle(500, "user.Profile(GetFeeds)", err)
return return
} }
default: default:
ctx.Data["Repos"], err = models.GetRepositories(user.Id, ctx.IsSigned && ctx.User.Id == user.Id) ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
if err != nil { if err != nil {
ctx.Handle(500, "user.Profile(GetRepositories)", err) ctx.Handle(500, "user.Profile(GetRepositories)", err)
return return
} }
} }
ctx.HTML(200, "user/profile") ctx.HTML(200, PROFILE)
} }
func Email2User(ctx *middleware.Context) { func Email2User(ctx *middleware.Context) {
@ -119,7 +138,7 @@ func Feeds(ctx *middleware.Context, form auth.FeedsForm) {
for _, act := range actions { for _, act := range actions {
if act.IsPrivate { if act.IsPrivate {
if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName, if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
models.AU_READABLE); !has { models.READABLE); !has {
continue continue
} }
} }
@ -254,13 +273,13 @@ func Issues(ctx *middleware.Context) {
} else { } else {
ctx.Data["ShowCount"] = issueStats.OpenCount ctx.Data["ShowCount"] = issueStats.OpenCount
} }
ctx.HTML(200, "user/issue") ctx.HTML(200, ISSUES)
} }
func Pulls(ctx *middleware.Context) { func Pulls(ctx *middleware.Context) {
ctx.HTML(200, "user/pulls") ctx.HTML(200, PULLS)
} }
func Stars(ctx *middleware.Context) { func Stars(ctx *middleware.Context) {
ctx.HTML(200, "user/stars") ctx.HTML(200, STARS)
} }

View File

@ -14,12 +14,21 @@ import (
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
) )
const (
SETTING base.TplName = "user/setting"
SOCIAL base.TplName = "user/social"
PASSWORD base.TplName = "user/password"
PUBLICKEY base.TplName = "user/publickey"
NOTIFICATION base.TplName = "user/notification"
SECURITY base.TplName = "user/security"
)
func Setting(ctx *middleware.Context) { func Setting(ctx *middleware.Context) {
ctx.Data["Title"] = "Setting" ctx.Data["Title"] = "Setting"
ctx.Data["PageIsUserSetting"] = true ctx.Data["PageIsUserSetting"] = true
ctx.Data["IsUserPageSetting"] = true ctx.Data["IsUserPageSetting"] = true
ctx.Data["Owner"] = ctx.User ctx.Data["Owner"] = ctx.User
ctx.HTML(200, "user/setting") ctx.HTML(200, SETTING)
} }
func SettingPost(ctx *middleware.Context, form auth.UpdateProfileForm) { func SettingPost(ctx *middleware.Context, form auth.UpdateProfileForm) {
@ -28,7 +37,7 @@ func SettingPost(ctx *middleware.Context, form auth.UpdateProfileForm) {
ctx.Data["IsUserPageSetting"] = true ctx.Data["IsUserPageSetting"] = true
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "user/setting") ctx.HTML(200, SETTING)
return return
} }
@ -59,7 +68,7 @@ func SettingPost(ctx *middleware.Context, form auth.UpdateProfileForm) {
ctx.User.Avatar = base.EncodeMd5(form.Avatar) ctx.User.Avatar = base.EncodeMd5(form.Avatar)
ctx.User.AvatarEmail = form.Avatar ctx.User.AvatarEmail = form.Avatar
if err := models.UpdateUser(ctx.User); err != nil { if err := models.UpdateUser(ctx.User); err != nil {
ctx.Handle(500, "setting.Setting", err) ctx.Handle(500, "setting.Setting(UpdateUser)", err)
return return
} }
log.Trace("%s User setting updated: %s", ctx.Req.RequestURI, ctx.User.LowerName) log.Trace("%s User setting updated: %s", ctx.Req.RequestURI, ctx.User.LowerName)
@ -90,14 +99,14 @@ func SettingSocial(ctx *middleware.Context) {
ctx.Handle(500, "user.SettingSocial(GetOauthByUserId)", err) ctx.Handle(500, "user.SettingSocial(GetOauthByUserId)", err)
return return
} }
ctx.HTML(200, "user/social") ctx.HTML(200, SOCIAL)
} }
func SettingPassword(ctx *middleware.Context) { func SettingPassword(ctx *middleware.Context) {
ctx.Data["Title"] = "Password" ctx.Data["Title"] = "Password"
ctx.Data["PageIsUserSetting"] = true ctx.Data["PageIsUserSetting"] = true
ctx.Data["IsUserPageSettingPasswd"] = true ctx.Data["IsUserPageSettingPasswd"] = true
ctx.HTML(200, "user/password") ctx.HTML(200, PASSWORD)
} }
func SettingPasswordPost(ctx *middleware.Context, form auth.UpdatePasswdForm) { func SettingPasswordPost(ctx *middleware.Context, form auth.UpdatePasswdForm) {
@ -106,7 +115,7 @@ func SettingPasswordPost(ctx *middleware.Context, form auth.UpdatePasswdForm) {
ctx.Data["IsUserPageSettingPasswd"] = true ctx.Data["IsUserPageSettingPasswd"] = true
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "user/password") ctx.HTML(200, PASSWORD)
return return
} }
@ -207,7 +216,7 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) {
} }
} }
ctx.HTML(200, "user/publickey") ctx.HTML(200, PUBLICKEY)
} }
func SettingNotification(ctx *middleware.Context) { func SettingNotification(ctx *middleware.Context) {
@ -215,7 +224,7 @@ func SettingNotification(ctx *middleware.Context) {
ctx.Data["Title"] = "Notification" ctx.Data["Title"] = "Notification"
ctx.Data["PageIsUserSetting"] = true ctx.Data["PageIsUserSetting"] = true
ctx.Data["IsUserPageSettingNotify"] = true ctx.Data["IsUserPageSettingNotify"] = true
ctx.HTML(200, "user/notification") ctx.HTML(200, NOTIFICATION)
} }
func SettingSecurity(ctx *middleware.Context) { func SettingSecurity(ctx *middleware.Context) {
@ -223,5 +232,5 @@ func SettingSecurity(ctx *middleware.Context) {
ctx.Data["Title"] = "Security" ctx.Data["Title"] = "Security"
ctx.Data["PageIsUserSetting"] = true ctx.Data["PageIsUserSetting"] = true
ctx.Data["IsUserPageSettingSecurity"] = true ctx.Data["IsUserPageSettingSecurity"] = true
ctx.HTML(200, "user/security") ctx.HTML(200, SECURITY)
} }

View File

@ -17,12 +17,21 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const (
SIGNIN base.TplName = "user/signin"
SIGNUP base.TplName = "user/signup"
DELETE base.TplName = "user/delete"
ACTIVATE base.TplName = "user/activate"
FORGOT_PASSWORD base.TplName = "user/forgot_passwd"
RESET_PASSWORD base.TplName = "user/reset_passwd"
)
func SignIn(ctx *middleware.Context) { func SignIn(ctx *middleware.Context) {
ctx.Data["Title"] = "Log In" ctx.Data["Title"] = "Log In"
if _, ok := ctx.Session.Get("socialId").(int64); ok { if _, ok := ctx.Session.Get("socialId").(int64); ok {
ctx.Data["IsSocialLogin"] = true ctx.Data["IsSocialLogin"] = true
ctx.HTML(200, "user/signin") ctx.HTML(200, SIGNIN)
return return
} }
@ -34,7 +43,7 @@ func SignIn(ctx *middleware.Context) {
// Check auto-login. // Check auto-login.
uname := ctx.GetCookie(setting.CookieUserName) uname := ctx.GetCookie(setting.CookieUserName)
if len(uname) == 0 { if len(uname) == 0 {
ctx.HTML(200, "user/signin") ctx.HTML(200, SIGNIN)
return return
} }
@ -57,7 +66,7 @@ func SignIn(ctx *middleware.Context) {
secret := base.EncodeMd5(user.Rands + user.Passwd) secret := base.EncodeMd5(user.Rands + user.Passwd)
value, _ := ctx.GetSecureCookie(secret, setting.CookieRememberName) value, _ := ctx.GetSecureCookie(secret, setting.CookieRememberName)
if value != user.Name { if value != user.Name {
ctx.HTML(200, "user/signin") ctx.HTML(200, SIGNIN)
return return
} }
@ -86,7 +95,7 @@ func SignInPost(ctx *middleware.Context, form auth.LogInForm) {
} }
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "user/signin") ctx.HTML(200, SIGNIN)
return return
} }
@ -94,7 +103,7 @@ func SignInPost(ctx *middleware.Context, form auth.LogInForm) {
if err != nil { if err != nil {
if err == models.ErrUserNotExist { if err == models.ErrUserNotExist {
log.Trace("%s Log in failed: %s", ctx.Req.RequestURI, form.UserName) log.Trace("%s Log in failed: %s", ctx.Req.RequestURI, form.UserName)
ctx.RenderWithErr("Username or password is not correct", "user/signin", &form) ctx.RenderWithErr("Username or password is not correct", SIGNIN, &form)
return return
} }
@ -151,7 +160,7 @@ func SignUp(ctx *middleware.Context) {
if setting.Service.DisableRegistration { if setting.Service.DisableRegistration {
ctx.Data["DisableRegistration"] = true ctx.Data["DisableRegistration"] = true
ctx.HTML(200, "user/signup") ctx.HTML(200, SIGNUP)
return return
} }
@ -160,7 +169,7 @@ func SignUp(ctx *middleware.Context) {
return return
} }
ctx.HTML(200, "user/signup") ctx.HTML(200, SIGNUP)
} }
func oauthSignUp(ctx *middleware.Context, sid int64) { func oauthSignUp(ctx *middleware.Context, sid int64) {
@ -180,7 +189,7 @@ func oauthSignUp(ctx *middleware.Context, sid int64) {
ctx.Data["username"] = strings.Replace(ctx.Session.Get("socialName").(string), " ", "", -1) ctx.Data["username"] = strings.Replace(ctx.Session.Get("socialName").(string), " ", "", -1)
ctx.Data["email"] = ctx.Session.Get("socialEmail") ctx.Data["email"] = ctx.Session.Get("socialEmail")
log.Trace("user.oauthSignUp(social ID): %v", ctx.Session.Get("socialId")) log.Trace("user.oauthSignUp(social ID): %v", ctx.Session.Get("socialId"))
ctx.HTML(200, "user/signup") ctx.HTML(200, SIGNUP)
} }
func SignUpPost(ctx *middleware.Context, form auth.RegisterForm) { func SignUpPost(ctx *middleware.Context, form auth.RegisterForm) {
@ -198,14 +207,14 @@ func SignUpPost(ctx *middleware.Context, form auth.RegisterForm) {
} }
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, "user/signup") ctx.HTML(200, SIGNUP)
return return
} }
if form.Password != form.RetypePasswd { if form.Password != form.RetypePasswd {
ctx.Data["Err_Password"] = true ctx.Data["Err_Password"] = true
ctx.Data["Err_RetypePasswd"] = true ctx.Data["Err_RetypePasswd"] = true
ctx.RenderWithErr("Password and re-type password are not same.", "user/signup", &form) ctx.RenderWithErr("Password and re-type password are not same.", SIGNUP, &form)
return return
} }
@ -217,22 +226,23 @@ func SignUpPost(ctx *middleware.Context, form auth.RegisterForm) {
} }
var err error var err error
if u, err = models.RegisterUser(u); err != nil { if u, err = models.CreateUser(u); err != nil {
switch err { switch err {
case models.ErrUserAlreadyExist: case models.ErrUserAlreadyExist:
ctx.Data["Err_UserName"] = true ctx.Data["Err_UserName"] = true
ctx.RenderWithErr("Username has been already taken", "user/signup", &form) ctx.RenderWithErr("Username has been already taken", SIGNUP, &form)
case models.ErrEmailAlreadyUsed: case models.ErrEmailAlreadyUsed:
ctx.Data["Err_Email"] = true ctx.Data["Err_Email"] = true
ctx.RenderWithErr("E-mail address has been already used", "user/signup", &form) ctx.RenderWithErr("E-mail address has been already used", SIGNUP, &form)
case models.ErrUserNameIllegal: case models.ErrUserNameIllegal:
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "user/signup", &form) ctx.Data["Err_UserName"] = true
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), SIGNUP, &form)
default: default:
ctx.Handle(500, "user.SignUpPost(RegisterUser)", err) ctx.Handle(500, "user.SignUpPost(CreateUser)", err)
} }
return return
} }
log.Trace("%s User created: %s", ctx.Req.RequestURI, form.UserName) log.Trace("%s User created: %s", ctx.Req.RequestURI, u.Name)
// Bind social account. // Bind social account.
if isOauth { if isOauth {
@ -265,7 +275,7 @@ func Delete(ctx *middleware.Context) {
ctx.Data["Title"] = "Delete Account" ctx.Data["Title"] = "Delete Account"
ctx.Data["PageIsUserSetting"] = true ctx.Data["PageIsUserSetting"] = true
ctx.Data["IsUserPageSettingDelete"] = true ctx.Data["IsUserPageSettingDelete"] = true
ctx.HTML(200, "user/delete") ctx.HTML(200, DELETE)
} }
func DeletePost(ctx *middleware.Context) { func DeletePost(ctx *middleware.Context) {
@ -286,7 +296,7 @@ func DeletePost(ctx *middleware.Context) {
case models.ErrUserOwnRepos: case models.ErrUserOwnRepos:
ctx.Flash.Error("Your account still have ownership of repository, you have to delete or transfer them first.") ctx.Flash.Error("Your account still have ownership of repository, you have to delete or transfer them first.")
default: default:
ctx.Handle(500, "user.Delete", err) ctx.Handle(500, "user.Delete(DeleteUser)", err)
return return
} }
} else { } else {
@ -321,7 +331,7 @@ func Activate(ctx *middleware.Context) {
} else { } else {
ctx.Data["ServiceNotEnabled"] = true ctx.Data["ServiceNotEnabled"] = true
} }
ctx.HTML(200, "user/activate") ctx.HTML(200, ACTIVATE)
return return
} }
@ -343,7 +353,7 @@ func Activate(ctx *middleware.Context) {
} }
ctx.Data["IsActivateFailed"] = true ctx.Data["IsActivateFailed"] = true
ctx.HTML(200, "user/activate") ctx.HTML(200, ACTIVATE)
} }
func ForgotPasswd(ctx *middleware.Context) { func ForgotPasswd(ctx *middleware.Context) {
@ -351,12 +361,12 @@ func ForgotPasswd(ctx *middleware.Context) {
if setting.MailService == nil { if setting.MailService == nil {
ctx.Data["IsResetDisable"] = true ctx.Data["IsResetDisable"] = true
ctx.HTML(200, "user/forgot_passwd") ctx.HTML(200, FORGOT_PASSWORD)
return return
} }
ctx.Data["IsResetRequest"] = true ctx.Data["IsResetRequest"] = true
ctx.HTML(200, "user/forgot_passwd") ctx.HTML(200, FORGOT_PASSWORD)
} }
func ForgotPasswdPost(ctx *middleware.Context) { func ForgotPasswdPost(ctx *middleware.Context) {
@ -381,7 +391,7 @@ func ForgotPasswdPost(ctx *middleware.Context) {
if ctx.Cache.IsExist("MailResendLimit_" + u.LowerName) { if ctx.Cache.IsExist("MailResendLimit_" + u.LowerName) {
ctx.Data["ResendLimited"] = true ctx.Data["ResendLimited"] = true
ctx.HTML(200, "user/forgot_passwd") ctx.HTML(200, FORGOT_PASSWORD)
return return
} }
@ -393,7 +403,7 @@ func ForgotPasswdPost(ctx *middleware.Context) {
ctx.Data["Email"] = email ctx.Data["Email"] = email
ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60 ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60
ctx.Data["IsResetSent"] = true ctx.Data["IsResetSent"] = true
ctx.HTML(200, "user/forgot_passwd") ctx.HTML(200, FORGOT_PASSWORD)
} }
func ResetPasswd(ctx *middleware.Context) { func ResetPasswd(ctx *middleware.Context) {
@ -406,7 +416,7 @@ func ResetPasswd(ctx *middleware.Context) {
} }
ctx.Data["Code"] = code ctx.Data["Code"] = code
ctx.Data["IsResetForm"] = true ctx.Data["IsResetForm"] = true
ctx.HTML(200, "user/reset_passwd") ctx.HTML(200, RESET_PASSWORD)
} }
func ResetPasswdPost(ctx *middleware.Context) { func ResetPasswdPost(ctx *middleware.Context) {
@ -443,5 +453,5 @@ func ResetPasswdPost(ctx *middleware.Context) {
} }
ctx.Data["IsResetFailed"] = true ctx.Data["IsResetFailed"] = true
ctx.HTML(200, "user/reset_passwd") ctx.HTML(200, RESET_PASSWORD)
} }

View File

@ -1 +1 @@
0.4.5.0621 Alpha 0.4.5.0627 Alpha

View File

@ -36,8 +36,8 @@
<dd>{{.LogRootPath}}</dd> <dd>{{.LogRootPath}}</dd>
<dt>Script Type</dt> <dt>Script Type</dt>
<dd>{{.ScriptType}}</dd> <dd>{{.ScriptType}}</dd>
<dt>Reverse Authentication UID</dt> <dt>Reverse Authentication User</dt>
<dd>{{.ReverseProxyAuthUid}}</dd> <dd>{{.ReverseProxyAuthUser}}</dd>
</dl> </dl>
</div> </div>
</div> </div>

View File

@ -0,0 +1,75 @@
{{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><a href="#"><i class="fa fa-users"></i>Members
<span class="label label-default">5</span></a>
</li>
<li class="active"><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>
</div>
<div id="body" class="container">
<div id="org">
<form id="org-teams-edit" class="form-horizontal card">
<h3>Edit team</h3>
<div class="form-group">
<label class="col-md-2 control-label">Team Name<strong class="text-danger">*</strong></label>
<div class="col-md-8">
<input name="team" type="text" class="form-control" placeholder="Type your team name" value="" required="required">
<span class="help-block">You'll use this name to mention this team in conversations.</span>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Description</label>
<div class="col-md-8">
<input name="desc" type="text" class="form-control" placeholder="Type your team description (optional)" value="">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Permission</label>
<div class="col-md-8">
<div class="radio">
<label>
<input type="radio" name="permission" value="pull" checked="">
<strong>Read & Clone</strong>
</label>
<p>This team will be able to view and clone its repositories.</p>
</div>
<div class="radio">
<label>
<input type="radio" name="permission" value="push">
<strong>Push, Read & Clone</strong>
</label>
<p>This team will be able to read its repositories, as well as push to them.</p>
</div>
<div class="radio">
<label>
<input type="radio" name="permission" value="admin">
<strong>Collaboration, Push, Read & Clone</strong>
</label>
<p>This team will be able to push/pull to its repositories, as well as add other collaborators to them.</p>
</div>
</div>
</div>
<hr/>
<div class="form-group">
<label class="col-md-2">&nbsp;</label>
<div class="col-md-8">
<button class="btn btn-primary">Edit this team</button>
<button class="btn btn-danger pull-right" value="delete" name="delete">Delete this team</button>
</div>
</div>
</form>
</div>
</div>
{{template "base/footer" .}}

View File

@ -0,0 +1,56 @@
{{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>
</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" .}}

32
templates/org/new.tmpl Normal file
View File

@ -0,0 +1,32 @@
{{template "base/head" .}}
{{template "base/navbar" .}}
<div class="container" id="body">
<form action="/org/create" method="post" class="form-horizontal card" id="org-create">
{{.CsrfTokenHtml}}
<h3>Create New Organization</h3>
{{template "base/alert" .}}
<div class="form-group {{if .Err_OrgName}}has-error has-feedback{{end}}">
<label class="col-md-2 control-label">Organization<strong class="text-danger">*</strong></label>
<div class="col-md-8">
<input name="orgname" type="text" class="form-control" placeholder="Type your organization name" value="{{.orgname}}" required="required">
<span class="help-block">Great organization names are short and memorable. </span>
</div>
</div>
<div class="form-group {{if .Err_Email}}has-error has-feedback{{end}}">
<label class="col-md-2 control-label">Email<strong class="text-danger">*</strong></label>
<div class="col-md-8">
<input name="email" type="text" class="form-control" placeholder="Type organization's email" value="{{.email}}" required="required">
<span class="help-block">Organization's Email receives all notifications and confirmations.</span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-8">
<button type="submit" class="btn btn-lg btn-primary">Create An Organization</button>
<a href="/" class="text-danger">Cancel</a>
</div>
</div>
</form>
</div>
{{template "base/footer" .}}

View File

@ -0,0 +1,74 @@
{{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><a href="#"><i class="fa fa-users"></i>Members
<span class="label label-default">5</span></a>
</li>
<li class="active"><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>
</div>
<div id="body" class="container">
<div id="org">
<form id="org-teams-create" class="form-horizontal card">
<h3>Create new team</h3>
<div class="form-group">
<label class="col-md-2 control-label">Team Name<strong class="text-danger">*</strong></label>
<div class="col-md-8">
<input name="team" type="text" class="form-control" placeholder="Type your team name" value="" required="required">
<span class="help-block">You'll use this name to mention this team in conversations.</span>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Description</label>
<div class="col-md-8">
<input name="desc" type="text" class="form-control" placeholder="Type your team description (optional)" value="">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Permission</label>
<div class="col-md-8">
<div class="radio">
<label>
<input type="radio" name="permission" value="pull" checked="">
<strong>Read & Clone</strong>
</label>
<p>This team will be able to view and clone its repositories.</p>
</div>
<div class="radio">
<label>
<input type="radio" name="permission" value="push">
<strong>Push, Read & Clone</strong>
</label>
<p>This team will be able to read its repositories, as well as push to them.</p>
</div>
<div class="radio">
<label>
<input type="radio" name="permission" value="admin">
<strong>Collaboration, Push, Read & Clone</strong>
</label>
<p>This team will be able to push/pull to its repositories, as well as add other collaborators to them.</p>
</div>
</div>
</div>
<hr/>
<div class="form-group">
<label class="col-md-2">&nbsp;</label>
<div class="col-md-8">
<button class="btn btn-primary">Create team</button>
</div>
</div>
</form>
</div>
</div>
{{template "base/footer" .}}

130
templates/org/settings.tmpl Normal file
View File

@ -0,0 +1,130 @@
{{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" .}}

71
templates/org/teams.tmpl Normal file
View File

@ -0,0 +1,71 @@
{{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><a href="#"><i class="fa fa-users"></i>Members
<span class="label label-default">5</span></a>
</li>
<li class="active"><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>
</div>
<div id="body" class="container">
<div id="org">
<div id="org-teams">
<div id="org-teams-action">
<div class="col-md-12">
<a href="#"><button class="btn btn-success"><i class="fa fa-plus-square"></i>New Team</button></a>
<hr/>
</div>
</div>
<div class="org-team col-md-6">
<div class="panel panel-default">
<h2 class="panel-heading org-team-name"><a href="#"><strong>Team Name</strong></a></h2>
<div class="panel-body">
<p class="org-team-meta">4 members · 10 repositories</p>
<p class="org-team-members">
<a href="#">
<img class="img-thumbnail" src="https://avatars2.githubusercontent.com/u/2946214?s=60" alt=""/>
</a>
<a href="#">
<img class="img-thumbnail" src="https://avatars2.githubusercontent.com/u/2946214?s=60" alt=""/>
</a>
</p>
</div>
<div class="panel-footer">
<button class="pull-right btn btn-default">Join</button>
</div>
</div>
</div>
<div class="org-team col-md-6">
<div class="panel panel-default">
<h2 class="panel-heading org-team-name"><a href="#"><strong>Team Name</strong></a></h2>
<div class="panel-body">
<p class="org-team-meta">4 members · 10 repositories</p>
<p class="org-team-members">
<a href="#">
<img class="img-thumbnail" src="https://avatars2.githubusercontent.com/u/2946214?s=60" alt=""/>
</a>
<a href="#">
<img class="img-thumbnail" src="https://avatars2.githubusercontent.com/u/2946214?s=60" alt=""/>
</a>
</p>
</div>
<div class="panel-footer">
<button class="pull-right btn btn-danger">Leave</button>
</div>
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

View File

@ -8,9 +8,37 @@
<div class="form-group"> <div class="form-group">
<label class="col-md-2 control-label">Owner<strong class="text-danger">*</strong></label> <label class="col-md-2 control-label">Owner<strong class="text-danger">*</strong></label>
<div class="col-md-8"> <div class="col-md-8">
<p class="form-control-static">{{.SignedUserName}}</p> <div class="btn-group" id="repo-owner-switch">
<input type="hidden" value="{{.SignedUserId}}" name="userId"/> <button type="button" class="btn btn-default" id="repo-owner-current">
<img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username" id="repo-owner-avatar">
<span id="repo-owner-name">{{.SignedUser.Name}}</span>
</button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<div class="dropdown-menu clone-group-btn no-propagation">
<ul id="dashboard-switch-menu" class="list-unstyled">
<li data-uid="{{.SignedUser.Id}}" class="checked">
<a>
<i class="fa fa-check"></i>
<img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username">
{{.SignedUser.Name}}
</a>
</li>
{{range .Orgs}}
<li data-uid="{{.Id}}">
<a>
<i class="fa fa-check"></i>
<img src="{{.AvatarLink}}?s=28" alt="user-avatar" title="username">
{{.Name}}
</a>
</li>
{{end}}
</ul>
</div>
</div>
</div> </div>
<input type="hidden" value="{{.SignedUserId}}" name="uid" id="repo-owner-id"/>
</div> </div>
<div class="form-group {{if .Err_RepoName}}has-error has-feedback{{end}}"> <div class="form-group {{if .Err_RepoName}}has-error has-feedback{{end}}">

View File

@ -44,9 +44,37 @@
<div class="form-group"> <div class="form-group">
<label class="col-md-2 control-label">Owner<strong class="text-danger">*</strong></label> <label class="col-md-2 control-label">Owner<strong class="text-danger">*</strong></label>
<div class="col-md-8"> <div class="col-md-8">
<p class="form-control-static">{{.SignedUserName}}</p> <div class="btn-group" id="repo-owner-switch">
<input type="hidden" value="{{.SignedUserId}}" name="userId"/> <button type="button" class="btn btn-default" id="repo-owner-current">
<img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username" id="repo-owner-avatar">
<span id="repo-owner-name">{{.SignedUser.Name}}</span>
</button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<div class="dropdown-menu clone-group-btn no-propagation">
<ul id="dashboard-switch-menu" class="list-unstyled">
<li data-uid="{{.SignedUser.Id}}" class="checked">
<a>
<i class="fa fa-check"></i>
<img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username">
{{.SignedUser.Name}}
</a>
</li>
{{range .Orgs}}
<li data-uid="{{.Id}}">
<a>
<i class="fa fa-check"></i>
<img src="{{.AvatarLink}}?s=28" alt="user-avatar" title="username">
{{.Name}}
</a>
</li>
{{end}}
</ul>
</div>
</div>
</div> </div>
<input type="hidden" value="{{.SignedUserId}}" name="uid" id="repo-owner-id"/>
</div> </div>
<div class="form-group {{if .Err_RepoName}}has-error has-feedback{{end}}"> <div class="form-group {{if .Err_RepoName}}has-error has-feedback{{end}}">

View File

@ -4,27 +4,46 @@
<div class="container"> <div class="container">
<div class="btn-group pull-left" id="dashboard-switch"> <div class="btn-group pull-left" id="dashboard-switch">
<button type="button" class="btn btn-default"> <button type="button" class="btn btn-default">
<img src="//1.gravatar.com/avatar/f72f7454ce9d710baa506394f68f4132?s=28" alt="user-avatar" title="username"> <img src="{{.ContextUser.AvatarLink}}?s=28" alt="user-avatar" title="username">
fuxiaohei {{.ContextUser.Name}}
</button> </button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<div class="dropdown-menu clone-group-btn no-propagation"> <div class="dropdown-menu clone-group-btn no-propagation">
<ul id="dashboard-switch-menu" class="list-unstyled"> <ul id="dashboard-switch-menu" class="list-unstyled">
<li class="checked"><a href="#"><i class="fa fa-check"></i> gogits/gogs</a></li> <li{{if not .PageIsOrgDashboard}} class="checked"{{end}}>
<a href="/">
<i class="fa fa-check"></i>
<img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username">
{{.SignedUser.Name}}
</a>
</li>
{{range .Orgs}}
<li{{if eq $.ContextUser.Id .Id}} class="checked"{{end}}>
<a href="/org/{{.Name}}/dashboard">
<i class="fa fa-check"></i>
<img src="{{.AvatarLink}}?s=28" alt="user-avatar" title="username">
{{.Name}}
</a>
</li>
{{end}}
<li>
<a href="/org/create">Create organization</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
<ul class="nav nav-pills pull-right"> <ul class="nav nav-pills pull-right">
<li class="active"><a href="/">Feed</a></li> <li class="active"><a href="/{{if .PageIsOrgDashboard}}org/{{.ContextUser.Name}}/dashboard{{end}}">News Feed</a></li>
<li><a href="/issues">Issues</a></li> <li><a href="/{{if .PageIsOrgDashboard}}org/{{.ContextUser.Name}}/dashboard/{{end}}issues">Issues</a></li>
{{if .PageIsOrgDashboard}}<li><a href="/org/{{.ContextUser.Name}}/settings">Settings</a></li>{{end}}
<!-- <li><a href="/pulls">Pull Requests</a></li> <!-- <li><a href="/pulls">Pull Requests</a></li>
<li><a href="/stars">Stars</a></li> --> <li><a href="/stars">Stars</a></li> -->
</ul> </ul>
<h3>News Feed</h3>
</div> </div>
</div> </div>
<div id="body" class="container" data-page="user"> <div id="body" class="container" data-page="user">
{{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}} {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}}
<div id="feed-left" class="col-md-8"> <div id="feed-left" class="col-md-8">
@ -42,7 +61,7 @@
</div> </div>
<div id="feed-right" class="col-md-4"> <div id="feed-right" class="col-md-4">
<div class="panel panel-default repo-panel"> <div class="panel panel-default repo-panel">
<div class="panel-heading">Your Repositories <div class="panel-heading">{{if not .PageIsOrgDashboard}}Your {{end}}Repositories
<div class="btn-group pull-right" id="user-dashboard-repo-new"> <div class="btn-group pull-right" id="user-dashboard-repo-new">
<button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown"><i class="fa fa-plus-square"></i>New</button> <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown"><i class="fa fa-plus-square"></i>New</button>
<div class="dropdown-menu dropdown-menu-right"> <div class="dropdown-menu dropdown-menu-right">
@ -65,6 +84,7 @@
</div> </div>
</div> </div>
{{if not .PageIsOrgDashboard}}
<div class="panel panel-default repo-panel"> <div class="panel panel-default repo-panel">
<div class="panel-heading">Collaborative Repositories</div> <div class="panel-heading">Collaborative Repositories</div>
<div class="panel-body"> <div class="panel-body">
@ -76,6 +96,7 @@
</ul> </ul>
</div> </div>
</div> </div>
{{end}}
</div> </div>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}

View File

@ -3,7 +3,7 @@
<div id="body-nav"> <div id="body-nav">
<div class="container"> <div class="container">
<ul class="nav nav-pills pull-right"> <ul class="nav nav-pills pull-right">
<li><a href="/">Feed</a></li> <li><a href="/">News Feed</a></li>
<li class="active"><a href="/issues">Issues</a></li> <li class="active"><a href="/issues">Issues</a></li>
<!-- <li><a href="/pulls">Pull Requests</a></li> <!-- <li><a href="/pulls">Pull Requests</a></li>
<li><a href="/stars">Stars</a></li> --> <li><a href="/stars">Stars</a></li> -->
@ -11,6 +11,7 @@
<h3>Your Issues</h3> <h3>Your Issues</h3>
</div> </div>
</div> </div>
<div id="body" class="container" data-page="user"> <div id="body" class="container" data-page="user">
{{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}} {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}}
<div id="issue"> <div id="issue">