From 3ff0a126e12109b6c3aceaa229dd1bf229b6ad4b Mon Sep 17 00:00:00 2001
From: mrsdizzie <info@mrsdizzie.com>
Date: Fri, 12 Apr 2019 01:53:34 -0400
Subject: [PATCH] Improve issue autolinks (#6273)

* Improve issue autolinks

Update autolinks to match what github does here:

Issue in same repo: #1
Issue in different repo: org/repo#1

Fixes #6264

* Use setting.AppURL when parsing URL

Using setting.AppURL here is a more reliable way of parsing the current
URL and what other functions in this file seem to use.

* Make ComposeMetas always return a valid context

* Add per repository markdown renderers for better context

* Update for use of context metas

Now that we include the user and repo name inside context metas, update
various code and tests for this new logic
---
 models/repo.go                           | 18 ++++++-------
 models/repo_test.go                      |  5 +++-
 modules/markup/html.go                   | 34 ++++++++++++++++++------
 modules/markup/html_internal_test.go     | 20 ++++++++++----
 modules/markup/markdown/markdown_test.go | 14 +++++++---
 routers/api/v1/api.go                    |  2 ++
 routers/api/v1/misc/markdown.go          | 22 ++++++++++++---
 templates/repo/diff/box.tmpl             |  2 +-
 templates/repo/diff/comment_form.tmpl    |  2 +-
 templates/repo/editor/edit.tmpl          |  4 +--
 templates/repo/issue/comment_tab.tmpl    |  4 +--
 templates/repo/issue/view_content.tmpl   |  2 +-
 templates/repo/wiki/new.tmpl             |  2 +-
 13 files changed, 94 insertions(+), 37 deletions(-)

diff --git a/models/repo.go b/models/repo.go
index 5cdee6c3f9..8b51f14043 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -469,19 +469,19 @@ func (repo *Repository) mustOwnerName(e Engine) string {
 	return repo.OwnerName
 }
 
-// ComposeMetas composes a map of metas for rendering external issue tracker URL.
+// ComposeMetas composes a map of metas for properly rendering issue links and external issue trackers.
 func (repo *Repository) ComposeMetas() map[string]string {
-	unit, err := repo.GetUnit(UnitTypeExternalTracker)
-	if err != nil {
-		return nil
-	}
-
 	if repo.ExternalMetas == nil {
 		repo.ExternalMetas = map[string]string{
-			"format": unit.ExternalTrackerConfig().ExternalTrackerFormat,
-			"user":   repo.MustOwner().Name,
-			"repo":   repo.Name,
+			"user": repo.MustOwner().Name,
+			"repo": repo.Name,
 		}
+		unit, err := repo.GetUnit(UnitTypeExternalTracker)
+		if err != nil {
+			return repo.ExternalMetas
+		}
+
+		repo.ExternalMetas["format"] = unit.ExternalTrackerConfig().ExternalTrackerFormat
 		switch unit.ExternalTrackerConfig().ExternalTrackerStyle {
 		case markup.IssueNameStyleAlphanumeric:
 			repo.ExternalMetas["style"] = markup.IssueNameStyleAlphanumeric
diff --git a/models/repo_test.go b/models/repo_test.go
index 752ffc2dd9..a5b8cce9b9 100644
--- a/models/repo_test.go
+++ b/models/repo_test.go
@@ -20,7 +20,10 @@ func TestRepo(t *testing.T) {
 	repo.Owner = &User{Name: "testOwner"}
 
 	repo.Units = nil
-	assert.Nil(t, repo.ComposeMetas())
+
+	metas := repo.ComposeMetas()
+	assert.Equal(t, "testRepo", metas["repo"])
+	assert.Equal(t, "testOwner", metas["user"])
 
 	externalTracker := RepoUnit{
 		Type: UnitTypeExternalTracker,
diff --git a/modules/markup/html.go b/modules/markup/html.go
index 136f76d301..930c6b3a3e 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -551,20 +551,37 @@ func shortLinkProcessorFull(ctx *postProcessCtx, node *html.Node, noLink bool) {
 }
 
 func fullIssuePatternProcessor(ctx *postProcessCtx, node *html.Node) {
+	if ctx.metas == nil {
+		return
+	}
 	m := getIssueFullPattern().FindStringSubmatchIndex(node.Data)
 	if m == nil {
 		return
 	}
 	link := node.Data[m[0]:m[1]]
 	id := "#" + node.Data[m[2]:m[3]]
-	// TODO if m[4]:m[5] is not nil, then link is to a comment,
-	// and we should indicate that in the text somehow
-	replaceContent(node, m[0], m[1], createLink(link, id))
+
+	// extract repo and org name from matched link like
+	// http://localhost:3000/gituser/myrepo/issues/1
+	linkParts := strings.Split(path.Clean(link), "/")
+	matchOrg := linkParts[len(linkParts)-4]
+	matchRepo := linkParts[len(linkParts)-3]
+
+	if matchOrg == ctx.metas["user"] && matchRepo == ctx.metas["repo"] {
+		// TODO if m[4]:m[5] is not nil, then link is to a comment,
+		// and we should indicate that in the text somehow
+		replaceContent(node, m[0], m[1], createLink(link, id))
+
+	} else {
+		orgRepoID := matchOrg + "/" + matchRepo + id
+		replaceContent(node, m[0], m[1], createLink(link, orgRepoID))
+	}
 }
 
 func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
-	prefix := cutoutVerbosePrefix(ctx.urlPrefix)
-
+	if ctx.metas == nil {
+		return
+	}
 	// default to numeric pattern, unless alphanumeric is requested.
 	pattern := issueNumericPattern
 	if ctx.metas["style"] == IssueNameStyleAlphanumeric {
@@ -575,11 +592,10 @@ func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
 	if match == nil {
 		return
 	}
+
 	id := node.Data[match[2]:match[3]]
 	var link *html.Node
-	if ctx.metas == nil {
-		link = createLink(util.URLJoin(prefix, "issues", id[1:]), id)
-	} else {
+	if _, ok := ctx.metas["format"]; ok {
 		// Support for external issue tracker
 		if ctx.metas["style"] == IssueNameStyleAlphanumeric {
 			ctx.metas["index"] = id
@@ -587,6 +603,8 @@ func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
 			ctx.metas["index"] = id[1:]
 		}
 		link = createLink(com.Expand(ctx.metas["format"], ctx.metas), id)
+	} else {
+		link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "issues", id[1:]), id)
 	}
 	replaceContent(node, match[2], match[3], link)
 }
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index c71948593d..135a8e103c 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -53,6 +53,12 @@ var alphanumericMetas = map[string]string{
 	"style":  IssueNameStyleAlphanumeric,
 }
 
+// these values should match the Repo const above
+var localMetas = map[string]string{
+	"user": "gogits",
+	"repo": "gogs",
+}
+
 func TestRender_IssueIndexPattern(t *testing.T) {
 	// numeric: render inputs without valid mentions
 	test := func(s string) {
@@ -91,7 +97,7 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
 			links[i] = numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), index)
 		}
 		expectedNil := fmt.Sprintf(expectedFmt, links...)
-		testRenderIssueIndexPattern(t, s, expectedNil, nil)
+		testRenderIssueIndexPattern(t, s, expectedNil, &postProcessCtx{metas: localMetas})
 
 		for i, index := range indices {
 			links[i] = numericIssueLink("https://someurl.com/someUser/someRepo/", index)
@@ -171,6 +177,7 @@ func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *post
 	if ctx.urlPrefix == "" {
 		ctx.urlPrefix = AppSubURL
 	}
+
 	res, err := ctx.postProcess([]byte(input))
 	assert.NoError(t, err)
 	assert.Equal(t, expected, string(res))
@@ -181,10 +188,10 @@ func TestRender_AutoLink(t *testing.T) {
 	setting.AppSubURL = AppSubURL
 
 	test := func(input, expected string) {
-		buffer, err := PostProcess([]byte(input), setting.AppSubURL, nil, false)
+		buffer, err := PostProcess([]byte(input), setting.AppSubURL, localMetas, false)
 		assert.Equal(t, err, nil)
 		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
-		buffer, err = PostProcess([]byte(input), setting.AppSubURL, nil, true)
+		buffer, err = PostProcess([]byte(input), setting.AppSubURL, localMetas, true)
 		assert.Equal(t, err, nil)
 		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
 	}
@@ -214,6 +221,7 @@ func TestRender_FullIssueURLs(t *testing.T) {
 		if ctx.urlPrefix == "" {
 			ctx.urlPrefix = AppSubURL
 		}
+		ctx.metas = localMetas
 		result, err := ctx.postProcess([]byte(input))
 		assert.NoError(t, err)
 		assert.Equal(t, expected, string(result))
@@ -221,9 +229,11 @@ func TestRender_FullIssueURLs(t *testing.T) {
 	test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6",
 		"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6")
 	test("Look here http://localhost:3000/person/repo/issues/4",
-		`Look here <a href="http://localhost:3000/person/repo/issues/4">#4</a>`)
+		`Look here <a href="http://localhost:3000/person/repo/issues/4">person/repo#4</a>`)
 	test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
-		`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234">#4</a>`)
+		`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234">person/repo#4</a>`)
+	test("http://localhost:3000/gogits/gogs/issues/4",
+		`<a href="http://localhost:3000/gogits/gogs/issues/4">#4</a>`)
 }
 
 func TestRegExp_issueNumericPattern(t *testing.T) {
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index 5aa9c3d7d2..8ba51e6a1b 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -19,6 +19,12 @@ const AppURL = "http://localhost:3000/"
 const Repo = "gogits/gogs"
 const AppSubURL = AppURL + Repo + "/"
 
+// these values should match the Repo const above
+var localMetas = map[string]string{
+	"user": "gogits",
+	"repo": "gogs",
+}
+
 func TestRender_StandardLinks(t *testing.T) {
 	setting.AppURL = AppURL
 	setting.AppSubURL = AppSubURL
@@ -100,7 +106,8 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
 <p>Ideas and codes</p>
 
 <ul>
-<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" rel="nofollow">#786</a></li>
+<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" rel="nofollow">ocornut/imgui#786</a></li>
+<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" rel="nofollow">#786</a></li>
 <li>Node graph editors <a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">https://github.com/ocornut/imgui/issues/306</a></li>
 <li><a href="` + baseURLContent + `/memory_editor_example" rel="nofollow">Memory Editor</a></li>
 <li><a href="` + baseURLContent + `/plot_var_example" rel="nofollow">Plot var helper</a></li>
@@ -188,6 +195,7 @@ var sameCases = []string{
 Ideas and codes
 
 - Bezier widget (by @r-lyeh) ` + AppURL + `ocornut/imgui/issues/786
+- Bezier widget (by @r-lyeh) ` + AppURL + `gogits/gogs/issues/786
 - Node graph editors https://github.com/ocornut/imgui/issues/306
 - [[Memory Editor|memory_editor_example]]
 - [[Plot var helper|plot_var_example]]`,
@@ -243,7 +251,7 @@ func TestTotal_RenderWiki(t *testing.T) {
 	answers := testAnswers(util.URLJoin(AppSubURL, "wiki/"), util.URLJoin(AppSubURL, "wiki", "raw/"))
 
 	for i := 0; i < len(sameCases); i++ {
-		line := RenderWiki([]byte(sameCases[i]), AppSubURL, nil)
+		line := RenderWiki([]byte(sameCases[i]), AppSubURL, localMetas)
 		assert.Equal(t, answers[i], line)
 	}
 
@@ -270,7 +278,7 @@ func TestTotal_RenderString(t *testing.T) {
 	answers := testAnswers(util.URLJoin(AppSubURL, "src", "master/"), util.URLJoin(AppSubURL, "raw", "master/"))
 
 	for i := 0; i < len(sameCases); i++ {
-		line := RenderString(sameCases[i], util.URLJoin(AppSubURL, "src", "master/"), nil)
+		line := RenderString(sameCases[i], util.URLJoin(AppSubURL, "src", "master/"), localMetas)
 		assert.Equal(t, answers[i], line)
 	}
 
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 02c74e5056..4194d98db8 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -584,6 +584,8 @@ func RegisterRoutes(m *macaron.Macaron) {
 						Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel).
 						Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteLabel)
 				})
+				m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
+				m.Post("/markdown/raw", misc.MarkdownRaw)
 				m.Group("/milestones", func() {
 					m.Combo("").Get(repo.ListMilestones).
 						Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone)
diff --git a/routers/api/v1/misc/markdown.go b/routers/api/v1/misc/markdown.go
index 633dff98eb..9ae7a6c58c 100644
--- a/routers/api/v1/misc/markdown.go
+++ b/routers/api/v1/misc/markdown.go
@@ -5,12 +5,16 @@
 package misc
 
 import (
+	"strings"
+
 	api "code.gitea.io/sdk/gitea"
 
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/markup/markdown"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
+
+	"mvdan.cc/xurls/v2"
 )
 
 // Markdown render markdown document to HTML
@@ -45,11 +49,23 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
 	switch form.Mode {
 	case "gfm":
 		md := []byte(form.Text)
-		context := util.URLJoin(setting.AppURL, form.Context)
+		urlPrefix := form.Context
+		var meta map[string]string
+		if !strings.HasPrefix(setting.AppSubURL+"/", urlPrefix) {
+			// check if urlPrefix is already set to a URL
+			linkRegex, _ := xurls.StrictMatchingScheme("https?://")
+			m := linkRegex.FindStringIndex(urlPrefix)
+			if m == nil {
+				urlPrefix = util.URLJoin(setting.AppURL, form.Context)
+			}
+		}
+		if ctx.Repo != nil && ctx.Repo.Repository != nil {
+			meta = ctx.Repo.Repository.ComposeMetas()
+		}
 		if form.Wiki {
-			ctx.Write([]byte(markdown.RenderWiki(md, context, nil)))
+			ctx.Write([]byte(markdown.RenderWiki(md, urlPrefix, meta)))
 		} else {
-			ctx.Write(markdown.Render(md, context, nil))
+			ctx.Write(markdown.Render(md, urlPrefix, meta))
 		}
 	default:
 		ctx.Write(markdown.RenderRaw([]byte(form.Text), "", false))
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index 331a17a5e9..2bdf9e5881 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -202,7 +202,7 @@
             <div class="ui comment form">
                 <div class="ui top attached tabular menu">
                     <a class="active write item">{{$.i18n.Tr "write"}}</a>
-                    <a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
+                    <a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
                 </div>
                 <div class="ui bottom attached active write tab segment">
                     <textarea tabindex="1" name="content"></textarea>
diff --git a/templates/repo/diff/comment_form.tmpl b/templates/repo/diff/comment_form.tmpl
index 1fb8b963da..38fd3fa7fb 100644
--- a/templates/repo/diff/comment_form.tmpl
+++ b/templates/repo/diff/comment_form.tmpl
@@ -12,7 +12,7 @@
 		<input type="hidden" name="diff_base_cid">
 		<div class="ui top attached tabular menu" {{if not $.hidden}}onload="assingMenuAttributes(this)" {{end}}data-write="write" data-preview="preview">
 			<a class="active item" data-tab="write">{{$.root.i18n.Tr "write"}}</a>
-			<a class="item" data-tab="preview" data-url="{{$.root.AppSubUrl}}/api/v1/markdown" data-context="{{$.root.RepoLink}}">{{$.root.i18n.Tr "preview"}}</a>
+			<a class="item" data-tab="preview" data-url="{{$.root.Repository.APIURL}}/markdown" data-context="{{$.root.RepoLink}}">{{$.root.i18n.Tr "preview"}}</a>
 		</div>
 		<div class="ui bottom attached active tab segment" data-tab="write">
 			<div class="field">
diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl
index 82656d6557..134dc818d4 100644
--- a/templates/repo/editor/edit.tmpl
+++ b/templates/repo/editor/edit.tmpl
@@ -30,13 +30,13 @@
 				<div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff">
 					<a class="active item" data-tab="write"><i class="octicon octicon-code"></i> {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a>
 					{{if not .IsNewFile}}
-					<a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL | EscapePound}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "preview"}}</a>
+					<a class="item" data-tab="preview" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL | EscapePound}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "preview"}}</a>
 					<a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName | EscapePound}}/{{.TreePath | EscapePound}}" data-context="{{.BranchLink}}"><i class="octicon octicon-diff"></i> {{.i18n.Tr "repo.editor.preview_changes"}}</a>
 					{{end}}
 				</div>
 				<div class="ui bottom attached active tab segment" data-tab="write">
 					<textarea id="edit_area" name="content" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
-						data-url="{{AppSubUrl}}/api/v1/markdown"
+						data-url="{{.Repository.APIURL}}/markdown"
 						data-context="{{.RepoLink}}"
 						data-markdown-file-exts="{{.MarkdownFileExts}}"
 						data-line-wrap-extensions="{{.LineWrapExtensions}}"
diff --git a/templates/repo/issue/comment_tab.tmpl b/templates/repo/issue/comment_tab.tmpl
index b1630824e5..be113e539a 100644
--- a/templates/repo/issue/comment_tab.tmpl
+++ b/templates/repo/issue/comment_tab.tmpl
@@ -1,10 +1,10 @@
 <div class="field">
 	<div class="ui top attached tabular menu" data-write="write" data-preview="preview">
 		<a class="active item" data-tab="write">{{.i18n.Tr "write"}}</a>
-		<a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "preview"}}</a>
+		<a class="item" data-tab="preview" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "preview"}}</a>
 	</div>
 	<div class="ui bottom attached active tab segment" data-tab="write">
-		<textarea id="content" class="edit_area js-quick-submit" name="content" tabindex="4" data-id="issue-{{.RepoName}}" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.Repo.RepoLink}}">
+		<textarea id="content" class="edit_area js-quick-submit" name="content" tabindex="4" data-id="issue-{{.RepoName}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.Repo.RepoLink}}">
 {{if .BodyQuery}}{{.BodyQuery}}{{else if .IssueTemplate}}{{.IssueTemplate}}{{else if .PullRequestTemplate}}{{.PullRequestTemplate}}{{else}}{{.content}}{{end}}</textarea>
 	</div>
 	<div class="ui bottom attached tab segment markdown" data-tab="preview">
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index 7445dcce86..cb58fbef63 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -156,7 +156,7 @@
 	<div class="ui comment form">
 		<div class="ui top attached tabular menu">
 			<a class="active write item">{{$.i18n.Tr "write"}}</a>
-			<a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
+			<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
 		</div>
 		<div class="ui bottom attached active write tab segment">
 			<textarea tabindex="1" name="content"></textarea>
diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl
index 7dc066ffb8..bf6c24220b 100644
--- a/templates/repo/wiki/new.tmpl
+++ b/templates/repo/wiki/new.tmpl
@@ -17,7 +17,7 @@
 				<input name="title" value="{{.title}}" autofocus required>
 			</div>
 			<div class="field">
-				<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/wiki" required>{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea>
+				<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/wiki" required>{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea>
 			</div>
 			<div class="field">
 				<input name="message" placeholder="{{.i18n.Tr "repo.wiki.default_commit_message"}}">