mirror of
https://github.com/go-gitea/gitea.git
synced 2026-03-27 17:50:13 +01:00
Require additional user confirmation for making repo private (#36959)
To align with how GitHub requires additional explicit user interaction to make a repo private, including informing them of implications on what happens if they do. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
cfd9008891
commit
943ff75233
@ -45,6 +45,14 @@ func ParseJSONError(buf []byte) (ret struct {
|
||||
return ret
|
||||
}
|
||||
|
||||
func ParseJSONRedirect(buf []byte) (ret struct {
|
||||
Redirect string `json:"redirect"`
|
||||
},
|
||||
) {
|
||||
_ = json.Unmarshal(buf, &ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
func IsNormalPageCompleted(s string) bool {
|
||||
return strings.Contains(s, `<footer class="page-footer"`) && strings.Contains(s, `</html>`)
|
||||
}
|
||||
|
||||
@ -969,7 +969,6 @@
|
||||
"repo.visibility_description": "Only the owner or the organization members if they have rights, will be able to see it.",
|
||||
"repo.visibility_helper": "Make repository private",
|
||||
"repo.visibility_helper_forced": "Your site administrator forces new repositories to be private.",
|
||||
"repo.visibility_fork_helper": "(Changing this will affect all forks.)",
|
||||
"repo.clone_helper": "Need help cloning? Visit <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">Help</a>.",
|
||||
"repo.fork_repo": "Fork Repository",
|
||||
"repo.fork_from": "Fork From",
|
||||
@ -2174,7 +2173,8 @@
|
||||
"repo.settings.transfer_abort_invalid": "You cannot cancel a non existent repository transfer.",
|
||||
"repo.settings.transfer_abort_success": "The repository transfer to %s was successfully canceled.",
|
||||
"repo.settings.transfer_desc": "Transfer this repository to a user or to an organization for which you have administrator rights.",
|
||||
"repo.settings.transfer_form_title": "Enter the repository name as confirmation:",
|
||||
"repo.settings.enter_repo_name_to_confirm": "Enter the repository name as confirmation:",
|
||||
"repo.settings.enter_repo_full_name_to_confirm": "Enter the full repository name (owner/name) as confirmation:",
|
||||
"repo.settings.transfer_in_progress": "There is currently an ongoing transfer. Please cancel it if you would like to transfer this repository to another user.",
|
||||
"repo.settings.transfer_notices_1": "- You will lose access to the repository if you transfer it to an individual user.",
|
||||
"repo.settings.transfer_notices_2": "- You will keep access to the repository if you transfer it to an organization that you (co-)own.",
|
||||
@ -2477,7 +2477,10 @@
|
||||
"repo.settings.visibility.private.text": "Changing the visibility to private will make the repo visible only to allowed members and may remove the relationship between it and existing forks, watchers, and stars.",
|
||||
"repo.settings.visibility.private.bullet_title": "<strong>Changing the visibility to private will:</strong>",
|
||||
"repo.settings.visibility.private.bullet_one": "Make the repo visible only to allowed members.",
|
||||
"repo.settings.visibility.private.bullet_two": "May remove the relationship between it and <strong>forks</strong>, <strong>watchers</strong>, and <strong>stars</strong>.",
|
||||
"repo.settings.visibility.private.bullet_two": "Apply the visibility to its forks, and remove the <strong>watchers</strong> and <strong>stars</strong>.",
|
||||
"repo.settings.visibility.private.stats_stars": "This repository has <strong>%d</strong> star(s) that may be lost.",
|
||||
"repo.settings.visibility.private.stats_watchers": "This repository has <strong>%d</strong> watcher(s) that may be lost.",
|
||||
"repo.settings.visibility.private.stats_forks": "This repository has <strong>%d</strong> fork(s) that are associated.",
|
||||
"repo.settings.visibility.public.button": "Make Public",
|
||||
"repo.settings.visibility.public.text": "Changing the visibility to public will make the repo visible to anyone.",
|
||||
"repo.settings.visibility.public.bullet_title": "<strong>Changing the visibility to public will:</strong>",
|
||||
|
||||
@ -999,39 +999,33 @@ func handleSettingsPostUnarchive(ctx *context.Context) {
|
||||
}
|
||||
|
||||
func handleSettingsPostVisibility(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.RepoSettingForm)
|
||||
repo := ctx.Repo.Repository
|
||||
if repo.IsFork {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.visibility.fork_error"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
|
||||
ctx.JSONError(ctx.Tr("repo.settings.visibility.fork_error"))
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
private := ctx.FormOptionalBool("private").ValueOrDefault(true) // default to true for privacy & safety
|
||||
|
||||
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
|
||||
if setting.Repository.ForcePrivate && repo.IsPrivate && !ctx.Doer.IsAdmin {
|
||||
ctx.RenderWithErrDeprecated(ctx.Tr("form.repository_force_private"), tplSettingsOptions, form)
|
||||
if !private && setting.Repository.ForcePrivate && !ctx.Doer.IsAdmin {
|
||||
ctx.JSONError(ctx.Tr("form.repository_force_private"))
|
||||
return
|
||||
}
|
||||
if private && repo.FullName() != ctx.FormString("confirm_repo_name") {
|
||||
ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name"))
|
||||
return
|
||||
}
|
||||
|
||||
if repo.IsPrivate {
|
||||
err = repo_service.MakeRepoPublic(ctx, repo)
|
||||
} else {
|
||||
err = repo_service.MakeRepoPrivate(ctx, repo)
|
||||
}
|
||||
|
||||
err := repo_service.MakeRepoPrivate(ctx, repo, private)
|
||||
if err != nil {
|
||||
log.Error("Tried to change the visibility of the repo: %s", err)
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.visibility.error"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
|
||||
ctx.JSONError(ctx.Tr("repo.settings.visibility.error"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.visibility.success"))
|
||||
|
||||
log.Trace("Repository visibility changed: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings")
|
||||
}
|
||||
|
||||
func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.RepoSettingForm) {
|
||||
|
||||
@ -122,9 +122,9 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili
|
||||
})
|
||||
}
|
||||
|
||||
func MakeRepoPublic(ctx context.Context, repo *repo_model.Repository) (err error) {
|
||||
func MakeRepoPrivate(ctx context.Context, repo *repo_model.Repository, private bool) (err error) {
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
repo.IsPrivate = false
|
||||
repo.IsPrivate = private
|
||||
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -144,15 +144,33 @@ func MakeRepoPublic(ctx context.Context, repo *repo_model.Repository) (err error
|
||||
return err
|
||||
}
|
||||
|
||||
forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getRepositoriesByForkID: %w", err)
|
||||
// If repo has become private, we need to set its actions to private, and clear stars and watches.
|
||||
if private {
|
||||
_, err = db.GetEngine(ctx).
|
||||
Where("repo_id = ?", repo.ID).Cols("is_private").Update(&activities_model.Action{IsPrivate: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = repo_model.ClearRepoStars(ctx, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = repo_model.ClearRepoWatches(ctx, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if repo.Owner.Visibility != structs.VisibleTypePrivate {
|
||||
for i := range forkRepos {
|
||||
if err = MakeRepoPublic(ctx, forkRepos[i]); err != nil {
|
||||
return fmt.Errorf("MakeRepoPublic[%d]: %w", forkRepos[i].ID, err)
|
||||
shouldUpdateForks := private
|
||||
if !private && repo.Owner.Visibility != structs.VisibleTypePrivate {
|
||||
shouldUpdateForks = true
|
||||
}
|
||||
if shouldUpdateForks {
|
||||
forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getRepositoriesByForkID: %w", err)
|
||||
}
|
||||
for _, forkRepo := range forkRepos {
|
||||
if err = MakeRepoPrivate(ctx, forkRepo, private); err != nil {
|
||||
return fmt.Errorf("MakeRepoPrivate[%d]: %w", forkRepo.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -160,63 +178,6 @@ func MakeRepoPublic(ctx context.Context, repo *repo_model.Repository) (err error
|
||||
// If visibility is changed, we need to update the issue indexer.
|
||||
// Since the data in the issue indexer have field to indicate if the repo is public or not.
|
||||
issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func MakeRepoPrivate(ctx context.Context, repo *repo_model.Repository) (err error) {
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
repo.IsPrivate = true
|
||||
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return fmt.Errorf("LoadOwner: %w", err)
|
||||
}
|
||||
if repo.Owner.IsOrganization() {
|
||||
// Organization repository need to recalculate access table when visibility is changed.
|
||||
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
|
||||
return fmt.Errorf("recalculateTeamAccesses: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// If repo has become private, we need to set its actions to private.
|
||||
_, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).Cols("is_private").Update(&activities_model.Action{
|
||||
IsPrivate: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = repo_model.ClearRepoStars(ctx, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = repo_model.ClearRepoWatches(ctx, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create/Remove git-daemon-export-ok for git-daemon...
|
||||
if err := CheckDaemonExportOK(ctx, repo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getRepositoriesByForkID: %w", err)
|
||||
}
|
||||
for i := range forkRepos {
|
||||
if err = MakeRepoPrivate(ctx, forkRepos[i]); err != nil {
|
||||
return fmt.Errorf("MakeRepoPrivate[%d]: %w", forkRepos[i].ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// If visibility is changed, we need to update the issue indexer.
|
||||
// Since the data in the issue indexer have field to indicate if the repo is public or not.
|
||||
issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@ -74,13 +74,13 @@ func TestMakeRepoPrivateClearsWatches(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
repo.IsPrivate = false
|
||||
assert.False(t, repo.IsPrivate)
|
||||
|
||||
watchers, err := repo_model.GetRepoWatchersIDs(t.Context(), repo.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, watchers)
|
||||
|
||||
assert.NoError(t, MakeRepoPrivate(t.Context(), repo))
|
||||
assert.NoError(t, MakeRepoPrivate(t.Context(), repo, true))
|
||||
|
||||
watchers, err = repo_model.GetRepoWatchersIDs(t.Context(), repo.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@ -67,13 +67,13 @@
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||
{{ctx.Locale.Tr "repo.settings.enter_repo_name_to_confirm"}}
|
||||
<span class="tw-text-red">{{.Repository.Name}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="required field">
|
||||
<label for="repo_name_to_delete">{{ctx.Locale.Tr "repo.repo_name"}}</label>
|
||||
<input id="repo_name_to_delete" name="repo_name" required>
|
||||
<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
|
||||
<input name="repo_name" required>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
|
||||
@ -887,21 +887,8 @@
|
||||
</div>
|
||||
<form class="ui form" action="{{.Link}}" method="post">
|
||||
<input type="hidden" name="action" value="convert">
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||
<span class="tw-text-red">{{.Repository.Name}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="required field">
|
||||
<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
|
||||
<input name="repo_name" required maxlength="100">
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
|
||||
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.convert_confirm"}}</button>
|
||||
</div>
|
||||
{{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}}
|
||||
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.settings.convert_confirm"))}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -917,21 +904,8 @@
|
||||
</div>
|
||||
<form class="ui form" action="{{.Link}}" method="post">
|
||||
<input type="hidden" name="action" value="convert_fork">
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||
<span class="tw-text-red">{{.Repository.Name}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="required field">
|
||||
<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
|
||||
<input name="repo_name" required>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
|
||||
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.convert_fork_confirm"}}</button>
|
||||
</div>
|
||||
{{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}}
|
||||
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.settings.convert_fork_confirm"))}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -949,25 +923,13 @@
|
||||
</div>
|
||||
<form class="ui form" action="{{.Link}}" method="post">
|
||||
<input type="hidden" name="action" value="transfer">
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||
<span class="tw-text-red">{{.Repository.Name}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="required field">
|
||||
<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
|
||||
<input name="repo_name" required>
|
||||
</div>
|
||||
{{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}}
|
||||
<div class="required field">
|
||||
<label for="new_owner_name">{{ctx.Locale.Tr "repo.settings.transfer_owner"}}</label>
|
||||
<input id="new_owner_name" name="new_owner_name" required>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
|
||||
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.transfer_perform"}}</button>
|
||||
</div>
|
||||
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.settings.transfer_perform"))}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -986,49 +948,57 @@
|
||||
</div>
|
||||
<form class="ui form" action="{{.Link}}" method="post">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||
<span class="tw-text-red">{{.Repository.Name}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="required field">
|
||||
<label for="repo_name_to_delete">{{ctx.Locale.Tr "repo.repo_name"}}</label>
|
||||
<input id="repo_name_to_delete" name="repo_name" required>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
|
||||
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.confirm_delete"}}</button>
|
||||
</div>
|
||||
{{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}}
|
||||
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.settings.confirm_delete"))}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if not .Repository.IsFork}}
|
||||
<div class="ui g-modal-confirm modal" id="visibility-repo-modal">
|
||||
<div class="ui small modal" id="visibility-repo-modal">
|
||||
<div class="header">
|
||||
{{ctx.Locale.Tr "repo.visibility"}}
|
||||
</div>
|
||||
<div class="content">
|
||||
{{if .Repository.IsPrivate}}
|
||||
<p>{{ctx.Locale.Tr "repo.settings.visibility.public.bullet_title"}}</p>
|
||||
<ul>
|
||||
<li>{{ctx.Locale.Tr "repo.settings.visibility.public.bullet_one"}}</li>
|
||||
</ul>
|
||||
{{else}}
|
||||
<p>{{ctx.Locale.Tr "repo.settings.visibility.private.bullet_title"}}</p>
|
||||
<ul>
|
||||
<li>{{ctx.Locale.Tr "repo.settings.visibility.private.bullet_one"}}</li>
|
||||
<li>{{ctx.Locale.Tr "repo.settings.visibility.private.bullet_two"}}{{if .Repository.NumForks}}<span class="tw-text-red">{{ctx.Locale.Tr "repo.visibility_fork_helper"}}</span>{{end}}</li>
|
||||
{{if .Repository.IsPrivate}}
|
||||
<p>{{ctx.Locale.Tr "repo.settings.visibility.public.bullet_title"}}</p>
|
||||
<ul>
|
||||
<li>{{ctx.Locale.Tr "repo.settings.visibility.public.bullet_one"}}</li>
|
||||
</ul>
|
||||
{{else}}
|
||||
<p>{{ctx.Locale.Tr "repo.settings.visibility.private.bullet_title"}}</p>
|
||||
<ul>
|
||||
<li>{{ctx.Locale.Tr "repo.settings.visibility.private.bullet_one"}}</li>
|
||||
<li>
|
||||
{{ctx.Locale.Tr "repo.settings.visibility.private.bullet_two"}}
|
||||
</li>
|
||||
{{if or .Repository.NumStars .Repository.NumWatches .Repository.NumForks}}
|
||||
<ul class="tw-my-0 tw-pl-4">
|
||||
{{if .Repository.NumStars}}<li>{{ctx.Locale.Tr "repo.settings.visibility.private.stats_stars" .Repository.NumStars}}</li>{{end}}
|
||||
{{if .Repository.NumWatches}}<li>{{ctx.Locale.Tr "repo.settings.visibility.private.stats_watchers" .Repository.NumWatches}}</li>{{end}}
|
||||
{{if .Repository.NumForks}}<li>{{ctx.Locale.Tr "repo.settings.visibility.private.stats_forks" .Repository.NumForks}}</li>{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
<form class="ui form tw-mt-5 form-fetch-action" action="{{.Link}}" method="post">
|
||||
<input type="hidden" name="action" value="visibility">
|
||||
<input type="hidden" name="private" value="{{not .Repository.IsPrivate}}">
|
||||
{{if not .Repository.IsPrivate}}
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ctx.Locale.Tr "repo.settings.enter_repo_full_name_to_confirm"}}
|
||||
<span class="tw-text-red">{{.Repository.FullName}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="required field">
|
||||
<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
|
||||
<input name="confirm_repo_name" required maxlength="200">
|
||||
</div>
|
||||
{{end}}
|
||||
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (Iif .Repository.IsPrivate (ctx.Locale.Tr "repo.settings.visibility.public.button") (ctx.Locale.Tr "repo.settings.visibility.private.button")))}}
|
||||
</form>
|
||||
</div>
|
||||
<form action="{{.Link}}" method="post">
|
||||
<input type="hidden" name="action" value="visibility">
|
||||
<input type="hidden" name="repo_id" value="{{.Repository.ID}}">
|
||||
{{template "base/modal_actions_confirm" .}}
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@ -1044,21 +1014,8 @@
|
||||
</div>
|
||||
<form class="ui form" action="{{.Link}}" method="post">
|
||||
<input type="hidden" name="action" value="delete-wiki">
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||
<span class="tw-text-red">{{.Repository.Name}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="required field">
|
||||
<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
|
||||
<input name="repo_name" required>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
|
||||
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.confirm_wiki_delete"}}</button>
|
||||
</div>
|
||||
{{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}}
|
||||
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.settings.confirm_wiki_delete"))}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
10
templates/repo/settings/repo_name_confirm_fields.tmpl
Normal file
10
templates/repo/settings/repo_name_confirm_fields.tmpl
Normal file
@ -0,0 +1,10 @@
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ctx.Locale.Tr "repo.settings.enter_repo_name_to_confirm"}}
|
||||
<span class="tw-text-red">{{.RepoName}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="required field">
|
||||
<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
|
||||
<input name="repo_name" required maxlength="100">
|
||||
</div>
|
||||
59
tests/integration/repo_visibility_test.go
Normal file
59
tests/integration/repo_visibility_test.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRepositoryVisibilityChange(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
t.Run("MakePrivateRequiresCorrectName", func(t *testing.T) {
|
||||
// Wrong name should be rejected with a JSON error
|
||||
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||
"action": "visibility",
|
||||
"private": "true",
|
||||
"confirm_repo_name": "wrong-name",
|
||||
})
|
||||
resp := session.MakeRequest(t, req, http.StatusBadRequest)
|
||||
assert.NotEmpty(t, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
assert.False(t, repo1.IsPrivate)
|
||||
|
||||
// Correct full name (owner/repo) should succeed with a JSON redirect
|
||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||
"action": "visibility",
|
||||
"private": "true",
|
||||
"confirm_repo_name": "user2/repo1",
|
||||
})
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEmpty(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect)
|
||||
|
||||
repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
assert.True(t, repo1.IsPrivate)
|
||||
})
|
||||
|
||||
t.Run("MakePublicDoesNotRequireName", func(t *testing.T) {
|
||||
req := NewRequestWithValues(t, "POST", "/user2/repo2/settings", map[string]string{
|
||||
"action": "visibility",
|
||||
"private": "false",
|
||||
})
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEmpty(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect)
|
||||
|
||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
assert.False(t, repo2.IsPrivate)
|
||||
})
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user