mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-03 00:01:21 -04:00
Compare commits
9 Commits
3958605259
...
f89cbb9e72
Author | SHA1 | Date | |
---|---|---|---|
|
f89cbb9e72 | ||
|
8cbec63cc7 | ||
|
6455c8202b | ||
|
97fc87af89 | ||
|
6fe5c4c4d9 | ||
|
59e91b9585 | ||
|
2785ae5c25 | ||
|
475f40da1f | ||
|
ef7a98b505 |
@ -715,7 +715,8 @@ func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Comment) loadReview(ctx context.Context) (err error) {
|
// LoadReview loads the associated review
|
||||||
|
func (c *Comment) LoadReview(ctx context.Context) (err error) {
|
||||||
if c.ReviewID == 0 {
|
if c.ReviewID == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -732,11 +733,6 @@ func (c *Comment) loadReview(ctx context.Context) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadReview loads the associated review
|
|
||||||
func (c *Comment) LoadReview(ctx context.Context) error {
|
|
||||||
return c.loadReview(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiffSide returns "previous" if Comment.Line is a LOC of the previous changes and "proposed" if it is a LOC of the proposed changes.
|
// DiffSide returns "previous" if Comment.Line is a LOC of the previous changes and "proposed" if it is a LOC of the proposed changes.
|
||||||
func (c *Comment) DiffSide() string {
|
func (c *Comment) DiffSide() string {
|
||||||
if c.Line < 0 {
|
if c.Line < 0 {
|
||||||
@ -856,7 +852,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
|
|||||||
}
|
}
|
||||||
if comment.ReviewID != 0 {
|
if comment.ReviewID != 0 {
|
||||||
if comment.Review == nil {
|
if comment.Review == nil {
|
||||||
if err := comment.loadReview(ctx); err != nil {
|
if err := comment.LoadReview(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -955,6 +955,16 @@ func UpdateUserCols(ctx context.Context, u *User, cols ...string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserColsWithNoAutotime update user according special columns
|
||||||
|
func UpdateUserColsWithNoAutotime(ctx context.Context, u *User, cols ...string) error {
|
||||||
|
if err := ValidateUser(u, cols...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := db.GetEngine(ctx).ID(u.ID).Cols(cols...).NoAutoTime().Update(u)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// GetInactiveUsers gets all inactive users
|
// GetInactiveUsers gets all inactive users
|
||||||
func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, error) {
|
func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, error) {
|
||||||
cond := builder.And(
|
cond := builder.And(
|
||||||
|
@ -97,10 +97,14 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
|
|||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := issue.Repo.LoadOwner(ctx); err != nil {
|
||||||
|
return nil, false, fmt.Errorf("issue.Repo.LoadOwner: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return &internal.IndexerData{
|
return &internal.IndexerData{
|
||||||
ID: issue.ID,
|
ID: issue.ID,
|
||||||
RepoID: issue.RepoID,
|
RepoID: issue.RepoID,
|
||||||
IsPublic: !issue.Repo.IsPrivate,
|
IsPublic: !issue.Repo.IsPrivate && issue.Repo.Owner.Visibility.IsPublic(),
|
||||||
Title: issue.Title,
|
Title: issue.Title,
|
||||||
Content: issue.Content,
|
Content: issue.Content,
|
||||||
Comments: comments,
|
Comments: comments,
|
||||||
|
@ -116,14 +116,17 @@ type ContentsExtResponse struct {
|
|||||||
|
|
||||||
// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content
|
// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content
|
||||||
type ContentsResponse struct {
|
type ContentsResponse struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
SHA string `json:"sha"`
|
SHA string `json:"sha"`
|
||||||
LastCommitSHA string `json:"last_commit_sha"`
|
|
||||||
|
LastCommitSHA *string `json:"last_commit_sha,omitempty"`
|
||||||
// swagger:strfmt date-time
|
// swagger:strfmt date-time
|
||||||
LastCommitterDate time.Time `json:"last_committer_date"`
|
LastCommitterDate *time.Time `json:"last_committer_date,omitempty"`
|
||||||
// swagger:strfmt date-time
|
// swagger:strfmt date-time
|
||||||
LastAuthorDate time.Time `json:"last_author_date"`
|
LastAuthorDate *time.Time `json:"last_author_date,omitempty"`
|
||||||
|
LastCommitMessage *string `json:"last_commit_message,omitempty"`
|
||||||
|
|
||||||
// `type` will be `file`, `dir`, `symlink`, or `submodule`
|
// `type` will be `file`, `dir`, `symlink`, or `submodule`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Size int64 `json:"size"`
|
Size int64 `json:"size"`
|
||||||
@ -141,8 +144,8 @@ type ContentsResponse struct {
|
|||||||
SubmoduleGitURL *string `json:"submodule_git_url"`
|
SubmoduleGitURL *string `json:"submodule_git_url"`
|
||||||
Links *FileLinksResponse `json:"_links"`
|
Links *FileLinksResponse `json:"_links"`
|
||||||
|
|
||||||
LfsOid *string `json:"lfs_oid"`
|
LfsOid *string `json:"lfs_oid,omitempty"`
|
||||||
LfsSize *int64 `json:"lfs_size"`
|
LfsSize *int64 `json:"lfs_size,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileCommitResponse contains information generated from a Git commit for a repo's file.
|
// FileCommitResponse contains information generated from a Git commit for a repo's file.
|
||||||
|
@ -39,6 +39,10 @@ func (vt VisibleType) IsPrivate() bool {
|
|||||||
return vt == VisibleTypePrivate
|
return vt == VisibleTypePrivate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vt VisibleType) IsValid() bool {
|
||||||
|
return vt.String() != ""
|
||||||
|
}
|
||||||
|
|
||||||
// VisibilityString provides the mode string of the visibility type (public, limited, private)
|
// VisibilityString provides the mode string of the visibility type (public, limited, private)
|
||||||
func (vt VisibleType) String() string {
|
func (vt VisibleType) String() string {
|
||||||
for k, v := range VisibilityModes {
|
for k, v := range VisibilityModes {
|
||||||
@ -56,3 +60,10 @@ func ExtractKeysFromMapString(in map[string]VisibleType) (keys []string) {
|
|||||||
}
|
}
|
||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConvertStringToVisibleType(s string) VisibleType {
|
||||||
|
if vt, ok := VisibilityModes[s]; ok {
|
||||||
|
return vt
|
||||||
|
}
|
||||||
|
return VisibleType(-1) // Invalid type
|
||||||
|
}
|
||||||
|
@ -2839,6 +2839,14 @@ settings.location = Location
|
|||||||
settings.permission = Permissions
|
settings.permission = Permissions
|
||||||
settings.repoadminchangeteam = Repository admin can add and remove access for teams
|
settings.repoadminchangeteam = Repository admin can add and remove access for teams
|
||||||
settings.visibility = Visibility
|
settings.visibility = Visibility
|
||||||
|
settings.change_visibility = Change Visibility
|
||||||
|
settings.invalid_visibility = The new visibility is not valid.
|
||||||
|
settings.change_visibility_notices_1 = This operation <strong>CANNOT</strong> be undone.
|
||||||
|
settings.change_visibility_notices_2 = Some users will not visit the repositories of the orgniazation.
|
||||||
|
settings.change_visibility_no_change = The visibility is no change.
|
||||||
|
settings.change_visibility_failed = Change visibility of %s failed because of internal error
|
||||||
|
settings.change_visibility_success = Organization %s visibility has been changed successfully.
|
||||||
|
settings.visibility_desc = Changing the organisation visibility
|
||||||
settings.visibility.public = Public
|
settings.visibility.public = Public
|
||||||
settings.visibility.limited = Limited (Visible to authenticated users only)
|
settings.visibility.limited = Limited (Visible to authenticated users only)
|
||||||
settings.visibility.limited_shortname = Limited
|
settings.visibility.limited_shortname = Limited
|
||||||
|
@ -1969,6 +1969,7 @@ pulls.cmd_instruction_checkout_title=Basculer
|
|||||||
pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications.
|
pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications.
|
||||||
pulls.cmd_instruction_merge_title=Fusionner
|
pulls.cmd_instruction_merge_title=Fusionner
|
||||||
pulls.cmd_instruction_merge_desc=Fusionner les modifications et mettre à jour sur Gitea.
|
pulls.cmd_instruction_merge_desc=Fusionner les modifications et mettre à jour sur Gitea.
|
||||||
|
pulls.cmd_instruction_merge_warning=Attention : cette opération ne peut pas fusionner la demande d’ajout car la « détection automatique de fusion manuelle » n’a pas été activée
|
||||||
pulls.clear_merge_message=Effacer le message de fusion
|
pulls.clear_merge_message=Effacer le message de fusion
|
||||||
pulls.clear_merge_message_hint=Effacer le message de fusion ne supprimera que le message de la révision, mais pas les pieds de révision générés tels que "Co-Authored-By:".
|
pulls.clear_merge_message_hint=Effacer le message de fusion ne supprimera que le message de la révision, mais pas les pieds de révision générés tels que "Co-Authored-By:".
|
||||||
|
|
||||||
@ -2768,6 +2769,8 @@ branch.new_branch_from=`Créer une nouvelle branche à partir de "%s"`
|
|||||||
branch.renamed=La branche %s à été renommée en %s.
|
branch.renamed=La branche %s à été renommée en %s.
|
||||||
branch.rename_default_or_protected_branch_error=Seuls les administrateurs peuvent renommer les branches par défaut ou protégées.
|
branch.rename_default_or_protected_branch_error=Seuls les administrateurs peuvent renommer les branches par défaut ou protégées.
|
||||||
branch.rename_protected_branch_failed=Cette branche est protégée par des règles de protection basées sur des globs.
|
branch.rename_protected_branch_failed=Cette branche est protégée par des règles de protection basées sur des globs.
|
||||||
|
branch.commits_divergence_from=Divergence de révisions : %[1]d en retard et %[2]d en avance sur %[3]s
|
||||||
|
branch.commits_no_divergence=Identique à la branche %[1]s
|
||||||
|
|
||||||
tag.create_tag=Créer l'étiquette %s
|
tag.create_tag=Créer l'étiquette %s
|
||||||
tag.create_tag_operation=Créer une étiquette
|
tag.create_tag_operation=Créer une étiquette
|
||||||
|
@ -2769,6 +2769,8 @@ branch.new_branch_from=`Cruthaigh brainse nua ó "%s"`
|
|||||||
branch.renamed=Ainmníodh brainse %s go %s.
|
branch.renamed=Ainmníodh brainse %s go %s.
|
||||||
branch.rename_default_or_protected_branch_error=Ní féidir ach le riarthóirí brainsí réamhshocraithe nó cosanta a athainmniú.
|
branch.rename_default_or_protected_branch_error=Ní féidir ach le riarthóirí brainsí réamhshocraithe nó cosanta a athainmniú.
|
||||||
branch.rename_protected_branch_failed=Tá an brainse seo faoi chosaint ag rialacha cosanta domhanda.
|
branch.rename_protected_branch_failed=Tá an brainse seo faoi chosaint ag rialacha cosanta domhanda.
|
||||||
|
branch.commits_divergence_from=Déanann sé dialltacht a thiomnú: %[1]d taobh thiar agus %[2]d chun tosaigh ar %[3]s
|
||||||
|
branch.commits_no_divergence=Mar an gcéanna le brainse %[1]s
|
||||||
|
|
||||||
tag.create_tag=Cruthaigh clib %s
|
tag.create_tag=Cruthaigh clib %s
|
||||||
tag.create_tag_operation=Cruthaigh clib
|
tag.create_tag_operation=Cruthaigh clib
|
||||||
|
@ -2769,6 +2769,8 @@ branch.new_branch_from=`Criar um novo ramo a partir do ramo "%s"`
|
|||||||
branch.renamed=O ramo %s foi renomeado para %s.
|
branch.renamed=O ramo %s foi renomeado para %s.
|
||||||
branch.rename_default_or_protected_branch_error=Só os administradores é que podem renomear o ramo principal ou ramos protegidos.
|
branch.rename_default_or_protected_branch_error=Só os administradores é que podem renomear o ramo principal ou ramos protegidos.
|
||||||
branch.rename_protected_branch_failed=Este ramo está protegido por regras de salvaguarda baseadas em padrões glob.
|
branch.rename_protected_branch_failed=Este ramo está protegido por regras de salvaguarda baseadas em padrões glob.
|
||||||
|
branch.commits_divergence_from=Divergência nos cometimentos: %[1]d atrás e %[2]d à frente de %[3]s
|
||||||
|
branch.commits_no_divergence=Idêntico ao ramo %[1]s
|
||||||
|
|
||||||
tag.create_tag=Criar etiqueta %s
|
tag.create_tag=Criar etiqueta %s
|
||||||
tag.create_tag_operation=Criar etiqueta
|
tag.create_tag_operation=Criar etiqueta
|
||||||
|
@ -812,7 +812,8 @@ func GetContentsExt(ctx *context.APIContext) {
|
|||||||
// required: true
|
// required: true
|
||||||
// - name: filepath
|
// - name: filepath
|
||||||
// in: path
|
// in: path
|
||||||
// description: path of the dir, file, symlink or submodule in the repo
|
// description: path of the dir, file, symlink or submodule in the repo. Swagger requires path parameter to be "required",
|
||||||
|
// you can leave it empty or pass a single dot (".") to get the root directory.
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: ref
|
// - name: ref
|
||||||
@ -823,7 +824,8 @@ func GetContentsExt(ctx *context.APIContext) {
|
|||||||
// - name: includes
|
// - name: includes
|
||||||
// in: query
|
// in: query
|
||||||
// description: By default this API's response only contains file's metadata. Use comma-separated "includes" options to retrieve more fields.
|
// description: By default this API's response only contains file's metadata. Use comma-separated "includes" options to retrieve more fields.
|
||||||
// Option "file_content" will try to retrieve the file content, option "lfs_metadata" will try to retrieve LFS metadata.
|
// Option "file_content" will try to retrieve the file content, "lfs_metadata" will try to retrieve LFS metadata,
|
||||||
|
// "commit_metadata" will try to retrieve commit metadata, and "commit_message" will try to retrieve commit message.
|
||||||
// type: string
|
// type: string
|
||||||
// required: false
|
// required: false
|
||||||
// responses:
|
// responses:
|
||||||
@ -832,6 +834,9 @@ func GetContentsExt(ctx *context.APIContext) {
|
|||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
if treePath := ctx.PathParam("*"); treePath == "." || treePath == "/" {
|
||||||
|
ctx.SetPathParam("*", "") // workaround for swagger, it requires path parameter to be "required", but we need to list root directory
|
||||||
|
}
|
||||||
opts := files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*")}
|
opts := files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*")}
|
||||||
for includeOpt := range strings.SplitSeq(ctx.FormString("includes"), ",") {
|
for includeOpt := range strings.SplitSeq(ctx.FormString("includes"), ",") {
|
||||||
if includeOpt == "" {
|
if includeOpt == "" {
|
||||||
@ -842,6 +847,10 @@ func GetContentsExt(ctx *context.APIContext) {
|
|||||||
opts.IncludeSingleFileContent = true
|
opts.IncludeSingleFileContent = true
|
||||||
case "lfs_metadata":
|
case "lfs_metadata":
|
||||||
opts.IncludeLfsMetadata = true
|
opts.IncludeLfsMetadata = true
|
||||||
|
case "commit_metadata":
|
||||||
|
opts.IncludeCommitMetadata = true
|
||||||
|
case "commit_message":
|
||||||
|
opts.IncludeCommitMessage = true
|
||||||
default:
|
default:
|
||||||
ctx.APIError(http.StatusBadRequest, fmt.Sprintf("unknown include option %q", includeOpt))
|
ctx.APIError(http.StatusBadRequest, fmt.Sprintf("unknown include option %q", includeOpt))
|
||||||
return
|
return
|
||||||
@ -883,7 +892,11 @@ func GetContents(ctx *context.APIContext) {
|
|||||||
// "$ref": "#/responses/ContentsResponse"
|
// "$ref": "#/responses/ContentsResponse"
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
ret := getRepoContents(ctx, files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*"), IncludeSingleFileContent: true})
|
ret := getRepoContents(ctx, files_service.GetContentsOrListOptions{
|
||||||
|
TreePath: ctx.PathParam("*"),
|
||||||
|
IncludeSingleFileContent: true,
|
||||||
|
IncludeCommitMetadata: true,
|
||||||
|
})
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
"code.gitea.io/gitea/modules/templates"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
@ -25,7 +26,6 @@ import (
|
|||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
org_service "code.gitea.io/gitea/services/org"
|
org_service "code.gitea.io/gitea/services/org"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
|
||||||
user_service "code.gitea.io/gitea/services/user"
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -83,38 +83,17 @@ func SettingsPost(ctx *context.Context) {
|
|||||||
Description: optional.Some(form.Description),
|
Description: optional.Some(form.Description),
|
||||||
Website: optional.Some(form.Website),
|
Website: optional.Some(form.Website),
|
||||||
Location: optional.Some(form.Location),
|
Location: optional.Some(form.Location),
|
||||||
Visibility: optional.Some(form.Visibility),
|
|
||||||
RepoAdminChangeTeamAccess: optional.Some(form.RepoAdminChangeTeamAccess),
|
RepoAdminChangeTeamAccess: optional.Some(form.RepoAdminChangeTeamAccess),
|
||||||
}
|
}
|
||||||
if ctx.Doer.IsAdmin {
|
if ctx.Doer.IsAdmin {
|
||||||
opts.MaxRepoCreation = optional.Some(form.MaxRepoCreation)
|
opts.MaxRepoCreation = optional.Some(form.MaxRepoCreation)
|
||||||
}
|
}
|
||||||
|
|
||||||
visibilityChanged := org.Visibility != form.Visibility
|
|
||||||
|
|
||||||
if err := user_service.UpdateUser(ctx, org.AsUser(), opts); err != nil {
|
if err := user_service.UpdateUser(ctx, org.AsUser(), opts); err != nil {
|
||||||
ctx.ServerError("UpdateUser", err)
|
ctx.ServerError("UpdateUser", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// update forks visibility
|
|
||||||
if visibilityChanged {
|
|
||||||
repos, _, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
|
|
||||||
Actor: org.AsUser(), Private: true, ListOptions: db.ListOptions{Page: 1, PageSize: org.NumRepos},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetRepositories", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, repo := range repos {
|
|
||||||
repo.OwnerName = org.Name
|
|
||||||
if err := repo_service.UpdateRepository(ctx, repo, true); err != nil {
|
|
||||||
ctx.ServerError("UpdateRepository", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("Organization setting updated: %s", org.Name)
|
log.Trace("Organization setting updated: %s", org.Name)
|
||||||
ctx.Flash.Success(ctx.Tr("org.settings.update_setting_success"))
|
ctx.Flash.Success(ctx.Tr("org.settings.update_setting_success"))
|
||||||
ctx.Redirect(ctx.Org.OrgLink + "/settings")
|
ctx.Redirect(ctx.Org.OrgLink + "/settings")
|
||||||
@ -251,3 +230,26 @@ func SettingsRenamePost(ctx *context.Context) {
|
|||||||
ctx.Flash.Success(ctx.Tr("org.settings.rename_success", oldOrgName, newOrgName))
|
ctx.Flash.Success(ctx.Tr("org.settings.rename_success", oldOrgName, newOrgName))
|
||||||
ctx.JSONRedirect(setting.AppSubURL + "/org/" + url.PathEscape(newOrgName) + "/settings")
|
ctx.JSONRedirect(setting.AppSubURL + "/org/" + url.PathEscape(newOrgName) + "/settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SettingsChangeVisibilityPost response for change organization visibility
|
||||||
|
func SettingsChangeVisibilityPost(ctx *context.Context) {
|
||||||
|
visibility := structs.VisibleType(ctx.FormInt("visibility"))
|
||||||
|
if !visibility.IsValid() {
|
||||||
|
ctx.JSONError(ctx.Tr("org.settings.invalid_visibility"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Org.Organization.Visibility == visibility {
|
||||||
|
ctx.JSONError(ctx.Tr("org.settings.change_visibility_no_change"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := org_service.ChangeOrganizationVisibility(ctx, ctx.Org.Organization, visibility); err != nil {
|
||||||
|
log.Error("ChangeOrganizationVisibility: %v", err)
|
||||||
|
ctx.JSONError(util.Iif(ctx.Doer.IsAdmin, err.Error(), string(ctx.Tr("org.settings.change_visibility_failed", ctx.Org.Organization.Name))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Flash.Success(ctx.Tr("org.settings.change_visibility_success", ctx.Org.Organization.Name))
|
||||||
|
ctx.JSONRedirect(setting.AppSubURL + "/org/" + url.PathEscape(ctx.Org.Organization.Name) + "/settings")
|
||||||
|
}
|
||||||
|
@ -966,6 +966,7 @@ func registerWebRoutes(m *web.Router) {
|
|||||||
|
|
||||||
m.Post("/rename", web.Bind(forms.RenameOrgForm{}), org.SettingsRenamePost)
|
m.Post("/rename", web.Bind(forms.RenameOrgForm{}), org.SettingsRenamePost)
|
||||||
m.Post("/delete", org.SettingsDeleteOrgPost)
|
m.Post("/delete", org.SettingsDeleteOrgPost)
|
||||||
|
m.Post("/visibility", org.SettingsChangeVisibilityPost)
|
||||||
|
|
||||||
m.Group("/packages", func() {
|
m.Group("/packages", func() {
|
||||||
m.Get("", org.Packages)
|
m.Get("", org.Packages)
|
||||||
|
@ -41,7 +41,6 @@ type UpdateOrgSettingForm struct {
|
|||||||
Description string `binding:"MaxSize(255)"`
|
Description string `binding:"MaxSize(255)"`
|
||||||
Website string `binding:"ValidUrl;MaxSize(255)"`
|
Website string `binding:"ValidUrl;MaxSize(255)"`
|
||||||
Location string `binding:"MaxSize(50)"`
|
Location string `binding:"MaxSize(50)"`
|
||||||
Visibility structs.VisibleType
|
|
||||||
MaxRepoCreation int
|
MaxRepoCreation int
|
||||||
RepoAdminChangeTeamAccess bool
|
RepoAdminChangeTeamAccess bool
|
||||||
}
|
}
|
||||||
|
@ -46,10 +46,25 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldSendCommentChangeNotification(ctx context.Context, comment *issues_model.Comment) bool {
|
||||||
|
if err := comment.LoadReview(ctx); err != nil {
|
||||||
|
log.Error("LoadReview: %v", err)
|
||||||
|
return false
|
||||||
|
} else if comment.Review != nil && comment.Review.Type == issues_model.ReviewTypePending {
|
||||||
|
// Pending review comments updating should not triggered
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// CreateIssueComment notifies issue comment related message to notifiers
|
// CreateIssueComment notifies issue comment related message to notifiers
|
||||||
func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository,
|
func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository,
|
||||||
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
|
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
|
||||||
) {
|
) {
|
||||||
|
if !shouldSendCommentChangeNotification(ctx, comment) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, notifier := range notifiers {
|
for _, notifier := range notifiers {
|
||||||
notifier.CreateIssueComment(ctx, doer, repo, issue, comment, mentions)
|
notifier.CreateIssueComment(ctx, doer, repo, issue, comment, mentions)
|
||||||
}
|
}
|
||||||
@ -156,6 +171,10 @@ func PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issue
|
|||||||
|
|
||||||
// UpdateComment notifies update comment to notifiers
|
// UpdateComment notifies update comment to notifiers
|
||||||
func UpdateComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment, oldContent string) {
|
func UpdateComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment, oldContent string) {
|
||||||
|
if !shouldSendCommentChangeNotification(ctx, c) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, notifier := range notifiers {
|
for _, notifier := range notifiers {
|
||||||
notifier.UpdateComment(ctx, doer, c, oldContent)
|
notifier.UpdateComment(ctx, doer, c, oldContent)
|
||||||
}
|
}
|
||||||
@ -163,6 +182,10 @@ func UpdateComment(ctx context.Context, doer *user_model.User, c *issues_model.C
|
|||||||
|
|
||||||
// DeleteComment notifies delete comment to notifiers
|
// DeleteComment notifies delete comment to notifiers
|
||||||
func DeleteComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment) {
|
func DeleteComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment) {
|
||||||
|
if !shouldSendCommentChangeNotification(ctx, c) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, notifier := range notifiers {
|
for _, notifier := range notifiers {
|
||||||
notifier.DeleteComment(ctx, doer, c)
|
notifier.DeleteComment(ctx, doer, c)
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
activities_model "code.gitea.io/gitea/models/activities"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
org_model "code.gitea.io/gitea/models/organization"
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
packages_model "code.gitea.io/gitea/models/packages"
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
secret_model "code.gitea.io/gitea/models/secret"
|
secret_model "code.gitea.io/gitea/models/secret"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
|
"code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
@ -102,3 +106,70 @@ func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateOrgRepoForVisibilityChanged(ctx context.Context, repo *repo_model.Repository, makePrivate bool) error {
|
||||||
|
// 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 makePrivate {
|
||||||
|
if _, err := db.GetEngine(ctx).Where("repo_id = ?", repo.ID).Cols("is_private").Update(&activities_model.Action{
|
||||||
|
IsPrivate: true,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo_model.ClearRepoStars(ctx, repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create/Remove git-daemon-export-ok for git-daemon...
|
||||||
|
if err := repo_service.CheckDaemonExportOK(ctx, repo); err != nil {
|
||||||
|
return 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.
|
||||||
|
// FIXME: it should check organization visibility instead of repository visibility only.
|
||||||
|
issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
|
||||||
|
|
||||||
|
forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("getRepositoriesByForkID: %w", err)
|
||||||
|
}
|
||||||
|
for i := range forkRepos {
|
||||||
|
if err := updateOrgRepoForVisibilityChanged(ctx, forkRepos[i], makePrivate); err != nil {
|
||||||
|
return fmt.Errorf("updateRepoForVisibilityChanged[%s]: %w", forkRepos[i].FullName(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeOrganizationVisibility(ctx context.Context, org *org_model.Organization, visibility structs.VisibleType) error {
|
||||||
|
if org.Visibility == visibility {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
org.Visibility = visibility
|
||||||
|
// FIXME: If it's a big forks network(forks and sub forks), the database transaction will be too long to fail.
|
||||||
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
if err := user_model.UpdateUserColsWithNoAutotime(ctx, org.AsUser(), "visibility"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repos, _, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
|
||||||
|
Actor: org.AsUser(), Private: true, ListOptions: db.ListOptionsAll,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, repo := range repos {
|
||||||
|
if err := updateOrgRepoForVisibilityChanged(ctx, repo, visibility == structs.VisibleTypePrivate); err != nil {
|
||||||
|
return fmt.Errorf("updateOrgRepoForVisibilityChanged: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -469,7 +469,7 @@ func cleanupRepository(repoID int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateGitRepoAfterCreate(ctx context.Context, repo *repo_model.Repository) error {
|
func updateGitRepoAfterCreate(ctx context.Context, repo *repo_model.Repository) error {
|
||||||
if err := checkDaemonExportOK(ctx, repo); err != nil {
|
if err := CheckDaemonExportOK(ctx, repo); err != nil {
|
||||||
return fmt.Errorf("checkDaemonExportOK: %w", err)
|
return fmt.Errorf("checkDaemonExportOK: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,8 @@ type GetContentsOrListOptions struct {
|
|||||||
TreePath string
|
TreePath string
|
||||||
IncludeSingleFileContent bool // include the file's content when the tree path is a file
|
IncludeSingleFileContent bool // include the file's content when the tree path is a file
|
||||||
IncludeLfsMetadata bool
|
IncludeLfsMetadata bool
|
||||||
|
IncludeCommitMetadata bool
|
||||||
|
IncludeCommitMessage bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
|
// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
|
||||||
@ -132,39 +134,46 @@ func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Reposito
|
|||||||
}
|
}
|
||||||
selfURLString := selfURL.String()
|
selfURLString := selfURL.String()
|
||||||
|
|
||||||
err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lastCommit, err := refCommit.Commit.GetCommitByPath(opts.TreePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// All content types have these fields in populated
|
// All content types have these fields in populated
|
||||||
contentsResponse := &api.ContentsResponse{
|
contentsResponse := &api.ContentsResponse{
|
||||||
Name: entry.Name(),
|
Name: entry.Name(),
|
||||||
Path: opts.TreePath,
|
Path: opts.TreePath,
|
||||||
SHA: entry.ID.String(),
|
SHA: entry.ID.String(),
|
||||||
LastCommitSHA: lastCommit.ID.String(),
|
Size: entry.Size(),
|
||||||
Size: entry.Size(),
|
URL: &selfURLString,
|
||||||
URL: &selfURLString,
|
|
||||||
Links: &api.FileLinksResponse{
|
Links: &api.FileLinksResponse{
|
||||||
Self: &selfURLString,
|
Self: &selfURLString,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitHub doesn't have these fields in the response, but we could follow other similar APIs to name them
|
if opts.IncludeCommitMetadata || opts.IncludeCommitMessage {
|
||||||
// https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits
|
err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID)
|
||||||
if lastCommit.Committer != nil {
|
if err != nil {
|
||||||
contentsResponse.LastCommitterDate = lastCommit.Committer.When
|
return nil, err
|
||||||
}
|
}
|
||||||
if lastCommit.Author != nil {
|
|
||||||
contentsResponse.LastAuthorDate = lastCommit.Author.When
|
lastCommit, err := refCommit.Commit.GetCommitByPath(opts.TreePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.IncludeCommitMetadata {
|
||||||
|
contentsResponse.LastCommitSHA = util.ToPointer(lastCommit.ID.String())
|
||||||
|
// GitHub doesn't have these fields in the response, but we could follow other similar APIs to name them
|
||||||
|
// https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits
|
||||||
|
if lastCommit.Committer != nil {
|
||||||
|
contentsResponse.LastCommitterDate = util.ToPointer(lastCommit.Committer.When)
|
||||||
|
}
|
||||||
|
if lastCommit.Author != nil {
|
||||||
|
contentsResponse.LastAuthorDate = util.ToPointer(lastCommit.Author.When)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.IncludeCommitMessage {
|
||||||
|
contentsResponse.LastCommitMessage = util.ToPointer(lastCommit.Message())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now populate the rest of the ContentsResponse based on entry type
|
// Now populate the rest of the ContentsResponse based on the entry type
|
||||||
if entry.IsRegular() || entry.IsExecutable() {
|
if entry.IsRegular() || entry.IsExecutable() {
|
||||||
contentsResponse.Type = string(ContentTypeRegular)
|
contentsResponse.Type = string(ContentTypeRegular)
|
||||||
// if it is listing the repo root dir, don't waste system resources on reading content
|
// if it is listing the repo root dir, don't waste system resources on reading content
|
||||||
|
@ -5,56 +5,21 @@ package files
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
|
||||||
"code.gitea.io/gitea/services/contexttest"
|
"code.gitea.io/gitea/services/contexttest"
|
||||||
|
|
||||||
_ "code.gitea.io/gitea/models/actions"
|
_ "code.gitea.io/gitea/models/actions"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
unittest.MainTest(m)
|
unittest.MainTest(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExpectedReadmeContentsResponse() *api.ContentsResponse {
|
|
||||||
treePath := "README.md"
|
|
||||||
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
|
|
||||||
encoding := "base64"
|
|
||||||
content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"
|
|
||||||
selfURL := "https://try.gitea.io/api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
|
|
||||||
htmlURL := "https://try.gitea.io/user2/repo1/src/branch/master/" + treePath
|
|
||||||
gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha
|
|
||||||
downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath
|
|
||||||
return &api.ContentsResponse{
|
|
||||||
Name: treePath,
|
|
||||||
Path: treePath,
|
|
||||||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
|
||||||
LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
|
||||||
LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
|
||||||
LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
|
||||||
Type: "file",
|
|
||||||
Size: 30,
|
|
||||||
Encoding: &encoding,
|
|
||||||
Content: &content,
|
|
||||||
URL: &selfURL,
|
|
||||||
HTMLURL: &htmlURL,
|
|
||||||
GitURL: &gitURL,
|
|
||||||
DownloadURL: &downloadURL,
|
|
||||||
Links: &api.FileLinksResponse{
|
|
||||||
Self: &selfURL,
|
|
||||||
GitURL: &gitURL,
|
|
||||||
HTMLURL: &htmlURL,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetContents(t *testing.T) {
|
func TestGetContents(t *testing.T) {
|
||||||
unittest.PrepareTestEnv(t)
|
unittest.PrepareTestEnv(t)
|
||||||
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
||||||
@ -63,45 +28,8 @@ func TestGetContents(t *testing.T) {
|
|||||||
contexttest.LoadRepoCommit(t, ctx)
|
contexttest.LoadRepoCommit(t, ctx)
|
||||||
contexttest.LoadUser(t, ctx, 2)
|
contexttest.LoadUser(t, ctx, 2)
|
||||||
contexttest.LoadGitRepo(t, ctx)
|
contexttest.LoadGitRepo(t, ctx)
|
||||||
defer ctx.Repo.GitRepo.Close()
|
|
||||||
repo, gitRepo := ctx.Repo.Repository, ctx.Repo.GitRepo
|
|
||||||
refCommit, err := utils.ResolveRefCommit(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
t.Run("GetContentsOrList(README.md)-MetaOnly", func(t *testing.T) {
|
// GetContentsOrList's behavior is fully tested in integration tests, so we don't need to test it here.
|
||||||
expectedContentsResponse := getExpectedReadmeContentsResponse()
|
|
||||||
expectedContentsResponse.Encoding = nil // because will be in a list, doesn't have encoding and content
|
|
||||||
expectedContentsResponse.Content = nil
|
|
||||||
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "README.md", IncludeSingleFileContent: false})
|
|
||||||
assert.Equal(t, expectedContentsResponse, extResp.FileContents)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("GetContentsOrList(README.md)", func(t *testing.T) {
|
|
||||||
expectedContentsResponse := getExpectedReadmeContentsResponse()
|
|
||||||
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "README.md", IncludeSingleFileContent: true})
|
|
||||||
assert.Equal(t, expectedContentsResponse, extResp.FileContents)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("GetContentsOrList(RootDir)", func(t *testing.T) {
|
|
||||||
readmeContentsResponse := getExpectedReadmeContentsResponse()
|
|
||||||
readmeContentsResponse.Encoding = nil // because will be in a list, doesn't have encoding and content
|
|
||||||
readmeContentsResponse.Content = nil
|
|
||||||
expectedContentsListResponse := []*api.ContentsResponse{readmeContentsResponse}
|
|
||||||
// even if IncludeFileContent is true, it has no effect for directory listing
|
|
||||||
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "", IncludeSingleFileContent: true})
|
|
||||||
assert.Equal(t, expectedContentsListResponse, extResp.DirContents)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("GetContentsOrList(NoSuchTreePath)", func(t *testing.T) {
|
|
||||||
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "no-such/file.md"})
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.EqualError(t, err, "object does not exist [id: , rel_path: no-such]")
|
|
||||||
assert.Nil(t, extResp.DirContents)
|
|
||||||
assert.Nil(t, extResp.FileContents)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("GetBlobBySHA", func(t *testing.T) {
|
t.Run("GetBlobBySHA", func(t *testing.T) {
|
||||||
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
|
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
|
||||||
|
@ -22,7 +22,12 @@ import (
|
|||||||
func GetContentsListFromTreePaths(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treePaths []string) (files []*api.ContentsResponse) {
|
func GetContentsListFromTreePaths(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treePaths []string) (files []*api.ContentsResponse) {
|
||||||
var size int64
|
var size int64
|
||||||
for _, treePath := range treePaths {
|
for _, treePath := range treePaths {
|
||||||
fileContents, _ := GetFileContents(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: treePath, IncludeSingleFileContent: true}) // ok if fails, then will be nil
|
// ok if fails, then will be nil
|
||||||
|
fileContents, _ := GetFileContents(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{
|
||||||
|
TreePath: treePath,
|
||||||
|
IncludeSingleFileContent: true,
|
||||||
|
IncludeCommitMetadata: true,
|
||||||
|
})
|
||||||
if fileContents != nil && fileContents.Content != nil && *fileContents.Content != "" {
|
if fileContents != nil && fileContents.Content != nil && *fileContents.Content != "" {
|
||||||
// if content isn't empty (e.g., due to the single blob being too large), add file size to response size
|
// if content isn't empty (e.g., due to the single blob being too large), add file size to response size
|
||||||
size += int64(len(*fileContents.Content))
|
size += int64(len(*fileContents.Content))
|
||||||
|
@ -142,7 +142,7 @@ func MakeRepoPublic(ctx context.Context, repo *repo_model.Repository) (err error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create/Remove git-daemon-export-ok for git-daemon...
|
// Create/Remove git-daemon-export-ok for git-daemon...
|
||||||
if err := checkDaemonExportOK(ctx, repo); err != nil {
|
if err := CheckDaemonExportOK(ctx, repo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ func MakeRepoPrivate(ctx context.Context, repo *repo_model.Repository) (err erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create/Remove git-daemon-export-ok for git-daemon...
|
// Create/Remove git-daemon-export-ok for git-daemon...
|
||||||
if err := checkDaemonExportOK(ctx, repo); err != nil {
|
if err := CheckDaemonExportOK(ctx, repo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,8 +243,8 @@ func LinkedRepository(ctx context.Context, a *repo_model.Attachment) (*repo_mode
|
|||||||
return nil, -1, nil
|
return nil, -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
|
// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
|
||||||
func checkDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error {
|
func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error {
|
||||||
if err := repo.LoadOwner(ctx); err != nil {
|
if err := repo.LoadOwner(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -314,7 +314,7 @@ func updateRepository(ctx context.Context, repo *repo_model.Repository, visibili
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create/Remove git-daemon-export-ok for git-daemon...
|
// Create/Remove git-daemon-export-ok for git-daemon...
|
||||||
if err := checkDaemonExportOK(ctx, repo); err != nil {
|
if err := CheckDaemonExportOK(ctx, repo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export default {
|
|||||||
important: true, // the frameworks are mixed together, so tailwind needs to override other framework's styles
|
important: true, // the frameworks are mixed together, so tailwind needs to override other framework's styles
|
||||||
content: [
|
content: [
|
||||||
isProduction && '!./templates/devtest/**/*',
|
isProduction && '!./templates/devtest/**/*',
|
||||||
isProduction && '!./web_src/js/standalone/devtest.js',
|
isProduction && '!./web_src/js/standalone/devtest.ts',
|
||||||
'!./templates/swagger/v1_json.tmpl',
|
'!./templates/swagger/v1_json.tmpl',
|
||||||
'!./templates/user/auth/oidc_wellknown.tmpl',
|
'!./templates/user/auth/oidc_wellknown.tmpl',
|
||||||
'!**/*_test.go',
|
'!**/*_test.go',
|
||||||
|
@ -29,29 +29,6 @@
|
|||||||
<input id="location" name="location" value="{{.Org.Location}}" maxlength="50">
|
<input id="location" name="location" value="{{.Org.Location}}" maxlength="50">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="divider"></div>
|
|
||||||
<div class="field" id="visibility_box">
|
|
||||||
<label for="visibility">{{ctx.Locale.Tr "org.settings.visibility"}}</label>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui radio checkbox">
|
|
||||||
<input class="enable-system-radio" name="visibility" type="radio" value="0" {{if eq .CurrentVisibility 0}}checked{{end}}>
|
|
||||||
<label>{{ctx.Locale.Tr "org.settings.visibility.public"}}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui radio checkbox">
|
|
||||||
<input class="enable-system-radio" name="visibility" type="radio" value="1" {{if eq .CurrentVisibility 1}}checked{{end}}>
|
|
||||||
<label>{{ctx.Locale.Tr "org.settings.visibility.limited"}}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui radio checkbox">
|
|
||||||
<input class="enable-system-radio" name="visibility" type="radio" value="2" {{if eq .CurrentVisibility 2}}checked{{end}}>
|
|
||||||
<label>{{ctx.Locale.Tr "org.settings.visibility.private"}}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field" id="permission_box">
|
<div class="field" id="permission_box">
|
||||||
<label>{{ctx.Locale.Tr "org.settings.permission"}}</label>
|
<label>{{ctx.Locale.Tr "org.settings.permission"}}</label>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -3,6 +3,16 @@
|
|||||||
</h4>
|
</h4>
|
||||||
<div class="ui attached error danger segment">
|
<div class="ui attached error danger segment">
|
||||||
<div class="flex-list">
|
<div class="flex-list">
|
||||||
|
<div class="flex-item tw-items-center">
|
||||||
|
<div class="flex-item-main">
|
||||||
|
<div class="flex-item-title">{{ctx.Locale.Tr "org.settings.visibility"}}</div>
|
||||||
|
<div class="flex-item-body">{{ctx.Locale.Tr "org.settings.visibility_desc"}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-item-trailing">
|
||||||
|
<button class="ui basic red show-modal button" data-modal="#change-visibility-org-modal">{{ctx.Locale.Tr "org.settings.change_visibility"}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex-item tw-items-center">
|
<div class="flex-item tw-items-center">
|
||||||
<div class="flex-item-main">
|
<div class="flex-item-main">
|
||||||
<div class="flex-item-title">{{ctx.Locale.Tr "org.settings.rename"}}</div>
|
<div class="flex-item-title">{{ctx.Locale.Tr "org.settings.rename"}}</div>
|
||||||
@ -25,6 +35,48 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="ui small modal" id="change-visibility-org-modal">
|
||||||
|
<div class="header">
|
||||||
|
{{ctx.Locale.Tr "org.settings.change_visibility"}}
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<ul class="ui warning message">
|
||||||
|
<li>{{ctx.Locale.Tr "org.settings.change_visibility_notices_1"}}</li>
|
||||||
|
<li>{{ctx.Locale.Tr "org.settings.change_visibility_notices_2"}}</li>
|
||||||
|
</ul>
|
||||||
|
<form class="ui form form-fetch-action" action="{{.Link}}/visibility" method="post">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label>{{ctx.Locale.Tr "org.settings.visibility"}}</label>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input name="visibility" type="radio" value="0" {{if eq .CurrentVisibility 0}}checked{{end}}>
|
||||||
|
<label>{{ctx.Locale.Tr "org.settings.visibility.public"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input name="visibility" type="radio" value="1" {{if eq .CurrentVisibility 1}}checked{{end}}>
|
||||||
|
<label>{{ctx.Locale.Tr "org.settings.visibility.limited"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input name="visibility" type="radio" value="2" {{if eq .CurrentVisibility 2}}checked{{end}}>
|
||||||
|
<label>{{ctx.Locale.Tr "org.settings.visibility.private"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
|
||||||
|
<button class="ui red button">{{ctx.Locale.Tr "org.settings.change_visibility"}}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ui small modal" id="rename-org-modal">
|
<div class="ui small modal" id="rename-org-modal">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
{{ctx.Locale.Tr "org.settings.rename"}}
|
{{ctx.Locale.Tr "org.settings.rename"}}
|
||||||
|
8
templates/swagger/v1_json.tmpl
generated
8
templates/swagger/v1_json.tmpl
generated
@ -7547,7 +7547,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "path of the dir, file, symlink or submodule in the repo",
|
"description": "path of the dir, file, symlink or submodule in the repo. Swagger requires path parameter to be \"required\", you can leave it empty or pass a single dot (\".\") to get the root directory.",
|
||||||
"name": "filepath",
|
"name": "filepath",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -7560,7 +7560,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "By default this API's response only contains file's metadata. Use comma-separated \"includes\" options to retrieve more fields. Option \"file_content\" will try to retrieve the file content, option \"lfs_metadata\" will try to retrieve LFS metadata.",
|
"description": "By default this API's response only contains file's metadata. Use comma-separated \"includes\" options to retrieve more fields. Option \"file_content\" will try to retrieve the file content, \"lfs_metadata\" will try to retrieve LFS metadata, \"commit_metadata\" will try to retrieve commit metadata, and \"commit_message\" will try to retrieve commit message.",
|
||||||
"name": "includes",
|
"name": "includes",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
}
|
}
|
||||||
@ -22368,6 +22368,10 @@
|
|||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"x-go-name": "LastAuthorDate"
|
"x-go-name": "LastAuthorDate"
|
||||||
},
|
},
|
||||||
|
"last_commit_message": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "LastCommitMessage"
|
||||||
|
},
|
||||||
"last_commit_sha": {
|
"last_commit_sha": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-go-name": "LastCommitSHA"
|
"x-go-name": "LastCommitSHA"
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -52,8 +53,8 @@ func getCreateFileOptions() api.CreateFileOptions {
|
|||||||
func normalizeFileContentResponseCommitTime(c *api.ContentsResponse) {
|
func normalizeFileContentResponseCommitTime(c *api.ContentsResponse) {
|
||||||
// decoded JSON response may contain different timezone from the one parsed by git commit
|
// decoded JSON response may contain different timezone from the one parsed by git commit
|
||||||
// so we need to normalize the time to UTC to make "assert.Equal" pass
|
// so we need to normalize the time to UTC to make "assert.Equal" pass
|
||||||
c.LastCommitterDate = c.LastCommitterDate.UTC()
|
c.LastCommitterDate = util.ToPointer(c.LastCommitterDate.UTC())
|
||||||
c.LastAuthorDate = c.LastAuthorDate.UTC()
|
c.LastAuthorDate = util.ToPointer(c.LastAuthorDate.UTC())
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiFileResponseInfo struct {
|
type apiFileResponseInfo struct {
|
||||||
@ -74,9 +75,9 @@ func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileRespons
|
|||||||
Name: path.Base(info.treePath),
|
Name: path.Base(info.treePath),
|
||||||
Path: info.treePath,
|
Path: info.treePath,
|
||||||
SHA: sha,
|
SHA: sha,
|
||||||
LastCommitSHA: info.lastCommitSHA,
|
LastCommitSHA: util.ToPointer(info.lastCommitSHA),
|
||||||
LastCommitterDate: info.lastCommitterWhen,
|
LastCommitterDate: util.ToPointer(info.lastCommitterWhen),
|
||||||
LastAuthorDate: info.lastAuthorWhen,
|
LastAuthorDate: util.ToPointer(info.lastAuthorWhen),
|
||||||
Size: 16,
|
Size: 16,
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Encoding: &encoding,
|
Encoding: &encoding,
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -60,9 +61,9 @@ func getExpectedFileResponseForUpdate(info apiFileResponseInfo) *api.FileRespons
|
|||||||
Name: path.Base(info.treePath),
|
Name: path.Base(info.treePath),
|
||||||
Path: info.treePath,
|
Path: info.treePath,
|
||||||
SHA: sha,
|
SHA: sha,
|
||||||
LastCommitSHA: info.lastCommitSHA,
|
LastCommitSHA: util.ToPointer(info.lastCommitSHA),
|
||||||
LastCommitterDate: info.lastCommitterWhen,
|
LastCommitterDate: util.ToPointer(info.lastCommitterWhen),
|
||||||
LastAuthorDate: info.lastAuthorWhen,
|
LastAuthorDate: util.ToPointer(info.lastAuthorWhen),
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Size: 20,
|
Size: 20,
|
||||||
Encoding: &encoding,
|
Encoding: &encoding,
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -35,9 +36,9 @@ func getExpectedContentsListResponseForContents(ref, refType, lastCommitSHA stri
|
|||||||
Name: path.Base(treePath),
|
Name: path.Base(treePath),
|
||||||
Path: treePath,
|
Path: treePath,
|
||||||
SHA: sha,
|
SHA: sha,
|
||||||
LastCommitSHA: lastCommitSHA,
|
LastCommitSHA: util.ToPointer(lastCommitSHA),
|
||||||
LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))),
|
||||||
LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))),
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Size: 30,
|
Size: 30,
|
||||||
URL: &selfURL,
|
URL: &selfURL,
|
||||||
@ -65,7 +66,6 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
|
|||||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo
|
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo
|
||||||
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo
|
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo
|
||||||
repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo
|
repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo
|
||||||
treePath := "" // root dir
|
|
||||||
|
|
||||||
// Get user2's token
|
// Get user2's token
|
||||||
session := loginUser(t, user2.Name)
|
session := loginUser(t, user2.Name)
|
||||||
@ -94,7 +94,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
|
|||||||
// ref is default ref
|
// ref is default ref
|
||||||
ref := repo1.DefaultBranch
|
ref := repo1.DefaultBranch
|
||||||
refType := "branch"
|
refType := "branch"
|
||||||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents?ref=%s", user2.Name, repo1.Name, ref)
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
var contentsListResponse []*api.ContentsResponse
|
var contentsListResponse []*api.ContentsResponse
|
||||||
DecodeJSON(t, resp, &contentsListResponse)
|
DecodeJSON(t, resp, &contentsListResponse)
|
||||||
@ -106,7 +106,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
|
|||||||
|
|
||||||
// No ref
|
// No ref
|
||||||
refType = "branch"
|
refType = "branch"
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath)
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", user2.Name, repo1.Name)
|
||||||
resp = MakeRequest(t, req, http.StatusOK)
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
DecodeJSON(t, resp, &contentsListResponse)
|
DecodeJSON(t, resp, &contentsListResponse)
|
||||||
assert.NotNil(t, contentsListResponse)
|
assert.NotNil(t, contentsListResponse)
|
||||||
@ -117,7 +117,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
|
|||||||
// ref is the branch we created above in setup
|
// ref is the branch we created above in setup
|
||||||
ref = newBranch
|
ref = newBranch
|
||||||
refType = "branch"
|
refType = "branch"
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents?ref=%s", user2.Name, repo1.Name, ref)
|
||||||
resp = MakeRequest(t, req, http.StatusOK)
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
DecodeJSON(t, resp, &contentsListResponse)
|
DecodeJSON(t, resp, &contentsListResponse)
|
||||||
assert.NotNil(t, contentsListResponse)
|
assert.NotNil(t, contentsListResponse)
|
||||||
@ -131,7 +131,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
|
|||||||
// ref is the new tag we created above in setup
|
// ref is the new tag we created above in setup
|
||||||
ref = newTag
|
ref = newTag
|
||||||
refType = "tag"
|
refType = "tag"
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/?ref=%s", user2.Name, repo1.Name, ref)
|
||||||
resp = MakeRequest(t, req, http.StatusOK)
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
DecodeJSON(t, resp, &contentsListResponse)
|
DecodeJSON(t, resp, &contentsListResponse)
|
||||||
assert.NotNil(t, contentsListResponse)
|
assert.NotNil(t, contentsListResponse)
|
||||||
@ -145,7 +145,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
|
|||||||
// ref is a commit
|
// ref is a commit
|
||||||
ref = commitID
|
ref = commitID
|
||||||
refType = "commit"
|
refType = "commit"
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/?ref=%s", user2.Name, repo1.Name, ref)
|
||||||
resp = MakeRequest(t, req, http.StatusOK)
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
DecodeJSON(t, resp, &contentsListResponse)
|
DecodeJSON(t, resp, &contentsListResponse)
|
||||||
assert.NotNil(t, contentsListResponse)
|
assert.NotNil(t, contentsListResponse)
|
||||||
@ -154,21 +154,21 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
|
|||||||
|
|
||||||
// Test file contents a file with a bad ref
|
// Test file contents a file with a bad ref
|
||||||
ref = "badref"
|
ref = "badref"
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/?ref=%s", user2.Name, repo1.Name, ref)
|
||||||
MakeRequest(t, req, http.StatusNotFound)
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
|
||||||
// Test accessing private ref with user token that does not have access - should fail
|
// Test accessing private ref with user token that does not have access - should fail
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath).
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", user2.Name, repo16.Name).
|
||||||
AddTokenAuth(token4)
|
AddTokenAuth(token4)
|
||||||
MakeRequest(t, req, http.StatusNotFound)
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
|
||||||
// Test access private ref of owner of token
|
// Test access private ref of owner of token
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/readme.md", user2.Name, repo16.Name).
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", user2.Name, repo16.Name).
|
||||||
AddTokenAuth(token2)
|
AddTokenAuth(token2)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
// Test access of org org3 private repo file by owner user2
|
// Test access of org org3 private repo file by owner user2
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", org3.Name, repo3.Name, treePath).
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", org3.Name, repo3.Name).
|
||||||
AddTokenAuth(token2)
|
AddTokenAuth(token2)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,9 @@ func getExpectedContentsResponseForContents(ref, refType, lastCommitSHA string)
|
|||||||
Name: treePath,
|
Name: treePath,
|
||||||
Path: treePath,
|
Path: treePath,
|
||||||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
||||||
LastCommitSHA: lastCommitSHA,
|
LastCommitSHA: util.ToPointer(lastCommitSHA),
|
||||||
LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))),
|
||||||
LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))),
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Size: 30,
|
Size: 30,
|
||||||
Encoding: util.ToPointer("base64"),
|
Encoding: util.ToPointer("base64"),
|
||||||
@ -97,11 +97,16 @@ func testAPIGetContents(t *testing.T, u *url.URL) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
/*** END SETUP ***/
|
/*** END SETUP ***/
|
||||||
|
|
||||||
|
// not found
|
||||||
|
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/no-such/file.md", user2.Name, repo1.Name)
|
||||||
|
resp := MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
assert.Contains(t, resp.Body.String(), "object does not exist [id: , rel_path: no-such]")
|
||||||
|
|
||||||
// ref is default ref
|
// ref is default ref
|
||||||
ref := repo1.DefaultBranch
|
ref := repo1.DefaultBranch
|
||||||
refType := "branch"
|
refType := "branch"
|
||||||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
var contentsResponse api.ContentsResponse
|
var contentsResponse api.ContentsResponse
|
||||||
DecodeJSON(t, resp, &contentsResponse)
|
DecodeJSON(t, resp, &contentsResponse)
|
||||||
lastCommit, _ := gitRepo.GetCommitByPath("README.md")
|
lastCommit, _ := gitRepo.GetCommitByPath("README.md")
|
||||||
@ -116,7 +121,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) {
|
|||||||
expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String())
|
expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String())
|
||||||
assert.Equal(t, *expectedContentsResponse, contentsResponse)
|
assert.Equal(t, *expectedContentsResponse, contentsResponse)
|
||||||
|
|
||||||
// ref is the branch we created above in setup
|
// ref is the branch we created above in setup
|
||||||
ref = newBranch
|
ref = newBranch
|
||||||
refType = "branch"
|
refType = "branch"
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||||
@ -206,14 +211,30 @@ func testAPIGetContentsExt(t *testing.T) {
|
|||||||
session := loginUser(t, "user2")
|
session := loginUser(t, "user2")
|
||||||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||||
t.Run("DirContents", func(t *testing.T) {
|
t.Run("DirContents", func(t *testing.T) {
|
||||||
req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs?ref=sub-home-md-img-check")
|
req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext?ref=sub-home-md-img-check")
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
var contentsResponse api.ContentsExtResponse
|
var contentsResponse api.ContentsExtResponse
|
||||||
DecodeJSON(t, resp, &contentsResponse)
|
DecodeJSON(t, resp, &contentsResponse)
|
||||||
assert.Nil(t, contentsResponse.FileContents)
|
assert.Nil(t, contentsResponse.FileContents)
|
||||||
|
assert.NotNil(t, contentsResponse.DirContents)
|
||||||
|
|
||||||
|
req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/.?ref=sub-home-md-img-check")
|
||||||
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
contentsResponse = api.ContentsExtResponse{}
|
||||||
|
DecodeJSON(t, resp, &contentsResponse)
|
||||||
|
assert.Nil(t, contentsResponse.FileContents)
|
||||||
|
assert.NotNil(t, contentsResponse.DirContents)
|
||||||
|
|
||||||
|
req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs?ref=sub-home-md-img-check")
|
||||||
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
contentsResponse = api.ContentsExtResponse{}
|
||||||
|
DecodeJSON(t, resp, &contentsResponse)
|
||||||
|
assert.Nil(t, contentsResponse.FileContents)
|
||||||
assert.Equal(t, "README.md", contentsResponse.DirContents[0].Name)
|
assert.Equal(t, "README.md", contentsResponse.DirContents[0].Name)
|
||||||
assert.Nil(t, contentsResponse.DirContents[0].Encoding)
|
assert.Nil(t, contentsResponse.DirContents[0].Encoding)
|
||||||
assert.Nil(t, contentsResponse.DirContents[0].Content)
|
assert.Nil(t, contentsResponse.DirContents[0].Content)
|
||||||
|
assert.Nil(t, contentsResponse.DirContents[0].LastCommitSHA)
|
||||||
|
assert.Nil(t, contentsResponse.DirContents[0].LastCommitMessage)
|
||||||
|
|
||||||
// "includes=file_content" shouldn't affect directory listing
|
// "includes=file_content" shouldn't affect directory listing
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs?ref=sub-home-md-img-check&includes=file_content")
|
req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs?ref=sub-home-md-img-check&includes=file_content")
|
||||||
@ -240,7 +261,7 @@ func testAPIGetContentsExt(t *testing.T) {
|
|||||||
assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid)
|
assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid)
|
||||||
})
|
})
|
||||||
t.Run("FileContents", func(t *testing.T) {
|
t.Run("FileContents", func(t *testing.T) {
|
||||||
// by default, no file content is returned
|
// by default, no file content or commit info is returned
|
||||||
req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs/README.md?ref=sub-home-md-img-check")
|
req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs/README.md?ref=sub-home-md-img-check")
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
var contentsResponse api.ContentsExtResponse
|
var contentsResponse api.ContentsExtResponse
|
||||||
@ -249,9 +270,11 @@ func testAPIGetContentsExt(t *testing.T) {
|
|||||||
assert.Equal(t, "README.md", contentsResponse.FileContents.Name)
|
assert.Equal(t, "README.md", contentsResponse.FileContents.Name)
|
||||||
assert.Nil(t, contentsResponse.FileContents.Encoding)
|
assert.Nil(t, contentsResponse.FileContents.Encoding)
|
||||||
assert.Nil(t, contentsResponse.FileContents.Content)
|
assert.Nil(t, contentsResponse.FileContents.Content)
|
||||||
|
assert.Nil(t, contentsResponse.FileContents.LastCommitSHA)
|
||||||
|
assert.Nil(t, contentsResponse.FileContents.LastCommitMessage)
|
||||||
|
|
||||||
// file content is only returned when `includes=file_content`
|
// file content is only returned when `includes=file_content`
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs/README.md?ref=sub-home-md-img-check&includes=file_content")
|
req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs/README.md?ref=sub-home-md-img-check&includes=file_content,commit_metadata,commit_message")
|
||||||
resp = MakeRequest(t, req, http.StatusOK)
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
contentsResponse = api.ContentsExtResponse{}
|
contentsResponse = api.ContentsExtResponse{}
|
||||||
DecodeJSON(t, resp, &contentsResponse)
|
DecodeJSON(t, resp, &contentsResponse)
|
||||||
@ -259,6 +282,8 @@ func testAPIGetContentsExt(t *testing.T) {
|
|||||||
assert.Equal(t, "README.md", contentsResponse.FileContents.Name)
|
assert.Equal(t, "README.md", contentsResponse.FileContents.Name)
|
||||||
assert.NotNil(t, contentsResponse.FileContents.Encoding)
|
assert.NotNil(t, contentsResponse.FileContents.Encoding)
|
||||||
assert.NotNil(t, contentsResponse.FileContents.Content)
|
assert.NotNil(t, contentsResponse.FileContents.Content)
|
||||||
|
assert.Equal(t, "4649299398e4d39a5c09eb4f534df6f1e1eb87cc", *contentsResponse.FileContents.LastCommitSHA)
|
||||||
|
assert.Equal(t, "Test how READMEs render images when found in a subfolder\n", *contentsResponse.FileContents.LastCommitMessage)
|
||||||
|
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/user2/lfs/contents-ext/jpeg.jpg?includes=file_content").AddTokenAuth(token2)
|
req = NewRequestf(t, "GET", "/api/v1/repos/user2/lfs/contents-ext/jpeg.jpg?includes=file_content").AddTokenAuth(token2)
|
||||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
@ -270,6 +295,8 @@ func testAPIGetContentsExt(t *testing.T) {
|
|||||||
assert.Equal(t, "jpeg.jpg", respFile.Name)
|
assert.Equal(t, "jpeg.jpg", respFile.Name)
|
||||||
assert.NotNil(t, respFile.Encoding)
|
assert.NotNil(t, respFile.Encoding)
|
||||||
assert.NotNil(t, respFile.Content)
|
assert.NotNil(t, respFile.Content)
|
||||||
|
assert.Nil(t, contentsResponse.FileContents.LastCommitSHA)
|
||||||
|
assert.Nil(t, contentsResponse.FileContents.LastCommitMessage)
|
||||||
assert.Equal(t, util.ToPointer(int64(107)), respFile.LfsSize)
|
assert.Equal(t, util.ToPointer(int64(107)), respFile.LfsSize)
|
||||||
assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid)
|
assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid)
|
||||||
})
|
})
|
||||||
|
@ -155,9 +155,9 @@ func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git.
|
|||||||
Name: path.Base(treePath),
|
Name: path.Base(treePath),
|
||||||
Path: treePath,
|
Path: treePath,
|
||||||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||||
LastCommitSHA: lastCommit.ID.String(),
|
LastCommitSHA: util.ToPointer(lastCommit.ID.String()),
|
||||||
LastCommitterDate: lastCommit.Committer.When,
|
LastCommitterDate: util.ToPointer(lastCommit.Committer.When),
|
||||||
LastAuthorDate: lastCommit.Author.When,
|
LastAuthorDate: util.ToPointer(lastCommit.Author.When),
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Size: 18,
|
Size: 18,
|
||||||
Encoding: &encoding,
|
Encoding: &encoding,
|
||||||
@ -198,7 +198,7 @@ func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git.
|
|||||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Message: "Updates README.md\n",
|
Message: "Creates new/file.txt\n",
|
||||||
Tree: &api.CommitMeta{
|
Tree: &api.CommitMeta{
|
||||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
|
||||||
SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc",
|
SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc",
|
||||||
@ -225,9 +225,9 @@ func getExpectedFileResponseForRepoFilesUpdate(commitID, filename, lastCommitSHA
|
|||||||
Name: filename,
|
Name: filename,
|
||||||
Path: filename,
|
Path: filename,
|
||||||
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
|
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
|
||||||
LastCommitSHA: lastCommitSHA,
|
LastCommitSHA: util.ToPointer(lastCommitSHA),
|
||||||
LastCommitterDate: lastCommitterWhen,
|
LastCommitterDate: util.ToPointer(lastCommitterWhen),
|
||||||
LastAuthorDate: lastAuthorWhen,
|
LastAuthorDate: util.ToPointer(lastAuthorWhen),
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Size: 43,
|
Size: 43,
|
||||||
Encoding: &encoding,
|
Encoding: &encoding,
|
||||||
@ -331,7 +331,7 @@ func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA str
|
|||||||
Name: detail.filename,
|
Name: detail.filename,
|
||||||
Path: detail.filename,
|
Path: detail.filename,
|
||||||
SHA: detail.sha,
|
SHA: detail.sha,
|
||||||
LastCommitSHA: lastCommitSHA,
|
LastCommitSHA: util.ToPointer(lastCommitSHA),
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Size: detail.size,
|
Size: detail.size,
|
||||||
Encoding: util.ToPointer("base64"),
|
Encoding: util.ToPointer("base64"),
|
||||||
@ -537,7 +537,7 @@ func TestChangeRepoFilesForUpdateWithFileRename(t *testing.T) {
|
|||||||
lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
|
lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
|
||||||
expectedFileResponse := getExpectedFileResponseForRepoFilesUpdateRename(commit.ID.String(), lastCommit.ID.String())
|
expectedFileResponse := getExpectedFileResponseForRepoFilesUpdateRename(commit.ID.String(), lastCommit.ID.String())
|
||||||
for _, file := range filesResponse.Files {
|
for _, file := range filesResponse.Files {
|
||||||
file.LastCommitterDate, file.LastAuthorDate = time.Time{}, time.Time{} // there might be different time in one operation, so we ignore them
|
file.LastCommitterDate, file.LastAuthorDate = nil, nil // there might be different time in one operation, so we ignore them
|
||||||
}
|
}
|
||||||
assert.Len(t, filesResponse.Files, 4)
|
assert.Len(t, filesResponse.Files, 4)
|
||||||
assert.Equal(t, expectedFileResponse.Files, filesResponse.Files)
|
assert.Equal(t, expectedFileResponse.Files, filesResponse.Files)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user