From 9a43fcb61cec9952ff80be7a0d7d27635186b9e5 Mon Sep 17 00:00:00 2001 From: Nikko Miu Date: Thu, 24 Mar 2016 20:48:52 -0500 Subject: [PATCH 01/14] Changed EscapePound function with string replace function --- modules/template/template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/template/template.go b/modules/template/template.go index 79b3f8356..c5379e188 100644 --- a/modules/template/template.go +++ b/modules/template/template.go @@ -97,7 +97,7 @@ func NewFuncMap() []template.FuncMap { "ActionContent2Commits": ActionContent2Commits, "ToUtf8": ToUtf8, "EscapePound": func(str string) string { - return strings.Replace(strings.Replace(str, "%", "%25", -1), "#", "%23", -1) + return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20").Replace(str) }, "RenderCommitMessage": RenderCommitMessage, "ThemeColorMetaTag": func() string { From b1d41cfa603aee63711fbc23bb02fced5a4c54e3 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Fri, 25 Mar 2016 18:04:02 -0400 Subject: [PATCH 02/14] #1692 add admin APIs to add/remove a user from teams --- README.md | 2 +- gogs.go | 2 +- models/org.go | 612 ------------------------------ models/org_team.go | 618 +++++++++++++++++++++++++++++++ modules/context/api.go | 2 + modules/context/api_org.go | 14 + routers/api/v1/admin/org_team.go | 34 +- routers/api/v1/api.go | 46 ++- routers/api/v1/org/org.go | 12 +- routers/api/v1/org/team.go | 7 +- templates/.VERSION | 2 +- 11 files changed, 712 insertions(+), 639 deletions(-) create mode 100644 models/org_team.go create mode 100644 modules/context/api_org.go diff --git a/README.md b/README.md index ec077e7ab..5ae998780 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) -##### Current version: 0.9.15 +##### Current version: 0.9.16 | Web | UI | Preview | |:-------------:|:-------:|:-------:| diff --git a/gogs.go b/gogs.go index f6bd4f70c..aa7763567 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.9.15.0323" +const APP_VER = "0.9.16.0325" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/org.go b/models/org.go index 62a57ae04..540498d1c 100644 --- a/models/org.go +++ b/models/org.go @@ -426,618 +426,6 @@ func RemoveOrgUser(orgId, uid int64) error { return sess.Commit() } -// ___________ -// \__ ___/___ _____ _____ -// | |_/ __ \\__ \ / \ -// | |\ ___/ / __ \| Y Y \ -// |____| \___ >____ /__|_| / -// \/ \/ \/ - -const OWNER_TEAM = "Owners" - -// Team represents a organization team. -type Team struct { - ID int64 `xorm:"pk autoincr"` - OrgID int64 `xorm:"INDEX"` - LowerName string - Name string - Description string - Authorize AccessMode - Repos []*Repository `xorm:"-"` - Members []*User `xorm:"-"` - NumRepos int - NumMembers int -} - -// IsOwnerTeam returns true if team is owner team. -func (t *Team) IsOwnerTeam() bool { - return t.Name == OWNER_TEAM -} - -// IsTeamMember returns true if given user is a member of team. -func (t *Team) IsMember(uid int64) bool { - return IsTeamMember(t.OrgID, t.ID, uid) -} - -func (t *Team) getRepositories(e Engine) (err error) { - teamRepos := make([]*TeamRepo, 0, t.NumRepos) - if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil { - return fmt.Errorf("get team-repos: %v", err) - } - - t.Repos = make([]*Repository, 0, len(teamRepos)) - for i := range teamRepos { - repo, err := getRepositoryByID(e, teamRepos[i].RepoID) - if err != nil { - return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err) - } - t.Repos = append(t.Repos, repo) - } - return nil -} - -// GetRepositories returns all repositories in team of organization. -func (t *Team) GetRepositories() error { - return t.getRepositories(x) -} - -func (t *Team) getMembers(e Engine) (err error) { - t.Members, err = getTeamMembers(e, t.ID) - return err -} - -// GetMembers returns all members in team of organization. -func (t *Team) GetMembers() (err error) { - return t.getMembers(x) -} - -// AddMember adds new member to team of organization. -func (t *Team) AddMember(uid int64) error { - return AddTeamMember(t.OrgID, t.ID, uid) -} - -// RemoveMember removes member from team of organization. -func (t *Team) RemoveMember(uid int64) error { - return RemoveTeamMember(t.OrgID, t.ID, uid) -} - -func (t *Team) hasRepository(e Engine, repoID int64) bool { - return hasTeamRepo(e, t.OrgID, t.ID, repoID) -} - -// HasRepository returns true if given repository belong to team. -func (t *Team) HasRepository(repoID int64) bool { - return t.hasRepository(x, repoID) -} - -func (t *Team) addRepository(e Engine, repo *Repository) (err error) { - if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil { - return err - } - - t.NumRepos++ - if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { - return fmt.Errorf("update team: %v", err) - } - - if err = repo.recalculateTeamAccesses(e, 0); err != nil { - return fmt.Errorf("recalculateAccesses: %v", err) - } - - if err = t.getMembers(e); err != nil { - return fmt.Errorf("getMembers: %v", err) - } - for _, u := range t.Members { - if err = watchRepo(e, u.Id, repo.ID, true); err != nil { - return fmt.Errorf("watchRepo: %v", err) - } - } - return nil -} - -// AddRepository adds new repository to team of organization. -func (t *Team) AddRepository(repo *Repository) (err error) { - if repo.OwnerID != t.OrgID { - return errors.New("Repository does not belong to organization") - } else if t.HasRepository(repo.ID) { - return nil - } - - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - if err = t.addRepository(sess, repo); err != nil { - return err - } - - return sess.Commit() -} - -func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) { - if err = removeTeamRepo(e, t.ID, repo.ID); err != nil { - return err - } - - t.NumRepos-- - if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { - return err - } - - // Don't need to recalculate when delete a repository from organization. - if recalculate { - if err = repo.recalculateTeamAccesses(e, t.ID); err != nil { - return err - } - } - - if err = t.getMembers(e); err != nil { - return fmt.Errorf("get team members: %v", err) - } - for _, u := range t.Members { - has, err := hasAccess(e, u, repo, ACCESS_MODE_READ) - if err != nil { - return err - } else if has { - continue - } - - if err = watchRepo(e, u.Id, repo.ID, false); err != nil { - return err - } - } - - return nil -} - -// RemoveRepository removes repository from team of organization. -func (t *Team) RemoveRepository(repoID int64) error { - if !t.HasRepository(repoID) { - return nil - } - - repo, err := GetRepositoryByID(repoID) - if err != nil { - return err - } - - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - if err = t.removeRepository(sess, repo, true); err != nil { - return err - } - - return sess.Commit() -} - -// NewTeam creates a record of new team. -// It's caller's responsibility to assign organization ID. -func NewTeam(t *Team) error { - if len(t.Name) == 0 { - return errors.New("empty team name") - } - - has, err := x.Id(t.OrgID).Get(new(User)) - if err != nil { - return err - } else if !has { - return ErrOrgNotExist - } - - t.LowerName = strings.ToLower(t.Name) - has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team)) - if err != nil { - return err - } else if has { - return ErrTeamAlreadyExist{t.OrgID, t.LowerName} - } - - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - if _, err = sess.Insert(t); err != nil { - sess.Rollback() - return err - } - - // Update organization number of teams. - if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { - sess.Rollback() - return err - } - return sess.Commit() -} - -func getTeam(e Engine, orgId int64, name string) (*Team, error) { - t := &Team{ - OrgID: orgId, - LowerName: strings.ToLower(name), - } - has, err := e.Get(t) - if err != nil { - return nil, err - } else if !has { - return nil, ErrTeamNotExist - } - return t, nil -} - -// GetTeam returns team by given team name and organization. -func GetTeam(orgId int64, name string) (*Team, error) { - return getTeam(x, orgId, name) -} - -func getTeamById(e Engine, teamId int64) (*Team, error) { - t := new(Team) - has, err := e.Id(teamId).Get(t) - if err != nil { - return nil, err - } else if !has { - return nil, ErrTeamNotExist - } - return t, nil -} - -// GetTeamById returns team by given ID. -func GetTeamById(teamId int64) (*Team, error) { - return getTeamById(x, teamId) -} - -// UpdateTeam updates information of team. -func UpdateTeam(t *Team, authChanged bool) (err error) { - if len(t.Name) == 0 { - return errors.New("empty team name") - } - - if len(t.Description) > 255 { - t.Description = t.Description[:255] - } - - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - t.LowerName = strings.ToLower(t.Name) - has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(new(Team)) - if err != nil { - return err - } else if has { - return ErrTeamAlreadyExist{t.OrgID, t.LowerName} - } - - if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil { - return fmt.Errorf("update: %v", err) - } - - // Update access for team members if needed. - if authChanged { - if err = t.getRepositories(sess); err != nil { - return fmt.Errorf("getRepositories:%v", err) - } - - for _, repo := range t.Repos { - if err = repo.recalculateTeamAccesses(sess, 0); err != nil { - return fmt.Errorf("recalculateTeamAccesses: %v", err) - } - } - } - - return sess.Commit() -} - -// DeleteTeam deletes given team. -// It's caller's responsibility to assign organization ID. -func DeleteTeam(t *Team) error { - if err := t.GetRepositories(); err != nil { - return err - } - - // Get organization. - org, err := GetUserByID(t.OrgID) - if err != nil { - return err - } - - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - // Delete all accesses. - for _, repo := range t.Repos { - if err = repo.recalculateTeamAccesses(sess, t.ID); err != nil { - return err - } - } - - // Delete team-user. - if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil { - return err - } - - // Delete team. - if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil { - return err - } - // Update organization number of teams. - if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil { - return err - } - - return sess.Commit() -} - -// ___________ ____ ___ -// \__ ___/___ _____ _____ | | \______ ___________ -// | |_/ __ \\__ \ / \| | / ___// __ \_ __ \ -// | |\ ___/ / __ \| Y Y \ | /\___ \\ ___/| | \/ -// |____| \___ >____ /__|_| /______//____ >\___ >__| -// \/ \/ \/ \/ \/ - -// TeamUser represents an team-user relation. -type TeamUser struct { - ID int64 `xorm:"pk autoincr"` - OrgID int64 `xorm:"INDEX"` - TeamID int64 `xorm:"UNIQUE(s)"` - Uid int64 `xorm:"UNIQUE(s)"` -} - -func isTeamMember(e Engine, orgID, teamID, uid int64) bool { - has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser)) - return has -} - -// IsTeamMember returns true if given user is a member of team. -func IsTeamMember(orgID, teamID, uid int64) bool { - return isTeamMember(x, orgID, teamID, uid) -} - -func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) { - teamUsers := make([]*TeamUser, 0, 10) - if err = e.Where("team_id=?", teamID).Find(&teamUsers); err != nil { - return nil, fmt.Errorf("get team-users: %v", err) - } - members := make([]*User, 0, len(teamUsers)) - for i := range teamUsers { - member := new(User) - if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil { - return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err) - } - members = append(members, member) - } - return members, nil -} - -// GetTeamMembers returns all members in given team of organization. -func GetTeamMembers(teamID int64) ([]*User, error) { - return getTeamMembers(x, teamID) -} - -func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) { - tus := make([]*TeamUser, 0, 5) - if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil { - return nil, err - } - - ts := make([]*Team, len(tus)) - for i, tu := range tus { - t := new(Team) - has, err := e.Id(tu.TeamID).Get(t) - if err != nil { - return nil, err - } else if !has { - return nil, ErrTeamNotExist - } - ts[i] = t - } - return ts, nil -} - -// GetUserTeams returns all teams that user belongs to in given organization. -func GetUserTeams(orgId, uid int64) ([]*Team, error) { - return getUserTeams(x, orgId, uid) -} - -// AddTeamMember adds new member to given team of given organization. -func AddTeamMember(orgId, teamId, uid int64) error { - if IsTeamMember(orgId, teamId, uid) { - return nil - } - - if err := AddOrgUser(orgId, uid); err != nil { - return err - } - - // Get team and its repositories. - t, err := GetTeamById(teamId) - if err != nil { - return err - } - t.NumMembers++ - - if err = t.GetRepositories(); err != nil { - return err - } - - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - tu := &TeamUser{ - Uid: uid, - OrgID: orgId, - TeamID: teamId, - } - if _, err = sess.Insert(tu); err != nil { - return err - } else if _, err = sess.Id(t.ID).Update(t); err != nil { - return err - } - - // Give access to team repositories. - for _, repo := range t.Repos { - if err = repo.recalculateTeamAccesses(sess, 0); err != nil { - return err - } - } - - // We make sure it exists before. - ou := new(OrgUser) - if _, err = sess.Where("uid=?", uid).And("org_id=?", orgId).Get(ou); err != nil { - return err - } - ou.NumTeams++ - if t.IsOwnerTeam() { - ou.IsOwner = true - } - if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil { - return err - } - - return sess.Commit() -} - -func removeTeamMember(e Engine, orgId, teamId, uid int64) error { - if !isTeamMember(e, orgId, teamId, uid) { - return nil - } - - // Get team and its repositories. - t, err := getTeamById(e, teamId) - if err != nil { - return err - } - - // Check if the user to delete is the last member in owner team. - if t.IsOwnerTeam() && t.NumMembers == 1 { - return ErrLastOrgOwner{UID: uid} - } - - t.NumMembers-- - - if err = t.getRepositories(e); err != nil { - return err - } - - // Get organization. - org, err := getUserByID(e, orgId) - if err != nil { - return err - } - - tu := &TeamUser{ - Uid: uid, - OrgID: orgId, - TeamID: teamId, - } - if _, err := e.Delete(tu); err != nil { - return err - } else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { - return err - } - - // Delete access to team repositories. - for _, repo := range t.Repos { - if err = repo.recalculateTeamAccesses(e, 0); err != nil { - return err - } - } - - // This must exist. - ou := new(OrgUser) - _, err = e.Where("uid=?", uid).And("org_id=?", org.Id).Get(ou) - if err != nil { - return err - } - ou.NumTeams-- - if t.IsOwnerTeam() { - ou.IsOwner = false - } - if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil { - return err - } - return nil -} - -// RemoveTeamMember removes member from given team of given organization. -func RemoveTeamMember(orgId, teamId, uid int64) error { - sess := x.NewSession() - defer sessionRelease(sess) - if err := sess.Begin(); err != nil { - return err - } - if err := removeTeamMember(sess, orgId, teamId, uid); err != nil { - return err - } - return sess.Commit() -} - -// ___________ __________ -// \__ ___/___ _____ _____\______ \ ____ ______ ____ -// | |_/ __ \\__ \ / \| _// __ \\____ \ / _ \ -// | |\ ___/ / __ \| Y Y \ | \ ___/| |_> > <_> ) -// |____| \___ >____ /__|_| /____|_ /\___ > __/ \____/ -// \/ \/ \/ \/ \/|__| - -// TeamRepo represents an team-repository relation. -type TeamRepo struct { - ID int64 `xorm:"pk autoincr"` - OrgID int64 `xorm:"INDEX"` - TeamID int64 `xorm:"UNIQUE(s)"` - RepoID int64 `xorm:"UNIQUE(s)"` -} - -func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool { - has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo)) - return has -} - -// HasTeamRepo returns true if given repository belongs to team. -func HasTeamRepo(orgID, teamID, repoID int64) bool { - return hasTeamRepo(x, orgID, teamID, repoID) -} - -func addTeamRepo(e Engine, orgID, teamID, repoID int64) error { - _, err := e.InsertOne(&TeamRepo{ - OrgID: orgID, - TeamID: teamID, - RepoID: repoID, - }) - return err -} - -// AddTeamRepo adds new repository relation to team. -func AddTeamRepo(orgID, teamID, repoID int64) error { - return addTeamRepo(x, orgID, teamID, repoID) -} - -func removeTeamRepo(e Engine, teamID, repoID int64) error { - _, err := e.Delete(&TeamRepo{ - TeamID: teamID, - RepoID: repoID, - }) - return err -} - -// RemoveTeamRepo deletes repository relation to team. -func RemoveTeamRepo(teamID, repoID int64) error { - return removeTeamRepo(x, teamID, repoID) -} - func removeOrgRepo(e Engine, orgID, repoID int64) error { _, err := e.Delete(&TeamRepo{ OrgID: orgID, diff --git a/models/org_team.go b/models/org_team.go new file mode 100644 index 000000000..85af2d983 --- /dev/null +++ b/models/org_team.go @@ -0,0 +1,618 @@ +// Copyright 2016 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 ( + "errors" + "fmt" + "strings" +) + +const OWNER_TEAM = "Owners" + +// Team represents a organization team. +type Team struct { + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` + LowerName string + Name string + Description string + Authorize AccessMode + Repos []*Repository `xorm:"-"` + Members []*User `xorm:"-"` + NumRepos int + NumMembers int +} + +// IsOwnerTeam returns true if team is owner team. +func (t *Team) IsOwnerTeam() bool { + return t.Name == OWNER_TEAM +} + +// IsTeamMember returns true if given user is a member of team. +func (t *Team) IsMember(uid int64) bool { + return IsTeamMember(t.OrgID, t.ID, uid) +} + +func (t *Team) getRepositories(e Engine) (err error) { + teamRepos := make([]*TeamRepo, 0, t.NumRepos) + if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil { + return fmt.Errorf("get team-repos: %v", err) + } + + t.Repos = make([]*Repository, 0, len(teamRepos)) + for i := range teamRepos { + repo, err := getRepositoryByID(e, teamRepos[i].RepoID) + if err != nil { + return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err) + } + t.Repos = append(t.Repos, repo) + } + return nil +} + +// GetRepositories returns all repositories in team of organization. +func (t *Team) GetRepositories() error { + return t.getRepositories(x) +} + +func (t *Team) getMembers(e Engine) (err error) { + t.Members, err = getTeamMembers(e, t.ID) + return err +} + +// GetMembers returns all members in team of organization. +func (t *Team) GetMembers() (err error) { + return t.getMembers(x) +} + +// AddMember adds new membership of the team to the organization, +// the user will have membership to the organization automatically when needed. +func (t *Team) AddMember(uid int64) error { + return AddTeamMember(t.OrgID, t.ID, uid) +} + +// RemoveMember removes member from team of organization. +func (t *Team) RemoveMember(uid int64) error { + return RemoveTeamMember(t.OrgID, t.ID, uid) +} + +func (t *Team) hasRepository(e Engine, repoID int64) bool { + return hasTeamRepo(e, t.OrgID, t.ID, repoID) +} + +// HasRepository returns true if given repository belong to team. +func (t *Team) HasRepository(repoID int64) bool { + return t.hasRepository(x, repoID) +} + +func (t *Team) addRepository(e Engine, repo *Repository) (err error) { + if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil { + return err + } + + t.NumRepos++ + if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { + return fmt.Errorf("update team: %v", err) + } + + if err = repo.recalculateTeamAccesses(e, 0); err != nil { + return fmt.Errorf("recalculateAccesses: %v", err) + } + + if err = t.getMembers(e); err != nil { + return fmt.Errorf("getMembers: %v", err) + } + for _, u := range t.Members { + if err = watchRepo(e, u.Id, repo.ID, true); err != nil { + return fmt.Errorf("watchRepo: %v", err) + } + } + return nil +} + +// AddRepository adds new repository to team of organization. +func (t *Team) AddRepository(repo *Repository) (err error) { + if repo.OwnerID != t.OrgID { + return errors.New("Repository does not belong to organization") + } else if t.HasRepository(repo.ID) { + return nil + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = t.addRepository(sess, repo); err != nil { + return err + } + + return sess.Commit() +} + +func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) { + if err = removeTeamRepo(e, t.ID, repo.ID); err != nil { + return err + } + + t.NumRepos-- + if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { + return err + } + + // Don't need to recalculate when delete a repository from organization. + if recalculate { + if err = repo.recalculateTeamAccesses(e, t.ID); err != nil { + return err + } + } + + if err = t.getMembers(e); err != nil { + return fmt.Errorf("get team members: %v", err) + } + for _, u := range t.Members { + has, err := hasAccess(e, u, repo, ACCESS_MODE_READ) + if err != nil { + return err + } else if has { + continue + } + + if err = watchRepo(e, u.Id, repo.ID, false); err != nil { + return err + } + } + + return nil +} + +// RemoveRepository removes repository from team of organization. +func (t *Team) RemoveRepository(repoID int64) error { + if !t.HasRepository(repoID) { + return nil + } + + repo, err := GetRepositoryByID(repoID) + if err != nil { + return err + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = t.removeRepository(sess, repo, true); err != nil { + return err + } + + return sess.Commit() +} + +// NewTeam creates a record of new team. +// It's caller's responsibility to assign organization ID. +func NewTeam(t *Team) error { + if len(t.Name) == 0 { + return errors.New("empty team name") + } + + has, err := x.Id(t.OrgID).Get(new(User)) + if err != nil { + return err + } else if !has { + return ErrOrgNotExist + } + + t.LowerName = strings.ToLower(t.Name) + has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team)) + if err != nil { + return err + } else if has { + return ErrTeamAlreadyExist{t.OrgID, t.LowerName} + } + + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + + if _, err = sess.Insert(t); err != nil { + sess.Rollback() + return err + } + + // Update organization number of teams. + if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { + sess.Rollback() + return err + } + return sess.Commit() +} + +func getTeam(e Engine, orgId int64, name string) (*Team, error) { + t := &Team{ + OrgID: orgId, + LowerName: strings.ToLower(name), + } + has, err := e.Get(t) + if err != nil { + return nil, err + } else if !has { + return nil, ErrTeamNotExist + } + return t, nil +} + +// GetTeam returns team by given team name and organization. +func GetTeam(orgId int64, name string) (*Team, error) { + return getTeam(x, orgId, name) +} + +func getTeamByID(e Engine, teamId int64) (*Team, error) { + t := new(Team) + has, err := e.Id(teamId).Get(t) + if err != nil { + return nil, err + } else if !has { + return nil, ErrTeamNotExist + } + return t, nil +} + +// GetTeamByID returns team by given ID. +func GetTeamByID(teamId int64) (*Team, error) { + return getTeamByID(x, teamId) +} + +// UpdateTeam updates information of team. +func UpdateTeam(t *Team, authChanged bool) (err error) { + if len(t.Name) == 0 { + return errors.New("empty team name") + } + + if len(t.Description) > 255 { + t.Description = t.Description[:255] + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + t.LowerName = strings.ToLower(t.Name) + has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(new(Team)) + if err != nil { + return err + } else if has { + return ErrTeamAlreadyExist{t.OrgID, t.LowerName} + } + + if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil { + return fmt.Errorf("update: %v", err) + } + + // Update access for team members if needed. + if authChanged { + if err = t.getRepositories(sess); err != nil { + return fmt.Errorf("getRepositories:%v", err) + } + + for _, repo := range t.Repos { + if err = repo.recalculateTeamAccesses(sess, 0); err != nil { + return fmt.Errorf("recalculateTeamAccesses: %v", err) + } + } + } + + return sess.Commit() +} + +// DeleteTeam deletes given team. +// It's caller's responsibility to assign organization ID. +func DeleteTeam(t *Team) error { + if err := t.GetRepositories(); err != nil { + return err + } + + // Get organization. + org, err := GetUserByID(t.OrgID) + if err != nil { + return err + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + // Delete all accesses. + for _, repo := range t.Repos { + if err = repo.recalculateTeamAccesses(sess, t.ID); err != nil { + return err + } + } + + // Delete team-user. + if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil { + return err + } + + // Delete team. + if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil { + return err + } + // Update organization number of teams. + if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil { + return err + } + + return sess.Commit() +} + +// ___________ ____ ___ +// \__ ___/___ _____ _____ | | \______ ___________ +// | |_/ __ \\__ \ / \| | / ___// __ \_ __ \ +// | |\ ___/ / __ \| Y Y \ | /\___ \\ ___/| | \/ +// |____| \___ >____ /__|_| /______//____ >\___ >__| +// \/ \/ \/ \/ \/ + +// TeamUser represents an team-user relation. +type TeamUser struct { + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` + TeamID int64 `xorm:"UNIQUE(s)"` + Uid int64 `xorm:"UNIQUE(s)"` +} + +func isTeamMember(e Engine, orgID, teamID, uid int64) bool { + has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser)) + return has +} + +// IsTeamMember returns true if given user is a member of team. +func IsTeamMember(orgID, teamID, uid int64) bool { + return isTeamMember(x, orgID, teamID, uid) +} + +func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) { + teamUsers := make([]*TeamUser, 0, 10) + if err = e.Where("team_id=?", teamID).Find(&teamUsers); err != nil { + return nil, fmt.Errorf("get team-users: %v", err) + } + members := make([]*User, 0, len(teamUsers)) + for i := range teamUsers { + member := new(User) + if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil { + return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err) + } + members = append(members, member) + } + return members, nil +} + +// GetTeamMembers returns all members in given team of organization. +func GetTeamMembers(teamID int64) ([]*User, error) { + return getTeamMembers(x, teamID) +} + +func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) { + tus := make([]*TeamUser, 0, 5) + if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil { + return nil, err + } + + ts := make([]*Team, len(tus)) + for i, tu := range tus { + t := new(Team) + has, err := e.Id(tu.TeamID).Get(t) + if err != nil { + return nil, err + } else if !has { + return nil, ErrTeamNotExist + } + ts[i] = t + } + return ts, nil +} + +// GetUserTeams returns all teams that user belongs to in given organization. +func GetUserTeams(orgId, uid int64) ([]*Team, error) { + return getUserTeams(x, orgId, uid) +} + +// AddTeamMember adds new membership of given team to given organization, +// the user will have membership to given organization automatically when needed. +func AddTeamMember(orgID, teamID, uid int64) error { + if IsTeamMember(orgID, teamID, uid) { + return nil + } + + if err := AddOrgUser(orgID, uid); err != nil { + return err + } + + // Get team and its repositories. + t, err := GetTeamByID(teamID) + if err != nil { + return err + } + t.NumMembers++ + + if err = t.GetRepositories(); err != nil { + return err + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + tu := &TeamUser{ + Uid: uid, + OrgID: orgID, + TeamID: teamID, + } + if _, err = sess.Insert(tu); err != nil { + return err + } else if _, err = sess.Id(t.ID).Update(t); err != nil { + return err + } + + // Give access to team repositories. + for _, repo := range t.Repos { + if err = repo.recalculateTeamAccesses(sess, 0); err != nil { + return err + } + } + + // We make sure it exists before. + ou := new(OrgUser) + if _, err = sess.Where("uid = ?", uid).And("org_id = ?", orgID).Get(ou); err != nil { + return err + } + ou.NumTeams++ + if t.IsOwnerTeam() { + ou.IsOwner = true + } + if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil { + return err + } + + return sess.Commit() +} + +func removeTeamMember(e Engine, orgID, teamID, uid int64) error { + if !isTeamMember(e, orgID, teamID, uid) { + return nil + } + + // Get team and its repositories. + t, err := getTeamByID(e, teamID) + if err != nil { + return err + } + + // Check if the user to delete is the last member in owner team. + if t.IsOwnerTeam() && t.NumMembers == 1 { + return ErrLastOrgOwner{UID: uid} + } + + t.NumMembers-- + + if err = t.getRepositories(e); err != nil { + return err + } + + // Get organization. + org, err := getUserByID(e, orgID) + if err != nil { + return err + } + + tu := &TeamUser{ + Uid: uid, + OrgID: orgID, + TeamID: teamID, + } + if _, err := e.Delete(tu); err != nil { + return err + } else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { + return err + } + + // Delete access to team repositories. + for _, repo := range t.Repos { + if err = repo.recalculateTeamAccesses(e, 0); err != nil { + return err + } + } + + // This must exist. + ou := new(OrgUser) + _, err = e.Where("uid = ?", uid).And("org_id = ?", org.Id).Get(ou) + if err != nil { + return err + } + ou.NumTeams-- + if t.IsOwnerTeam() { + ou.IsOwner = false + } + if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil { + return err + } + return nil +} + +// RemoveTeamMember removes member from given team of given organization. +func RemoveTeamMember(orgID, teamID, uid int64) error { + sess := x.NewSession() + defer sessionRelease(sess) + if err := sess.Begin(); err != nil { + return err + } + if err := removeTeamMember(sess, orgID, teamID, uid); err != nil { + return err + } + return sess.Commit() +} + +// ___________ __________ +// \__ ___/___ _____ _____\______ \ ____ ______ ____ +// | |_/ __ \\__ \ / \| _// __ \\____ \ / _ \ +// | |\ ___/ / __ \| Y Y \ | \ ___/| |_> > <_> ) +// |____| \___ >____ /__|_| /____|_ /\___ > __/ \____/ +// \/ \/ \/ \/ \/|__| + +// TeamRepo represents an team-repository relation. +type TeamRepo struct { + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` + TeamID int64 `xorm:"UNIQUE(s)"` + RepoID int64 `xorm:"UNIQUE(s)"` +} + +func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool { + has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo)) + return has +} + +// HasTeamRepo returns true if given repository belongs to team. +func HasTeamRepo(orgID, teamID, repoID int64) bool { + return hasTeamRepo(x, orgID, teamID, repoID) +} + +func addTeamRepo(e Engine, orgID, teamID, repoID int64) error { + _, err := e.InsertOne(&TeamRepo{ + OrgID: orgID, + TeamID: teamID, + RepoID: repoID, + }) + return err +} + +// AddTeamRepo adds new repository relation to team. +func AddTeamRepo(orgID, teamID, repoID int64) error { + return addTeamRepo(x, orgID, teamID, repoID) +} + +func removeTeamRepo(e Engine, teamID, repoID int64) error { + _, err := e.Delete(&TeamRepo{ + TeamID: teamID, + RepoID: repoID, + }) + return err +} + +// RemoveTeamRepo deletes repository relation to team. +func RemoveTeamRepo(teamID, repoID int64) error { + return removeTeamRepo(x, teamID, repoID) +} diff --git a/modules/context/api.go b/modules/context/api.go index 8d036be9c..770daa3ad 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -18,6 +18,7 @@ import ( type APIContext struct { *Context + Org *APIOrganization } // Error responses error message to client with given message. @@ -40,6 +41,7 @@ func (ctx *APIContext) Error(status int, title string, obj interface{}) { }) } +// SetLinkHeader sets pagination link header by given totol number and page size. func (ctx *APIContext) SetLinkHeader(total, pageSize int) { page := paginater.New(total, pageSize, ctx.QueryInt("page"), 0) links := make([]string, 0, 4) diff --git a/modules/context/api_org.go b/modules/context/api_org.go new file mode 100644 index 000000000..ecf60a190 --- /dev/null +++ b/modules/context/api_org.go @@ -0,0 +1,14 @@ +// Copyright 2016 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 context + +import ( + "github.com/gogits/gogs/models" +) + +type APIOrganization struct { + Organization *models.User + Team *models.Team +} diff --git a/routers/api/v1/admin/org_team.go b/routers/api/v1/admin/org_team.go index 4c67b5743..9b5411b12 100644 --- a/routers/api/v1/admin/org_team.go +++ b/routers/api/v1/admin/org_team.go @@ -14,13 +14,8 @@ import ( ) func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { - org := user.GetUserByParamsName(ctx, ":orgname") - if ctx.Written() { - return - } - team := &models.Team{ - OrgID: org.Id, + OrgID: ctx.Org.Organization.Id, Name: form.Name, Description: form.Description, Authorize: models.ParseAccessMode(form.Permission), @@ -36,3 +31,30 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { ctx.JSON(201, convert.ToTeam(team)) } + +func AddTeamMember(ctx *context.APIContext) { + u := user.GetUserByParams(ctx) + if ctx.Written() { + return + } + if err := ctx.Org.Team.AddMember(u.Id); err != nil { + ctx.Error(500, "AddMember", err) + return + } + + ctx.Status(204) +} + +func RemoveTeamMember(ctx *context.APIContext) { + u := user.GetUserByParams(ctx) + if ctx.Written() { + return + } + + if err := ctx.Org.Team.RemoveMember(u.Id); err != nil { + ctx.Error(500, "RemoveMember", err) + return + } + + ctx.Status(204) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 08e6a7515..34a441f41 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -110,6 +110,42 @@ func ReqAdmin() macaron.Handler { } } +func OrgAssignment(args ...bool) macaron.Handler { + var ( + assignTeam bool + ) + + if len(args) > 0 { + assignTeam = args[0] + } + return func(ctx *context.APIContext) { + org, err := models.GetUserByName(ctx.Params(":orgname")) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetUserByName", err) + } + return + } + ctx.Org = &context.APIOrganization{ + Organization: org, + } + + if assignTeam { + ctx.Org.Team, err = models.GetTeamByID(ctx.ParamsInt64(":teamid")) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetTeamById", err) + } + return + } + } + } +} + // RegisterRoutes registers all v1 APIs routes to web application. // FIXME: custom form error response func RegisterRoutes(m *macaron.Macaron) { @@ -208,7 +244,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/orgs/:orgname", func() { m.Combo("").Get(org.Get).Patch(bind(api.EditOrgOption{}), org.Edit) m.Combo("/teams").Get(org.ListTeams) - }) + }, OrgAssignment()) m.Any("/*", func(ctx *context.Context) { ctx.Error(404) @@ -228,7 +264,13 @@ func RegisterRoutes(m *macaron.Macaron) { }) m.Group("/orgs/:orgname", func() { - m.Combo("/teams").Post(bind(api.CreateTeamOption{}), admin.CreateTeam) + m.Group("/teams", func() { + m.Post("", OrgAssignment(), bind(api.CreateTeamOption{}), admin.CreateTeam) + + m.Group("/:teamid", func() { + m.Combo("/memberships/:username").Put(admin.AddTeamMember).Delete(admin.RemoveTeamMember) + }, OrgAssignment(true)) + }) }) }, ReqAdmin()) }, context.APIContexter()) diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index b4e52d486..5b1ea007e 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -42,20 +42,12 @@ func ListUserOrgs(ctx *context.APIContext) { // https://github.com/gogits/go-gogs-client/wiki/Organizations#get-an-organization func Get(ctx *context.APIContext) { - org := user.GetUserByParamsName(ctx, ":orgname") - if ctx.Written() { - return - } - ctx.JSON(200, convert.ToOrganization(org)) + ctx.JSON(200, convert.ToOrganization(ctx.Org.Organization)) } // https://github.com/gogits/go-gogs-client/wiki/Organizations#edit-an-organization func Edit(ctx *context.APIContext, form api.EditOrgOption) { - org := user.GetUserByParamsName(ctx, ":orgname") - if ctx.Written() { - return - } - + org := ctx.Org.Organization if !org.IsOwnedBy(ctx.User.Id) { ctx.Status(403) return diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index fad0d2f70..70f8d8420 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -9,15 +9,10 @@ import ( "github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/routers/api/v1/convert" - "github.com/gogits/gogs/routers/api/v1/user" ) func ListTeams(ctx *context.APIContext) { - org := user.GetUserByParamsName(ctx, ":orgname") - if ctx.Written() { - return - } - + org := ctx.Org.Organization if err := org.GetTeams(); err != nil { ctx.Error(500, "GetTeams", err) return diff --git a/templates/.VERSION b/templates/.VERSION index f179a94d3..d9a598020 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.9.15.0323 \ No newline at end of file +0.9.16.0325 \ No newline at end of file From dd36c431ece67e47beb0e64070015808627538e5 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Fri, 25 Mar 2016 22:11:58 -0400 Subject: [PATCH 03/14] #2842 add quotes to attachment file name --- routers/repo/download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/repo/download.go b/routers/repo/download.go index 3329073e0..9e7c1d047 100644 --- a/routers/repo/download.go +++ b/routers/repo/download.go @@ -25,7 +25,7 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error { if !isTextFile { _, isImageFile := base.IsImageFile(buf) if !isImageFile { - ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+path.Base(ctx.Repo.TreeName)) + ctx.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+path.Base(ctx.Repo.TreeName)+"\"") ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") } } else { From ac53bb593d12bac3b44380defe73d13e728cd142 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sat, 26 Mar 2016 16:42:20 -0400 Subject: [PATCH 04/14] #2878 print error of JSON unmarshal and always returns a valid object --- README.md | 2 +- docker/README.md | 2 ++ gogs.go | 2 +- modules/template/template.go | 3 ++- templates/.VERSION | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5ae998780..52d0b3cce 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) -##### Current version: 0.9.16 +##### Current version: 0.9.17 | Web | UI | Preview | |:-------------:|:-------:|:-------:| diff --git a/docker/README.md b/docker/README.md index 07bf780c3..1b18b8e78 100644 --- a/docker/README.md +++ b/docker/README.md @@ -43,9 +43,11 @@ If you're more comfortable with mounting data to a data container, the commands ``` # Create data container docker run --name=gogs-data --entrypoint /bin/true gogs/gogs + # Use `docker run` for the first time. docker run --name=gogs --volumes-from gogs-data -p 10022:22 -p 10080:3000 gogs/gogs ``` + #### Using Docker 1.9 Volume command ``` diff --git a/gogs.go b/gogs.go index aa7763567..d49af55d6 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.9.16.0325" +const APP_VER = "0.9.17.0326" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/modules/template/template.go b/modules/template/template.go index c5379e188..7c3544973 100644 --- a/modules/template/template.go +++ b/modules/template/template.go @@ -18,6 +18,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/setting" ) @@ -255,7 +256,7 @@ func ActionIcon(opType int) string { func ActionContent2Commits(act Actioner) *models.PushCommits { push := models.NewPushCommits() if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil { - return nil + log.Error(4, "json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err) } return push } diff --git a/templates/.VERSION b/templates/.VERSION index d9a598020..c9a2d0438 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.9.16.0325 \ No newline at end of file +0.9.17.0326 \ No newline at end of file From 79a1bfd9632c40ce3ee15c2dcbb8f322d871ade0 Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Sun, 27 Mar 2016 22:54:31 +0200 Subject: [PATCH 05/14] Try to make the SQL queries cleaner and more secure --- models/issue.go | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/models/issue.go b/models/issue.go index edc46689d..5727e07b1 100644 --- a/models/issue.go +++ b/models/issue.go @@ -5,7 +5,6 @@ package models import ( - "bytes" "errors" "fmt" "io" @@ -513,7 +512,7 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) { if len(opts.RepoIDs) == 0 { return make([]*Issue, 0), nil } - sess.Where("issue.repo_id IN ("+strings.Join(base.Int64sToStrings(opts.RepoIDs), ",")+")").And("issue.is_closed=?", opts.IsClosed) + sess.In("issue.repo_id", base.Int64sToStrings(opts.RepoIDs)).And("issue.is_closed=?", opts.IsClosed) } else { sess.Where("issue.is_closed=?", opts.IsClosed) } @@ -684,18 +683,8 @@ func GetIssueUserPairsByRepoIds(rids []int64, isClosed bool, page int) ([]*Issue return []*IssueUser{}, nil } - buf := bytes.NewBufferString("") - for _, rid := range rids { - buf.WriteString("repo_id=") - buf.WriteString(com.ToStr(rid)) - buf.WriteString(" OR ") - } - cond := strings.TrimSuffix(buf.String(), " OR ") ius := make([]*IssueUser, 0, 10) - sess := x.Limit(20, (page-1)*20).Where("is_closed=?", isClosed) - if len(cond) > 0 { - sess.And(cond) - } + sess := x.Limit(20, (page-1)*20).Where("is_closed=?", isClosed).In("repo_id", rids) err := sess.Find(&ius) return ius, err } From b5948f2e715d25ff1221f139a232c8904dd6df6b Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Sun, 27 Mar 2016 23:26:45 +0200 Subject: [PATCH 06/14] Made the issues query more secure and simpler --- models/issue.go | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/models/issue.go b/models/issue.go index 5727e07b1..f70fd1247 100644 --- a/models/issue.go +++ b/models/issue.go @@ -547,27 +547,16 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) { } labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ",")) - if len(labelIDs) > 0 { - validJoin := false - queryStr := "issue.id=issue_label.issue_id" - for _, id := range labelIDs { - if id == 0 { - continue - } - validJoin = true - queryStr += " AND issue_label.label_id=" + com.ToStr(id) - } - if validJoin { - sess.Join("INNER", "issue_label", queryStr) - } + if len(labelIDs) > 1 { + sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id").In("issue_label.label_id", labelIDs) } if opts.IsMention { - queryStr := "issue.id=issue_user.issue_id AND issue_user.is_mentioned=1" + sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id AND issue_user.is_mentioned = 1") + if opts.UserID > 0 { - queryStr += " AND issue_user.uid=" + com.ToStr(opts.UserID) + sess.Where("issue_user.uid = ?", opts.UserID) } - sess.Join("INNER", "issue_user", queryStr) } issues := make([]*Issue, 0, setting.IssuePagingNum) From 746c7fd4e74e03cfbb0239308e0f1ba8e43eecc2 Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Mon, 28 Mar 2016 00:05:49 +0200 Subject: [PATCH 07/14] Followup fix for previous query fix --- models/issue.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/issue.go b/models/issue.go index f70fd1247..8b37a24f1 100644 --- a/models/issue.go +++ b/models/issue.go @@ -552,10 +552,10 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) { } if opts.IsMention { - sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id AND issue_user.is_mentioned = 1") + sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id").And("issue_user.is_mentioned = ?", true) if opts.UserID > 0 { - sess.Where("issue_user.uid = ?", opts.UserID) + sess.And("issue_user.uid = ?", opts.UserID) } } From 762ab056a2ee976037d9fe929c37ab5bdb940e7a Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sun, 27 Mar 2016 18:21:37 -0400 Subject: [PATCH 08/14] Fix XORM IN condition table name parse --- README.md | 2 +- gogs.go | 2 +- models/issue.go | 2 +- routers/user/home.go | 2 +- templates/.VERSION | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 52d0b3cce..68b7592cf 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) -##### Current version: 0.9.17 +##### Current version: 0.9.18 | Web | UI | Preview | |:-------------:|:-------:|:-------:| diff --git a/gogs.go b/gogs.go index d49af55d6..99619bb90 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.9.17.0326" +const APP_VER = "0.9.18.0327" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/issue.go b/models/issue.go index 8b37a24f1..335172aeb 100644 --- a/models/issue.go +++ b/models/issue.go @@ -512,7 +512,7 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) { if len(opts.RepoIDs) == 0 { return make([]*Issue, 0), nil } - sess.In("issue.repo_id", base.Int64sToStrings(opts.RepoIDs)).And("issue.is_closed=?", opts.IsClosed) + sess.In("repo_id", base.Int64sToStrings(opts.RepoIDs)).And("issue.is_closed=?", opts.IsClosed) } else { sess.Where("issue.is_closed=?", opts.IsClosed) } diff --git a/routers/user/home.go b/routers/user/home.go index 0c48d6cc2..eb6a6f099 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -268,7 +268,7 @@ func Issues(ctx *context.Context) { SortType: sortType, }) if err != nil { - ctx.Handle(500, "Issues: %v", err) + ctx.Handle(500, "Issues", err) return } diff --git a/templates/.VERSION b/templates/.VERSION index c9a2d0438..47e7831c4 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.9.17.0326 \ No newline at end of file +0.9.18.0327 \ No newline at end of file From b35157f4ffa6b94b49494f9de13fe7bc4307ed2b Mon Sep 17 00:00:00 2001 From: PsychoMario Date: Wed, 30 Mar 2016 20:28:40 +0100 Subject: [PATCH 09/14] reduce line number creation to one DOM manipulation --- public/js/gogs.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/public/js/gogs.js b/public/js/gogs.js index 33da03455..0d7271c13 100644 --- a/public/js/gogs.js +++ b/public/js/gogs.js @@ -1043,10 +1043,14 @@ $(window).load(function () { var $num_list = $('.code-view .lines-num'); // Building blocks. + var $toappendblock = []; + var $toappendnum_list = []; for (var i = 0; i < lines.length; i++) { - $block.append('
  • ' + lines[i] + '
  • '); - $num_list.append('' + (i + 1) + ''); + $toappendblock.push('
  • ' + lines[i] + '
  • '); + $toappendnum_list.push('' + (i + 1) + ''); } + $block.append($toappendblock.join('')); + $num_list.append($toappendnum_list.join('')); $(document).on('click', '.lines-num span', function (e) { var $select = $(this); From e75a1444aff3ba318ff7ef65caf44e7e55a07905 Mon Sep 17 00:00:00 2001 From: l2dy Date: Sun, 3 Apr 2016 01:56:48 +0000 Subject: [PATCH 10/14] Update ISC license --- conf/license/ISC license | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/conf/license/ISC license b/conf/license/ISC license index 5294c90b7..a4bac658d 100644 --- a/conf/license/ISC license +++ b/conf/license/ISC license @@ -1,6 +1,5 @@ ISC License: -Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") -Copyright (c) 1995-2003 by Internet Software Consortium +Copyright (c) Year(s), Company or Person's Name Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. From d27ca649c7e5ccec97ba285cc529c7b3d8f71ac4 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Mon, 4 Apr 2016 19:41:34 -0400 Subject: [PATCH 11/14] api/admin: add/remove organization team repository --- routers/api/v1/admin/org_repo.go | 49 ++++++++++++++++++++++++++++++++ routers/api/v1/api.go | 43 ++++++++++++++++------------ 2 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 routers/api/v1/admin/org_repo.go diff --git a/routers/api/v1/admin/org_repo.go b/routers/api/v1/admin/org_repo.go new file mode 100644 index 000000000..71c84ad0e --- /dev/null +++ b/routers/api/v1/admin/org_repo.go @@ -0,0 +1,49 @@ +// Copyright 2016 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 admin + +import ( + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/context" +) + +func GetRepositoryByParams(ctx *context.APIContext) *models.Repository { + repo, err := models.GetRepositoryByName(ctx.Org.Team.OrgID, ctx.Params(":reponame")) + if err != nil { + if models.IsErrRepoNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetRepositoryByName", err) + } + return nil + } + return repo +} + +func AddTeamRepository(ctx *context.APIContext) { + repo := GetRepositoryByParams(ctx) + if ctx.Written() { + return + } + if err := ctx.Org.Team.AddRepository(repo); err != nil { + ctx.Error(500, "AddRepository", err) + return + } + + ctx.Status(204) +} + +func RemoveTeamRepository(ctx *context.APIContext) { + repo := GetRepositoryByParams(ctx) + if ctx.Written() { + return + } + if err := ctx.Org.Team.RemoveRepository(repo.ID); err != nil { + ctx.Error(500, "RemoveRepository", err) + return + } + + ctx.Status(204) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 34a441f41..5050b8cb7 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -112,24 +112,29 @@ func ReqAdmin() macaron.Handler { func OrgAssignment(args ...bool) macaron.Handler { var ( + assignOrg bool assignTeam bool ) - if len(args) > 0 { - assignTeam = args[0] + assignOrg = args[0] + } + if len(args) > 1 { + assignTeam = args[1] } return func(ctx *context.APIContext) { - org, err := models.GetUserByName(ctx.Params(":orgname")) - if err != nil { - if models.IsErrUserNotExist(err) { - ctx.Status(404) - } else { - ctx.Error(500, "GetUserByName", err) + ctx.Org = new(context.APIOrganization) + + var err error + if assignOrg { + ctx.Org.Organization, err = models.GetUserByName(ctx.Params(":orgname")) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetUserByName", err) + } + return } - return - } - ctx.Org = &context.APIOrganization{ - Organization: org, } if assignTeam { @@ -244,7 +249,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/orgs/:orgname", func() { m.Combo("").Get(org.Get).Patch(bind(api.EditOrgOption{}), org.Edit) m.Combo("/teams").Get(org.ListTeams) - }, OrgAssignment()) + }, OrgAssignment(true)) m.Any("/*", func(ctx *context.Context) { ctx.Error(404) @@ -265,13 +270,15 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/orgs/:orgname", func() { m.Group("/teams", func() { - m.Post("", OrgAssignment(), bind(api.CreateTeamOption{}), admin.CreateTeam) - - m.Group("/:teamid", func() { - m.Combo("/memberships/:username").Put(admin.AddTeamMember).Delete(admin.RemoveTeamMember) - }, OrgAssignment(true)) + m.Post("", OrgAssignment(true), bind(api.CreateTeamOption{}), admin.CreateTeam) }) }) + m.Group("/teams", func() { + m.Group("/:teamid", func() { + m.Combo("/members/:username").Put(admin.AddTeamMember).Delete(admin.RemoveTeamMember) + m.Combo("/repos/:reponame").Put(admin.AddTeamRepository).Delete(admin.RemoveTeamRepository) + }, OrgAssignment(false, true)) + }) }, ReqAdmin()) }, context.APIContexter()) } From a27c6f4b751ec99805dc3b43ed2aa3f767c7076d Mon Sep 17 00:00:00 2001 From: Unknwon Date: Mon, 4 Apr 2016 19:48:10 -0400 Subject: [PATCH 12/14] #2916 fix sort' field missing on issue pagination link --- README.md | 2 +- gogs.go | 2 +- templates/.VERSION | 2 +- templates/repo/issue/list.tmpl | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 68b7592cf..2a061a725 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) -##### Current version: 0.9.18 +##### Current version: 0.9.20 | Web | UI | Preview | |:-------------:|:-------:|:-------:| diff --git a/gogs.go b/gogs.go index 99619bb90..0fd3e41c9 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.9.18.0327" +const APP_VER = "0.9.20.0404" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/templates/.VERSION b/templates/.VERSION index 47e7831c4..95c3f2581 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.9.18.0327 \ No newline at end of file +0.9.20.0404 \ No newline at end of file diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 2e7de1f7a..694c7b41f 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -132,17 +132,17 @@ {{if gt .TotalPages 1}}
    From 3257df0da3b6843cc827b6c9cd7020e25402397a Mon Sep 17 00:00:00 2001 From: Unknwon Date: Mon, 11 Apr 2016 18:38:25 -0400 Subject: [PATCH 13/14] Update locales --- conf/locale/locale_bg-BG.ini | 0 conf/locale/locale_de-DE.ini | 12 +++--- conf/locale/locale_es-ES.ini | 2 +- conf/locale/locale_fi-FI.ini | 0 conf/locale/locale_fr-FR.ini | 2 +- conf/locale/locale_it-IT.ini | 24 +++++------ conf/locale/locale_ja-JP.ini | 6 +-- conf/locale/locale_lv-LV.ini | 0 conf/locale/locale_nl-NL.ini | 82 ++++++++++++++++++------------------ conf/locale/locale_pl-PL.ini | 0 conf/locale/locale_pt-BR.ini | 0 conf/locale/locale_ru-RU.ini | 2 +- conf/locale/locale_zh-CN.ini | 0 conf/locale/locale_zh-HK.ini | 62 +++++++++++++-------------- modules/bindata/bindata.go | 52 +++++++++++------------ 15 files changed, 122 insertions(+), 122 deletions(-) mode change 100755 => 100644 conf/locale/locale_bg-BG.ini mode change 100755 => 100644 conf/locale/locale_de-DE.ini mode change 100755 => 100644 conf/locale/locale_es-ES.ini mode change 100755 => 100644 conf/locale/locale_fi-FI.ini mode change 100755 => 100644 conf/locale/locale_fr-FR.ini mode change 100755 => 100644 conf/locale/locale_it-IT.ini mode change 100755 => 100644 conf/locale/locale_ja-JP.ini mode change 100755 => 100644 conf/locale/locale_lv-LV.ini mode change 100755 => 100644 conf/locale/locale_nl-NL.ini mode change 100755 => 100644 conf/locale/locale_pl-PL.ini mode change 100755 => 100644 conf/locale/locale_pt-BR.ini mode change 100755 => 100644 conf/locale/locale_ru-RU.ini mode change 100755 => 100644 conf/locale/locale_zh-CN.ini mode change 100755 => 100644 conf/locale/locale_zh-HK.ini diff --git a/conf/locale/locale_bg-BG.ini b/conf/locale/locale_bg-BG.ini old mode 100755 new mode 100644 diff --git a/conf/locale/locale_de-DE.ini b/conf/locale/locale_de-DE.ini old mode 100755 new mode 100644 index 76b632491..57dc8e644 --- a/conf/locale/locale_de-DE.ini +++ b/conf/locale/locale_de-DE.ini @@ -326,7 +326,7 @@ delete_account=Konto löschen delete_prompt=Diese Aktion wird Ihr Konto dauerhaft löschen und kann NICHT rückgängig gemacht werden! confirm_delete_account=Löschvorgang bestätigen delete_account_title=Konto löschen -delete_account_desc=Sie sind dabei dieses Konto dauerhaft zu löschen, möchten Sie wirklich fortfahren? +delete_account_desc=Sie sind dabei dieses Konto dauerhaft zu löschen. Möchten Sie wirklich fortfahren? [repo] owner=Besitzer @@ -342,7 +342,7 @@ fork_from=Fork von fork_visiblity_helper=Die Sichtbarkeit von geforkten Repositories ist nicht veränderbar. repo_desc=Beschreibung repo_lang=Sprache -repo_lang_helper=.gitignore Dateien auswählen +repo_lang_helper=Wählen Sie eine .gitignore-Datei aus license=Lizenz license_helper=Wählen Sie eine Lizenz aus readme=Readme @@ -505,11 +505,11 @@ pulls.merged_title_desc=hat %[1]d Commits von %[2]s nach %[3] pulls.tab_conversation=Diskussion pulls.tab_commits=Commits pulls.tab_files=Geänderte Dateien -pulls.reopen_to_merge=Bitte diesen Pull-Request wiedereröffnen, um die Merge-Operation auszuführen. +pulls.reopen_to_merge=Bitte diesen Pull-Request wieder eröffnen, um die Merge-Operation auszuführen. pulls.merged=Zusammengeführt pulls.has_merged=Dieser Pull-Request wurde erfolgreich zusammengeführt! pulls.data_broken=Die Daten dieses Pull-Requests sind defekt, da Fork-Informationen gelöscht wurden. -pulls.is_checking=Die Konfliktprüfung ist in Arbeit. Bitte aktualisieren Sie die Seite in wenigen Momenten. +pulls.is_checking=Die Konfliktprüfung läuft noch. Bitte aktualisieren Sie die Seite in wenigen Augenblicken. pulls.can_auto_merge_desc=Dieser Pull-Request kann automatisch zusammengeführt werden. pulls.cannot_auto_merge_desc=Dieser Pull-Request kann nicht automatisch zusammengeführt werden, da es Konflikte gibt. pulls.cannot_auto_merge_helper=Bitte manuell zusammenführen, um die Konflikte zu lösen. @@ -938,7 +938,7 @@ auths.pam_service_name=PAM Dienstname auths.enable_auto_register=Automatische Registrierung aktivieren auths.tips=Tipps auths.edit=Authentifizierungseinstellungen bearbeiten -auths.activated=Diese Authentifizierung ist aktiviert +auths.activated=Diese Authentifizierung ist aktiv auths.new_success=Neue Authentifizierung '%s' wurde erfolgreich hinzugefügt. auths.update_success=Die Authentifizierungseinstellungen wurden erfolgreich aktualisiert. auths.update=Authentifizierungseinstellungen aktualisieren @@ -946,7 +946,7 @@ auths.delete=Diese Authentifizierung löschen auths.delete_auth_title=Authentifizierung löschen auths.delete_auth_desc=Diese Authentifizierung wird gelöscht. Möchten Sie fortfahren? auths.still_in_used=Diese Authentifizierung wird noch von einigen Benutzern verwendet. Bitte löschen Sie diese Benutzer oder ändern Sie deren Anmeldetyp. -auths.deletion_success=Authentifizierung wurde erfolgreich entfernt! +auths.deletion_success=Authentifizierung wurde erfolgreich gelöscht! config.server_config=Serverkonfiguration config.app_name=Anwendungsname diff --git a/conf/locale/locale_es-ES.ini b/conf/locale/locale_es-ES.ini old mode 100755 new mode 100644 index 3dc05ea6d..cf7113548 --- a/conf/locale/locale_es-ES.ini +++ b/conf/locale/locale_es-ES.ini @@ -384,7 +384,7 @@ unstar=Eliminar destacado star=Destacar fork=Fork -no_desc=Sin Descripción +no_desc=Sin descripción quick_guide=Guía Rápida clone_this_repo=Clonar este repositorio create_new_repo_command=Crear un nuevo repositorio desde línea de comandos diff --git a/conf/locale/locale_fi-FI.ini b/conf/locale/locale_fi-FI.ini old mode 100755 new mode 100644 diff --git a/conf/locale/locale_fr-FR.ini b/conf/locale/locale_fr-FR.ini old mode 100755 new mode 100644 index 70ffea682..8ff96d2de --- a/conf/locale/locale_fr-FR.ini +++ b/conf/locale/locale_fr-FR.ini @@ -57,7 +57,7 @@ password=Mot de passe db_name=Nom de base de données db_helper=Veuillez utiliser le moteur INNODB avec le jeu de caractères utf8_general_ci pour MySQL. ssl_mode=Mode SSL -path=Chemin +path=Emplacement sqlite_helper=Le chemin du fichier de base de données SQLite3 ou TiDB.
    Utilisez un chemin absolu lorsque vous démarrez en tant que service. err_empty_db_path=Le chemin de la base de données SQLite3 ou TiDB ne peut être vide. err_invalid_tidb_name=Le nom de la base de données TiDB ne peut contenir les caractères "." ou "-". diff --git a/conf/locale/locale_it-IT.ini b/conf/locale/locale_it-IT.ini old mode 100755 new mode 100644 index fbc54fceb..fa35eb7f4 --- a/conf/locale/locale_it-IT.ini +++ b/conf/locale/locale_it-IT.ini @@ -467,8 +467,8 @@ issues.close_comment_issue=Commenta e chiudi issues.reopen_issue=Riapri issues.reopen_comment_issue=Commenta e riapri issues.create_comment=Commento -issues.closed_at=`closed %[2]s` -issues.reopened_at=`reopened %[2]s` +issues.closed_at=`chiuso %[2]s` +issues.reopened_at=`riaperto %[2]s` issues.commit_ref_at=`referenced this issue from a commit %[2]s` issues.poster=Autore issues.collaborator=Collaboratori @@ -500,8 +500,8 @@ pulls.no_results=Nessun risultato trovato. pulls.nothing_to_compare=Non c'è niente da confrontare perchè i branch base e head uguali. pulls.has_pull_request=`E' già presente una pull request tra questi due trargets: %[2]s#%[3]d` pulls.create=Crea Pull Request -pulls.title_desc=wants to merge %[1]d commits from %[2]s into %[3]s -pulls.merged_title_desc=merged %[1]d commits from %[2]s into %[3]s %[4]s +pulls.title_desc=vorrebbe unire %[1]d commit da %[2]s a %[3]s +pulls.merged_title_desc=ha unito %[1]d commit da %[2]s a %[3]s %[4]s pulls.tab_conversation=Conversazione pulls.tab_commits=Commit pulls.tab_files=File modificati @@ -574,7 +574,7 @@ settings.external_wiki_url=URL Wiki esterno settings.external_wiki_url_desc=I visitatori verranno reindirizzati all'URL quando cliccano sulla scheda. settings.issues_desc=Abilita l'issue tracker builtin leggero settings.use_external_issue_tracker=Utilizza gestore di problemi esterno -settings.tracker_url_format=External Issue Tracker URL Format +settings.tracker_url_format=Formato URL Gestore Problemi Esterno settings.tracker_url_format_desc=You can use placeholder {user} {repo} {index} for user name, repository name and issue index. settings.pulls_desc=Abilita le pull requests per accettare contributi pubblici settings.danger_zone=Zona Pericolosa @@ -583,7 +583,7 @@ settings.convert=Converti in Repository Regolare settings.convert_desc=Puoi convertire questo mirror in un repository regolare. Questa operazione non può essere annullata. settings.convert_notices_1=- Questa operazione non potrà essere annullata e convertirà questo mirror in un repository regolare. settings.convert_confirm=Conferma la conversione -settings.convert_succeed=Repository has been converted to regular type successfully. +settings.convert_succeed=Il repository è stato convertito con successo al formato normale. settings.transfer=Trasferisci proprietà settings.transfer_desc=Trasferisci questa repository a un altro utente o a un'organizzazione nella quale hai diritti d'amministratore. settings.transfer_notices_1=- You will lose access if new owner is a individual user. @@ -591,7 +591,7 @@ settings.transfer_notices_2=- You will conserve access if new owner is an organi settings.transfer_form_title=Per favore inserisci le informazioni seguenti per confermare l'operazione: settings.wiki_delete=Elimina i dati della Wiki settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain. -settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s +settings.wiki_delete_notices_1=Questo eliminerà e disabiliterà la wiki per %s settings.wiki_deletion_success=I dati della wiki del repository sono stati eliminati con successo. settings.delete=Elimina questo repository settings.delete_desc=Una volta che hai cancellato il repository, non puoi tornare indietro. Si prega di fare attenzione. @@ -619,7 +619,7 @@ settings.add_webhook=Aggiungi Webhook settings.hooks_desc=I Webhooks sono molto simili a un basilare evento trigger HTTP POST. Ogni volta che qualcosa si verifica in Gogs, tratteremo la notifica all'host di destinazione specificato. Ulteriori informazioni in questa Guida ai Webhooks. settings.webhook_deletion=Elimina Webhook settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue? -settings.webhook_deletion_success=Webhook has been deleted successfully! +settings.webhook_deletion_success=Il Webhook è stato eliminato con successo! settings.webhook.test_delivery=Test di consegna settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history. @@ -1067,7 +1067,7 @@ comment_issue=`ha commentato il problema %s#%[2]s` merge_pull_request=`merged pull request %s#%[2]s` transfer_repo=ha trasferito il repository %s a %s push_tag=ha pushato il tag %[2]s a %[3]s -compare_commits=View comparison for these %d commits +compare_commits=Visualizza comparazione tra questi %d commit [tool] ago=fa @@ -1091,8 +1091,8 @@ raw_seconds=secondi raw_minutes=minuti [dropzone] -default_message=Drop files here or click to upload. -invalid_input_type=You can't upload files of this type. -file_too_big=File size ({{filesize}} MB) exceeds maximum size ({{maxFilesize}} MB). +default_message=Trascina i file qui o clicca per caricare. +invalid_input_type=Non è possibile caricare file di questo tipo. +file_too_big=La dimensione del file ({{filesize}} MB) supera la dimensione massima ({{maxFilesize}} MB). remove_file=Rimuovi file diff --git a/conf/locale/locale_ja-JP.ini b/conf/locale/locale_ja-JP.ini old mode 100755 new mode 100644 index 691eac8c7..e61b60604 --- a/conf/locale/locale_ja-JP.ini +++ b/conf/locale/locale_ja-JP.ini @@ -133,8 +133,8 @@ issues.in_your_repos=あなたのリポジトリ [explore] repos=リポジトリ -users=Users -search=Search +users=ユーザ +search=検索 [auth] create_new_account=新規アカウントを作成 @@ -371,7 +371,7 @@ migrate.permission_denied=ローカル リポジトリをインポートする migrate.invalid_local_path=ローカルパスが無効です。存在しないかディレクトリではありません。 migrate.failed=移行に失敗しました: %v -mirror_from=mirror of +mirror_from=同期ミラー forked_from=フォーク元 fork_from_self=すでにあなたの所有しているリポジトリはフォークできません copy_link=コピー diff --git a/conf/locale/locale_lv-LV.ini b/conf/locale/locale_lv-LV.ini old mode 100755 new mode 100644 diff --git a/conf/locale/locale_nl-NL.ini b/conf/locale/locale_nl-NL.ini old mode 100755 new mode 100644 index 1f560eb6c..f5c212b68 --- a/conf/locale/locale_nl-NL.ini +++ b/conf/locale/locale_nl-NL.ini @@ -471,7 +471,7 @@ issues.closed_at=`gesloten om %[2]s` issues.reopened_at=`heropend om %[2]s` issues.commit_ref_at='verwees naar dit probleem vanuit een commit %[2]s' issues.poster=Poster -issues.collaborator=Collaborator +issues.collaborator=Medewerker issues.owner=Eigenaar issues.sign_up_for_free=Gratis aanmelden issues.sign_in_require_desc=om deel te nemen in deze conversatie. Heeft u al een account? Meld u aan om te reageren @@ -512,9 +512,9 @@ pulls.data_broken=Omdat informatie over de fork is verwijderd, zijn de gegevens pulls.is_checking=Controle van conflicten is nog bezig, ververs deze pagina in enkele ogenblikken. pulls.can_auto_merge_desc=Dit pull-request kan automatisch samengevoegd worden. pulls.cannot_auto_merge_desc=Dit pull-request kan niet worden gemerged omdat er conflicten zijn. -pulls.cannot_auto_merge_helper=Please merge manually in order to resolve the conflicts. +pulls.cannot_auto_merge_helper=Gelieve beide versies manueel samen te voegen om de conflicten op te lossen. pulls.merge_pull_request=Samenvoegen van pull verzoek -pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.` +pulls.open_unmerged_pull_exists=U kan de bewerking 'heropenen' niet uitvoeren omdat er al een pull-aanvraag (#%d) is van dezelfde repository met dezelfde informatie. Voeg deze eerst samen. milestones.new=Nieuwe mijlpaal milestones.open_tab=%d geopend @@ -553,7 +553,7 @@ wiki.last_commit_info=%s heeft deze pagina aangepast %s wiki.edit_page_button=Bewerken wiki.new_page_button=Nieuwe pagina wiki.delete_page_button=Verwijder pagina -wiki.delete_page_notice_1=This will delete the page "%s". Please be certain. +wiki.delete_page_notice_1=Dit zal pagina "%s" verwijderen. Weet u het zeker? wiki.page_already_exists=Er bestaat al een wiki-pagina met deze naam. wiki.pages=Pagina’s wiki.last_updated=Laatst bijgewerkt: %s @@ -566,40 +566,40 @@ settings.githooks=Git-hooks settings.basic_settings=Basis instellingen settings.site=Officiële site settings.update_settings=Instellingen bewerken -settings.change_reponame_prompt=This change will affect how links relate to the repository. +settings.change_reponame_prompt=Deze verandering zal gevolgen hebben voor hoe links zich verhouden tot de repository. settings.advanced_settings=Geavanceerde opties -settings.wiki_desc=Enable wiki to allow people write documents +settings.wiki_desc=Wiki inschakelen, om mensen documenten te laten schrijven settings.use_external_wiki=Externe wiki gebruiken settings.external_wiki_url=Externe wiki-URL settings.external_wiki_url_desc=Bezoekers worden doorgestuurd naar de URL als ze op het tabblad klikken. settings.issues_desc=Ingebouwde compacte issuetracker inschakelen settings.use_external_issue_tracker=Externe issuetracker gebruiken settings.tracker_url_format=URL-formaat externe issuetracker -settings.tracker_url_format_desc=You can use placeholder {user} {repo} {index} for user name, repository name and issue index. -settings.pulls_desc=Enable pull requests to accept public contributions +settings.tracker_url_format_desc=U kan de aanduidingen {user} {repo} {index} gebruiken voor de gebruikersnaam, de naam van de repository en de lijst van open tickets. +settings.pulls_desc=Schakel 'pull request' in om publieke bijdragen te mogelijk te maken settings.danger_zone=Gevaren zone settings.new_owner_has_same_repo=De nieuwe eigenaar heeft al een repositorie met deze naam settings.convert=Converteren naar gewone repository settings.convert_desc=U kunt deze mirror converteren naar een gewone repository. Dit kan niet ongedaan worden gemaakt. -settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone. +settings.convert_notices_1=- Deze operatie zet de mirror repository om in een gewone repository en dit kan niet ongedaan gemaakt worden. settings.convert_confirm=Conversie bevestigen settings.convert_succeed=Deze repository is geconverteerd naar een normale repository. settings.transfer=Eigendom overdragen settings.transfer_desc=Draag deze repo over aan een andere gebruiker of een organisatie waar u beheerders rechten heeft. -settings.transfer_notices_1=- You will lose access if new owner is a individual user. -settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners. -settings.transfer_form_title=Please enter following information to confirm your operation: -settings.wiki_delete=Erase Wiki Data -settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain. -settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s -settings.wiki_deletion_success=Repository wiki data have been erased successfully. +settings.transfer_notices_1=- U verliest toegang als de nieuwe gebruiker een individuele gebruiker is. +settings.transfer_notices_2=- U behoudt toegang indien de nieuwe eigenaar een organisatie is en U één van de eigenaren van de organisatie bent. +settings.transfer_form_title=Voer de volgende informatie in om de bewerking te bevestigen: +settings.wiki_delete=Wiki gegevens verwijderen +settings.wiki_delete_desc=Als U wiki informatie wist gaat deze onherroepelijk verloren. Bent U zeker? +settings.wiki_delete_notices_1=- Deze operatie wist de wiki voor %s en schakelt de wiki uit +settings.wiki_deletion_success=De repository met wiki data is succesvol gewist. settings.delete=Verwijder deze repositorie settings.delete_desc=Als u eenmaal een repositorie verwijderd is er geen weg terug. Gelieve zeker te zijn van uw acties. -settings.delete_notices_1=- This operation CANNOT be undone. -settings.delete_notices_2=- This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators. -settings.delete_notices_fork_1=- If this repository is public, all forks will become independent after deletion. -settings.delete_notices_fork_2=- If this repository is private, all forks will be removed at the same time. -settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first. +settings.delete_notices_1=- Deze bewerking kan NIET ongedaan gemaakt worden. +settings.delete_notices_2=- Deze bewerking verwijdert permanent alle informatie van deze repository met inbegrip van Git gegevens, tickets, opmerkingen en de toegang van medewerkers. +settings.delete_notices_fork_1=- Als deze repository publiek is worden alle vorken onafhankelijk na verwijdering. +settings.delete_notices_fork_2=- Als deze repository privé is worden alle vorken ook verwijderd. +settings.delete_notices_fork_3=- Als U alle vorken na verwijderen van deze repository wil behouden, verander dan eerste de zichtbaarheid van deze repository naar 'publiek'. settings.deletion_success=Repository is succesvol verwijderd! settings.update_settings_success=Repositorie instellingen zijn succesvol bijgewerkt. settings.transfer_owner=Nieuwe eigenaar @@ -609,20 +609,20 @@ settings.confirm_delete=Bevestig verwijdering settings.add_collaborator=Nieuwe medewerker toevoegen settings.add_collaborator_success=medewerker is toegevoegd. settings.delete_collaborator=Verwijderen -settings.collaborator_deletion=Collaborator Deletion -settings.collaborator_deletion_desc=This user will no longer have collaboration access to this repository after deletion. Do you want to continue? +settings.collaborator_deletion=Verwijder Medewerker +settings.collaborator_deletion_desc=Deze gebruiker zal niet langer toegang hebben tot deze repository. Wilt U doorgaan? settings.remove_collaborator_success=medewerker is verwijderd. settings.search_user_placeholder=Zoek gebruiker... -settings.org_not_allowed_to_be_collaborator=Organization is not allowed to be added as a collaborator. +settings.org_not_allowed_to_be_collaborator=De organisatie kan niet toegevoegd worden als medewerker. settings.user_is_org_member=Gebruiker is lid van de organisatie die als een medewerker kan niet worden toegevoegd. settings.add_webhook=Webhook toevoegen settings.hooks_desc=Webhooks dat de externe diensten om kennisgevingen te ontvangen wanneer bepaalde gebeurtenissen op Gogs plaatsvinden. Wanneer de opgegeven gebeurtenissen plaatsvinden, sturen we een POST-aanvraag naar elk van de URL's die u opgeeft. Meer informatie vindt u in onze Webhooks gids. settings.webhook_deletion=Webhook verwijderen -settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue? +settings.webhook_deletion_desc=Verwijderen van deze webhook zal de informatie en alle geschiedenis verwijderen. Wilt u doorgaan? settings.webhook_deletion_success=Webhook is succesvol verwijderd! settings.webhook.test_delivery=Test-bezorging -settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings -settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history. +settings.webhook.test_delivery_desc=Stuur een nep push bericht om de webhook te testen +settings.webhook.test_delivery_success=De test webhook is toegevoegd aan de wachtrij. Het kan enkele seconden duren voor deze in de geschiedenis wordt weergegeven. settings.webhook.request=Verzoek settings.webhook.response=Antwoord settings.webhook.headers=Headers @@ -662,7 +662,7 @@ settings.slack_domain=Slack domein settings.slack_channel=Slack kanaal settings.deploy_keys=Installeer sleutels settings.add_deploy_key=Toevoegen deploy sleutel -settings.deploy_key_desc=Deploy keys have read-only access. They are not the same as personal account SSH keys. +settings.deploy_key_desc=Sleutels voor uitrol hebben enkel leesrechten. Ze zijn niet dezelfde als de SSH sleutels van persoonlijke accounts. settings.no_deploy_keys=U hebt nog geen deploy sleutels toegevoegd. settings.title=Titel settings.deploy_key_content=Inhoud @@ -678,8 +678,8 @@ diff.parent=bovenliggende diff.commit=commit diff.data_not_available=Diff gegevens niet beschikbaar. diff.show_diff_stats=Toon Diff Stats -diff.show_split_view=Split View -diff.show_unified_view=Unified View +diff.show_split_view=Zij-aan-zij weergave +diff.show_unified_view=Gecombineerde weergave diff.stats_desc=%d gewijzigde bestanden met toevoegingen van %d en %d verwijderingen diff.bin=BIN diff.view_file=Bestand weergeven @@ -692,7 +692,7 @@ release.stable=Stabiel release.edit=bewerken release.ahead=%d aanpassingen aan %s sinds deze versie release.source_code=Broncode -release.new_subheader=Publish releases to iterate product. +release.new_subheader=Publiceer releases om te itereren. release.edit_subheader=Een gedetailleerd changelog helpt gebruikers te begrijpen wat er is verbeterd in deze release. release.tag_name=Tagnaam release.target=Doel @@ -710,7 +710,7 @@ release.save_draft=Concept opslaan release.edit_release=Release bewerken release.delete_release=Deze release verwijderen release.deletion=Release verwijderen -release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue? +release.deletion_desc=Als deze release verwijdert, worden de bijbehorende Git tag ook gewist. Wilt u doorgaan? release.deletion_success=Release is verwijderd! release.tag_name_already_exist=Versie met deze naam bestaat al. release.downloads=Downloads @@ -764,7 +764,7 @@ members.owner=Eigenaar members.member=Lid members.remove=Verwijderen members.leave=Verlaat -members.invite_desc=Add a new member to %s: +members.invite_desc=Voeg nieuw lid toe aan %s: members.invite_now=Nu uitnodigen teams.join=Lid worden @@ -874,7 +874,7 @@ users.edit=Bewerken users.auth_source=Authenticatiebron users.local=Lokaal users.auth_login_name=Authenticatie-loginnaam -users.password_helper=Leave it empty to remain unchanged. +users.password_helper=Laat leeg om ongewijzigd te blijven. users.update_profile_success=Profiel is succesvol bijgewerkt. users.edit_account=Bewerk account users.max_repo_creation=Maximum Repository Creation Limit @@ -914,11 +914,11 @@ auths.domain=Domein auths.host=Host auths.port=Poort auths.bind_dn=Binden DN -auths.bind_password=Bind Password -auths.bind_password_helper=Warning: This password is stored in plain text. Do not use a high privileged account. +auths.bind_password=Bind wachtwoord +auths.bind_password_helper=Opgelet: Dit wachtwoord wordt opgeslagen als leesbare tekst. Gebruik geen account met verhoogde rechten. auths.user_base=User Search Base auths.user_dn=User DN -auths.attribute_username=Username attribute +auths.attribute_username=Gebruikersnaam attribuut auths.attribute_username_placeholder=Leave empty to use sign-in form field value for user name. auths.attribute_name=Voornaam attribuut auths.attribute_surname=Achternaam attribuut @@ -937,9 +937,9 @@ auths.skip_tls_verify=TLS-verificatie overslaan auths.pam_service_name=PAM servicenaam auths.enable_auto_register=Activeer automatische registratie auths.tips=Tips -auths.edit=Edit Authentication Setting +auths.edit=Verificatie-instelling bewerken auths.activated=Deze autorisatiemethode is geactiveerd -auths.new_success=New authentication '%s' has been added successfully. +auths.new_success=Nieuwe authenticatie '%s' werd toegevoegd. auths.update_success=Authentication setting has been updated successfully. auths.update=Authenticatie-instellingen bijwerken auths.delete=Deze authenticatiewijze verwijderen @@ -1040,13 +1040,13 @@ monitor.start=Starttijd monitor.execute_time=Uitvoertijd notices.system_notice_list=Systeem aankondigingen -notices.view_detail_header=View Notice Detail +notices.view_detail_header=Bekijk bericht details notices.actions=Acties notices.select_all=Alles selecteren notices.deselect_all=Alles deselecteren notices.inverse_selection=Selectie omkeren notices.delete_selected=Selectie verwijderen -notices.delete_all=Delete All Notices +notices.delete_all=Verwijder alle berichten notices.type=Type notices.type_1=Opslagplaats notices.desc=Beschrijving diff --git a/conf/locale/locale_pl-PL.ini b/conf/locale/locale_pl-PL.ini old mode 100755 new mode 100644 diff --git a/conf/locale/locale_pt-BR.ini b/conf/locale/locale_pt-BR.ini old mode 100755 new mode 100644 diff --git a/conf/locale/locale_ru-RU.ini b/conf/locale/locale_ru-RU.ini old mode 100755 new mode 100644 index c4ce5fbdb..809a85b49 --- a/conf/locale/locale_ru-RU.ini +++ b/conf/locale/locale_ru-RU.ini @@ -676,7 +676,7 @@ settings.deploy_key_deletion_success=Ключ развертывания усп diff.browse_source=Просмотр исходного кода diff.parent=Родитель diff.commit=Сommit -diff.data_not_available=Данные Diff не доступны. +diff.data_not_available=Данные Diff недоступны. diff.show_diff_stats=Показать статистику Diff diff.show_split_view=Разделённый вид diff.show_unified_view=Единый вид diff --git a/conf/locale/locale_zh-CN.ini b/conf/locale/locale_zh-CN.ini old mode 100755 new mode 100644 diff --git a/conf/locale/locale_zh-HK.ini b/conf/locale/locale_zh-HK.ini old mode 100755 new mode 100644 index 817e57d36..facdd76a8 --- a/conf/locale/locale_zh-HK.ini +++ b/conf/locale/locale_zh-HK.ini @@ -38,7 +38,7 @@ settings=設定 your_profile=個人資料 your_settings=用戶設定 -activities=Activities +activities=活動 pull_requests=合併請求 issues=問題 @@ -58,7 +58,7 @@ db_name=資料庫名稱 db_helper=如果您使用 MySQL,請使用 INNODB 引擎以及 utf8_general_ci 字符集。 ssl_mode=SSL 模式 path=數據庫文件路徑 -sqlite_helper=The file path of SQLite3 or TiDB database.
    Please use absolute path when you start as service. +sqlite_helper=SQLite3 或 TiDB 資料庫檔案路徑。
    作為以服務執行時,請使用絕對路徑。 err_empty_db_path=SQLite3 或 TiDB 的數據庫路徑不能為空。 err_invalid_tidb_name=TiDB 數據庫名稱不允許包含字符 "." 或 "-" 。 no_admin_and_disable_registration=您不能夠在未創建管理員用戶的情況下禁止註冊。 @@ -79,8 +79,8 @@ http_port=HTTP 端口號 http_port_helper=應用監聽的端口號 app_url=應用程式網址 app_url_helper=該設置影響 HTTP/HTTPS 複製地址和一些郵箱中的連結。 -log_root_path=Log Path -log_root_path_helper=Directory to write log files to. +log_root_path=日誌路徑 +log_root_path_helper=寫入日誌檔目錄 optional_title=可選設置 email_title=電子郵件服務設定 @@ -117,7 +117,7 @@ run_user_not_match=執行系統用戶非當前用戶:%s -> %s save_config_failed=應用配置保存失敗:%v invalid_admin_setting=管理員帳戶設置不正確:%v install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG! -invalid_log_root_path=Log root path is invalid: %v +invalid_log_root_path=日誌根目錄無效: %v [home] uname_holder=用戶名或郵箱 @@ -133,8 +133,8 @@ issues.in_your_repos=屬於該用戶倉庫的 [explore] repos=探索倉庫 -users=Users -search=Search +users=用戶 +search=搜索 [auth] create_new_account=創建帳戶 @@ -259,7 +259,7 @@ cancel=取消操作 enable_custom_avatar=啟動自定義頭像 choose_new_avatar=選擇新的頭像 update_avatar=更新頭像設置 -delete_current_avatar=Delete Current Avatar +delete_current_avatar=刪除當前頭像 uploaded_avatar_not_a_image=上傳的文件不是一張圖片! update_avatar_success=您的頭像設置更新成功! @@ -382,7 +382,7 @@ unwatch=取消關注 watch=關註 unstar=取消讚好 star=讚好 -fork=派生 +fork=複刻 no_desc=暫無描述 quick_guide=快速幫助 @@ -471,7 +471,7 @@ issues.closed_at=`於 %[2]s 關閉` issues.reopened_at=`於 %[2]s 重新開啟` issues.commit_ref_at=`在代碼提交 %[2]s 中引用了該問題` issues.poster=發佈者 -issues.collaborator=Collaborator +issues.collaborator=協同者 issues.owner=所有者 issues.sign_up_for_free=免費註冊 issues.sign_in_require_desc=及加入到對話當中。如果您已經註冊,可以直接 登錄及評論 @@ -488,7 +488,7 @@ issues.label_modify=修改標籤 issues.label_deletion=刪除標籤 issues.label_deletion_desc=刪除該標籤將會移除所有問題中相關的訊息。是否繼續? issues.label_deletion_success=標籤刪除成功! -issues.num_participants=%d Participants +issues.num_participants=%d 參與者 pulls.new=創建合併請求 pulls.compare_changes=對比文件變化 @@ -552,8 +552,8 @@ wiki.save_page=保存頁面 wiki.last_commit_info=%s 於 %s 修改了此頁面 wiki.edit_page_button=修改 wiki.new_page_button=新的頁面 -wiki.delete_page_button=Delete Page -wiki.delete_page_notice_1=This will delete the page "%s". Please be certain. +wiki.delete_page_button=刪除頁面 +wiki.delete_page_notice_1=這將刪除頁面 "%s"。請三思而後行。 wiki.page_already_exists=相同名稱的 Wiki 頁面已經存在。 wiki.pages=所有頁面 wiki.last_updated=最後更新於 %s @@ -579,8 +579,8 @@ settings.tracker_url_format_desc=您可以使用 {user} {repo} {index} Date: Tue, 12 Apr 2016 16:46:32 +0200 Subject: [PATCH 14/14] docker: update documentation for container options (#2965) - Created a Container options section in `docker/README.md` - Add documentation for SOCAT_LINK - Move CROND documentation to the new section --- docker/README.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/docker/README.md b/docker/README.md index 1b18b8e78..7465bd10f 100644 --- a/docker/README.md +++ b/docker/README.md @@ -73,9 +73,27 @@ Most of settings are obvious and easy to understand, but there are some settings Full documentation of application settings can be found [here](http://gogs.io/docs/advanced/configuration_cheat_sheet.html). -### Crond - -Please set environment variable `RUN_CROND` to be `true` or `1` in order to start `crond` inside the container. +### Container options + +This container have some options available via environment variables, these options are opt-in features that can help the administration of this container: + +- **SOCAT_LINK**: + - Possible value: + `true`, `false`, `1`, `0` + - Default: + `true` + - Action: + Bind linked docker container to localhost socket using socat. + Any exported port from a linked container will be binded to the matching port on localhost. + - Disclaimer: + As this option rely on the environment variable created by docker when a container is linked, this option should be deactivated in managed environment such as Rancher or Kubernetes (set to `0` or `false`) +- **RUN_CROND**: + - Possible value: + `true`, `false`, `1`, `0` + - Default: + `false` + - Action: + Request crond to be run inside the container. Its default configuration will periodically run all scripts from `/etc/periodic/${period}` but custom crontabs can be added to `/var/spool/cron/crontabs/`. ## Upgrade