mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-28 12:46:09 +01:00
Compare commits
20 commits
cbf366643b
...
fd1edb9d9d
Author | SHA1 | Date | |
---|---|---|---|
fd1edb9d9d | |||
633996db8e | |||
7f0ce2dfc7 | |||
b7c944b9e4 | |||
cf9a416d62 | |||
8c7bda8755 | |||
b7e32b2382 | |||
e3dfb512d6 | |||
0d50f27469 | |||
62f2d717b7 | |||
89960c3dfb | |||
8b5c9186a5 | |||
8b4abb1719 | |||
f4923854f6 | |||
d590607106 | |||
4746291b08 | |||
022552d5b6 | |||
376fa0d8c4 | |||
be541d9877 | |||
ae99233db0 |
54
CHANGELOG.md
54
CHANGELOG.md
|
@ -4,6 +4,60 @@ This changelog goes through all the changes that have been made in each release
|
||||||
without substantial changes to our git log; to see the highlights of what has
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||||
|
|
||||||
|
## [1.21.4](https://github.com/go-gitea/gitea/releases/tag/1.21.4) - 2024-01-16
|
||||||
|
|
||||||
|
* SECURITY
|
||||||
|
* Update github.com/cloudflare/circl (#28789) (#28790)
|
||||||
|
* Require token for GET subscription endpoint (#28765) (#28768)
|
||||||
|
* BUGFIXES
|
||||||
|
* Use refname:strip-2 instead of refname:short when syncing tags (#28797) (#28811)
|
||||||
|
* Fix links in issue card (#28806) (#28807)
|
||||||
|
* Fix nil pointer panic when exec some gitea cli command (#28791) (#28795)
|
||||||
|
* Require token for GET subscription endpoint (#28765) (#28778)
|
||||||
|
* Fix button size in "attached header right" (#28770) (#28774)
|
||||||
|
* Fix `convert.ToTeams` on empty input (#28426) (#28767)
|
||||||
|
* Hide code related setting options in repository when code unit is disabled (#28631) (#28749)
|
||||||
|
* Fix incorrect URL for "Reference in New Issue" (#28716) (#28723)
|
||||||
|
* Fix panic when parsing empty pgsql host (#28708) (#28709)
|
||||||
|
* Upgrade xorm to new version which supported update join for all supported databases (#28590) (#28668)
|
||||||
|
* Fix alpine package files are not rebuilt (#28638) (#28665)
|
||||||
|
* Avoid cycle-redirecting user/login page (#28636) (#28658)
|
||||||
|
* Fix empty ref for cron workflow runs (#28640) (#28647)
|
||||||
|
* Remove unnecessary syncbranchToDB with tests (#28624) (#28629)
|
||||||
|
* Use known issue IID to generate new PR index number when migrating from GitLab (#28616) (#28618)
|
||||||
|
* Fix flex container width (#28603) (#28605)
|
||||||
|
* Fix the scroll behavior for emoji/mention list (#28597) (#28601)
|
||||||
|
* Fix wrong due date rendering in issue list page (#28588) (#28591)
|
||||||
|
* Fix `status_check_contexts` matching bug (#28582) (#28589)
|
||||||
|
* Fix 500 error of searching commits (#28576) (#28579)
|
||||||
|
* Use information from previous blame parts (#28572) (#28577)
|
||||||
|
* Update mermaid for 1.21 (#28571)
|
||||||
|
* Fix 405 method not allowed CORS / OIDC (#28583) (#28586) (#28587) (#28611)
|
||||||
|
* Fix `GetCommitStatuses` (#28787) (#28804)
|
||||||
|
* Forbid removing the last admin user (#28337) (#28793)
|
||||||
|
* Fix schedule tasks bugs (#28691) (#28780)
|
||||||
|
* Fix issue dependencies (#27736) (#28776)
|
||||||
|
* Fix system webhooks API bug (#28531) (#28666)
|
||||||
|
* Fix when private user following user, private user will not be counted in his own view (#28037) (#28792)
|
||||||
|
* Render code block in activity tab (#28816) (#28818)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Rework markup link rendering (#26745) (#28803)
|
||||||
|
* Modernize merge button (#28140) (#28786)
|
||||||
|
* Speed up loading the dashboard on mysql/mariadb (#28546) (#28784)
|
||||||
|
* Assign pull request to project during creation (#28227) (#28775)
|
||||||
|
* Show description as tooltip instead of title for labels (#28754) (#28766)
|
||||||
|
* Make template `DateTime` show proper tooltip (#28677) (#28683)
|
||||||
|
* Switch destination directory for apt signing keys (#28639) (#28642)
|
||||||
|
* Include heap pprof in diagnosis report to help debugging memory leaks (#28596) (#28599)
|
||||||
|
* DOCS
|
||||||
|
* Suggest to use Type=simple for systemd service (#28717) (#28722)
|
||||||
|
* Extend description for ARTIFACT_RETENTION_DAYS (#28626) (#28630)
|
||||||
|
* MISC
|
||||||
|
* Add -F to commit search to treat keywords as strings (#28744) (#28748)
|
||||||
|
* Add download attribute to release attachments (#28739) (#28740)
|
||||||
|
* Concatenate error in `checkIfPRContentChanged` (#28731) (#28737)
|
||||||
|
* Improve 1.21 document for Database Preparation (#28643) (#28644)
|
||||||
|
|
||||||
## [1.21.3](https://github.com/go-gitea/gitea/releases/tag/1.21.3) - 2023-12-21
|
## [1.21.3](https://github.com/go-gitea/gitea/releases/tag/1.21.3) - 2023-12-21
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
|
|
|
@ -110,6 +110,9 @@ func migrateLFS(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||||
|
|
||||||
func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||||
return db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error {
|
return db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error {
|
||||||
|
if user.CustomAvatarRelativePath() == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
|
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
@ -117,6 +120,9 @@ func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error
|
||||||
|
|
||||||
func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||||
return db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error {
|
return db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error {
|
||||||
|
if repo.CustomAvatarRelativePath() == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
|
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
|
@ -57,6 +57,21 @@ func (err ErrUserOwnPackages) Error() string {
|
||||||
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
|
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
|
||||||
|
type ErrDeleteLastAdminUser struct {
|
||||||
|
UID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
|
||||||
|
func IsErrDeleteLastAdminUser(err error) bool {
|
||||||
|
_, ok := err.(ErrDeleteLastAdminUser)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrDeleteLastAdminUser) Error() string {
|
||||||
|
return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
|
||||||
|
}
|
||||||
|
|
||||||
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
|
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
|
||||||
// transfer request
|
// transfer request
|
||||||
type ErrNoPendingRepoTransfer struct {
|
type ErrNoPendingRepoTransfer struct {
|
||||||
|
|
|
@ -649,3 +649,10 @@
|
||||||
repo_id: 49
|
repo_id: 49
|
||||||
type: 2
|
type: 2
|
||||||
created_unix: 946684810
|
created_unix: 946684810
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 98
|
||||||
|
repo_id: 59
|
||||||
|
type: 1
|
||||||
|
config: "{}"
|
||||||
|
created_unix: 946684810
|
||||||
|
|
|
@ -1693,3 +1693,16 @@
|
||||||
size: 0
|
size: 0
|
||||||
is_fsck_enabled: true
|
is_fsck_enabled: true
|
||||||
close_issues_via_commit_in_any_branch: false
|
close_issues_via_commit_in_any_branch: false
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 59
|
||||||
|
owner_id: 2
|
||||||
|
owner_name: user2
|
||||||
|
lower_name: test_commit_revert
|
||||||
|
name: test_commit_revert
|
||||||
|
default_branch: main
|
||||||
|
is_empty: false
|
||||||
|
is_archived: false
|
||||||
|
is_private: true
|
||||||
|
status: 0
|
||||||
|
num_issues: 0
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
num_followers: 2
|
num_followers: 2
|
||||||
num_following: 1
|
num_following: 1
|
||||||
num_stars: 2
|
num_stars: 2
|
||||||
num_repos: 14
|
num_repos: 15
|
||||||
num_teams: 0
|
num_teams: 0
|
||||||
num_members: 0
|
num_members: 0
|
||||||
visibility: 0
|
visibility: 0
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CommitStatus holds a single Status of a single Commit
|
// CommitStatus holds a single Status of a single Commit
|
||||||
|
@ -220,60 +219,58 @@ func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
|
||||||
// CommitStatusOptions holds the options for query commit statuses
|
// CommitStatusOptions holds the options for query commit statuses
|
||||||
type CommitStatusOptions struct {
|
type CommitStatusOptions struct {
|
||||||
db.ListOptions
|
db.ListOptions
|
||||||
|
RepoID int64
|
||||||
|
SHA string
|
||||||
State string
|
State string
|
||||||
SortType string
|
SortType string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommitStatuses returns all statuses for a given commit.
|
func (opts *CommitStatusOptions) ToConds() builder.Cond {
|
||||||
func GetCommitStatuses(ctx context.Context, repo *repo_model.Repository, sha string, opts *CommitStatusOptions) ([]*CommitStatus, int64, error) {
|
var cond builder.Cond = builder.Eq{
|
||||||
if opts.Page <= 0 {
|
"repo_id": opts.RepoID,
|
||||||
opts.Page = 1
|
"sha": opts.SHA,
|
||||||
}
|
|
||||||
if opts.PageSize <= 0 {
|
|
||||||
opts.Page = setting.ItemsPerPage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
countSession := listCommitStatusesStatement(ctx, repo, sha, opts)
|
|
||||||
countSession = db.SetSessionPagination(countSession, opts)
|
|
||||||
maxResults, err := countSession.Count(new(CommitStatus))
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Count PRs: %v", err)
|
|
||||||
return nil, maxResults, err
|
|
||||||
}
|
|
||||||
|
|
||||||
statuses := make([]*CommitStatus, 0, opts.PageSize)
|
|
||||||
findSession := listCommitStatusesStatement(ctx, repo, sha, opts)
|
|
||||||
findSession = db.SetSessionPagination(findSession, opts)
|
|
||||||
sortCommitStatusesSession(findSession, opts.SortType)
|
|
||||||
return statuses, maxResults, findSession.Find(&statuses)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listCommitStatusesStatement(ctx context.Context, repo *repo_model.Repository, sha string, opts *CommitStatusOptions) *xorm.Session {
|
|
||||||
sess := db.GetEngine(ctx).Where("repo_id = ?", repo.ID).And("sha = ?", sha)
|
|
||||||
switch opts.State {
|
switch opts.State {
|
||||||
case "pending", "success", "error", "failure", "warning":
|
case "pending", "success", "error", "failure", "warning":
|
||||||
sess.And("state = ?", opts.State)
|
cond = cond.And(builder.Eq{
|
||||||
|
"state": opts.State,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return sess
|
|
||||||
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortCommitStatusesSession(sess *xorm.Session, sortType string) {
|
func (opts *CommitStatusOptions) ToOrders() string {
|
||||||
switch sortType {
|
switch opts.SortType {
|
||||||
case "oldest":
|
case "oldest":
|
||||||
sess.Asc("created_unix")
|
return "created_unix ASC"
|
||||||
case "recentupdate":
|
case "recentupdate":
|
||||||
sess.Desc("updated_unix")
|
return "updated_unix DESC"
|
||||||
case "leastupdate":
|
case "leastupdate":
|
||||||
sess.Asc("updated_unix")
|
return "updated_unix ASC"
|
||||||
case "leastindex":
|
case "leastindex":
|
||||||
sess.Desc("index")
|
return "`index` DESC"
|
||||||
case "highestindex":
|
case "highestindex":
|
||||||
sess.Asc("index")
|
return "`index` ASC"
|
||||||
default:
|
default:
|
||||||
sess.Desc("created_unix")
|
return "created_unix DESC"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCommitStatuses returns all statuses for a given commit.
|
||||||
|
func GetCommitStatuses(ctx context.Context, opts *CommitStatusOptions) ([]*CommitStatus, int64, error) {
|
||||||
|
sess := db.GetEngine(ctx).
|
||||||
|
Where(opts.ToConds()).
|
||||||
|
OrderBy(opts.ToOrders())
|
||||||
|
|
||||||
|
db.SetSessionPagination(sess, opts)
|
||||||
|
|
||||||
|
statuses := make([]*CommitStatus, 0, opts.PageSize)
|
||||||
|
count, err := sess.FindAndCount(&statuses)
|
||||||
|
return statuses, count, err
|
||||||
|
}
|
||||||
|
|
||||||
// CommitStatusIndex represents a table for commit status index
|
// CommitStatusIndex represents a table for commit status index
|
||||||
type CommitStatusIndex struct {
|
type CommitStatusIndex struct {
|
||||||
ID int64
|
ID int64
|
||||||
|
|
|
@ -22,7 +22,11 @@ func TestGetCommitStatuses(t *testing.T) {
|
||||||
|
|
||||||
sha1 := "1234123412341234123412341234123412341234"
|
sha1 := "1234123412341234123412341234123412341234"
|
||||||
|
|
||||||
statuses, maxResults, err := git_model.GetCommitStatuses(db.DefaultContext, repo1, sha1, &git_model.CommitStatusOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 50}})
|
statuses, maxResults, err := git_model.GetCommitStatuses(db.DefaultContext, &git_model.CommitStatusOptions{
|
||||||
|
ListOptions: db.ListOptions{Page: 1, PageSize: 50},
|
||||||
|
RepoID: repo1.ID,
|
||||||
|
SHA: sha1,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, int(maxResults), 5)
|
assert.Equal(t, int(maxResults), 5)
|
||||||
assert.Len(t, statuses, 5)
|
assert.Len(t, statuses, 5)
|
||||||
|
@ -46,4 +50,13 @@ func TestGetCommitStatuses(t *testing.T) {
|
||||||
assert.Equal(t, "deploy/awesomeness", statuses[4].Context)
|
assert.Equal(t, "deploy/awesomeness", statuses[4].Context)
|
||||||
assert.Equal(t, structs.CommitStatusError, statuses[4].State)
|
assert.Equal(t, structs.CommitStatusError, statuses[4].State)
|
||||||
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL(db.DefaultContext))
|
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL(db.DefaultContext))
|
||||||
|
|
||||||
|
statuses, maxResults, err = git_model.GetCommitStatuses(db.DefaultContext, &git_model.CommitStatusOptions{
|
||||||
|
ListOptions: db.ListOptions{Page: 2, PageSize: 50},
|
||||||
|
RepoID: repo1.ID,
|
||||||
|
SHA: sha1,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, int(maxResults), 5)
|
||||||
|
assert.Empty(t, statuses)
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,9 +109,11 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: issue.Repo.Link(),
|
Links: markup.Links{
|
||||||
Metas: issue.Repo.ComposeMetas(),
|
Base: issue.Repo.Link(),
|
||||||
|
},
|
||||||
|
Metas: issue.Repo.ComposeMetas(),
|
||||||
}, comment.Content); err != nil {
|
}, comment.Content); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -578,8 +578,7 @@ func (repo *Repository) CanEnableEditor() bool {
|
||||||
// DescriptionHTML does special handles to description and return HTML string.
|
// DescriptionHTML does special handles to description and return HTML string.
|
||||||
func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
|
func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
|
||||||
desc, err := markup.RenderDescriptionHTML(&markup.RenderContext{
|
desc, err := markup.RenderDescriptionHTML(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: repo.HTMLURL(),
|
|
||||||
// Don't use Metas to speedup requests
|
// Don't use Metas to speedup requests
|
||||||
}, repo.Description)
|
}, repo.Description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -705,9 +705,18 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve
|
||||||
return committer.Commit()
|
return committer.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsLastAdminUser check whether user is the last admin
|
||||||
|
func IsLastAdminUser(ctx context.Context, user *User) bool {
|
||||||
|
if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: util.OptionalBoolTrue}) <= 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// CountUserFilter represent optional filters for CountUsers
|
// CountUserFilter represent optional filters for CountUsers
|
||||||
type CountUserFilter struct {
|
type CountUserFilter struct {
|
||||||
LastLoginSince *int64
|
LastLoginSince *int64
|
||||||
|
IsAdmin util.OptionalBool
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountUsers returns number of users.
|
// CountUsers returns number of users.
|
||||||
|
@ -716,13 +725,25 @@ func CountUsers(ctx context.Context, opts *CountUserFilter) int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
|
func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
|
||||||
sess := db.GetEngine(ctx).Where(builder.Eq{"type": "0"})
|
sess := db.GetEngine(ctx)
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"type": UserTypeIndividual})
|
||||||
|
|
||||||
if opts != nil && opts.LastLoginSince != nil {
|
if opts != nil {
|
||||||
sess = sess.Where(builder.Gte{"last_login_unix": *opts.LastLoginSince})
|
if opts.LastLoginSince != nil {
|
||||||
|
cond = cond.And(builder.Gte{"last_login_unix": *opts.LastLoginSince})
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.IsAdmin.IsNone() {
|
||||||
|
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.IsTrue()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := sess.Where(cond).Count(new(User))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("user.countUsers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
count, _ := sess.Count(new(User))
|
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any))
|
||||||
}
|
}
|
||||||
|
|
||||||
func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) {
|
func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) {
|
||||||
if setting.Service.RequireSignInView && doer == nil {
|
if setting.Service.RequireSignInView && (doer == nil || doer.IsGhost()) {
|
||||||
return perm.AccessModeNone, nil
|
return perm.AccessModeNone, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,9 +49,9 @@ func TestFormat_Flag(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "multiple fields",
|
name: "multiple fields",
|
||||||
|
|
||||||
givenFormat: foreachref.NewFormat("refname:short", "objecttype", "objectname"),
|
givenFormat: foreachref.NewFormat("refname:lstrip=2", "objecttype", "objectname"),
|
||||||
|
|
||||||
wantFlag: "refname:short %(refname:short)%00objecttype %(objecttype)%00objectname %(objectname)%00%00",
|
wantFlag: "refname:lstrip=2 %(refname:lstrip=2)%00objecttype %(objecttype)%00objectname %(objectname)%00%00",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,10 +166,6 @@ func InitSimple(ctx context.Context) error {
|
||||||
// InitFull initializes git module with version check and change global variables, sync gitconfig.
|
// InitFull initializes git module with version check and change global variables, sync gitconfig.
|
||||||
// It should only be called once at the beginning of the program initialization (TestMain/GlobalInitInstalled) as this code makes unsynchronized changes to variables.
|
// It should only be called once at the beginning of the program initialization (TestMain/GlobalInitInstalled) as this code makes unsynchronized changes to variables.
|
||||||
func InitFull(ctx context.Context) (err error) {
|
func InitFull(ctx context.Context) (err error) {
|
||||||
if err = checkInit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = InitSimple(ctx); err != nil {
|
if err = InitSimple(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -62,11 +63,15 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t
|
||||||
cmd.AddOptionFormat("--format=%s", format.String())
|
cmd.AddOptionFormat("--format=%s", format.String())
|
||||||
cmd.AddDynamicArguments(commitID)
|
cmd.AddDynamicArguments(commitID)
|
||||||
|
|
||||||
|
// Avoid LFS hooks getting installed because of /etc/gitconfig, which can break pull requests.
|
||||||
|
env := append(os.Environ(), "GIT_CONFIG_NOSYSTEM=1")
|
||||||
|
|
||||||
var stderr strings.Builder
|
var stderr strings.Builder
|
||||||
err := cmd.Run(&RunOpts{
|
err := cmd.Run(&RunOpts{
|
||||||
Dir: repo.Path,
|
Dir: repo.Path,
|
||||||
Stdout: target,
|
Stdout: target,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
|
Env: env,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ConcatenateError(err, stderr.String())
|
return ConcatenateError(err, stderr.String())
|
||||||
|
|
|
@ -112,7 +112,9 @@ func (repo *Repository) GetTagWithID(idStr, name string) (*Tag, error) {
|
||||||
|
|
||||||
// GetTagInfos returns all tag infos of the repository.
|
// GetTagInfos returns all tag infos of the repository.
|
||||||
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
|
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
|
||||||
forEachRefFmt := foreachref.NewFormat("objecttype", "refname:short", "object", "objectname", "creator", "contents", "contents:signature")
|
// Generally, refname:short should be equal to refname:lstrip=2 except core.warnAmbiguousRefs is used to select the strict abbreviation mode.
|
||||||
|
// https://git-scm.com/docs/git-for-each-ref#Documentation/git-for-each-ref.txt-refname
|
||||||
|
forEachRefFmt := foreachref.NewFormat("objecttype", "refname:lstrip=2", "object", "objectname", "creator", "contents", "contents:signature")
|
||||||
|
|
||||||
stdoutReader, stdoutWriter := io.Pipe()
|
stdoutReader, stdoutWriter := io.Pipe()
|
||||||
defer stdoutReader.Close()
|
defer stdoutReader.Close()
|
||||||
|
@ -162,7 +164,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
|
||||||
func parseTagRef(ref map[string]string) (tag *Tag, err error) {
|
func parseTagRef(ref map[string]string) (tag *Tag, err error) {
|
||||||
tag = &Tag{
|
tag = &Tag{
|
||||||
Type: ref["objecttype"],
|
Type: ref["objecttype"],
|
||||||
Name: ref["refname:short"],
|
Name: ref["refname:lstrip=2"],
|
||||||
}
|
}
|
||||||
|
|
||||||
tag.ID, err = NewIDFromString(ref["objectname"])
|
tag.ID, err = NewIDFromString(ref["objectname"])
|
||||||
|
|
|
@ -209,8 +209,8 @@ func TestRepository_parseTagRef(t *testing.T) {
|
||||||
name: "lightweight tag",
|
name: "lightweight tag",
|
||||||
|
|
||||||
givenRef: map[string]string{
|
givenRef: map[string]string{
|
||||||
"objecttype": "commit",
|
"objecttype": "commit",
|
||||||
"refname:short": "v1.9.1",
|
"refname:lstrip=2": "v1.9.1",
|
||||||
// object will be empty for lightweight tags
|
// object will be empty for lightweight tags
|
||||||
"object": "",
|
"object": "",
|
||||||
"objectname": "ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889",
|
"objectname": "ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889",
|
||||||
|
@ -238,8 +238,8 @@ func TestRepository_parseTagRef(t *testing.T) {
|
||||||
name: "annotated tag",
|
name: "annotated tag",
|
||||||
|
|
||||||
givenRef: map[string]string{
|
givenRef: map[string]string{
|
||||||
"objecttype": "tag",
|
"objecttype": "tag",
|
||||||
"refname:short": "v0.0.1",
|
"refname:lstrip=2": "v0.0.1",
|
||||||
// object will refer to commit hash for annotated tag
|
// object will refer to commit hash for annotated tag
|
||||||
"object": "3325fd8a973321fd59455492976c042dde3fd1ca",
|
"object": "3325fd8a973321fd59455492976c042dde3fd1ca",
|
||||||
"objectname": "8c68a1f06fc59c655b7e3905b159d761e91c53c9",
|
"objectname": "8c68a1f06fc59c655b7e3905b159d761e91c53c9",
|
||||||
|
@ -267,11 +267,11 @@ func TestRepository_parseTagRef(t *testing.T) {
|
||||||
name: "annotated tag with signature",
|
name: "annotated tag with signature",
|
||||||
|
|
||||||
givenRef: map[string]string{
|
givenRef: map[string]string{
|
||||||
"objecttype": "tag",
|
"objecttype": "tag",
|
||||||
"refname:short": "v0.0.1",
|
"refname:lstrip=2": "v0.0.1",
|
||||||
"object": "3325fd8a973321fd59455492976c042dde3fd1ca",
|
"object": "3325fd8a973321fd59455492976c042dde3fd1ca",
|
||||||
"objectname": "8c68a1f06fc59c655b7e3905b159d761e91c53c9",
|
"objectname": "8c68a1f06fc59c655b7e3905b159d761e91c53c9",
|
||||||
"creator": "Foo Bar <foo@bar.com> 1565789218 +0300",
|
"creator": "Foo Bar <foo@bar.com> 1565789218 +0300",
|
||||||
"contents": `Add changelog of v1.9.1 (#7859)
|
"contents": `Add changelog of v1.9.1 (#7859)
|
||||||
|
|
||||||
* add changelog of v1.9.1
|
* add changelog of v1.9.1
|
||||||
|
|
13
modules/markup/external/external.go
vendored
13
modules/markup/external/external.go
vendored
|
@ -79,9 +79,10 @@ func envMark(envName string) string {
|
||||||
// Render renders the data of the document to HTML via the external tool.
|
// Render renders the data of the document to HTML via the external tool.
|
||||||
func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
||||||
var (
|
var (
|
||||||
urlRawPrefix = strings.Replace(ctx.URLPrefix, "/src/", "/raw/", 1)
|
command = strings.NewReplacer(
|
||||||
command = strings.NewReplacer(envMark("GITEA_PREFIX_SRC"), ctx.URLPrefix,
|
envMark("GITEA_PREFIX_SRC"), ctx.Links.SrcLink(),
|
||||||
envMark("GITEA_PREFIX_RAW"), urlRawPrefix).Replace(p.Command)
|
envMark("GITEA_PREFIX_RAW"), ctx.Links.RawLink(),
|
||||||
|
).Replace(p.Command)
|
||||||
commands = strings.Fields(command)
|
commands = strings.Fields(command)
|
||||||
args = commands[1:]
|
args = commands[1:]
|
||||||
)
|
)
|
||||||
|
@ -121,14 +122,14 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
|
||||||
ctx.Ctx = graceful.GetManager().ShutdownContext()
|
ctx.Ctx = graceful.GetManager().ShutdownContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
processCtx, _, finished := process.GetManager().AddContext(ctx.Ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.URLPrefix))
|
processCtx, _, finished := process.GetManager().AddContext(ctx.Ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.Links.SrcLink()))
|
||||||
defer finished()
|
defer finished()
|
||||||
|
|
||||||
cmd := exec.CommandContext(processCtx, commands[0], args...)
|
cmd := exec.CommandContext(processCtx, commands[0], args...)
|
||||||
cmd.Env = append(
|
cmd.Env = append(
|
||||||
os.Environ(),
|
os.Environ(),
|
||||||
"GITEA_PREFIX_SRC="+ctx.URLPrefix,
|
"GITEA_PREFIX_SRC="+ctx.Links.SrcLink(),
|
||||||
"GITEA_PREFIX_RAW="+urlRawPrefix,
|
"GITEA_PREFIX_RAW="+ctx.Links.RawLink(),
|
||||||
)
|
)
|
||||||
if !p.IsInputFile {
|
if !p.IsInputFile {
|
||||||
cmd.Stdin = input
|
cmd.Stdin = input
|
||||||
|
|
|
@ -80,15 +80,10 @@ const keywordClass = "issue-keyword"
|
||||||
|
|
||||||
// IsLink reports whether link fits valid format.
|
// IsLink reports whether link fits valid format.
|
||||||
func IsLink(link []byte) bool {
|
func IsLink(link []byte) bool {
|
||||||
return isLink(link)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isLink reports whether link fits valid format.
|
|
||||||
func isLink(link []byte) bool {
|
|
||||||
return validLinksPattern.Match(link)
|
return validLinksPattern.Match(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isLinkStr(link string) bool {
|
func IsLinkStr(link string) bool {
|
||||||
return validLinksPattern.MatchString(link)
|
return validLinksPattern.MatchString(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +339,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
|
||||||
node = node.FirstChild
|
node = node.FirstChild
|
||||||
}
|
}
|
||||||
|
|
||||||
visitNode(ctx, procs, procs, node)
|
visitNode(ctx, procs, node)
|
||||||
|
|
||||||
newNodes := make([]*html.Node, 0, 5)
|
newNodes := make([]*html.Node, 0, 5)
|
||||||
|
|
||||||
|
@ -375,7 +370,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node) {
|
func visitNode(ctx *RenderContext, procs []processor, node *html.Node) {
|
||||||
// Add user-content- to IDs and "#" links if they don't already have them
|
// Add user-content- to IDs and "#" links if they don't already have them
|
||||||
for idx, attr := range node.Attr {
|
for idx, attr := range node.Attr {
|
||||||
val := strings.TrimPrefix(attr.Val, "#")
|
val := strings.TrimPrefix(attr.Val, "#")
|
||||||
|
@ -390,35 +385,29 @@ func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
if attr.Key == "class" && attr.Val == "emoji" {
|
if attr.Key == "class" && attr.Val == "emoji" {
|
||||||
textProcs = nil
|
procs = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We ignore code and pre.
|
// We ignore code and pre.
|
||||||
switch node.Type {
|
switch node.Type {
|
||||||
case html.TextNode:
|
case html.TextNode:
|
||||||
textNode(ctx, textProcs, node)
|
textNode(ctx, procs, node)
|
||||||
case html.ElementNode:
|
case html.ElementNode:
|
||||||
if node.Data == "img" {
|
if node.Data == "img" {
|
||||||
for i, attr := range node.Attr {
|
for i, attr := range node.Attr {
|
||||||
if attr.Key != "src" {
|
if attr.Key != "src" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(attr.Val) > 0 && !isLinkStr(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") {
|
if len(attr.Val) > 0 && !IsLinkStr(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") {
|
||||||
prefix := ctx.URLPrefix
|
attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
|
||||||
if ctx.IsWiki {
|
|
||||||
prefix = util.URLJoin(prefix, "wiki", "raw")
|
|
||||||
}
|
|
||||||
prefix = strings.Replace(prefix, "/src/", "/media/", 1)
|
|
||||||
|
|
||||||
attr.Val = util.URLJoin(prefix, attr.Val)
|
|
||||||
}
|
}
|
||||||
attr.Val = camoHandleLink(attr.Val)
|
attr.Val = camoHandleLink(attr.Val)
|
||||||
node.Attr[i] = attr
|
node.Attr[i] = attr
|
||||||
}
|
}
|
||||||
} else if node.Data == "a" {
|
} else if node.Data == "a" {
|
||||||
// Restrict text in links to emojis
|
// Restrict text in links to emojis
|
||||||
textProcs = emojiProcessors
|
procs = emojiProcessors
|
||||||
} else if node.Data == "code" || node.Data == "pre" {
|
} else if node.Data == "code" || node.Data == "pre" {
|
||||||
return
|
return
|
||||||
} else if node.Data == "i" {
|
} else if node.Data == "i" {
|
||||||
|
@ -444,7 +433,7 @@ func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for n := node.FirstChild; n != nil; n = n.NextSibling {
|
for n := node.FirstChild; n != nil; n = n.NextSibling {
|
||||||
visitNode(ctx, procs, textProcs, n)
|
visitNode(ctx, procs, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ignore everything else
|
// ignore everything else
|
||||||
|
@ -641,10 +630,6 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
|
func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
|
||||||
shortLinkProcessorFull(ctx, node, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
|
||||||
next := node.NextSibling
|
next := node.NextSibling
|
||||||
for node != nil && node != next {
|
for node != nil && node != next {
|
||||||
m := shortLinkPattern.FindStringSubmatchIndex(node.Data)
|
m := shortLinkPattern.FindStringSubmatchIndex(node.Data)
|
||||||
|
@ -665,7 +650,7 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
||||||
if equalPos := strings.IndexByte(v, '='); equalPos == -1 {
|
if equalPos := strings.IndexByte(v, '='); equalPos == -1 {
|
||||||
// There is no equal in this argument; this is a mandatory arg
|
// There is no equal in this argument; this is a mandatory arg
|
||||||
if props["name"] == "" {
|
if props["name"] == "" {
|
||||||
if isLinkStr(v) {
|
if IsLinkStr(v) {
|
||||||
// If we clearly see it is a link, we save it so
|
// If we clearly see it is a link, we save it so
|
||||||
|
|
||||||
// But first we need to ensure, that if both mandatory args provided
|
// But first we need to ensure, that if both mandatory args provided
|
||||||
|
@ -740,7 +725,7 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
||||||
DataAtom: atom.A,
|
DataAtom: atom.A,
|
||||||
}
|
}
|
||||||
childNode.Parent = linkNode
|
childNode.Parent = linkNode
|
||||||
absoluteLink := isLinkStr(link)
|
absoluteLink := IsLinkStr(link)
|
||||||
if !absoluteLink {
|
if !absoluteLink {
|
||||||
if image {
|
if image {
|
||||||
link = strings.ReplaceAll(link, " ", "+")
|
link = strings.ReplaceAll(link, " ", "+")
|
||||||
|
@ -751,16 +736,9 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
||||||
link = url.PathEscape(link)
|
link = url.PathEscape(link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
urlPrefix := ctx.URLPrefix
|
|
||||||
if image {
|
if image {
|
||||||
if !absoluteLink {
|
if !absoluteLink {
|
||||||
if IsSameDomain(urlPrefix) {
|
link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), link)
|
||||||
urlPrefix = strings.Replace(urlPrefix, "/src/", "/raw/", 1)
|
|
||||||
}
|
|
||||||
if ctx.IsWiki {
|
|
||||||
link = util.URLJoin("wiki", "raw", link)
|
|
||||||
}
|
|
||||||
link = util.URLJoin(urlPrefix, link)
|
|
||||||
}
|
}
|
||||||
title := props["title"]
|
title := props["title"]
|
||||||
if title == "" {
|
if title == "" {
|
||||||
|
@ -789,18 +767,15 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
||||||
} else {
|
} else {
|
||||||
if !absoluteLink {
|
if !absoluteLink {
|
||||||
if ctx.IsWiki {
|
if ctx.IsWiki {
|
||||||
link = util.URLJoin("wiki", link)
|
link = util.URLJoin(ctx.Links.WikiLink(), link)
|
||||||
|
} else {
|
||||||
|
link = util.URLJoin(ctx.Links.SrcLink(), link)
|
||||||
}
|
}
|
||||||
link = util.URLJoin(urlPrefix, link)
|
|
||||||
}
|
}
|
||||||
childNode.Type = html.TextNode
|
childNode.Type = html.TextNode
|
||||||
childNode.Data = name
|
childNode.Data = name
|
||||||
}
|
}
|
||||||
if noLink {
|
linkNode.Attr = []html.Attribute{{Key: "href", Val: link}}
|
||||||
linkNode = childNode
|
|
||||||
} else {
|
|
||||||
linkNode.Attr = []html.Attribute{{Key: "href", Val: link}}
|
|
||||||
}
|
|
||||||
replaceContent(node, m[0], m[1], linkNode)
|
replaceContent(node, m[0], m[1], linkNode)
|
||||||
node = node.NextSibling.NextSibling
|
node = node.NextSibling.NextSibling
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,8 +287,8 @@ func TestRender_IssueIndexPattern_Document(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
|
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
|
||||||
if ctx.URLPrefix == "" {
|
if ctx.Links.Base == "" {
|
||||||
ctx.URLPrefix = TestAppURL
|
ctx.Links.Base = TestRepoURL
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
|
@ -303,19 +303,23 @@ func TestRender_AutoLink(t *testing.T) {
|
||||||
test := func(input, expected string) {
|
test := func(input, expected string) {
|
||||||
var buffer strings.Builder
|
var buffer strings.Builder
|
||||||
err := PostProcess(&RenderContext{
|
err := PostProcess(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: TestRepoURL,
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: TestRepoURL,
|
||||||
|
},
|
||||||
|
Metas: localMetas,
|
||||||
}, strings.NewReader(input), &buffer)
|
}, strings.NewReader(input), &buffer)
|
||||||
assert.Equal(t, err, nil)
|
assert.Equal(t, err, nil)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
|
||||||
|
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
err = PostProcess(&RenderContext{
|
err = PostProcess(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: TestRepoURL,
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: TestRepoURL,
|
||||||
IsWiki: true,
|
},
|
||||||
|
Metas: localMetas,
|
||||||
|
IsWiki: true,
|
||||||
}, strings.NewReader(input), &buffer)
|
}, strings.NewReader(input), &buffer)
|
||||||
assert.Equal(t, err, nil)
|
assert.Equal(t, err, nil)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
|
||||||
|
@ -342,9 +346,11 @@ func TestRender_FullIssueURLs(t *testing.T) {
|
||||||
test := func(input, expected string) {
|
test := func(input, expected string) {
|
||||||
var result strings.Builder
|
var result strings.Builder
|
||||||
err := postProcess(&RenderContext{
|
err := postProcess(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: TestRepoURL,
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: TestRepoURL,
|
||||||
|
},
|
||||||
|
Metas: localMetas,
|
||||||
}, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
|
}, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expected, result.String())
|
assert.Equal(t, expected, result.String())
|
||||||
|
|
|
@ -42,8 +42,10 @@ func TestRender_Commits(t *testing.T) {
|
||||||
buffer, err := RenderString(&RenderContext{
|
buffer, err := RenderString(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
RelativePath: ".md",
|
RelativePath: ".md",
|
||||||
URLPrefix: TestRepoURL,
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: TestRepoURL,
|
||||||
|
},
|
||||||
|
Metas: localMetas,
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
|
@ -93,8 +95,10 @@ func TestRender_CrossReferences(t *testing.T) {
|
||||||
buffer, err := RenderString(&RenderContext{
|
buffer, err := RenderString(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
RelativePath: "a.md",
|
RelativePath: "a.md",
|
||||||
URLPrefix: setting.AppSubURL,
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: setting.AppSubURL,
|
||||||
|
},
|
||||||
|
Metas: localMetas,
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
|
@ -138,7 +142,9 @@ func TestRender_links(t *testing.T) {
|
||||||
buffer, err := RenderString(&RenderContext{
|
buffer, err := RenderString(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
RelativePath: "a.md",
|
RelativePath: "a.md",
|
||||||
URLPrefix: TestRepoURL,
|
Links: Links{
|
||||||
|
Base: TestRepoURL,
|
||||||
|
},
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
|
@ -238,7 +244,9 @@ func TestRender_email(t *testing.T) {
|
||||||
res, err := RenderString(&RenderContext{
|
res, err := RenderString(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
RelativePath: "a.md",
|
RelativePath: "a.md",
|
||||||
URLPrefix: TestRepoURL,
|
Links: Links{
|
||||||
|
Base: TestRepoURL,
|
||||||
|
},
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
|
||||||
|
@ -309,7 +317,9 @@ func TestRender_emoji(t *testing.T) {
|
||||||
buffer, err := RenderString(&RenderContext{
|
buffer, err := RenderString(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
RelativePath: "a.md",
|
RelativePath: "a.md",
|
||||||
URLPrefix: TestRepoURL,
|
Links: Links{
|
||||||
|
Base: TestRepoURL,
|
||||||
|
},
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
|
@ -371,29 +381,34 @@ func TestRender_ShortLinks(t *testing.T) {
|
||||||
|
|
||||||
test := func(input, expected, expectedWiki string) {
|
test := func(input, expected, expectedWiki string) {
|
||||||
buffer, err := markdown.RenderString(&RenderContext{
|
buffer, err := markdown.RenderString(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: tree,
|
Links: Links{
|
||||||
|
Base: TestRepoURL,
|
||||||
|
BranchPath: "master",
|
||||||
|
},
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
buffer, err = markdown.RenderString(&RenderContext{
|
buffer, err = markdown.RenderString(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: TestRepoURL,
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: TestRepoURL,
|
||||||
IsWiki: true,
|
},
|
||||||
|
Metas: localMetas,
|
||||||
|
IsWiki: true,
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
rawtree := util.URLJoin(TestRepoURL, "raw", "master")
|
mediatree := util.URLJoin(TestRepoURL, "media", "master")
|
||||||
url := util.URLJoin(tree, "Link")
|
url := util.URLJoin(tree, "Link")
|
||||||
otherURL := util.URLJoin(tree, "Other-Link")
|
otherURL := util.URLJoin(tree, "Other-Link")
|
||||||
encodedURL := util.URLJoin(tree, "Link%3F")
|
encodedURL := util.URLJoin(tree, "Link%3F")
|
||||||
imgurl := util.URLJoin(rawtree, "Link.jpg")
|
imgurl := util.URLJoin(mediatree, "Link.jpg")
|
||||||
otherImgurl := util.URLJoin(rawtree, "Link+Other.jpg")
|
otherImgurl := util.URLJoin(mediatree, "Link+Other.jpg")
|
||||||
encodedImgurl := util.URLJoin(rawtree, "Link+%23.jpg")
|
encodedImgurl := util.URLJoin(mediatree, "Link+%23.jpg")
|
||||||
notencodedImgurl := util.URLJoin(rawtree, "some", "path", "Link+#.jpg")
|
notencodedImgurl := util.URLJoin(mediatree, "some", "path", "Link+#.jpg")
|
||||||
urlWiki := util.URLJoin(TestRepoURL, "wiki", "Link")
|
urlWiki := util.URLJoin(TestRepoURL, "wiki", "Link")
|
||||||
otherURLWiki := util.URLJoin(TestRepoURL, "wiki", "Other-Link")
|
otherURLWiki := util.URLJoin(TestRepoURL, "wiki", "Other-Link")
|
||||||
encodedURLWiki := util.URLJoin(TestRepoURL, "wiki", "Link%3F")
|
encodedURLWiki := util.URLJoin(TestRepoURL, "wiki", "Link%3F")
|
||||||
|
@ -475,21 +490,25 @@ func TestRender_ShortLinks(t *testing.T) {
|
||||||
|
|
||||||
func TestRender_RelativeImages(t *testing.T) {
|
func TestRender_RelativeImages(t *testing.T) {
|
||||||
setting.AppURL = TestAppURL
|
setting.AppURL = TestAppURL
|
||||||
tree := util.URLJoin(TestRepoURL, "src", "master")
|
|
||||||
|
|
||||||
test := func(input, expected, expectedWiki string) {
|
test := func(input, expected, expectedWiki string) {
|
||||||
buffer, err := markdown.RenderString(&RenderContext{
|
buffer, err := markdown.RenderString(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: tree,
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: TestRepoURL,
|
||||||
|
BranchPath: "master",
|
||||||
|
},
|
||||||
|
Metas: localMetas,
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
buffer, err = markdown.RenderString(&RenderContext{
|
buffer, err = markdown.RenderString(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: TestRepoURL,
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: TestRepoURL,
|
||||||
IsWiki: true,
|
},
|
||||||
|
Metas: localMetas,
|
||||||
|
IsWiki: true,
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
|
||||||
|
@ -521,9 +540,11 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
||||||
|
|
||||||
var res strings.Builder
|
var res strings.Builder
|
||||||
err := PostProcess(&RenderContext{
|
err := PostProcess(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: "https://example.com",
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: "https://example.com",
|
||||||
|
},
|
||||||
|
Metas: localMetas,
|
||||||
}, strings.NewReader(data), &res)
|
}, strings.NewReader(data), &res)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, res.String(), "<html")
|
assert.NotContains(t, res.String(), "<html")
|
||||||
|
@ -532,9 +553,11 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
||||||
|
|
||||||
res.Reset()
|
res.Reset()
|
||||||
err = PostProcess(&RenderContext{
|
err = PostProcess(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: "https://example.com",
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: "https://example.com",
|
||||||
|
},
|
||||||
|
Metas: localMetas,
|
||||||
}, strings.NewReader(data), &res)
|
}, strings.NewReader(data), &res)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -543,6 +566,7 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
||||||
|
|
||||||
func TestPostProcess_RenderDocument(t *testing.T) {
|
func TestPostProcess_RenderDocument(t *testing.T) {
|
||||||
setting.AppURL = TestAppURL
|
setting.AppURL = TestAppURL
|
||||||
|
setting.StaticURLPrefix = TestAppURL // can't run standalone
|
||||||
|
|
||||||
localMetas := map[string]string{
|
localMetas := map[string]string{
|
||||||
"user": "go-gitea",
|
"user": "go-gitea",
|
||||||
|
@ -553,9 +577,11 @@ func TestPostProcess_RenderDocument(t *testing.T) {
|
||||||
test := func(input, expected string) {
|
test := func(input, expected string) {
|
||||||
var res strings.Builder
|
var res strings.Builder
|
||||||
err := PostProcess(&RenderContext{
|
err := PostProcess(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: "https://example.com",
|
Links: Links{
|
||||||
Metas: localMetas,
|
Base: "https://example.com",
|
||||||
|
},
|
||||||
|
Metas: localMetas,
|
||||||
}, strings.NewReader(input), &res)
|
}, strings.NewReader(input), &res)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
|
||||||
|
@ -589,9 +615,8 @@ func TestIssue16020(t *testing.T) {
|
||||||
|
|
||||||
var res strings.Builder
|
var res strings.Builder
|
||||||
err := PostProcess(&RenderContext{
|
err := PostProcess(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: "https://example.com",
|
Metas: localMetas,
|
||||||
Metas: localMetas,
|
|
||||||
}, strings.NewReader(data), &res)
|
}, strings.NewReader(data), &res)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, data, res.String())
|
assert.Equal(t, data, res.String())
|
||||||
|
@ -606,9 +631,8 @@ func BenchmarkEmojiPostprocess(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
var res strings.Builder
|
var res strings.Builder
|
||||||
err := PostProcess(&RenderContext{
|
err := PostProcess(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: "https://example.com",
|
Metas: localMetas,
|
||||||
Metas: localMetas,
|
|
||||||
}, strings.NewReader(data), &res)
|
}, strings.NewReader(data), &res)
|
||||||
assert.NoError(b, err)
|
assert.NoError(b, err)
|
||||||
}
|
}
|
||||||
|
@ -617,8 +641,10 @@ func BenchmarkEmojiPostprocess(b *testing.B) {
|
||||||
func TestFuzz(t *testing.T) {
|
func TestFuzz(t *testing.T) {
|
||||||
s := "t/l/issues/8#/../../a"
|
s := "t/l/issues/8#/../../a"
|
||||||
renderContext := RenderContext{
|
renderContext := RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: "https://example.com/go-gitea/gitea",
|
Links: Links{
|
||||||
|
Base: "https://example.com/go-gitea/gitea",
|
||||||
|
},
|
||||||
Metas: map[string]string{
|
Metas: map[string]string{
|
||||||
"user": "go-gitea",
|
"user": "go-gitea",
|
||||||
"repo": "gitea",
|
"repo": "gitea",
|
||||||
|
@ -635,9 +661,8 @@ func TestIssue18471(t *testing.T) {
|
||||||
|
|
||||||
var res strings.Builder
|
var res strings.Builder
|
||||||
err := PostProcess(&RenderContext{
|
err := PostProcess(&RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: "https://example.com",
|
Metas: localMetas,
|
||||||
Metas: localMetas,
|
|
||||||
}, strings.NewReader(data), &res)
|
}, strings.NewReader(data), &res)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -87,18 +87,8 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
||||||
// Check if the destination is a real link
|
// Check if the destination is a real link
|
||||||
link := v.Destination
|
link := v.Destination
|
||||||
if len(link) > 0 && !markup.IsLink(link) {
|
if len(link) > 0 && !markup.IsLink(link) {
|
||||||
prefix := pc.Get(urlPrefixKey).(string)
|
v.Destination = []byte(giteautil.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), string(link)))
|
||||||
if pc.Get(isWikiKey).(bool) {
|
|
||||||
prefix = giteautil.URLJoin(prefix, "wiki", "raw")
|
|
||||||
}
|
|
||||||
prefix = strings.Replace(prefix, "/src/", "/media/", 1)
|
|
||||||
|
|
||||||
lnk := strings.TrimLeft(string(link), "/")
|
|
||||||
|
|
||||||
lnk = giteautil.URLJoin(prefix, lnk)
|
|
||||||
link = []byte(lnk)
|
|
||||||
}
|
}
|
||||||
v.Destination = link
|
|
||||||
|
|
||||||
parent := n.Parent()
|
parent := n.Parent()
|
||||||
// Create a link around image only if parent is not already a link
|
// Create a link around image only if parent is not already a link
|
||||||
|
@ -107,7 +97,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
||||||
|
|
||||||
// Create a link wrapper
|
// Create a link wrapper
|
||||||
wrap := ast.NewLink()
|
wrap := ast.NewLink()
|
||||||
wrap.Destination = link
|
wrap.Destination = v.Destination
|
||||||
wrap.Title = v.Title
|
wrap.Title = v.Title
|
||||||
wrap.SetAttributeString("target", []byte("_blank"))
|
wrap.SetAttributeString("target", []byte("_blank"))
|
||||||
|
|
||||||
|
@ -143,11 +133,15 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
||||||
link[0] != '#' && !bytes.HasPrefix(link, byteMailto) {
|
link[0] != '#' && !bytes.HasPrefix(link, byteMailto) {
|
||||||
// special case: this is not a link, a hash link or a mailto:, so it's a
|
// special case: this is not a link, a hash link or a mailto:, so it's a
|
||||||
// relative URL
|
// relative URL
|
||||||
lnk := string(link)
|
|
||||||
if pc.Get(isWikiKey).(bool) {
|
var base string
|
||||||
lnk = giteautil.URLJoin("wiki", lnk)
|
if ctx.IsWiki {
|
||||||
|
base = ctx.Links.WikiLink()
|
||||||
|
} else {
|
||||||
|
base = ctx.Links.Base
|
||||||
}
|
}
|
||||||
link = []byte(giteautil.URLJoin(pc.Get(urlPrefixKey).(string), lnk))
|
|
||||||
|
link = []byte(giteautil.URLJoin(base, string(link)))
|
||||||
}
|
}
|
||||||
if len(link) > 0 && link[0] == '#' {
|
if len(link) > 0 && link[0] == '#' {
|
||||||
link = []byte("#user-content-" + string(link)[1:])
|
link = []byte("#user-content-" + string(link)[1:])
|
||||||
|
@ -188,9 +182,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
||||||
applyElementDir(v)
|
applyElementDir(v)
|
||||||
case *ast.Text:
|
case *ast.Text:
|
||||||
if v.SoftLineBreak() && !v.HardLineBreak() {
|
if v.SoftLineBreak() && !v.HardLineBreak() {
|
||||||
renderMetas := pc.Get(renderMetasKey).(map[string]string)
|
if ctx.Metas["mode"] != "document" {
|
||||||
mode := renderMetas["mode"]
|
|
||||||
if mode != "document" {
|
|
||||||
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInComments)
|
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInComments)
|
||||||
} else {
|
} else {
|
||||||
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments)
|
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments)
|
||||||
|
|
|
@ -34,9 +34,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
urlPrefixKey = parser.NewContextKey()
|
|
||||||
isWikiKey = parser.NewContextKey()
|
|
||||||
renderMetasKey = parser.NewContextKey()
|
|
||||||
renderContextKey = parser.NewContextKey()
|
renderContextKey = parser.NewContextKey()
|
||||||
renderConfigKey = parser.NewContextKey()
|
renderConfigKey = parser.NewContextKey()
|
||||||
)
|
)
|
||||||
|
@ -66,9 +63,6 @@ func (l *limitWriter) Write(data []byte) (int, error) {
|
||||||
// newParserContext creates a parser.Context with the render context set
|
// newParserContext creates a parser.Context with the render context set
|
||||||
func newParserContext(ctx *markup.RenderContext) parser.Context {
|
func newParserContext(ctx *markup.RenderContext) parser.Context {
|
||||||
pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
|
pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
|
||||||
pc.Set(urlPrefixKey, ctx.URLPrefix)
|
|
||||||
pc.Set(isWikiKey, ctx.IsWiki)
|
|
||||||
pc.Set(renderMetasKey, ctx.Metas)
|
|
||||||
pc.Set(renderContextKey, ctx)
|
pc.Set(renderContextKey, ctx)
|
||||||
return pc
|
return pc
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,16 +52,20 @@ func TestRender_StandardLinks(t *testing.T) {
|
||||||
|
|
||||||
test := func(input, expected, expectedWiki string) {
|
test := func(input, expected, expectedWiki string) {
|
||||||
buffer, err := RenderString(&markup.RenderContext{
|
buffer, err := RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: setting.AppSubURL,
|
Links: markup.Links{
|
||||||
|
Base: setting.AppSubURL,
|
||||||
|
},
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
|
|
||||||
buffer, err = RenderString(&markup.RenderContext{
|
buffer, err = RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: setting.AppSubURL,
|
Links: markup.Links{
|
||||||
IsWiki: true,
|
Base: setting.AppSubURL,
|
||||||
|
},
|
||||||
|
IsWiki: true,
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
|
||||||
|
@ -83,8 +87,10 @@ func TestRender_Images(t *testing.T) {
|
||||||
|
|
||||||
test := func(input, expected string) {
|
test := func(input, expected string) {
|
||||||
buffer, err := RenderString(&markup.RenderContext{
|
buffer, err := RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: setting.AppSubURL,
|
Links: markup.Links{
|
||||||
|
Base: setting.AppSubURL,
|
||||||
|
},
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
|
@ -107,7 +113,6 @@ func TestRender_Images(t *testing.T) {
|
||||||
"[!["+title+"]("+url+")]("+href+")",
|
"[!["+title+"]("+url+")]("+href+")",
|
||||||
`<p><a href="`+href+`" rel="nofollow"><img src="`+result+`" alt="`+title+`"/></a></p>`)
|
`<p><a href="`+href+`" rel="nofollow"><img src="`+result+`" alt="`+title+`"/></a></p>`)
|
||||||
|
|
||||||
url = "/../../.images/src/02/train.jpg"
|
|
||||||
test(
|
test(
|
||||||
"!["+title+"]("+url+")",
|
"!["+title+"]("+url+")",
|
||||||
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`)
|
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`)
|
||||||
|
@ -286,14 +291,16 @@ func TestTotal_RenderWiki(t *testing.T) {
|
||||||
setting.AppURL = AppURL
|
setting.AppURL = AppURL
|
||||||
setting.AppSubURL = AppSubURL
|
setting.AppSubURL = AppSubURL
|
||||||
|
|
||||||
answers := testAnswers(util.URLJoin(AppSubURL, "wiki/"), util.URLJoin(AppSubURL, "wiki", "raw/"))
|
answers := testAnswers(util.URLJoin(AppSubURL, "wiki"), util.URLJoin(AppSubURL, "wiki", "raw"))
|
||||||
|
|
||||||
for i := 0; i < len(sameCases); i++ {
|
for i := 0; i < len(sameCases); i++ {
|
||||||
line, err := RenderString(&markup.RenderContext{
|
line, err := RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: AppSubURL,
|
Links: markup.Links{
|
||||||
Metas: localMetas,
|
Base: setting.AppSubURL,
|
||||||
IsWiki: true,
|
},
|
||||||
|
Metas: localMetas,
|
||||||
|
IsWiki: true,
|
||||||
}, sameCases[i])
|
}, sameCases[i])
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, answers[i], line)
|
assert.Equal(t, answers[i], line)
|
||||||
|
@ -314,9 +321,11 @@ func TestTotal_RenderWiki(t *testing.T) {
|
||||||
|
|
||||||
for i := 0; i < len(testCases); i += 2 {
|
for i := 0; i < len(testCases); i += 2 {
|
||||||
line, err := RenderString(&markup.RenderContext{
|
line, err := RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: AppSubURL,
|
Links: markup.Links{
|
||||||
IsWiki: true,
|
Base: setting.AppSubURL,
|
||||||
|
},
|
||||||
|
IsWiki: true,
|
||||||
}, testCases[i])
|
}, testCases[i])
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, testCases[i+1], line)
|
assert.Equal(t, testCases[i+1], line)
|
||||||
|
@ -327,13 +336,16 @@ func TestTotal_RenderString(t *testing.T) {
|
||||||
setting.AppURL = AppURL
|
setting.AppURL = AppURL
|
||||||
setting.AppSubURL = AppSubURL
|
setting.AppSubURL = AppSubURL
|
||||||
|
|
||||||
answers := testAnswers(util.URLJoin(AppSubURL, "src", "master/"), util.URLJoin(AppSubURL, "raw", "master/"))
|
answers := testAnswers(util.URLJoin(AppSubURL, "src", "master"), util.URLJoin(AppSubURL, "media", "master"))
|
||||||
|
|
||||||
for i := 0; i < len(sameCases); i++ {
|
for i := 0; i < len(sameCases); i++ {
|
||||||
line, err := RenderString(&markup.RenderContext{
|
line, err := RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: util.URLJoin(AppSubURL, "src", "master/"),
|
Links: markup.Links{
|
||||||
Metas: localMetas,
|
Base: AppSubURL,
|
||||||
|
BranchPath: "master",
|
||||||
|
},
|
||||||
|
Metas: localMetas,
|
||||||
}, sameCases[i])
|
}, sameCases[i])
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, answers[i], line)
|
assert.Equal(t, answers[i], line)
|
||||||
|
@ -343,8 +355,10 @@ func TestTotal_RenderString(t *testing.T) {
|
||||||
|
|
||||||
for i := 0; i < len(testCases); i += 2 {
|
for i := 0; i < len(testCases); i += 2 {
|
||||||
line, err := RenderString(&markup.RenderContext{
|
line, err := RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: AppSubURL,
|
Links: markup.Links{
|
||||||
|
Base: AppSubURL,
|
||||||
|
},
|
||||||
}, testCases[i])
|
}, testCases[i])
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, testCases[i+1], line)
|
assert.Equal(t, testCases[i+1], line)
|
||||||
|
@ -556,3 +570,367 @@ foo: bar
|
||||||
assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
|
assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenderLinks(t *testing.T) {
|
||||||
|
input := ` space @mention-user
|
||||||
|
/just/a/path.bin
|
||||||
|
https://example.com/file.bin
|
||||||
|
[local link](file.bin)
|
||||||
|
[remote link](https://example.com)
|
||||||
|
[[local link|file.bin]]
|
||||||
|
[[remote link|https://example.com]]
|
||||||
|
![local image](image.jpg)
|
||||||
|
![remote image](https://example.com/image.jpg)
|
||||||
|
[[local image|image.jpg]]
|
||||||
|
[[remote link|https://example.com/image.jpg]]
|
||||||
|
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||||
|
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||||
|
:+1:
|
||||||
|
mail@domain.com
|
||||||
|
@mention-user test
|
||||||
|
#123
|
||||||
|
space
|
||||||
|
`
|
||||||
|
cases := []struct {
|
||||||
|
Links markup.Links
|
||||||
|
IsWiki bool
|
||||||
|
Expected string
|
||||||
|
}{
|
||||||
|
{ // 0
|
||||||
|
Links: markup.Links{},
|
||||||
|
IsWiki: false,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/src/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 1
|
||||||
|
Links: markup.Links{},
|
||||||
|
IsWiki: true,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/wiki/raw/image.jpg" rel="nofollow"><img src="/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 2
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "https://gitea.io/",
|
||||||
|
},
|
||||||
|
IsWiki: false,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="https://gitea.io/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="https://gitea.io/src/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="https://gitea.io/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="https://gitea.io/image.jpg" rel="nofollow"><img src="https://gitea.io/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 3
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "https://gitea.io/",
|
||||||
|
},
|
||||||
|
IsWiki: true,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="https://gitea.io/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="https://gitea.io/wiki/raw/image.jpg" rel="nofollow"><img src="https://gitea.io/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 4
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "/relative/path",
|
||||||
|
},
|
||||||
|
IsWiki: false,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/relative/path/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/src/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/relative/path/image.jpg" rel="nofollow"><img src="/relative/path/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 5
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "/relative/path",
|
||||||
|
},
|
||||||
|
IsWiki: true,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 6
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "/user/repo",
|
||||||
|
BranchPath: "branch/main",
|
||||||
|
},
|
||||||
|
IsWiki: false,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/user/repo/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/user/repo/src/branch/main/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/user/repo/media/branch/main/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/user/repo/media/branch/main/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 7
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "/relative/path",
|
||||||
|
BranchPath: "branch/main",
|
||||||
|
},
|
||||||
|
IsWiki: true,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 8
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "/user/repo",
|
||||||
|
TreePath: "sub/folder",
|
||||||
|
},
|
||||||
|
IsWiki: false,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/user/repo/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/user/repo/src/sub/folder/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/user/repo/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/user/repo/image.jpg" rel="nofollow"><img src="/user/repo/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 9
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "/relative/path",
|
||||||
|
TreePath: "sub/folder",
|
||||||
|
},
|
||||||
|
IsWiki: true,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 10
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "/user/repo",
|
||||||
|
BranchPath: "branch/main",
|
||||||
|
TreePath: "sub/folder",
|
||||||
|
},
|
||||||
|
IsWiki: false,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/user/repo/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/user/repo/src/branch/main/sub/folder/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/user/repo/media/branch/main/sub/folder/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/user/repo/media/branch/main/sub/folder/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ // 11
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: "/relative/path",
|
||||||
|
BranchPath: "branch/main",
|
||||||
|
TreePath: "sub/folder",
|
||||||
|
},
|
||||||
|
IsWiki: true,
|
||||||
|
Expected: `<p>space @mention-user<br/>
|
||||||
|
/just/a/path.bin<br/>
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||||
|
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||||
|
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||||
|
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||||
|
@mention-user test<br/>
|
||||||
|
#123<br/>
|
||||||
|
space</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range cases {
|
||||||
|
result, err := RenderString(&markup.RenderContext{Ctx: context.Background(), Links: c.Links, IsWiki: c.IsWiki}, input)
|
||||||
|
assert.NoError(t, err, "Unexpected error in testcase: %v", i)
|
||||||
|
assert.Equal(t, c.Expected, result, "Unexpected result in testcase %v", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
package markup
|
package markup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
"io"
|
"io"
|
||||||
|
@ -101,8 +100,7 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
|
||||||
|
|
||||||
w := &Writer{
|
w := &Writer{
|
||||||
HTMLWriter: htmlWriter,
|
HTMLWriter: htmlWriter,
|
||||||
URLPrefix: ctx.URLPrefix,
|
Ctx: ctx,
|
||||||
IsWiki: ctx.IsWiki,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlWriter.ExtendingWriter = w
|
htmlWriter.ExtendingWriter = w
|
||||||
|
@ -132,63 +130,53 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri
|
||||||
// Writer implements org.Writer
|
// Writer implements org.Writer
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
*org.HTMLWriter
|
*org.HTMLWriter
|
||||||
URLPrefix string
|
Ctx *markup.RenderContext
|
||||||
IsWiki bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var byteMailto = []byte("mailto:")
|
const mailto = "mailto:"
|
||||||
|
|
||||||
// WriteRegularLink renders images, links or videos
|
func (r *Writer) resolveLink(l org.RegularLink) string {
|
||||||
func (r *Writer) WriteRegularLink(l org.RegularLink) {
|
link := html.EscapeString(l.URL)
|
||||||
link := []byte(html.EscapeString(l.URL))
|
|
||||||
if l.Protocol == "file" {
|
if l.Protocol == "file" {
|
||||||
link = link[len("file:"):]
|
link = link[len("file:"):]
|
||||||
}
|
}
|
||||||
if len(link) > 0 && !markup.IsLink(link) &&
|
if len(link) > 0 && !markup.IsLinkStr(link) &&
|
||||||
link[0] != '#' && !bytes.HasPrefix(link, byteMailto) {
|
link[0] != '#' && !strings.HasPrefix(link, mailto) {
|
||||||
lnk := string(link)
|
base := r.Ctx.Links.Base
|
||||||
if r.IsWiki {
|
switch l.Kind() {
|
||||||
lnk = util.URLJoin("wiki", lnk)
|
case "image", "video":
|
||||||
|
base = r.Ctx.Links.ResolveMediaLink(r.Ctx.IsWiki)
|
||||||
}
|
}
|
||||||
link = []byte(util.URLJoin(r.URLPrefix, lnk))
|
link = util.URLJoin(base, link)
|
||||||
}
|
}
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteRegularLink renders images, links or videos
|
||||||
|
func (r *Writer) WriteRegularLink(l org.RegularLink) {
|
||||||
|
link := r.resolveLink(l)
|
||||||
|
|
||||||
// Inspired by https://github.com/niklasfasching/go-org/blob/6eb20dbda93cb88c3503f7508dc78cbbc639378f/org/html_writer.go#L406-L427
|
// Inspired by https://github.com/niklasfasching/go-org/blob/6eb20dbda93cb88c3503f7508dc78cbbc639378f/org/html_writer.go#L406-L427
|
||||||
switch l.Kind() {
|
switch l.Kind() {
|
||||||
case "image":
|
case "image":
|
||||||
if l.Description == nil {
|
if l.Description == nil {
|
||||||
imageSrc := getMediaURL(link)
|
fmt.Fprintf(r, `<img src="%s" alt="%s" />`, link, link)
|
||||||
fmt.Fprintf(r, `<img src="%s" alt="%s" title="%s" />`, imageSrc, link, link)
|
|
||||||
} else {
|
} else {
|
||||||
description := strings.TrimPrefix(org.String(l.Description...), "file:")
|
imageSrc := r.resolveLink(l.Description[0].(org.RegularLink))
|
||||||
imageSrc := getMediaURL([]byte(description))
|
|
||||||
fmt.Fprintf(r, `<a href="%s"><img src="%s" alt="%s" /></a>`, link, imageSrc, imageSrc)
|
fmt.Fprintf(r, `<a href="%s"><img src="%s" alt="%s" /></a>`, link, imageSrc, imageSrc)
|
||||||
}
|
}
|
||||||
case "video":
|
case "video":
|
||||||
if l.Description == nil {
|
if l.Description == nil {
|
||||||
imageSrc := getMediaURL(link)
|
fmt.Fprintf(r, `<video src="%s">%s</video>`, link, link)
|
||||||
fmt.Fprintf(r, `<video src="%s" title="%s">%s</video>`, imageSrc, link, link)
|
|
||||||
} else {
|
} else {
|
||||||
description := strings.TrimPrefix(org.String(l.Description...), "file:")
|
videoSrc := r.resolveLink(l.Description[0].(org.RegularLink))
|
||||||
videoSrc := getMediaURL([]byte(description))
|
fmt.Fprintf(r, `<a href="%s"><video src="%s">%s</video></a>`, link, videoSrc, videoSrc)
|
||||||
fmt.Fprintf(r, `<a href="%s"><video src="%s" title="%s"></video></a>`, link, videoSrc, videoSrc)
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
description := string(link)
|
description := link
|
||||||
if l.Description != nil {
|
if l.Description != nil {
|
||||||
description = r.WriteNodesAsString(l.Description...)
|
description = r.WriteNodesAsString(l.Description...)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(r, `<a href="%s" title="%s">%s</a>`, link, description, description)
|
fmt.Fprintf(r, `<a href="%s" title="%s">%s</a>`, link, description, description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMediaURL(l []byte) string {
|
|
||||||
srcURL := string(l)
|
|
||||||
|
|
||||||
// Check if link is valid
|
|
||||||
if len(srcURL) > 0 && !markup.IsLink(l) {
|
|
||||||
srcURL = strings.Replace(srcURL, "/src/", "/media/", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return srcURL
|
|
||||||
}
|
|
||||||
|
|
|
@ -27,8 +27,10 @@ func TestRender_StandardLinks(t *testing.T) {
|
||||||
|
|
||||||
test := func(input, expected string) {
|
test := func(input, expected string) {
|
||||||
buffer, err := RenderString(&markup.RenderContext{
|
buffer, err := RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: setting.AppSubURL,
|
Links: markup.Links{
|
||||||
|
Base: setting.AppSubURL,
|
||||||
|
},
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
|
@ -48,8 +50,10 @@ func TestRender_Media(t *testing.T) {
|
||||||
|
|
||||||
test := func(input, expected string) {
|
test := func(input, expected string) {
|
||||||
buffer, err := RenderString(&markup.RenderContext{
|
buffer, err := RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: setting.AppSubURL,
|
Links: markup.Links{
|
||||||
|
Base: setting.AppSubURL,
|
||||||
|
},
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
|
@ -59,19 +63,19 @@ func TestRender_Media(t *testing.T) {
|
||||||
result := util.URLJoin(AppSubURL, url)
|
result := util.URLJoin(AppSubURL, url)
|
||||||
|
|
||||||
test("[[file:"+url+"]]",
|
test("[[file:"+url+"]]",
|
||||||
"<p><img src=\""+result+"\" alt=\""+result+"\" title=\""+result+"\" /></p>")
|
"<p><img src=\""+result+"\" alt=\""+result+"\" /></p>")
|
||||||
|
|
||||||
// With description.
|
// With description.
|
||||||
test("[[https://example.com][https://example.com/example.svg]]",
|
test("[[https://example.com][https://example.com/example.svg]]",
|
||||||
`<p><a href="https://example.com"><img src="https://example.com/example.svg" alt="https://example.com/example.svg" /></a></p>`)
|
`<p><a href="https://example.com"><img src="https://example.com/example.svg" alt="https://example.com/example.svg" /></a></p>`)
|
||||||
test("[[https://example.com][https://example.com/example.mp4]]",
|
test("[[https://example.com][https://example.com/example.mp4]]",
|
||||||
`<p><a href="https://example.com"><video src="https://example.com/example.mp4" title="https://example.com/example.mp4"></video></a></p>`)
|
`<p><a href="https://example.com"><video src="https://example.com/example.mp4">https://example.com/example.mp4</video></a></p>`)
|
||||||
|
|
||||||
// Without description.
|
// Without description.
|
||||||
test("[[https://example.com/example.svg]]",
|
test("[[https://example.com/example.svg]]",
|
||||||
`<p><img src="https://example.com/example.svg" alt="https://example.com/example.svg" title="https://example.com/example.svg" /></p>`)
|
`<p><img src="https://example.com/example.svg" alt="https://example.com/example.svg" /></p>`)
|
||||||
test("[[https://example.com/example.mp4]]",
|
test("[[https://example.com/example.mp4]]",
|
||||||
`<p><video src="https://example.com/example.mp4" title="https://example.com/example.mp4">https://example.com/example.mp4</video></p>`)
|
`<p><video src="https://example.com/example.mp4">https://example.com/example.mp4</video></p>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRender_Source(t *testing.T) {
|
func TestRender_Source(t *testing.T) {
|
||||||
|
@ -80,8 +84,7 @@ func TestRender_Source(t *testing.T) {
|
||||||
|
|
||||||
test := func(input, expected string) {
|
test := func(input, expected string) {
|
||||||
buffer, err := RenderString(&markup.RenderContext{
|
buffer, err := RenderString(&markup.RenderContext{
|
||||||
Ctx: git.DefaultContext,
|
Ctx: git.DefaultContext,
|
||||||
URLPrefix: setting.AppSubURL,
|
|
||||||
}, input)
|
}, input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/yuin/goldmark/ast"
|
"github.com/yuin/goldmark/ast"
|
||||||
)
|
)
|
||||||
|
@ -69,7 +70,7 @@ type RenderContext struct {
|
||||||
RelativePath string // relative path from tree root of the branch
|
RelativePath string // relative path from tree root of the branch
|
||||||
Type string
|
Type string
|
||||||
IsWiki bool
|
IsWiki bool
|
||||||
URLPrefix string
|
Links Links
|
||||||
Metas map[string]string
|
Metas map[string]string
|
||||||
DefaultLink string
|
DefaultLink string
|
||||||
GitRepo *git.Repository
|
GitRepo *git.Repository
|
||||||
|
@ -80,6 +81,45 @@ type RenderContext struct {
|
||||||
InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
|
InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Links struct {
|
||||||
|
Base string
|
||||||
|
BranchPath string
|
||||||
|
TreePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Links) HasBranchInfo() bool {
|
||||||
|
return l.BranchPath != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Links) SrcLink() string {
|
||||||
|
return util.URLJoin(l.Base, "src", l.BranchPath, l.TreePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Links) MediaLink() string {
|
||||||
|
return util.URLJoin(l.Base, "media", l.BranchPath, l.TreePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Links) RawLink() string {
|
||||||
|
return util.URLJoin(l.Base, "raw", l.BranchPath, l.TreePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Links) WikiLink() string {
|
||||||
|
return util.URLJoin(l.Base, "wiki")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Links) WikiRawLink() string {
|
||||||
|
return util.URLJoin(l.Base, "wiki/raw")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Links) ResolveMediaLink(isWiki bool) string {
|
||||||
|
if isWiki {
|
||||||
|
return l.WikiRawLink()
|
||||||
|
} else if l.HasBranchInfo() {
|
||||||
|
return l.MediaLink()
|
||||||
|
}
|
||||||
|
return l.Base
|
||||||
|
}
|
||||||
|
|
||||||
// Cancel runs any cleanup functions that have been registered for this Ctx
|
// Cancel runs any cleanup functions that have been registered for this Ctx
|
||||||
func (ctx *RenderContext) Cancel() {
|
func (ctx *RenderContext) Cancel() {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
|
|
@ -168,8 +168,8 @@ func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbsslMode s
|
||||||
RawQuery: dbParam,
|
RawQuery: dbParam,
|
||||||
}
|
}
|
||||||
query := connURL.Query()
|
query := connURL.Query()
|
||||||
if strings.HasPrefix(dbHost, "/") { // looks like a unix socket
|
if strings.HasPrefix(host, "/") { // looks like a unix socket
|
||||||
query.Add("host", dbHost)
|
query.Add("host", host)
|
||||||
connURL.Host = ":" + port
|
connURL.Host = ":" + port
|
||||||
}
|
}
|
||||||
query.Set("sslmode", dbsslMode)
|
query.Set("sslmode", dbsslMode)
|
||||||
|
|
|
@ -77,6 +77,14 @@ func Test_getPostgreSQLConnectionString(t *testing.T) {
|
||||||
SSLMode: "false",
|
SSLMode: "false",
|
||||||
Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
|
Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Host: "/tmp/pg.sock:6432",
|
||||||
|
User: "testuser",
|
||||||
|
Passwd: "pass",
|
||||||
|
Name: "gitea",
|
||||||
|
SSLMode: "false",
|
||||||
|
Output: "postgres://testuser:pass@:6432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
User: "pgsqlusername",
|
User: "pgsqlusername",
|
||||||
|
|
|
@ -161,10 +161,13 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sectionHasDisableQueryAuthToken := sec.HasKey("DISABLE_QUERY_AUTH_TOKEN")
|
||||||
|
|
||||||
// TODO: default value should be true in future releases
|
// TODO: default value should be true in future releases
|
||||||
DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false)
|
DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false)
|
||||||
|
|
||||||
if !DisableQueryAuthToken {
|
// warn if the setting is set to false explicitly
|
||||||
|
if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken {
|
||||||
log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.")
|
log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,6 @@ func NewFuncMap() template.FuncMap {
|
||||||
"RenderEmoji": RenderEmoji,
|
"RenderEmoji": RenderEmoji,
|
||||||
"RenderEmojiPlain": emoji.ReplaceAliases,
|
"RenderEmojiPlain": emoji.ReplaceAliases,
|
||||||
"ReactionToEmoji": ReactionToEmoji,
|
"ReactionToEmoji": ReactionToEmoji,
|
||||||
"RenderNote": RenderNote,
|
|
||||||
|
|
||||||
"RenderMarkdownToHtml": RenderMarkdownToHtml,
|
"RenderMarkdownToHtml": RenderMarkdownToHtml,
|
||||||
"RenderLabel": RenderLabel,
|
"RenderLabel": RenderLabel,
|
||||||
|
|
|
@ -24,21 +24,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// RenderCommitMessage renders commit message with XSS-safe and special links.
|
// RenderCommitMessage renders commit message with XSS-safe and special links.
|
||||||
func RenderCommitMessage(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
|
func RenderCommitMessage(ctx context.Context, msg string, metas map[string]string) template.HTML {
|
||||||
return RenderCommitMessageLink(ctx, msg, urlPrefix, "", metas)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderCommitMessageLink renders commit message as a XXS-safe link to the provided
|
|
||||||
// default url, handling for special links.
|
|
||||||
func RenderCommitMessageLink(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML {
|
|
||||||
cleanMsg := template.HTMLEscapeString(msg)
|
cleanMsg := template.HTMLEscapeString(msg)
|
||||||
// we can safely assume that it will not return any error, since there
|
// we can safely assume that it will not return any error, since there
|
||||||
// shouldn't be any special HTML.
|
// shouldn't be any special HTML.
|
||||||
fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
|
fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: urlPrefix,
|
Metas: metas,
|
||||||
DefaultLink: urlDefault,
|
|
||||||
Metas: metas,
|
|
||||||
}, cleanMsg)
|
}, cleanMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("RenderCommitMessage: %v", err)
|
log.Error("RenderCommitMessage: %v", err)
|
||||||
|
@ -51,9 +43,9 @@ func RenderCommitMessageLink(ctx context.Context, msg, urlPrefix, urlDefault str
|
||||||
return template.HTML(msgLines[0])
|
return template.HTML(msgLines[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderCommitMessageLinkSubject renders commit message as a XXS-safe link to
|
// RenderCommitMessageLinkSubject renders commit message as a XSS-safe link to
|
||||||
// the provided default url, handling for special links without email to links.
|
// the provided default url, handling for special links without email to links.
|
||||||
func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML {
|
func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlDefault string, metas map[string]string) template.HTML {
|
||||||
msgLine := strings.TrimLeftFunc(msg, unicode.IsSpace)
|
msgLine := strings.TrimLeftFunc(msg, unicode.IsSpace)
|
||||||
lineEnd := strings.IndexByte(msgLine, '\n')
|
lineEnd := strings.IndexByte(msgLine, '\n')
|
||||||
if lineEnd > 0 {
|
if lineEnd > 0 {
|
||||||
|
@ -68,7 +60,6 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefa
|
||||||
// shouldn't be any special HTML.
|
// shouldn't be any special HTML.
|
||||||
renderedMessage, err := markup.RenderCommitMessageSubject(&markup.RenderContext{
|
renderedMessage, err := markup.RenderCommitMessageSubject(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: urlPrefix,
|
|
||||||
DefaultLink: urlDefault,
|
DefaultLink: urlDefault,
|
||||||
Metas: metas,
|
Metas: metas,
|
||||||
}, template.HTMLEscapeString(msgLine))
|
}, template.HTMLEscapeString(msgLine))
|
||||||
|
@ -80,7 +71,7 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefa
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderCommitBody extracts the body of a commit message without its title.
|
// RenderCommitBody extracts the body of a commit message without its title.
|
||||||
func RenderCommitBody(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
|
func RenderCommitBody(ctx context.Context, msg string, metas map[string]string) template.HTML {
|
||||||
msgLine := strings.TrimSpace(msg)
|
msgLine := strings.TrimSpace(msg)
|
||||||
lineEnd := strings.IndexByte(msgLine, '\n')
|
lineEnd := strings.IndexByte(msgLine, '\n')
|
||||||
if lineEnd > 0 {
|
if lineEnd > 0 {
|
||||||
|
@ -94,9 +85,8 @@ func RenderCommitBody(ctx context.Context, msg, urlPrefix string, metas map[stri
|
||||||
}
|
}
|
||||||
|
|
||||||
renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
|
renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: urlPrefix,
|
Metas: metas,
|
||||||
Metas: metas,
|
|
||||||
}, template.HTMLEscapeString(msgLine))
|
}, template.HTMLEscapeString(msgLine))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("RenderCommitMessage: %v", err)
|
log.Error("RenderCommitMessage: %v", err)
|
||||||
|
@ -115,11 +105,10 @@ func RenderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderIssueTitle renders issue/pull title with defined post processors
|
// RenderIssueTitle renders issue/pull title with defined post processors
|
||||||
func RenderIssueTitle(ctx context.Context, text, urlPrefix string, metas map[string]string) template.HTML {
|
func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) template.HTML {
|
||||||
renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{
|
renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: urlPrefix,
|
Metas: metas,
|
||||||
Metas: metas,
|
|
||||||
}, template.HTMLEscapeString(text))
|
}, template.HTMLEscapeString(text))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("RenderIssueTitle: %v", err)
|
log.Error("RenderIssueTitle: %v", err)
|
||||||
|
@ -211,25 +200,10 @@ func ReactionToEmoji(reaction string) template.HTML {
|
||||||
return template.HTML(fmt.Sprintf(`<img alt=":%s:" src="%s/assets/img/emoji/%s.png"></img>`, reaction, setting.StaticURLPrefix, url.PathEscape(reaction)))
|
return template.HTML(fmt.Sprintf(`<img alt=":%s:" src="%s/assets/img/emoji/%s.png"></img>`, reaction, setting.StaticURLPrefix, url.PathEscape(reaction)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderNote renders the contents of a git-notes file as a commit message.
|
|
||||||
func RenderNote(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
|
|
||||||
cleanMsg := template.HTMLEscapeString(msg)
|
|
||||||
fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
|
|
||||||
Ctx: ctx,
|
|
||||||
URLPrefix: urlPrefix,
|
|
||||||
Metas: metas,
|
|
||||||
}, cleanMsg)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("RenderNote: %v", err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return template.HTML(fullMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //nolint:revive
|
func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //nolint:revive
|
||||||
output, err := markdown.RenderString(&markup.RenderContext{
|
output, err := markdown.RenderString(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: setting.AppSubURL,
|
Metas: map[string]string{"mode": "document"},
|
||||||
}, input)
|
}, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("RenderString: %v", err)
|
log.Error("RenderString: %v", err)
|
||||||
|
|
|
@ -6,17 +6,64 @@ package templates
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/markup"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const testInput = ` space @mention-user
|
||||||
|
/just/a/path.bin
|
||||||
|
https://example.com/file.bin
|
||||||
|
[local link](file.bin)
|
||||||
|
[remote link](https://example.com)
|
||||||
|
[[local link|file.bin]]
|
||||||
|
[[remote link|https://example.com]]
|
||||||
|
![local image](image.jpg)
|
||||||
|
![remote image](https://example.com/image.jpg)
|
||||||
|
[[local image|image.jpg]]
|
||||||
|
[[remote link|https://example.com/image.jpg]]
|
||||||
|
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||||
|
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||||
|
:+1:
|
||||||
|
mail@domain.com
|
||||||
|
@mention-user test
|
||||||
|
#123
|
||||||
|
space
|
||||||
|
`
|
||||||
|
|
||||||
|
var testMetas = map[string]string{
|
||||||
|
"user": "user13",
|
||||||
|
"repo": "repo11",
|
||||||
|
"repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/",
|
||||||
|
"mode": "comment",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
unittest.InitSettings()
|
||||||
|
if err := git.InitSimple(context.Background()); err != nil {
|
||||||
|
log.Fatal("git init failed, err: %v", err)
|
||||||
|
}
|
||||||
|
markup.Init(&markup.ProcessorHelper{
|
||||||
|
IsUsernameMentionable: func(ctx context.Context, username string) bool {
|
||||||
|
return username == "mention-user"
|
||||||
|
},
|
||||||
|
})
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
func TestRenderCommitBody(t *testing.T) {
|
func TestRenderCommitBody(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
msg string
|
msg string
|
||||||
urlPrefix string
|
metas map[string]string
|
||||||
metas map[string]string
|
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -50,7 +97,91 @@ func TestRenderCommitBody(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
assert.Equalf(t, tt.want, RenderCommitBody(tt.args.ctx, tt.args.msg, tt.args.urlPrefix, tt.args.metas), "RenderCommitBody(%v, %v, %v, %v)", tt.args.ctx, tt.args.msg, tt.args.urlPrefix, tt.args.metas)
|
assert.Equalf(t, tt.want, RenderCommitBody(tt.args.ctx, tt.args.msg, tt.args.metas), "RenderCommitBody(%v, %v, %v)", tt.args.ctx, tt.args.msg, tt.args.metas)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expected := `/just/a/path.bin
|
||||||
|
<a href="https://example.com/file.bin" class="link">https://example.com/file.bin</a>
|
||||||
|
[local link](file.bin)
|
||||||
|
[remote link](<a href="https://example.com" class="link">https://example.com</a>)
|
||||||
|
[[local link|file.bin]]
|
||||||
|
[[remote link|<a href="https://example.com" class="link">https://example.com</a>]]
|
||||||
|
![local image](image.jpg)
|
||||||
|
![remote image](<a href="https://example.com/image.jpg" class="link">https://example.com/image.jpg</a>)
|
||||||
|
[[local image|image.jpg]]
|
||||||
|
[[remote link|<a href="https://example.com/image.jpg" class="link">https://example.com/image.jpg</a>]]
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" class="compare"><code class="nohighlight">88fc37a3c0...12fc37a3c0 (hash)</code></a>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" class="commit"><code class="nohighlight">88fc37a3c0</code></a>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span>
|
||||||
|
<a href="mailto:mail@domain.com" class="mailto">mail@domain.com</a>
|
||||||
|
<a href="http://localhost:3000/mention-user" class="mention">@mention-user</a> test
|
||||||
|
<a href="http://localhost:3000/user13/repo11/issues/123" class="ref-issue">#123</a>
|
||||||
|
space`
|
||||||
|
|
||||||
|
assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderCommitMessage(t *testing.T) {
|
||||||
|
expected := `space <a href="http://localhost:3000/mention-user" class="mention">@mention-user</a> `
|
||||||
|
|
||||||
|
assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderCommitMessageLinkSubject(t *testing.T) {
|
||||||
|
expected := `<a href="https://example.com/link" class="default-link muted">space </a><a href="http://localhost:3000/mention-user" class="mention">@mention-user</a>`
|
||||||
|
|
||||||
|
assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderIssueTitle(t *testing.T) {
|
||||||
|
expected := ` space @mention-user
|
||||||
|
/just/a/path.bin
|
||||||
|
https://example.com/file.bin
|
||||||
|
[local link](file.bin)
|
||||||
|
[remote link](https://example.com)
|
||||||
|
[[local link|file.bin]]
|
||||||
|
[[remote link|https://example.com]]
|
||||||
|
![local image](image.jpg)
|
||||||
|
![remote image](https://example.com/image.jpg)
|
||||||
|
[[local image|image.jpg]]
|
||||||
|
[[remote link|https://example.com/image.jpg]]
|
||||||
|
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||||
|
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span>
|
||||||
|
mail@domain.com
|
||||||
|
@mention-user test
|
||||||
|
<a href="http://localhost:3000/user13/repo11/issues/123" class="ref-issue">#123</a>
|
||||||
|
space
|
||||||
|
`
|
||||||
|
assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderMarkdownToHtml(t *testing.T) {
|
||||||
|
expected := `<p>space <a href="http://localhost:3000/mention-user" rel="nofollow">@mention-user</a><br/>
|
||||||
|
/just/a/path.bin
|
||||||
|
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a>
|
||||||
|
<a href="/file.bin" rel="nofollow">local link</a>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a>
|
||||||
|
<a href="/src/file.bin" rel="nofollow">local link</a>
|
||||||
|
<a href="https://example.com" rel="nofollow">remote link</a>
|
||||||
|
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a>
|
||||||
|
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a>
|
||||||
|
<a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt="local image"/></a>
|
||||||
|
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a>
|
||||||
|
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow"><code>88fc37a3c0...12fc37a3c0 (hash)</code></a>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||||
|
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow"><code>88fc37a3c0</code></a>
|
||||||
|
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||||
|
<span class="emoji" aria-label="thumbs up">👍</span>
|
||||||
|
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a>
|
||||||
|
<a href="http://localhost:3000/mention-user" rel="nofollow">@mention-user</a> test
|
||||||
|
#123
|
||||||
|
space</p>
|
||||||
|
`
|
||||||
|
assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput))
|
||||||
}
|
}
|
||||||
|
|
|
@ -422,6 +422,7 @@ authorization_failed_desc = The authorization failed because we detected an inva
|
||||||
sspi_auth_failed = SSPI authentication failed
|
sspi_auth_failed = SSPI authentication failed
|
||||||
password_pwned = The password you chose is on a <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">list of stolen passwords</a> previously exposed in public data breaches. Please try again with a different password and consider changing this password elsewhere too.
|
password_pwned = The password you chose is on a <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">list of stolen passwords</a> previously exposed in public data breaches. Please try again with a different password and consider changing this password elsewhere too.
|
||||||
password_pwned_err = Could not complete request to HaveIBeenPwned
|
password_pwned_err = Could not complete request to HaveIBeenPwned
|
||||||
|
last_admin = You cannot remove the last admin. There must be at least one admin.
|
||||||
|
|
||||||
[mail]
|
[mail]
|
||||||
view_it_on = View it on %s
|
view_it_on = View it on %s
|
||||||
|
@ -587,6 +588,8 @@ org_still_own_packages = "This organization still owns one or more packages, del
|
||||||
|
|
||||||
target_branch_not_exist = Target branch does not exist.
|
target_branch_not_exist = Target branch does not exist.
|
||||||
|
|
||||||
|
admin_cannot_delete_self = You cannot delete yourself when you are an admin. Please remove your admin privileges first.
|
||||||
|
|
||||||
[user]
|
[user]
|
||||||
change_avatar = Change your avatar…
|
change_avatar = Change your avatar…
|
||||||
joined_on = Joined on %s
|
joined_on = Joined on %s
|
||||||
|
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -11,7 +11,7 @@
|
||||||
"@citation-js/plugin-software-formats": "0.6.1",
|
"@citation-js/plugin-software-formats": "0.6.1",
|
||||||
"@claviska/jquery-minicolors": "2.3.6",
|
"@claviska/jquery-minicolors": "2.3.6",
|
||||||
"@github/markdown-toolbar-element": "2.2.1",
|
"@github/markdown-toolbar-element": "2.2.1",
|
||||||
"@github/relative-time-element": "4.3.0",
|
"@github/relative-time-element": "4.3.1",
|
||||||
"@github/text-expander-element": "2.5.0",
|
"@github/text-expander-element": "2.5.0",
|
||||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||||
"@primer/octicons": "19.8.0",
|
"@primer/octicons": "19.8.0",
|
||||||
|
@ -1010,9 +1010,9 @@
|
||||||
"integrity": "sha512-ap+ulyqzG3aVqwKsKjbDdYwM75TQXZpPtmIuPwm+54OTgcC96267oX3cEqd1wSqGsH7O5PonZ//fE9jH7Q4JkA=="
|
"integrity": "sha512-ap+ulyqzG3aVqwKsKjbDdYwM75TQXZpPtmIuPwm+54OTgcC96267oX3cEqd1wSqGsH7O5PonZ//fE9jH7Q4JkA=="
|
||||||
},
|
},
|
||||||
"node_modules/@github/relative-time-element": {
|
"node_modules/@github/relative-time-element": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.3.1.tgz",
|
||||||
"integrity": "sha512-+tFjX9//HRS1HnBa5cNgfEtE52arwiutYg1TOF+Trk40SPxst9Q8Rtc3BKD6aKsvfbtub68vfhipgchGjj9o7g=="
|
"integrity": "sha512-zL79nlhZVCg7x2Pf/HT5MB0mowmErE71VXpF10/3Wy8dQwkninNO1M9aOizh2wKC5LkSpDXqNYjDZwbH0/bcSg=="
|
||||||
},
|
},
|
||||||
"node_modules/@github/text-expander-element": {
|
"node_modules/@github/text-expander-element": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"@citation-js/plugin-software-formats": "0.6.1",
|
"@citation-js/plugin-software-formats": "0.6.1",
|
||||||
"@claviska/jquery-minicolors": "2.3.6",
|
"@claviska/jquery-minicolors": "2.3.6",
|
||||||
"@github/markdown-toolbar-element": "2.2.1",
|
"@github/markdown-toolbar-element": "2.2.1",
|
||||||
"@github/relative-time-element": "4.3.0",
|
"@github/relative-time-element": "4.3.1",
|
||||||
"@github/text-expander-element": "2.5.0",
|
"@github/text-expander-element": "2.5.0",
|
||||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||||
"@primer/octicons": "19.8.0",
|
"@primer/octicons": "19.8.0",
|
||||||
|
|
|
@ -257,8 +257,11 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// update artifact size if zero
|
// update artifact size if zero or not match, over write artifact size
|
||||||
if artifact.FileSize == 0 || artifact.FileCompressedSize == 0 {
|
if artifact.FileSize == 0 ||
|
||||||
|
artifact.FileCompressedSize == 0 ||
|
||||||
|
artifact.FileSize != fileRealTotalSize ||
|
||||||
|
artifact.FileCompressedSize != chunksTotalSize {
|
||||||
artifact.FileSize = fileRealTotalSize
|
artifact.FileSize = fileRealTotalSize
|
||||||
artifact.FileCompressedSize = chunksTotalSize
|
artifact.FileCompressedSize = chunksTotalSize
|
||||||
artifact.ContentEncoding = ctx.Req.Header.Get("Content-Encoding")
|
artifact.ContentEncoding = ctx.Req.Header.Get("Content-Encoding")
|
||||||
|
@ -267,6 +270,8 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
|
||||||
ctx.Error(http.StatusInternalServerError, "Error update artifact")
|
ctx.Error(http.StatusInternalServerError, "Error update artifact")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Debug("[artifact] update artifact size, artifact_id: %d, size: %d, compressed size: %d",
|
||||||
|
artifact.ID, artifact.FileSize, artifact.FileCompressedSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, map[string]string{
|
ctx.JSON(http.StatusOK, map[string]string{
|
||||||
|
|
|
@ -182,7 +182,14 @@ func mergeChunksForArtifact(ctx *ArtifactContext, chunks []*chunkFileItem, st st
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// save storage path to artifact
|
// save storage path to artifact
|
||||||
log.Debug("[artifact] merge chunks to artifact: %d, %s", artifact.ID, storagePath)
|
log.Debug("[artifact] merge chunks to artifact: %d, %s, old:%s", artifact.ID, storagePath, artifact.StoragePath)
|
||||||
|
// if artifact is already uploaded, delete the old file
|
||||||
|
if artifact.StoragePath != "" {
|
||||||
|
if err := st.Delete(artifact.StoragePath); err != nil {
|
||||||
|
log.Warn("Error deleting old artifact: %s, %v", artifact.StoragePath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
artifact.StoragePath = storagePath
|
artifact.StoragePath = storagePath
|
||||||
artifact.Status = int64(actions.ArtifactStatusUploadConfirmed)
|
artifact.Status = int64(actions.ArtifactStatusUploadConfirmed)
|
||||||
if err := actions.UpdateArtifactByID(ctx, artifact.ID, artifact); err != nil {
|
if err := actions.UpdateArtifactByID(ctx, artifact.ID, artifact); err != nil {
|
||||||
|
|
|
@ -114,11 +114,15 @@ func apiErrorDefined(ctx *context.Context, err *namedError) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReqContainerAccess is a middleware which checks the current user valid (real user or ghost for anonymous access)
|
func apiUnauthorizedError(ctx *context.Context) {
|
||||||
|
ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token",service="container_registry",scope="*"`)
|
||||||
|
apiErrorDefined(ctx, errUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReqContainerAccess is a middleware which checks the current user valid (real user or ghost if anonymous access is enabled)
|
||||||
func ReqContainerAccess(ctx *context.Context) {
|
func ReqContainerAccess(ctx *context.Context) {
|
||||||
if ctx.Doer == nil {
|
if ctx.Doer == nil || (setting.Service.RequireSignInView && ctx.Doer.IsGhost()) {
|
||||||
ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token",service="container_registry",scope="*"`)
|
apiUnauthorizedError(ctx)
|
||||||
apiErrorDefined(ctx, errUnauthorized)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,10 +142,15 @@ func DetermineSupport(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate creates a token for the current user
|
// Authenticate creates a token for the current user
|
||||||
// If the current user is anonymous, the ghost user is used
|
// If the current user is anonymous, the ghost user is used unless RequireSignInView is enabled.
|
||||||
func Authenticate(ctx *context.Context) {
|
func Authenticate(ctx *context.Context) {
|
||||||
u := ctx.Doer
|
u := ctx.Doer
|
||||||
if u == nil {
|
if u == nil {
|
||||||
|
if setting.Service.RequireSignInView {
|
||||||
|
apiUnauthorizedError(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
u = user_model.NewGhostUser()
|
u = user_model.NewGhostUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,8 @@ func EditUser(ctx *context.APIContext) {
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/User"
|
// "$ref": "#/responses/User"
|
||||||
|
// "400":
|
||||||
|
// "$ref": "#/responses/error"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
// "422":
|
// "422":
|
||||||
|
@ -264,6 +266,10 @@ func EditUser(ctx *context.APIContext) {
|
||||||
ctx.ContextUser.Visibility = api.VisibilityModes[form.Visibility]
|
ctx.ContextUser.Visibility = api.VisibilityModes[form.Visibility]
|
||||||
}
|
}
|
||||||
if form.Admin != nil {
|
if form.Admin != nil {
|
||||||
|
if !*form.Admin && user_model.IsLastAdminUser(ctx, ctx.ContextUser) {
|
||||||
|
ctx.Error(http.StatusBadRequest, "LastAdmin", ctx.Tr("auth.last_admin"))
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx.ContextUser.IsAdmin = *form.Admin
|
ctx.ContextUser.IsAdmin = *form.Admin
|
||||||
}
|
}
|
||||||
if form.AllowGitHook != nil {
|
if form.AllowGitHook != nil {
|
||||||
|
@ -341,7 +347,8 @@ func DeleteUser(ctx *context.APIContext) {
|
||||||
if err := user_service.DeleteUser(ctx, ctx.ContextUser, ctx.FormBool("purge")); err != nil {
|
if err := user_service.DeleteUser(ctx, ctx.ContextUser, ctx.FormBool("purge")); err != nil {
|
||||||
if models.IsErrUserOwnRepos(err) ||
|
if models.IsErrUserOwnRepos(err) ||
|
||||||
models.IsErrUserHasOrgs(err) ||
|
models.IsErrUserHasOrgs(err) ||
|
||||||
models.IsErrUserOwnPackages(err) {
|
models.IsErrUserOwnPackages(err) ||
|
||||||
|
models.IsErrDeleteLastAdminUser(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "DeleteUser", err)
|
ctx.Error(http.StatusInternalServerError, "DeleteUser", err)
|
||||||
|
|
|
@ -194,8 +194,10 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
|
||||||
|
|
||||||
listOptions := utils.GetListOptions(ctx)
|
listOptions := utils.GetListOptions(ctx)
|
||||||
|
|
||||||
statuses, maxResults, err := git_model.GetCommitStatuses(ctx, repo, sha, &git_model.CommitStatusOptions{
|
statuses, maxResults, err := git_model.GetCommitStatuses(ctx, &git_model.CommitStatusOptions{
|
||||||
ListOptions: listOptions,
|
ListOptions: listOptions,
|
||||||
|
RepoID: repo.ID,
|
||||||
|
SHA: sha,
|
||||||
SortType: ctx.FormTrim("sort"),
|
SortType: ctx.FormTrim("sort"),
|
||||||
State: ctx.FormTrim("state"),
|
State: ctx.FormTrim("state"),
|
||||||
})
|
})
|
||||||
|
|
|
@ -32,8 +32,10 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr
|
||||||
case "markdown":
|
case "markdown":
|
||||||
// Raw markdown
|
// Raw markdown
|
||||||
if err := markdown.RenderRaw(&markup.RenderContext{
|
if err := markdown.RenderRaw(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: urlPrefix,
|
Links: markup.Links{
|
||||||
|
Base: urlPrefix,
|
||||||
|
},
|
||||||
}, strings.NewReader(text), ctx.Resp); err != nil {
|
}, strings.NewReader(text), ctx.Resp); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
|
@ -75,8 +77,10 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := markup.Render(&markup.RenderContext{
|
if err := markup.Render(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: urlPrefix,
|
Links: markup.Links{
|
||||||
|
Base: urlPrefix,
|
||||||
|
},
|
||||||
Metas: meta,
|
Metas: meta,
|
||||||
IsWiki: wiki,
|
IsWiki: wiki,
|
||||||
Type: markupType,
|
Type: markupType,
|
||||||
|
|
|
@ -429,6 +429,12 @@ func EditUserPost(ctx *context.Context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether user is the last admin
|
||||||
|
if !form.Admin && user_model.IsLastAdminUser(ctx, u) {
|
||||||
|
ctx.RenderWithErr(ctx.Tr("auth.last_admin"), tplUserEdit, &form)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
u.LoginName = form.LoginName
|
u.LoginName = form.LoginName
|
||||||
u.FullName = form.FullName
|
u.FullName = form.FullName
|
||||||
emailChanged := !strings.EqualFold(u.Email, form.Email)
|
emailChanged := !strings.EqualFold(u.Email, form.Email)
|
||||||
|
@ -496,7 +502,10 @@ func DeleteUser(ctx *context.Context) {
|
||||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
|
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
|
||||||
case models.IsErrUserOwnPackages(err):
|
case models.IsErrUserOwnPackages(err):
|
||||||
ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
|
ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"))
|
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
|
||||||
|
case models.IsErrDeleteLastAdminUser(err):
|
||||||
|
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
|
||||||
|
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
|
||||||
default:
|
default:
|
||||||
ctx.ServerError("DeleteUser", err)
|
ctx.ServerError("DeleteUser", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,11 @@ func toReleaseLink(ctx *context.Context, act *activities_model.Action) string {
|
||||||
// If rendering fails, the original markdown text is returned
|
// If rendering fails, the original markdown text is returned
|
||||||
func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) string {
|
func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) string {
|
||||||
markdownCtx := &markup.RenderContext{
|
markdownCtx := &markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: act.GetRepoLink(ctx),
|
Links: markup.Links{
|
||||||
Type: markdown.MarkupName,
|
Base: act.GetRepoLink(ctx),
|
||||||
|
},
|
||||||
|
Type: markdown.MarkupName,
|
||||||
Metas: map[string]string{
|
Metas: map[string]string{
|
||||||
"user": act.GetRepoUserName(ctx),
|
"user": act.GetRepoUserName(ctx),
|
||||||
"repo": act.GetRepoName(ctx),
|
"repo": act.GetRepoName(ctx),
|
||||||
|
@ -199,7 +201,6 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
||||||
switch act.OpType {
|
switch act.OpType {
|
||||||
case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
|
case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
|
||||||
push := templates.ActionContent2Commits(act)
|
push := templates.ActionContent2Commits(act)
|
||||||
repoLink := act.GetRepoAbsoluteLink(ctx)
|
|
||||||
|
|
||||||
for _, commit := range push.Commits {
|
for _, commit := range push.Commits {
|
||||||
if len(desc) != 0 {
|
if len(desc) != 0 {
|
||||||
|
@ -208,7 +209,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
||||||
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
|
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
|
||||||
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(ctx), commit.Sha1)),
|
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(ctx), commit.Sha1)),
|
||||||
commit.Sha1,
|
commit.Sha1,
|
||||||
templates.RenderCommitMessage(ctx, commit.Message, repoLink, nil),
|
templates.RenderCommitMessage(ctx, commit.Message, nil),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,9 +289,11 @@ func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release, i
|
||||||
|
|
||||||
link := &feeds.Link{Href: rel.HTMLURL()}
|
link := &feeds.Link{Href: rel.HTMLURL()}
|
||||||
content, err = markdown.RenderString(&markup.RenderContext{
|
content, err = markdown.RenderString(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: rel.Repo.Link(),
|
Links: markup.Links{
|
||||||
Metas: rel.Repo.ComposeMetas(),
|
Base: rel.Repo.Link(),
|
||||||
|
},
|
||||||
|
Metas: rel.Repo.ComposeMetas(),
|
||||||
}, rel.Note)
|
}, rel.Note)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -42,8 +42,10 @@ func showUserFeed(ctx *context.Context, formatType string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxUserDescription, err := markdown.RenderString(&markup.RenderContext{
|
ctxUserDescription, err := markdown.RenderString(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: ctx.ContextUser.HTMLURL(),
|
Links: markup.Links{
|
||||||
|
Base: ctx.ContextUser.HTMLURL(),
|
||||||
|
},
|
||||||
Metas: map[string]string{
|
Metas: map[string]string{
|
||||||
"user": ctx.ContextUser.GetDisplayName(),
|
"user": ctx.ContextUser.GetDisplayName(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -44,10 +44,8 @@ func Home(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = org.DisplayName()
|
ctx.Data["Title"] = org.DisplayName()
|
||||||
if len(org.Description) != 0 {
|
if len(org.Description) != 0 {
|
||||||
desc, err := markdown.RenderString(&markup.RenderContext{
|
desc, err := markdown.RenderString(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Metas: map[string]string{"mode": "document"},
|
||||||
Metas: map[string]string{"mode": "document"},
|
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
|
||||||
}, org.Description)
|
}, org.Description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
|
|
@ -7,7 +7,9 @@ package repo
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
|
@ -21,7 +23,9 @@ import (
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/gitgraph"
|
"code.gitea.io/gitea/modules/gitgraph"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/gitdiff"
|
"code.gitea.io/gitea/services/gitdiff"
|
||||||
git_service "code.gitea.io/gitea/services/repository"
|
git_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
@ -370,9 +374,21 @@ func Diff(ctx *context.Context) {
|
||||||
note := &git.Note{}
|
note := &git.Note{}
|
||||||
err = git.GetNote(ctx, ctx.Repo.GitRepo, commitID, note)
|
err = git.GetNote(ctx, ctx.Repo.GitRepo, commitID, note)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ctx.Data["Note"] = string(charset.ToUTF8WithFallback(note.Message))
|
|
||||||
ctx.Data["NoteCommit"] = note.Commit
|
ctx.Data["NoteCommit"] = note.Commit
|
||||||
ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit)
|
ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit)
|
||||||
|
ctx.Data["NoteRendered"], err = markup.RenderCommitMessage(&markup.RenderContext{
|
||||||
|
Links: markup.Links{
|
||||||
|
Base: ctx.Repo.RepoLink,
|
||||||
|
BranchPath: path.Join("commit", util.PathEscapeSegments(commitID)),
|
||||||
|
},
|
||||||
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
|
}, template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message))))
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("RenderCommitMessage", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["BranchName"], err = commit.GetBranchName()
|
ctx.Data["BranchName"], err = commit.GetBranchName()
|
||||||
|
|
|
@ -1424,12 +1424,13 @@ func ViewIssue(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.Data["IssueWatch"] = iw
|
ctx.Data["IssueWatch"] = iw
|
||||||
|
|
||||||
issue.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
issue.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, issue.Content)
|
}, issue.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
@ -1589,10 +1590,12 @@ func ViewIssue(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, comment.Content)
|
}, comment.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
@ -1666,10 +1669,12 @@ func ViewIssue(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
} else if comment.Type.HasContentSupport() {
|
} else if comment.Type.HasContentSupport() {
|
||||||
comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, comment.Content)
|
}, comment.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
@ -2220,10 +2225,12 @@ func UpdateIssueContent(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := markdown.RenderString(&markup.RenderContext{
|
content, err := markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, issue.Content)
|
}, issue.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
@ -3129,10 +3136,12 @@ func UpdateCommentContent(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := markdown.RenderString(&markup.RenderContext{
|
content, err := markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, comment.Content)
|
}, comment.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
|
|
@ -81,10 +81,12 @@ func Milestones(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
for _, m := range miles {
|
for _, m := range miles {
|
||||||
m.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
m.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, m.Content)
|
}, m.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
@ -275,10 +277,12 @@ func MilestoneIssuesAndPulls(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
milestone.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
milestone.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, milestone.Content)
|
}, milestone.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
|
|
@ -86,10 +86,12 @@ func Projects(ctx *context.Context) {
|
||||||
|
|
||||||
for i := range projects {
|
for i := range projects {
|
||||||
projects[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
projects[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, projects[i].Description)
|
}, projects[i].Description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
@ -353,10 +355,12 @@ func ViewProject(ctx *context.Context) {
|
||||||
ctx.Data["LinkedPRs"] = linkedPrsMap
|
ctx.Data["LinkedPRs"] = linkedPrsMap
|
||||||
|
|
||||||
project.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
project.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, project.Description)
|
}, project.Description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
|
|
@ -175,10 +175,12 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Note, err = markdown.RenderString(&markup.RenderContext{
|
r.Note, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, r.Note)
|
}, r.Note)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
@ -281,10 +283,12 @@ func SingleRelease(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
release.Note, err = markdown.RenderString(&markup.RenderContext{
|
release.Note, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
},
|
||||||
Ctx: ctx,
|
Metas: ctx.Repo.Repository.ComposeMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
|
Ctx: ctx,
|
||||||
}, release.Note)
|
}, release.Note)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
|
|
@ -57,16 +57,15 @@ func RenderFile(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
treeLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
|
||||||
if ctx.Repo.TreePath != "" {
|
|
||||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
|
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
|
||||||
err = markup.Render(&markup.RenderContext{
|
err = markup.Render(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
RelativePath: ctx.Repo.TreePath,
|
RelativePath: ctx.Repo.TreePath,
|
||||||
URLPrefix: path.Dir(treeLink),
|
Links: markup.Links{
|
||||||
|
Base: ctx.Repo.RepoLink,
|
||||||
|
BranchPath: ctx.Repo.BranchNameSubURL(),
|
||||||
|
TreePath: path.Dir(ctx.Repo.TreePath),
|
||||||
|
},
|
||||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
InStandalonePage: true,
|
InStandalonePage: true,
|
||||||
|
|
|
@ -158,7 +158,7 @@ func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, try
|
||||||
return "", readmeFile, nil
|
return "", readmeFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderDirectory(ctx *context.Context, treeLink string) {
|
func renderDirectory(ctx *context.Context) {
|
||||||
entries := renderDirectoryFiles(ctx, 1*time.Second)
|
entries := renderDirectoryFiles(ctx, 1*time.Second)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
|
@ -175,7 +175,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
renderReadmeFile(ctx, subfolder, readmeFile, treeLink)
|
renderReadmeFile(ctx, subfolder, readmeFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// localizedExtensions prepends the provided language code with and without a
|
// localizedExtensions prepends the provided language code with and without a
|
||||||
|
@ -259,7 +259,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte,
|
||||||
return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil
|
return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry, readmeTreelink string) {
|
func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) {
|
||||||
target := readmeFile
|
target := readmeFile
|
||||||
if readmeFile != nil && readmeFile.IsLink() {
|
if readmeFile != nil && readmeFile.IsLink() {
|
||||||
target, _ = readmeFile.FollowLinks()
|
target, _ = readmeFile.FollowLinks()
|
||||||
|
@ -312,9 +312,13 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr
|
||||||
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
|
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
RelativePath: path.Join(ctx.Repo.TreePath, readmeFile.Name()), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path).
|
RelativePath: path.Join(ctx.Repo.TreePath, readmeFile.Name()), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path).
|
||||||
URLPrefix: path.Join(readmeTreelink, subfolder),
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
BranchPath: ctx.Repo.BranchNameSubURL(),
|
||||||
|
TreePath: path.Join(ctx.Repo.TreePath, subfolder),
|
||||||
|
},
|
||||||
|
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
}, rd)
|
}, rd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err)
|
log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err)
|
||||||
|
@ -333,7 +337,7 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink string) {
|
func renderFile(ctx *context.Context, entry *git.TreeEntry) {
|
||||||
ctx.Data["IsViewFile"] = true
|
ctx.Data["IsViewFile"] = true
|
||||||
ctx.Data["HideRepoInfo"] = true
|
ctx.Data["HideRepoInfo"] = true
|
||||||
blob := entry.Blob()
|
blob := entry.Blob()
|
||||||
|
@ -347,7 +351,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
|
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
|
||||||
ctx.Data["FileIsSymlink"] = entry.IsLink()
|
ctx.Data["FileIsSymlink"] = entry.IsLink()
|
||||||
ctx.Data["FileName"] = blob.Name()
|
ctx.Data["FileName"] = blob.Name()
|
||||||
ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||||
|
|
||||||
if ctx.Repo.TreePath == ".editorconfig" {
|
if ctx.Repo.TreePath == ".editorconfig" {
|
||||||
_, editorconfigWarning, editorconfigErr := ctx.Repo.GetEditorconfig(ctx.Repo.Commit)
|
_, editorconfigWarning, editorconfigErr := ctx.Repo.GetEditorconfig(ctx.Repo.Commit)
|
||||||
|
@ -475,9 +479,13 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
Type: markupType,
|
Type: markupType,
|
||||||
RelativePath: ctx.Repo.TreePath,
|
RelativePath: ctx.Repo.TreePath,
|
||||||
URLPrefix: path.Dir(treeLink),
|
Links: markup.Links{
|
||||||
Metas: metas,
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
BranchPath: ctx.Repo.BranchNameSubURL(),
|
||||||
|
TreePath: path.Dir(ctx.Repo.TreePath),
|
||||||
|
},
|
||||||
|
Metas: metas,
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
}, rd)
|
}, rd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("Render", err)
|
ctx.ServerError("Render", err)
|
||||||
|
@ -576,9 +584,13 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
||||||
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
|
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
RelativePath: ctx.Repo.TreePath,
|
RelativePath: ctx.Repo.TreePath,
|
||||||
URLPrefix: path.Dir(treeLink),
|
Links: markup.Links{
|
||||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
Base: ctx.Repo.RepoLink,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
BranchPath: ctx.Repo.BranchNameSubURL(),
|
||||||
|
TreePath: path.Dir(ctx.Repo.TreePath),
|
||||||
|
},
|
||||||
|
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
||||||
|
GitRepo: ctx.Repo.GitRepo,
|
||||||
}, rd)
|
}, rd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("Render", err)
|
ctx.ServerError("Render", err)
|
||||||
|
@ -936,14 +948,6 @@ func renderCode(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
ctx.Data["Title"] = title
|
ctx.Data["Title"] = title
|
||||||
|
|
||||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
|
||||||
treeLink := branchLink
|
|
||||||
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
|
|
||||||
|
|
||||||
if len(ctx.Repo.TreePath) > 0 {
|
|
||||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Topics of this repo
|
// Get Topics of this repo
|
||||||
renderRepoTopics(ctx)
|
renderRepoTopics(ctx)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
|
@ -968,9 +972,9 @@ func renderCode(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
renderDirectory(ctx, treeLink)
|
renderDirectory(ctx)
|
||||||
} else {
|
} else {
|
||||||
renderFile(ctx, entry, treeLink, rawLink)
|
renderFile(ctx, entry)
|
||||||
}
|
}
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
|
@ -1011,6 +1015,12 @@ func renderCode(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["Paths"] = paths
|
ctx.Data["Paths"] = paths
|
||||||
|
|
||||||
|
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||||
|
treeLink := branchLink
|
||||||
|
if len(ctx.Repo.TreePath) > 0 {
|
||||||
|
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||||
|
}
|
||||||
ctx.Data["TreeLink"] = treeLink
|
ctx.Data["TreeLink"] = treeLink
|
||||||
ctx.Data["TreeNames"] = treeNames
|
ctx.Data["TreeNames"] = treeNames
|
||||||
ctx.Data["BranchLink"] = branchLink
|
ctx.Data["BranchLink"] = branchLink
|
||||||
|
|
|
@ -238,10 +238,12 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rctx := &markup.RenderContext{
|
rctx := &markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
||||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
Links: markup.Links{
|
||||||
IsWiki: true,
|
Base: ctx.Repo.RepoLink,
|
||||||
|
},
|
||||||
|
IsWiki: true,
|
||||||
}
|
}
|
||||||
buf := &strings.Builder{}
|
buf := &strings.Builder{}
|
||||||
|
|
||||||
|
|
|
@ -44,10 +44,8 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
|
||||||
|
|
||||||
if len(ctx.ContextUser.Description) != 0 {
|
if len(ctx.ContextUser.Description) != 0 {
|
||||||
content, err := markdown.RenderString(&markup.RenderContext{
|
content, err := markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: ctx.Repo.RepoLink,
|
Metas: map[string]string{"mode": "document"},
|
||||||
Metas: map[string]string{"mode": "document"},
|
Ctx: ctx,
|
||||||
GitRepo: ctx.Repo.GitRepo,
|
|
||||||
Ctx: ctx,
|
|
||||||
}, ctx.ContextUser.Description)
|
}, ctx.ContextUser.Description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
@ -84,7 +82,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) {
|
func FindUserProfileReadme(ctx *context.Context) (profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) {
|
||||||
profileDbRepo, err := repo_model.GetRepositoryByName(ctx.ContextUser.ID, ".profile")
|
profileDbRepo, err := repo_model.GetRepositoryByName(ctx.ContextUser.ID, ".profile")
|
||||||
if err == nil && !profileDbRepo.IsEmpty && !profileDbRepo.IsPrivate {
|
if err == nil && !profileDbRepo.IsEmpty && !profileDbRepo.IsPrivate {
|
||||||
if profileGitRepo, err = git.OpenRepository(ctx, profileDbRepo.RepoPath()); err != nil {
|
if profileGitRepo, err = git.OpenRepository(ctx, profileDbRepo.RepoPath()); err != nil {
|
||||||
|
@ -97,7 +95,7 @@ func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return profileGitRepo, profileReadmeBlob, func() {
|
return profileDbRepo, profileGitRepo, profileReadmeBlob, func() {
|
||||||
if profileGitRepo != nil {
|
if profileGitRepo != nil {
|
||||||
_ = profileGitRepo.Close()
|
_ = profileGitRepo.Close()
|
||||||
}
|
}
|
||||||
|
@ -107,7 +105,7 @@ func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository
|
||||||
func RenderUserHeader(ctx *context.Context) {
|
func RenderUserHeader(ctx *context.Context) {
|
||||||
prepareContextForCommonProfile(ctx)
|
prepareContextForCommonProfile(ctx)
|
||||||
|
|
||||||
_, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx)
|
_, _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx)
|
||||||
defer profileClose()
|
defer profileClose()
|
||||||
ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil
|
ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,9 +246,11 @@ func Milestones(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
milestones[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
milestones[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||||
URLPrefix: milestones[i].Repo.Link(),
|
Links: markup.Links{
|
||||||
Metas: milestones[i].Repo.ComposeMetas(),
|
Base: milestones[i].Repo.Link(),
|
||||||
Ctx: ctx,
|
},
|
||||||
|
Metas: milestones[i].Repo.ComposeMetas(),
|
||||||
|
Ctx: ctx,
|
||||||
}, milestones[i].Content)
|
}, milestones[i].Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RenderString", err)
|
ctx.ServerError("RenderString", err)
|
||||||
|
|
|
@ -7,6 +7,7 @@ package user
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
activities_model "code.gitea.io/gitea/models/activities"
|
activities_model "code.gitea.io/gitea/models/activities"
|
||||||
|
@ -64,17 +65,17 @@ func userProfile(ctx *context.Context) {
|
||||||
ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
|
ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx)
|
profileDbRepo, profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx)
|
||||||
defer profileClose()
|
defer profileClose()
|
||||||
|
|
||||||
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
|
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
|
||||||
prepareUserProfileTabData(ctx, showPrivate, profileGitRepo, profileReadmeBlob)
|
prepareUserProfileTabData(ctx, showPrivate, profileDbRepo, profileGitRepo, profileReadmeBlob)
|
||||||
// call PrepareContextForProfileBigAvatar later to avoid re-querying the NumFollowers & NumFollowing
|
// call PrepareContextForProfileBigAvatar later to avoid re-querying the NumFollowers & NumFollowing
|
||||||
shared_user.PrepareContextForProfileBigAvatar(ctx)
|
shared_user.PrepareContextForProfileBigAvatar(ctx)
|
||||||
ctx.HTML(http.StatusOK, tplProfile)
|
ctx.HTML(http.StatusOK, tplProfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGitRepo *git.Repository, profileReadme *git.Blob) {
|
func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadme *git.Blob) {
|
||||||
// if there is a profile readme, default to "overview" page, otherwise, default to "repositories" page
|
// if there is a profile readme, default to "overview" page, otherwise, default to "repositories" page
|
||||||
// if there is not a profile readme, the overview tab should be treated as the repositories tab
|
// if there is not a profile readme, the overview tab should be treated as the repositories tab
|
||||||
tab := ctx.FormString("tab")
|
tab := ctx.FormString("tab")
|
||||||
|
@ -236,7 +237,16 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGi
|
||||||
if profileContent, err := markdown.RenderString(&markup.RenderContext{
|
if profileContent, err := markdown.RenderString(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
GitRepo: profileGitRepo,
|
GitRepo: profileGitRepo,
|
||||||
Metas: map[string]string{"mode": "document"},
|
Links: markup.Links{
|
||||||
|
// Give the repo link to the markdown render for the full link of media element.
|
||||||
|
// the media link usually be like /[user]/[repoName]/media/branch/[branchName],
|
||||||
|
// Eg. /Tom/.profile/media/branch/main
|
||||||
|
// The branch shown on the profile page is the default branch, this need to be in sync with doc, see:
|
||||||
|
// https://docs.gitea.com/usage/profile-readme
|
||||||
|
Base: profileDbRepo.Link(),
|
||||||
|
BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
|
||||||
|
},
|
||||||
|
Metas: map[string]string{"mode": "document"},
|
||||||
}, bytes); err != nil {
|
}, bytes); err != nil {
|
||||||
log.Error("failed to RenderString: %v", err)
|
log.Error("failed to RenderString: %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -246,6 +246,13 @@ func DeleteAccount(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// admin should not delete themself
|
||||||
|
if ctx.Doer.IsAdmin {
|
||||||
|
ctx.Flash.Error(ctx.Tr("form.admin_cannot_delete_self"))
|
||||||
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := user.DeleteUser(ctx, ctx.Doer, false); err != nil {
|
if err := user.DeleteUser(ctx, ctx.Doer, false); err != nil {
|
||||||
switch {
|
switch {
|
||||||
case models.IsErrUserOwnRepos(err):
|
case models.IsErrUserOwnRepos(err):
|
||||||
|
@ -257,6 +264,9 @@ func DeleteAccount(ctx *context.Context) {
|
||||||
case models.IsErrUserOwnPackages(err):
|
case models.IsErrUserOwnPackages(err):
|
||||||
ctx.Flash.Error(ctx.Tr("form.still_own_packages"))
|
ctx.Flash.Error(ctx.Tr("form.still_own_packages"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
|
case models.IsErrDeleteLastAdminUser(err):
|
||||||
|
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
|
||||||
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
default:
|
default:
|
||||||
ctx.ServerError("DeleteUser", err)
|
ctx.ServerError("DeleteUser", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,24 +152,28 @@ func notify(ctx context.Context, input *notifyInput) error {
|
||||||
workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit,
|
workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit,
|
||||||
input.Event,
|
input.Event,
|
||||||
input.Payload,
|
input.Payload,
|
||||||
input.Event == webhook_module.HookEventPush && input.Ref == input.Repo.DefaultBranch,
|
input.Event == webhook_module.HookEventPush && git.RefName(input.Ref).BranchName() == input.Repo.DefaultBranch,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(workflows) == 0 {
|
log.Trace("repo %s with commit %s event %s find %d workflows and %d schedules",
|
||||||
log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID)
|
input.Repo.RepoPath(),
|
||||||
} else {
|
commit.ID,
|
||||||
for _, wf := range workflows {
|
input.Event,
|
||||||
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
len(workflows),
|
||||||
log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName)
|
len(schedules),
|
||||||
continue
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if wf.TriggerEvent.Name != actions_module.GithubEventPullRequestTarget {
|
for _, wf := range workflows {
|
||||||
detectedWorkflows = append(detectedWorkflows, wf)
|
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
||||||
}
|
log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if wf.TriggerEvent.Name != actions_module.GithubEventPullRequestTarget {
|
||||||
|
detectedWorkflows = append(detectedWorkflows, wf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -220,9 +220,11 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
|
|
||||||
// This is the body of the new issue or comment, not the mail body
|
// This is the body of the new issue or comment, not the mail body
|
||||||
body, err := markdown.RenderString(&markup.RenderContext{
|
body, err := markdown.RenderString(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: ctx.Issue.Repo.HTMLURL(),
|
Links: markup.Links{
|
||||||
Metas: ctx.Issue.Repo.ComposeMetas(),
|
Base: ctx.Issue.Repo.HTMLURL(),
|
||||||
|
},
|
||||||
|
Metas: ctx.Issue.Repo.ComposeMetas(),
|
||||||
}, ctx.Content)
|
}, ctx.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -57,9 +57,11 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
rel.RenderedNote, err = markdown.RenderString(&markup.RenderContext{
|
rel.RenderedNote, err = markdown.RenderString(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
URLPrefix: rel.Repo.Link(),
|
Links: markup.Links{
|
||||||
Metas: rel.Repo.ComposeMetas(),
|
Base: rel.Repo.HTMLURL(),
|
||||||
|
},
|
||||||
|
Metas: rel.Repo.ComposeMetas(),
|
||||||
}, rel.Note)
|
}, rel.Note)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("markdown.RenderString(%d): %v", rel.RepoID, err)
|
log.Error("markdown.RenderString(%d): %v", rel.RepoID, err)
|
||||||
|
|
|
@ -267,7 +267,7 @@ func alterRepositoryContent(ctx context.Context, doer *user_model.User, repo *re
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
|
|
||||||
var lastCommitID string
|
var lastCommitID string
|
||||||
if err := t.Clone(repo.DefaultBranch); err != nil {
|
if err := t.Clone(repo.DefaultBranch, true); err != nil {
|
||||||
if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
|
if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,12 @@ func DeleteCollaboration(ctx context.Context, repo *repo_model.Repository, uid i
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
defer committer.Close()
|
||||||
|
|
||||||
if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil || has == 0 {
|
if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
|
} else if has == 0 {
|
||||||
|
return committer.Commit()
|
||||||
|
}
|
||||||
|
if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,15 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
|
||||||
log.Error("%v", err)
|
log.Error("%v", err)
|
||||||
}
|
}
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
if err := t.Clone(opts.OldBranch); err != nil {
|
if err := t.Clone(opts.OldBranch, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := t.SetDefaultIndex(); err != nil {
|
if err := t.SetDefaultIndex(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := t.RefreshIndex(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Get the commit of the original branch
|
// Get the commit of the original branch
|
||||||
commit, err := t.GetBranchCommit(opts.OldBranch)
|
commit, err := t.GetBranchCommit(opts.OldBranch)
|
||||||
|
|
|
@ -21,7 +21,7 @@ func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, tr
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
if err := t.Clone(branch); err != nil {
|
if err := t.Clone(branch, true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := t.SetDefaultIndex(); err != nil {
|
if err := t.SetDefaultIndex(); err != nil {
|
||||||
|
|
|
@ -108,7 +108,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
|
||||||
log.Error("%v", err)
|
log.Error("%v", err)
|
||||||
}
|
}
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
if err := t.Clone(opts.OldBranch); err != nil {
|
if err := t.Clone(opts.OldBranch, true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := t.SetDefaultIndex(); err != nil {
|
if err := t.SetDefaultIndex(); err != nil {
|
||||||
|
|
|
@ -51,8 +51,13 @@ func (t *TemporaryUploadRepository) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone the base repository to our path and set branch as the HEAD
|
// Clone the base repository to our path and set branch as the HEAD
|
||||||
func (t *TemporaryUploadRepository) Clone(branch string) error {
|
func (t *TemporaryUploadRepository) Clone(branch string, bare bool) error {
|
||||||
if _, _, err := git.NewCommand(t.ctx, "clone", "-s", "--bare", "-b").AddDynamicArguments(branch, t.repo.RepoPath(), t.basePath).RunStdString(nil); err != nil {
|
cmd := git.NewCommand(t.ctx, "clone", "-s", "-b").AddDynamicArguments(branch, t.repo.RepoPath(), t.basePath)
|
||||||
|
if bare {
|
||||||
|
cmd.AddArguments("--bare")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, err := cmd.RunStdString(nil); err != nil {
|
||||||
stderr := err.Error()
|
stderr := err.Error()
|
||||||
if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched {
|
if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched {
|
||||||
return git.ErrBranchNotExist{
|
return git.ErrBranchNotExist{
|
||||||
|
@ -98,6 +103,14 @@ func (t *TemporaryUploadRepository) SetDefaultIndex() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshIndex looks at the current index and checks to see if merges or updates are needed by checking stat() information.
|
||||||
|
func (t *TemporaryUploadRepository) RefreshIndex() error {
|
||||||
|
if _, _, err := git.NewCommand(t.ctx, "update-index", "--refresh").RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil {
|
||||||
|
return fmt.Errorf("RefreshIndex: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// LsFiles checks if the given filename arguments are in the index
|
// LsFiles checks if the given filename arguments are in the index
|
||||||
func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, error) {
|
func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, error) {
|
||||||
stdOut := new(bytes.Buffer)
|
stdOut := new(bytes.Buffer)
|
||||||
|
|
|
@ -141,7 +141,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
||||||
}
|
}
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
hasOldBranch := true
|
hasOldBranch := true
|
||||||
if err := t.Clone(opts.OldBranch); err != nil {
|
if err := t.Clone(opts.OldBranch, true); err != nil {
|
||||||
for _, file := range opts.Files {
|
for _, file := range opts.Files {
|
||||||
if file.Operation == "delete" {
|
if file.Operation == "delete" {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -87,7 +87,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
|
|
||||||
hasOldBranch := true
|
hasOldBranch := true
|
||||||
if err = t.Clone(opts.OldBranch); err != nil {
|
if err = t.Clone(opts.OldBranch, true); err != nil {
|
||||||
if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
|
if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,10 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
|
||||||
return fmt.Errorf("%s is an organization not a user", u.Name)
|
return fmt.Errorf("%s is an organization not a user", u.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user_model.IsLastAdminUser(ctx, u) {
|
||||||
|
return models.ErrDeleteLastAdminUser{UID: u.ID}
|
||||||
|
}
|
||||||
|
|
||||||
if purge {
|
if purge {
|
||||||
// Disable the user first
|
// Disable the user first
|
||||||
// NOTE: This is deliberately not within a transaction as it must disable the user immediately to prevent any further action by the user to be purged.
|
// NOTE: This is deliberately not within a transaction as it must disable the user immediately to prevent any further action by the user to be purged.
|
||||||
|
@ -295,7 +299,8 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error {
|
||||||
}
|
}
|
||||||
if err := DeleteUser(ctx, u, false); err != nil {
|
if err := DeleteUser(ctx, u, false); err != nil {
|
||||||
// Ignore users that were set inactive by admin.
|
// Ignore users that were set inactive by admin.
|
||||||
if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) || models.IsErrUserOwnPackages(err) {
|
if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) ||
|
||||||
|
models.IsErrUserOwnPackages(err) || models.IsErrDeleteLastAdminUser(err) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -126,7 +126,7 @@
|
||||||
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.published_release_label"}}</span>
|
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.published_release_label"}}</span>
|
||||||
{{.TagName}}
|
{{.TagName}}
|
||||||
{{if not .IsTag}}
|
{{if not .IsTag}}
|
||||||
<a class="title" href="{{$.RepoLink}}/src/{{.TagName | PathEscapeSegments}}">{{.Title | RenderEmoji $.Context}}</a>
|
<a class="title" href="{{$.RepoLink}}/src/{{.TagName | PathEscapeSegments}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{TimeSinceUnix .CreatedUnix ctx.Locale}}
|
{{TimeSinceUnix .CreatedUnix ctx.Locale}}
|
||||||
</p>
|
</p>
|
||||||
|
@ -146,7 +146,7 @@
|
||||||
{{range .Activity.MergedPRs}}
|
{{range .Activity.MergedPRs}}
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
<span class="ui purple label">{{ctx.Locale.Tr "repo.activity.merged_prs_label"}}</span>
|
<span class="ui purple label">{{ctx.Locale.Tr "repo.activity.merged_prs_label"}}</span>
|
||||||
#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title | RenderEmoji $.Context}}</a>
|
#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
|
||||||
{{TimeSinceUnix .MergedUnix ctx.Locale}}
|
{{TimeSinceUnix .MergedUnix ctx.Locale}}
|
||||||
</p>
|
</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -165,7 +165,7 @@
|
||||||
{{range .Activity.OpenedPRs}}
|
{{range .Activity.OpenedPRs}}
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.opened_prs_label"}}</span>
|
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.opened_prs_label"}}</span>
|
||||||
#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title | RenderEmoji $.Context}}</a>
|
#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
|
||||||
{{TimeSinceUnix .Issue.CreatedUnix ctx.Locale}}
|
{{TimeSinceUnix .Issue.CreatedUnix ctx.Locale}}
|
||||||
</p>
|
</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -184,7 +184,7 @@
|
||||||
{{range .Activity.ClosedIssues}}
|
{{range .Activity.ClosedIssues}}
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
<span class="ui red label">{{ctx.Locale.Tr "repo.activity.closed_issue_label"}}</span>
|
<span class="ui red label">{{ctx.Locale.Tr "repo.activity.closed_issue_label"}}</span>
|
||||||
#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context}}</a>
|
#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
|
||||||
{{TimeSinceUnix .ClosedUnix ctx.Locale}}
|
{{TimeSinceUnix .ClosedUnix ctx.Locale}}
|
||||||
</p>
|
</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -203,7 +203,7 @@
|
||||||
{{range .Activity.OpenedIssues}}
|
{{range .Activity.OpenedIssues}}
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.new_issue_label"}}</span>
|
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.new_issue_label"}}</span>
|
||||||
#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context}}</a>
|
#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
|
||||||
{{TimeSinceUnix .CreatedUnix ctx.Locale}}
|
{{TimeSinceUnix .CreatedUnix ctx.Locale}}
|
||||||
</p>
|
</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -221,9 +221,9 @@
|
||||||
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.unresolved_conv_label"}}</span>
|
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.unresolved_conv_label"}}</span>
|
||||||
#{{.Index}}
|
#{{.Index}}
|
||||||
{{if .IsPull}}
|
{{if .IsPull}}
|
||||||
<a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Title | RenderEmoji $.Context}}</a>
|
<a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context}}</a>
|
<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{TimeSinceUnix .UpdatedUnix ctx.Locale}}
|
{{TimeSinceUnix .UpdatedUnix ctx.Locale}}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DefaultBranchBranch.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
|
<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DefaultBranchBranch.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
|
||||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}}
|
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}}
|
||||||
</div>
|
</div>
|
||||||
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage .RepoLink .Repository.ComposeMetas}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
|
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage .Repository.ComposeMetas}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="right aligned middle aligned overflow-visible">
|
<td class="right aligned middle aligned overflow-visible">
|
||||||
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
|
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
|
||||||
|
@ -101,7 +101,7 @@
|
||||||
<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
|
<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
|
||||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}}
|
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}}
|
||||||
</div>
|
</div>
|
||||||
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage $.RepoLink $.Repository.ComposeMetas}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} {{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
|
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage $.Repository.ComposeMetas}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} {{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td class="two wide ui">
|
<td class="two wide ui">
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="ui top attached header clearing segment gt-relative commit-header {{$class}}">
|
<div class="ui top attached header clearing segment gt-relative commit-header {{$class}}">
|
||||||
<div class="gt-df gt-mb-4 gt-fw">
|
<div class="gt-df gt-mb-4 gt-fw">
|
||||||
<h3 class="gt-mb-0 gt-f1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses "root" $}}</h3>
|
<h3 class="gt-mb-0 gt-f1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message $.Repository.ComposeMetas}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses "root" $}}</h3>
|
||||||
{{if not $.PageIsWiki}}
|
{{if not $.PageIsWiki}}
|
||||||
<div>
|
<div>
|
||||||
<a class="ui primary tiny button" href="{{.SourcePath}}">
|
<a class="ui primary tiny button" href="{{.SourcePath}}">
|
||||||
|
@ -135,7 +135,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{if IsMultilineCommitMessage .Commit.Message}}
|
{{if IsMultilineCommitMessage .Commit.Message}}
|
||||||
<pre class="commit-body">{{RenderCommitBody $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
|
<pre class="commit-body">{{RenderCommitBody $.Context .Commit.Message $.Repository.ComposeMetas}}</pre>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{template "repo/commit_load_branches_and_tags" .}}
|
{{template "repo/commit_load_branches_and_tags" .}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -258,7 +258,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .Note}}
|
{{if .NoteRendered}}
|
||||||
<div class="ui top attached header segment git-notes">
|
<div class="ui top attached header segment git-notes">
|
||||||
{{svg "octicon-note" 16 "gt-mr-3"}}
|
{{svg "octicon-note" 16 "gt-mr-3"}}
|
||||||
{{ctx.Locale.Tr "repo.diff.git-notes"}}:
|
{{ctx.Locale.Tr "repo.diff.git-notes"}}:
|
||||||
|
@ -276,7 +276,7 @@
|
||||||
<span class="text grey" id="note-authored-time">{{TimeSince .NoteCommit.Author.When ctx.Locale}}</span>
|
<span class="text grey" id="note-authored-time">{{TimeSince .NoteCommit.Author.When ctx.Locale}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui bottom attached info segment git-notes">
|
<div class="ui bottom attached info segment git-notes">
|
||||||
<pre class="commit-body">{{RenderNote $.Context .Note $.RepoLink $.Repository.ComposeMetas}}</pre>
|
<pre class="commit-body">{{.NoteRendered | Str2html}}</pre>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{template "repo/diff/box" .}}
|
{{template "repo/diff/box" .}}
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | RenderEmoji $.Context}}</span>
|
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | RenderEmoji $.Context}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
|
{{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
|
||||||
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.Context .Message $commitRepoLink $commitLink $.Repository.ComposeMetas}}</span>
|
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.Context .Message $commitLink $.Repository.ComposeMetas}}</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
</span>
|
</span>
|
||||||
{{if IsMultilineCommitMessage .Message}}
|
{{if IsMultilineCommitMessage .Message}}
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $}}
|
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $}}
|
||||||
{{if IsMultilineCommitMessage .Message}}
|
{{if IsMultilineCommitMessage .Message}}
|
||||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .Message $commitRepoLink $.Repository.ComposeMetas}}</pre>
|
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .Message $.Repository.ComposeMetas}}</pre>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
{{if .Committer}}
|
{{if .Committer}}
|
||||||
|
|
|
@ -38,12 +38,12 @@
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="gt-mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $commitLink $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</span>
|
<span class="gt-mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message $commitLink $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</span>
|
||||||
{{if IsMultilineCommitMessage .Message}}
|
{{if IsMultilineCommitMessage .Message}}
|
||||||
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if IsMultilineCommitMessage .Message}}
|
{{if IsMultilineCommitMessage .Message}}
|
||||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</pre>
|
<pre class="commit-body gt-hidden">{{RenderCommitBody $.root.Context .Message $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</pre>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -194,7 +194,7 @@
|
||||||
<div class="twelve wide column issue-title">
|
<div class="twelve wide column issue-title">
|
||||||
{{ctx.Locale.Tr "repo.pulls.has_pull_request" (print (Escape $.RepoLink) "/pulls/" .PullRequest.Issue.Index) (Escape $.RepoRelPath) .PullRequest.Index | Safe}}
|
{{ctx.Locale.Tr "repo.pulls.has_pull_request" (print (Escape $.RepoLink) "/pulls/" .PullRequest.Issue.Index) (Escape $.RepoRelPath) .PullRequest.Index | Safe}}
|
||||||
<h1>
|
<h1>
|
||||||
<span id="issue-title">{{RenderIssueTitle $.Context .PullRequest.Issue.Title $.RepoLink $.Repository.ComposeMetas}}</span>
|
<span id="issue-title">{{RenderIssueTitle $.Context .PullRequest.Issue.Title $.Repository.ComposeMetas}}</span>
|
||||||
<span class="index">#{{.PullRequest.Issue.Index}}</span>
|
<span class="index">#{{.PullRequest.Issue.Index}}</span>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<span class="message gt-dib gt-ellipsis gt-mr-3">
|
<span class="message gt-dib gt-ellipsis gt-mr-3">
|
||||||
<span>{{RenderCommitMessage $.Context $commit.Subject $.RepoLink $.Repository.ComposeMetas}}</span>
|
<span>{{RenderCommitMessage $.Context $commit.Subject $.Repository.ComposeMetas}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="commit-refs gt-df gt-ac gt-mr-2">
|
<span class="commit-refs gt-df gt-ac gt-mr-2">
|
||||||
{{range $commit.Refs}}
|
{{range $commit.Refs}}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{if .MilestoneID}}
|
{{if .MilestoneID}}
|
||||||
<div class="meta gt-my-2">
|
<div class="meta gt-my-2">
|
||||||
<a class="milestone" href="{{$.Page.RepoLink}}/milestone/{{.MilestoneID}}">
|
<a class="milestone" href="{{.Repo.Link}}/milestone/{{.MilestoneID}}">
|
||||||
{{svg "octicon-milestone" 16 "gt-mr-2 gt-vm"}}
|
{{svg "octicon-milestone" 16 "gt-mr-2 gt-vm"}}
|
||||||
<span class="gt-vm">{{.Milestone.Name}}</span>
|
<span class="gt-vm">{{.Milestone.Name}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
{{if $.Page.LinkedPRs}}
|
{{if $.Page.LinkedPRs}}
|
||||||
{{range index $.Page.LinkedPRs .ID}}
|
{{range index $.Page.LinkedPRs .ID}}
|
||||||
<div class="meta gt-my-2">
|
<div class="meta gt-my-2">
|
||||||
<a href="{{$.Page.RepoLink}}/pulls/{{.Index}}">
|
<a href="{{$.Issue.Repo.Link}}/pulls/{{.Index}}">
|
||||||
<span class="gt-m-0 text {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "gt-mr-2 gt-vm"}}</span>
|
<span class="gt-m-0 text {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "gt-mr-2 gt-vm"}}</span>
|
||||||
<span class="gt-vm">{{.Title}} <span class="text light grey">#{{.Index}}</span></span>
|
<span class="gt-vm">{{.Title}} <span class="text light grey">#{{.Index}}</span></span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
{{if or .Labels .Assignees}}
|
{{if or .Labels .Assignees}}
|
||||||
<div class="extra content labels-list gt-p-0 gt-pt-2">
|
<div class="extra content labels-list gt-p-0 gt-pt-2">
|
||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
<a target="_blank" href="{{$.Page.RepoLink}}/issues?labels={{.ID}}">{{RenderLabel ctx .}}</a>
|
<a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx .}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="right floated">
|
<div class="right floated">
|
||||||
{{range .Assignees}}
|
{{range .Assignees}}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="issue-title-header">
|
<div class="issue-title-header">
|
||||||
<div class="issue-title" id="issue-title-wrapper">
|
<div class="issue-title" id="issue-title-wrapper">
|
||||||
<h1 class="gt-word-break">
|
<h1 class="gt-word-break">
|
||||||
<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title $.RepoLink $.Repository.ComposeMetas | RenderCodeBlock}} <span class="index">#{{.Issue.Index}}</span>
|
<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title $.Repository.ComposeMetas | RenderCodeBlock}} <span class="index">#{{.Issue.Index}}</span>
|
||||||
</span>
|
</span>
|
||||||
<div id="edit-title-input" class="ui input gt-f1 gt-hidden">
|
<div id="edit-title-input" class="ui input gt-f1 gt-hidden">
|
||||||
<input value="{{.Issue.Title}}" maxlength="255" autocomplete="off">
|
<input value="{{.Issue.Title}}" maxlength="255" autocomplete="off">
|
||||||
|
|
|
@ -26,10 +26,10 @@
|
||||||
</a>
|
</a>
|
||||||
{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses "root" $}}
|
{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses "root" $}}
|
||||||
{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
|
{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
|
||||||
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span>
|
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $commitLink $.Repository.ComposeMetas}}</span>
|
||||||
{{if IsMultilineCommitMessage .LatestCommit.Message}}
|
{{if IsMultilineCommitMessage .LatestCommit.Message}}
|
||||||
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
||||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
|
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .LatestCommit.Message $.Repository.ComposeMetas}}</pre>
|
||||||
{{end}}
|
{{end}}
|
||||||
</span>
|
</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
<span class="truncate">
|
<span class="truncate">
|
||||||
{{if $commit}}
|
{{if $commit}}
|
||||||
{{$commitLink := printf "%s/commit/%s" $.RepoLink (PathEscape $commit.ID.String)}}
|
{{$commitLink := printf "%s/commit/%s" $.RepoLink (PathEscape $commit.ID.String)}}
|
||||||
{{RenderCommitMessageLinkSubject $.Context $commit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}
|
{{RenderCommitMessageLinkSubject $.Context $commit.Message $commitLink $.Repository.ComposeMetas}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="ui active tiny slow centered inline">…</div>
|
<div class="ui active tiny slow centered inline">…</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
3
templates/swagger/v1_json.tmpl
generated
3
templates/swagger/v1_json.tmpl
generated
|
@ -660,6 +660,9 @@
|
||||||
"200": {
|
"200": {
|
||||||
"$ref": "#/responses/User"
|
"$ref": "#/responses/User"
|
||||||
},
|
},
|
||||||
|
"400": {
|
||||||
|
"$ref": "#/responses/error"
|
||||||
|
},
|
||||||
"403": {
|
"403": {
|
||||||
"$ref": "#/responses/forbidden"
|
"$ref": "#/responses/forbidden"
|
||||||
},
|
},
|
||||||
|
|
|
@ -90,7 +90,7 @@
|
||||||
<img class="ui avatar" src="{{$push.AvatarLink $.Context .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16">
|
<img class="ui avatar" src="{{$push.AvatarLink $.Context .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16">
|
||||||
<a class="ui sha label" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
|
<a class="ui sha label" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
|
||||||
<span class="text truncate">
|
<span class="text truncate">
|
||||||
{{RenderCommitMessage $.Context .Message $repoLink $.ComposeMetas}}
|
{{RenderCommitMessage $.Context .Message $.ComposeMetas}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -15,8 +15,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var renderContext = markup.RenderContext{
|
var renderContext = markup.RenderContext{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
URLPrefix: "https://example.com/go-gitea/gitea",
|
Links: markup.Links{
|
||||||
|
Base: "https://example.com/go-gitea/gitea",
|
||||||
|
},
|
||||||
Metas: map[string]string{
|
Metas: map[string]string{
|
||||||
"user": "go-gitea",
|
"user": "go-gitea",
|
||||||
"repo": "gitea",
|
"repo": "gitea",
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ref: refs/heads/main
|
|
@ -0,0 +1,8 @@
|
||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = true
|
||||||
|
ignorecase = true
|
||||||
|
precomposeunicode = true
|
||||||
|
[remote "origin"]
|
||||||
|
url = https://try.gitea.io/me-heer/test_commit_revert.git
|
|
@ -0,0 +1 @@
|
||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
|
@ -0,0 +1,6 @@
|
||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
# pack-refs with: peeled fully-peeled sorted
|
||||||
|
46aa6ab2c881ae90e15d9ccfc947d1625c892ce5 refs/heads/develop
|
||||||
|
deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7 refs/heads/main
|
|
@ -0,0 +1 @@
|
||||||
|
deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7
|
|
@ -290,3 +290,93 @@ func TestActionsArtifactUploadWithRetentionDays(t *testing.T) {
|
||||||
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestActionsArtifactOverwrite(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
{
|
||||||
|
// download old artifact uploaded by tests above, it should 1024 A
|
||||||
|
req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
|
||||||
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
var listResp listArtifactsResponse
|
||||||
|
DecodeJSON(t, resp, &listResp)
|
||||||
|
|
||||||
|
idx := strings.Index(listResp.Value[0].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
|
||||||
|
url := listResp.Value[0].FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
|
||||||
|
req = NewRequest(t, "GET", url)
|
||||||
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
var downloadResp downloadArtifactResponse
|
||||||
|
DecodeJSON(t, resp, &downloadResp)
|
||||||
|
|
||||||
|
idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
|
||||||
|
url = downloadResp.Value[0].ContentLocation[idx:]
|
||||||
|
req = NewRequest(t, "GET", url)
|
||||||
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
body := strings.Repeat("A", 1024)
|
||||||
|
assert.Equal(t, resp.Body.String(), body)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// upload same artifact, it uses 4096 B
|
||||||
|
req := NewRequestWithJSON(t, "POST", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts", getUploadArtifactRequest{
|
||||||
|
Type: "actions_storage",
|
||||||
|
Name: "artifact",
|
||||||
|
})
|
||||||
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
var uploadResp uploadArtifactResponse
|
||||||
|
DecodeJSON(t, resp, &uploadResp)
|
||||||
|
|
||||||
|
idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
|
||||||
|
url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact/abc.txt"
|
||||||
|
body := strings.Repeat("B", 4096)
|
||||||
|
req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body))
|
||||||
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
|
req.Header.Add("Content-Range", "bytes 0-4095/4096")
|
||||||
|
req.Header.Add("x-tfs-filelength", "4096")
|
||||||
|
req.Header.Add("x-actions-results-md5", "wUypcJFeZCK5T6r4lfqzqg==") // base64(md5(body))
|
||||||
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
// confirm artifact upload
|
||||||
|
req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact")
|
||||||
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// download artifact again, it should 4096 B
|
||||||
|
req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
|
||||||
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
var listResp listArtifactsResponse
|
||||||
|
DecodeJSON(t, resp, &listResp)
|
||||||
|
|
||||||
|
var uploadedItem listArtifactsResponseItem
|
||||||
|
for _, item := range listResp.Value {
|
||||||
|
if item.Name == "artifact" {
|
||||||
|
uploadedItem = item
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.Equal(t, uploadedItem.Name, "artifact")
|
||||||
|
|
||||||
|
idx := strings.Index(uploadedItem.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
|
||||||
|
url := uploadedItem.FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
|
||||||
|
req = NewRequest(t, "GET", url)
|
||||||
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
var downloadResp downloadArtifactResponse
|
||||||
|
DecodeJSON(t, resp, &downloadResp)
|
||||||
|
|
||||||
|
idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
|
||||||
|
url = downloadResp.Value[0].ContentLocation[idx:]
|
||||||
|
req = NewRequest(t, "GET", url)
|
||||||
|
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
|
||||||
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
body := strings.Repeat("B", 4096)
|
||||||
|
assert.Equal(t, resp.Body.String(), body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
container_module "code.gitea.io/gitea/modules/packages/container"
|
container_module "code.gitea.io/gitea/modules/packages/container"
|
||||||
"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/test"
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
"github.com/minio/sha256-simd"
|
"github.com/minio/sha256-simd"
|
||||||
|
@ -106,6 +107,14 @@ func TestPackageContainer(t *testing.T) {
|
||||||
req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL))
|
req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL))
|
||||||
addTokenAuthHeader(req, anonymousToken)
|
addTokenAuthHeader(req, anonymousToken)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
defer test.MockVariableValue(&setting.Service.RequireSignInView, true)()
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL))
|
||||||
|
MakeRequest(t, req, http.StatusUnauthorized)
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL))
|
||||||
|
MakeRequest(t, req, http.StatusUnauthorized)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("User", func(t *testing.T) {
|
t.Run("User", func(t *testing.T) {
|
||||||
|
|
34
tests/integration/repo_mergecommit_revert_test.go
Normal file
34
tests/integration/repo_mergecommit_revert_test.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRepoMergeCommitRevert(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
|
req := NewRequest(t, "GET", "/user2/test_commit_revert/_cherrypick/deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7/main?ref=main&refType=branch&cherry-pick-type=revert")
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
req = NewRequestWithValues(t, "POST", "/user2/test_commit_revert/_cherrypick/deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7/main", map[string]string{
|
||||||
|
"_csrf": htmlDoc.GetCSRF(),
|
||||||
|
"last_commit": "deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7",
|
||||||
|
"page_has_posted": "true",
|
||||||
|
"revert": "true",
|
||||||
|
"commit_summary": "reverting test commit",
|
||||||
|
"commit_message": "test message",
|
||||||
|
"commit_choice": "direct",
|
||||||
|
"new_branch_name": "test-revert-branch-1",
|
||||||
|
})
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
|
// A successful revert redirects to the main branch
|
||||||
|
assert.EqualValues(t, "/user2/test_commit_revert/src/branch/main", resp.Header().Get("Location"))
|
||||||
|
}
|
19
web_src/js/bootstrap.js
vendored
19
web_src/js/bootstrap.js
vendored
|
@ -8,10 +8,21 @@ __webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`;
|
||||||
export function showGlobalErrorMessage(msg) {
|
export function showGlobalErrorMessage(msg) {
|
||||||
const pageContent = document.querySelector('.page-content');
|
const pageContent = document.querySelector('.page-content');
|
||||||
if (!pageContent) return;
|
if (!pageContent) return;
|
||||||
const el = document.createElement('div');
|
|
||||||
el.innerHTML = `<div class="ui container negative message center aligned js-global-error" style="white-space: pre-line;"></div>`;
|
// compact the message to a data attribute to avoid too many duplicated messages
|
||||||
el.childNodes[0].textContent = msg;
|
const msgCompact = msg.replace(/\W/g, '').trim();
|
||||||
pageContent.prepend(el.childNodes[0]);
|
let msgDiv = pageContent.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
|
||||||
|
if (!msgDiv) {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.innerHTML = `<div class="ui container negative message center aligned js-global-error" style="white-space: pre-line;"></div>`;
|
||||||
|
msgDiv = el.childNodes[0];
|
||||||
|
}
|
||||||
|
// merge duplicated messages into "the message (count)" format
|
||||||
|
const msgCount = Number(msgDiv.getAttribute(`data-global-error-msg-count`)) + 1;
|
||||||
|
msgDiv.setAttribute(`data-global-error-msg-compact`, msgCompact);
|
||||||
|
msgDiv.setAttribute(`data-global-error-msg-count`, msgCount.toString());
|
||||||
|
msgDiv.textContent = msg + (msgCount > 1 ? ` (${msgCount})` : '');
|
||||||
|
pageContent.prepend(msgDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
12
web_src/js/bootstrap.test.js
vendored
Normal file
12
web_src/js/bootstrap.test.js
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import {showGlobalErrorMessage} from './bootstrap.js';
|
||||||
|
|
||||||
|
test('showGlobalErrorMessage', () => {
|
||||||
|
document.body.innerHTML = '<div class="page-content"></div>';
|
||||||
|
showGlobalErrorMessage('test msg 1');
|
||||||
|
showGlobalErrorMessage('test msg 2');
|
||||||
|
showGlobalErrorMessage('test msg 1'); // duplicated
|
||||||
|
|
||||||
|
expect(document.body.innerHTML).toContain('>test msg 1 (2)<');
|
||||||
|
expect(document.body.innerHTML).toContain('>test msg 2<');
|
||||||
|
expect(document.querySelectorAll('.js-global-error').length).toEqual(2);
|
||||||
|
});
|
|
@ -1,3 +1,5 @@
|
||||||
|
window.__webpack_public_path__ = '';
|
||||||
|
|
||||||
window.config = {
|
window.config = {
|
||||||
csrfToken: 'test-csrf-token-123456',
|
csrfToken: 'test-csrf-token-123456',
|
||||||
pageData: {},
|
pageData: {},
|
||||||
|
|
Loading…
Reference in a new issue