From df8e58c5cb09b95037151c8484630e4872041771 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 16 Apr 2024 22:51:36 +0200 Subject: [PATCH] Allow changing global wiki editability via the API The global wiki editability can be set via the web UI, this patch makes it possible to set the same thing via the API too. This is accomplished by adjusting the GET and PATCH handlers of the `/api/v1/repos/{owner}/{repo}` route. The first will include the property when checking the repo's settings, the second allows a repo admin to change the setting too. Signed-off-by: Gergely Nagy --- modules/structs/repo.go | 3 ++ routers/api/v1/repo/repo.go | 24 +++++++++++-- services/convert/repository.go | 7 +++- templates/swagger/v1_json.tmpl | 9 +++++ tests/integration/api_wiki_test.go | 58 ++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 4 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index f6cc9803a..1158160b5 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -89,6 +89,7 @@ type Repository struct { HasWiki bool `json:"has_wiki"` ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` WikiBranch string `json:"wiki_branch,omitempty"` + GloballyEditableWiki bool `json:"globally_editable_wiki"` HasPullRequests bool `json:"has_pull_requests"` HasProjects bool `json:"has_projects"` HasReleases bool `json:"has_releases"` @@ -185,6 +186,8 @@ type EditRepoOption struct { HasWiki *bool `json:"has_wiki,omitempty"` // set this structure to use external wiki instead of internal ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` + // set the globally editable state of the wiki + GloballyEditableWiki *bool `json:"globally_editable_wiki,omitempty"` // sets the default branch for this repository. DefaultBranch *string `json:"default_branch,omitempty"` // sets the branch used for this repository's wiki. diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 562c3eb64..6692633fa 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -845,6 +845,15 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { newHasWiki = *opts.HasWiki } if currHasWiki || newHasWiki { + wikiPermissions := repo.MustGetUnit(ctx, unit_model.TypeWiki).DefaultPermissions + if opts.GloballyEditableWiki != nil { + if *opts.GloballyEditableWiki { + wikiPermissions = repo_model.UnitAccessModeWrite + } else { + wikiPermissions = repo_model.UnitAccessModeRead + } + } + if newHasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() { // Check that values are valid if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) { @@ -864,9 +873,10 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { } else if newHasWiki && opts.ExternalWiki == nil && !unit_model.TypeWiki.UnitGlobalDisabled() { config := &repo_model.UnitConfig{} units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeWiki, - Config: config, + RepoID: repo.ID, + Type: unit_model.TypeWiki, + Config: config, + DefaultPermissions: wikiPermissions, }) deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki) } else if !newHasWiki { @@ -876,6 +886,14 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if !unit_model.TypeWiki.UnitGlobalDisabled() { deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki) } + } else if *opts.GloballyEditableWiki { + config := &repo_model.UnitConfig{} + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeWiki, + Config: config, + DefaultPermissions: wikiPermissions, + }) } } diff --git a/services/convert/repository.go b/services/convert/repository.go index 466d19d56..fcd0824e4 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -77,9 +77,13 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR } } hasWiki := false + globallyEditableWiki := false var externalWiki *api.ExternalWiki - if _, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil { + if wikiUnit, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil { hasWiki = true + if wikiUnit.DefaultPermissions == repo_model.UnitAccessModeWrite { + globallyEditableWiki = true + } } else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalWiki); err == nil { hasWiki = true config := unit.ExternalWikiConfig() @@ -211,6 +215,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR InternalTracker: internalTracker, HasWiki: hasWiki, WikiBranch: repo.WikiBranch, + GloballyEditableWiki: globallyEditableWiki, HasProjects: hasProjects, HasReleases: hasReleases, HasPackages: hasPackages, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 4e4971dfe..2bec62694 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -20835,6 +20835,11 @@ "external_wiki": { "$ref": "#/definitions/ExternalWiki" }, + "globally_editable_wiki": { + "description": "set the globally editable state of the wiki", + "type": "boolean", + "x-go-name": "GloballyEditableWiki" + }, "has_actions": { "description": "either `true` to enable actions unit, or `false` to disable them.", "type": "boolean", @@ -23749,6 +23754,10 @@ "type": "string", "x-go-name": "FullName" }, + "globally_editable_wiki": { + "type": "boolean", + "x-go-name": "GloballyEditableWiki" + }, "has_actions": { "type": "boolean", "x-go-name": "HasActions" diff --git a/tests/integration/api_wiki_test.go b/tests/integration/api_wiki_test.go index c61b4a061..2b7161611 100644 --- a/tests/integration/api_wiki_test.go +++ b/tests/integration/api_wiki_test.go @@ -15,6 +15,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" @@ -286,6 +287,63 @@ func TestAPIEditOtherWikiPage(t *testing.T) { testCreateWiki(http.StatusCreated) } +func TestAPISetWikiGlobalEditability(t *testing.T) { + defer tests.PrepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + // Create a new repository for testing purposes + repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{ + unit_model.TypeCode, + unit_model.TypeWiki, + }, nil, nil) + defer f() + urlStr := fmt.Sprintf("/api/v1/repos/%s", repo.FullName()) + + assertGlobalEditability := func(t *testing.T, editability bool) { + t.Helper() + + req := NewRequest(t, "GET", urlStr) + resp := MakeRequest(t, req, http.StatusOK) + + var opts api.Repository + DecodeJSON(t, resp, &opts) + + assert.Equal(t, opts.GloballyEditableWiki, editability) + } + + t.Run("api includes GloballyEditableWiki", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + assertGlobalEditability(t, false) + }) + + t.Run("api can turn on GloballyEditableWiki", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + globallyEditable := true + req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditRepoOption{ + GloballyEditableWiki: &globallyEditable, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusOK) + + assertGlobalEditability(t, true) + }) + + t.Run("disabling the wiki disables GloballyEditableWiki", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + hasWiki := false + req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditRepoOption{ + HasWiki: &hasWiki, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusOK) + + assertGlobalEditability(t, false) + }) +} + func TestAPIListPageRevisions(t *testing.T) { defer tests.PrepareTestEnv(t)() username := "user2"