repo: support unlisted but publicly accessible repositories (#6176)

Co-authored-by: ᴜɴᴋɴᴡᴏɴ <u@gogs.io>
pull/6303/head
Achilleas Koutsou 2020-09-03 21:04:22 +02:00 committed by GitHub
parent 23ff182d1f
commit c4360747a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 482 additions and 427 deletions

View File

@ -6,6 +6,8 @@ All notable changes to Gogs are documented in this file.
### Added
- An unlisted option is added when create or migrate a repository. Unlisted repositories are public but not being listed for users without direct access in the UI. [#5733](https://github.com/gogs/gogs/issues/5733)
### Changed
- The default branch has been changed to `main`. [#6285](https://github.com/gogs/gogs/pull/6285)

View File

@ -399,7 +399,9 @@ owner = Owner
repo_name = Repository Name
repo_name_helper = A good repository name is usually composed of short, memorable and unique keywords.
visibility = Visibility
unlisted = Unlisted
visiblity_helper = This repository is <span class="ui red text">Private</span>
unlisted_helper = This repository is <span class="ui red text">Unlisted</span>
visiblity_helper_forced = Site admin has forced all new repositories to be <span class="ui red text">Private</span>
visiblity_fork_helper = (Change of this value will affect all forks)
clone_helper = Need help cloning? Visit <a target="_blank" href="%s">Help</a>!

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -188,7 +188,7 @@ func newRepoAction(e Engine, doer, owner *User, repo *Repository) (err error) {
RepoID: repo.ID,
RepoUserName: repo.Owner.Name,
RepoName: repo.Name,
IsPrivate: repo.IsPrivate,
IsPrivate: repo.IsPrivate || repo.IsUnlisted,
})
}
@ -205,7 +205,7 @@ func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Reposit
RepoID: repo.ID,
RepoUserName: repo.Owner.Name,
RepoName: repo.Name,
IsPrivate: repo.IsPrivate,
IsPrivate: repo.IsPrivate || repo.IsUnlisted,
Content: oldRepoName,
}); err != nil {
return fmt.Errorf("notify watchers: %v", err)
@ -512,7 +512,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
RepoUserName: repo.MustOwner().Name,
RepoName: repo.Name,
RefName: refName,
IsPrivate: repo.IsPrivate,
IsPrivate: repo.IsPrivate || repo.IsUnlisted,
}
apiRepo := repo.APIFormat(nil)
@ -628,7 +628,7 @@ func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err e
RepoID: repo.ID,
RepoUserName: repo.Owner.Name,
RepoName: repo.Name,
IsPrivate: repo.IsPrivate,
IsPrivate: repo.IsPrivate || repo.IsUnlisted,
Content: path.Join(oldOwner.Name, repo.Name),
}); err != nil {
return fmt.Errorf("notifyWatchers: %v", err)
@ -659,7 +659,7 @@ func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue
RepoID: repo.ID,
RepoUserName: repo.Owner.Name,
RepoName: repo.Name,
IsPrivate: repo.IsPrivate,
IsPrivate: repo.IsPrivate || repo.IsUnlisted,
})
}
@ -678,7 +678,7 @@ func mirrorSyncAction(opType ActionType, repo *Repository, refName string, data
RepoUserName: repo.MustOwner().Name,
RepoName: repo.Name,
RefName: refName,
IsPrivate: repo.IsPrivate,
IsPrivate: repo.IsPrivate || repo.IsUnlisted,
})
}

View File

@ -517,7 +517,7 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos
repos := make([]*Repository, 0, pageSize)
if err = x.Where("owner_id = ?", org.ID).
And(builder.Or(
builder.Expr("is_private = ?", false),
builder.And(builder.Expr("is_private = ?", false), builder.Expr("is_unlisted = ?", false)),
builder.In("id", teamRepoIDs))).
Desc("updated_unix").
Limit(pageSize, (page-1)*pageSize).

View File

@ -173,8 +173,11 @@ type Repository struct {
NumOpenMilestones int `xorm:"-" gorm:"-" json:"-"`
NumTags int `xorm:"-" gorm:"-" json:"-"`
IsPrivate bool
IsBare bool
IsPrivate bool
// TODO: When migrate to GORM, make sure to do a loose migration with `HasColumn` and `AddColumn`,
// see docs in https://gorm.io/docs/migration.html.
IsUnlisted bool
IsBare bool
IsMirror bool
*Mirror `xorm:"-" gorm:"-" json:"-"`
@ -717,6 +720,7 @@ type MigrateRepoOptions struct {
Name string
Description string
IsPrivate bool
IsUnlisted bool
IsMirror bool
RemoteAddr string
}
@ -746,6 +750,7 @@ func MigrateRepository(doer, owner *User, opts MigrateRepoOptions) (*Repository,
Name: opts.Name,
Description: opts.Description,
IsPrivate: opts.IsPrivate,
IsUnlisted: opts.IsUnlisted,
IsMirror: opts.IsMirror,
})
if err != nil {
@ -920,6 +925,7 @@ type CreateRepoOptions struct {
License string
Readme string
IsPrivate bool
IsUnlisted bool
IsMirror bool
AutoInit bool
}
@ -1131,6 +1137,7 @@ func CreateRepository(doer, owner *User, opts CreateRepoOptions) (_ *Repository,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
IsPrivate: opts.IsPrivate,
IsUnlisted: opts.IsUnlisted,
EnableWiki: true,
EnableIssues: true,
EnablePulls: true,
@ -1478,13 +1485,14 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
}
for i := range forkRepos {
forkRepos[i].IsPrivate = repo.IsPrivate
forkRepos[i].IsUnlisted = repo.IsUnlisted
if err = updateRepository(e, forkRepos[i], true); err != nil {
return fmt.Errorf("updateRepository[%d]: %v", forkRepos[i].ID, err)
}
}
// Change visibility of generated actions
if _, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&Action{IsPrivate: repo.IsPrivate}); err != nil {
if _, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&Action{IsPrivate: repo.IsPrivate || repo.IsUnlisted}); err != nil {
return fmt.Errorf("change action visibility of repository: %v", err)
}
}
@ -1687,6 +1695,7 @@ func GetUserRepositories(opts *UserRepoOptions) ([]*Repository, error) {
sess := x.Where("owner_id=?", opts.UserID).Desc("updated_unix")
if !opts.Private {
sess.And("is_private=?", false)
sess.And("is_unlisted=?", false)
}
if opts.Page <= 0 {
@ -1760,11 +1769,11 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (repos []*Repository, count
// this does not include other people's private repositories even if opts.UserID is an admin.
if !opts.Private && opts.UserID > 0 {
sess.Join("LEFT", "access", "access.repo_id = repo.id").
Where("repo.owner_id = ? OR access.user_id = ? OR repo.is_private = ? OR (repo.is_private = ? AND (repo.allow_public_wiki = ? OR repo.allow_public_issues = ?))", opts.UserID, opts.UserID, false, true, true, true)
Where("repo.owner_id = ? OR access.user_id = ? OR (repo.is_private = ? AND repo.is_unlisted = ?) OR (repo.is_private = ? AND (repo.allow_public_wiki = ? OR repo.allow_public_issues = ?))", opts.UserID, opts.UserID, false, false, true, true, true)
} else {
// Only return public repositories if opts.Private is not set
if !opts.Private {
sess.And("repo.is_private = ? OR (repo.is_private = ? AND (repo.allow_public_wiki = ? OR repo.allow_public_issues = ?))", false, true, true, true)
sess.And("(repo.is_private = ? AND repo.is_unlisted = ?) OR (repo.is_private = ? AND (repo.allow_public_wiki = ? OR repo.allow_public_issues = ?))", false, false, true, true, true)
}
}
if len(opts.Keyword) > 0 {
@ -2400,6 +2409,7 @@ func ForkRepository(doer, owner *User, baseRepo *Repository, name, desc string)
Description: desc,
DefaultBranch: baseRepo.DefaultBranch,
IsPrivate: baseRepo.IsPrivate,
IsUnlisted: baseRepo.IsUnlisted,
IsFork: true,
ForkID: baseRepo.ID,
}

View File

@ -26,6 +26,7 @@ type CreateRepo struct {
UserID int64 `binding:"Required"`
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Private bool
Unlisted bool
Description string `binding:"MaxSize(512)"`
AutoInit bool
Gitignores string
@ -45,6 +46,7 @@ type MigrateRepo struct {
RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
Mirror bool `json:"mirror"`
Private bool `json:"private"`
Unlisted bool `json:"unlisted"`
Description string `json:"description" binding:"MaxSize(512)"`
}
@ -88,6 +90,7 @@ type RepoSetting struct {
Interval int
MirrorAddress string
Private bool
Unlisted bool
EnablePrune bool
// Advanced settings

View File

@ -60,6 +60,7 @@ func parseBaseRepository(c *context.Context) *db.Repository {
c.Data["repo_name"] = baseRepo.Name
c.Data["description"] = baseRepo.Description
c.Data["IsPrivate"] = baseRepo.IsPrivate
c.Data["IsUnlisted"] = baseRepo.IsUnlisted
if err = baseRepo.GetOwner(); err != nil {
c.Error(err, "get owner")

View File

@ -126,6 +126,7 @@ func CreatePost(c *context.Context, f form.CreateRepo) {
License: f.License,
Readme: f.Readme,
IsPrivate: f.Private || conf.Repository.ForcePrivate,
IsUnlisted: f.Unlisted,
AutoInit: f.AutoInit,
})
if err == nil {
@ -197,6 +198,7 @@ func MigratePost(c *context.Context, f form.MigrateRepo) {
Name: f.RepoName,
Description: f.Description,
IsPrivate: f.Private || conf.Repository.ForcePrivate,
IsUnlisted: f.Unlisted,
IsMirror: f.Mirror,
RemoteAddr: remoteAddr,
})

View File

@ -87,10 +87,12 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
// Visibility of forked repository is forced sync with base repository.
if repo.IsFork {
f.Private = repo.BaseRepo.IsPrivate
f.Unlisted = repo.BaseRepo.IsUnlisted
}
visibilityChanged := repo.IsPrivate != f.Private
visibilityChanged := repo.IsPrivate != f.Private || repo.IsUnlisted != f.Unlisted
repo.IsPrivate = f.Private
repo.IsUnlisted = f.Unlisted
if err := db.UpdateRepository(repo, visibilityChanged); err != nil {
c.Error(err, "update repository")
return

View File

@ -10,6 +10,8 @@
<a class="name" href="{{AppSubURL}}/{{if .Owner}}{{.Owner.Name}}{{else if $.Org}}{{$.Org.Name}}{{else}}{{$.Owner.Name}}{{end}}/{{.Name}}">{{if $.PageIsExplore}}{{.Owner.Name}} / {{end}}{{.Name}}</a>
{{if .IsPrivate}}
<span class="text gold"><i class="octicon octicon-lock"></i></span>
{{else if .IsUnlisted}}
<span><i class="octicon octicon-eye"></i></span>
{{else if .IsFork}}
<span><i class="octicon octicon-repo-forked"></i></span>
{{else if .IsMirror}}

View File

@ -50,6 +50,13 @@
{{end}}
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui checkbox">
<input name="unlisted" type="checkbox">
<label>{{.i18n.Tr "repo.unlisted_helper" | Safe}}</label>
</div>
</div>
<div class="inline field {{if .Err_Description}}error{{end}}">
<label for="description">{{.i18n.Tr "repo.repo_desc"}}</label>
<textarea class="autosize" id="description" name="description" rows="3">{{.description}}</textarea>

View File

@ -7,9 +7,9 @@
<div class="ui huge breadcrumb">
{{if .UseCustomAvatar}}
<img class="ui mini spaced image" src="{{.RelAvatarLink}}">
<i class="{{if .IsPrivate}}mega-octicon octicon-lock{{else if .IsMirror}}mega-octicon octicon-repo-clone{{else if .IsFork}}mega-octicon octicon-repo-forked{{end}}"></i>
<i class="{{if .IsPrivate}}mega-octicon octicon-lock{{else if .IsUnlisted}}mega-octicon octicon-eye{{else if .IsMirror}}mega-octicon octicon-repo-clone{{else if .IsFork}}mega-octicon octicon-repo-forked{{end}}"></i>
{{else}}
<i class="mega-octicon octicon-{{if .IsPrivate}}lock{{else if .IsMirror}}repo-clone{{else if .IsFork}}repo-forked{{else}}repo{{end}}"></i>
<i class="mega-octicon octicon-{{if .IsPrivate}}lock{{else if .IsUnlisted}}eye{{else if .IsMirror}}repo-clone{{else if .IsFork}}repo-forked{{else}}repo{{end}}"></i>
{{end}}
<a href="{{AppSubURL}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
<div class="divider"> / </div>

View File

@ -80,6 +80,13 @@
{{end}}
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui checkbox">
<input name="unlisted" type="checkbox">
<label>{{.i18n.Tr "repo.unlisted_helper" | Safe}}</label>
</div>
</div>
<div class="inline field">
<label>{{.i18n.Tr "repo.migrate_type"}}</label>
<div class="ui checkbox">

View File

@ -49,6 +49,13 @@
<input type="checkbox" {{if .IsPrivate}}checked{{end}}>
<label>{{.i18n.Tr "repo.visiblity_helper" | Safe}}</label>
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui read-only checkbox">
<input type="checkbox" {{if .IsUnlisted}}checked{{end}}>
<label>{{.i18n.Tr "repo.unlisted_helper" | Safe}}</label>
</div>
<span class="help">{{.i18n.Tr "repo.fork_visiblity_helper"}}</span>
</div>
<div class="inline field {{if .Err_Description}}error{{end}}">

View File

@ -31,10 +31,20 @@
{{if not .Repository.IsFork}}
<div class="inline field">
<label>{{.i18n.Tr "repo.visibility"}}</label>
<div class="ui checkbox">
<input name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}}>
<label>{{.i18n.Tr "repo.visiblity_helper" | Safe}} {{if .Repository.NumForks}}<span class="text red">{{.i18n.Tr "repo.visiblity_fork_helper"}}</span>{{end}}</label>
<div class="ui segment">
<div class="field">
<div class="ui checkbox">
<input name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}}>
<label>{{.i18n.Tr "repo.visiblity_helper" | Safe}} {{if .Repository.NumForks}}<span class="text red">{{.i18n.Tr "repo.visiblity_fork_helper"}}</span>{{end}}</label>
</div>
</div>
<div class="field">
<div class="ui checkbox">
<input name="unlisted" type="checkbox" {{if .Repository.IsUnlisted}}checked{{end}}>
<label>{{.i18n.Tr "repo.unlisted_helper" | Safe}} {{if .Repository.NumForks}}<span class="text red">{{.i18n.Tr "repo.visiblity_fork_helper"}}</span>{{end}}</label>
</div>
</div>
</div>
</div>
{{end}}