Merge pull request 'Allow changing global wiki editability via the API' (#3276) from algernon/forgejo:let-the-api-control-your-wiki-editability into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3276
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
This commit is contained in:
Earl Warren 2024-04-17 05:46:10 +00:00
commit a003691c7b
5 changed files with 97 additions and 4 deletions

View file

@ -89,6 +89,7 @@ type Repository struct {
HasWiki bool `json:"has_wiki"` HasWiki bool `json:"has_wiki"`
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
WikiBranch string `json:"wiki_branch,omitempty"` WikiBranch string `json:"wiki_branch,omitempty"`
GloballyEditableWiki bool `json:"globally_editable_wiki"`
HasPullRequests bool `json:"has_pull_requests"` HasPullRequests bool `json:"has_pull_requests"`
HasProjects bool `json:"has_projects"` HasProjects bool `json:"has_projects"`
HasReleases bool `json:"has_releases"` HasReleases bool `json:"has_releases"`
@ -185,6 +186,8 @@ type EditRepoOption struct {
HasWiki *bool `json:"has_wiki,omitempty"` HasWiki *bool `json:"has_wiki,omitempty"`
// set this structure to use external wiki instead of internal // set this structure to use external wiki instead of internal
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` 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. // sets the default branch for this repository.
DefaultBranch *string `json:"default_branch,omitempty"` DefaultBranch *string `json:"default_branch,omitempty"`
// sets the branch used for this repository's wiki. // sets the branch used for this repository's wiki.

View file

@ -845,6 +845,15 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
newHasWiki = *opts.HasWiki newHasWiki = *opts.HasWiki
} }
if currHasWiki || newHasWiki { 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() { if newHasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
// Check that values are valid // Check that values are valid
if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) { if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) {
@ -867,6 +876,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
RepoID: repo.ID, RepoID: repo.ID,
Type: unit_model.TypeWiki, Type: unit_model.TypeWiki,
Config: config, Config: config,
DefaultPermissions: wikiPermissions,
}) })
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki) deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
} else if !newHasWiki { } else if !newHasWiki {
@ -876,6 +886,14 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
if !unit_model.TypeWiki.UnitGlobalDisabled() { if !unit_model.TypeWiki.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki) 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,
})
} }
} }

View file

@ -77,9 +77,13 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
} }
} }
hasWiki := false hasWiki := false
globallyEditableWiki := false
var externalWiki *api.ExternalWiki 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 hasWiki = true
if wikiUnit.DefaultPermissions == repo_model.UnitAccessModeWrite {
globallyEditableWiki = true
}
} else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalWiki); err == nil { } else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalWiki); err == nil {
hasWiki = true hasWiki = true
config := unit.ExternalWikiConfig() config := unit.ExternalWikiConfig()
@ -211,6 +215,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
InternalTracker: internalTracker, InternalTracker: internalTracker,
HasWiki: hasWiki, HasWiki: hasWiki,
WikiBranch: repo.WikiBranch, WikiBranch: repo.WikiBranch,
GloballyEditableWiki: globallyEditableWiki,
HasProjects: hasProjects, HasProjects: hasProjects,
HasReleases: hasReleases, HasReleases: hasReleases,
HasPackages: hasPackages, HasPackages: hasPackages,

View file

@ -20835,6 +20835,11 @@
"external_wiki": { "external_wiki": {
"$ref": "#/definitions/ExternalWiki" "$ref": "#/definitions/ExternalWiki"
}, },
"globally_editable_wiki": {
"description": "set the globally editable state of the wiki",
"type": "boolean",
"x-go-name": "GloballyEditableWiki"
},
"has_actions": { "has_actions": {
"description": "either `true` to enable actions unit, or `false` to disable them.", "description": "either `true` to enable actions unit, or `false` to disable them.",
"type": "boolean", "type": "boolean",
@ -23749,6 +23754,10 @@
"type": "string", "type": "string",
"x-go-name": "FullName" "x-go-name": "FullName"
}, },
"globally_editable_wiki": {
"type": "boolean",
"x-go-name": "GloballyEditableWiki"
},
"has_actions": { "has_actions": {
"type": "boolean", "type": "boolean",
"x-go-name": "HasActions" "x-go-name": "HasActions"

View file

@ -15,6 +15,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit" unit_model "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
repo_service "code.gitea.io/gitea/services/repository" repo_service "code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/tests" "code.gitea.io/gitea/tests"
@ -286,6 +287,63 @@ func TestAPIEditOtherWikiPage(t *testing.T) {
testCreateWiki(http.StatusCreated) 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) { func TestAPIListPageRevisions(t *testing.T) {
defer tests.PrepareTestEnv(t)() defer tests.PrepareTestEnv(t)()
username := "user2" username := "user2"