diff --git a/modules/structs/miscellaneous.go b/modules/structs/miscellaneous.go index bff10f95b7..7866cb5fc0 100644 --- a/modules/structs/miscellaneous.go +++ b/modules/structs/miscellaneous.go @@ -37,6 +37,10 @@ type MarkupOption struct { // // in: body FilePath string + // The current branch path where the form gets posted + // + // in: body + BranchPath string } // MarkupRender is a rendered markup document diff --git a/routers/api/v1/misc/markup.go b/routers/api/v1/misc/markup.go index 9699c79368..155ad15712 100644 --- a/routers/api/v1/misc/markup.go +++ b/routers/api/v1/misc/markup.go @@ -41,7 +41,16 @@ func Markup(ctx *context.APIContext) { return } - common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki) + re := common.Renderer{ + Mode: form.Mode, + Text: form.Text, + URLPrefix: form.Context, + FilePath: form.FilePath, + BranchPath: form.BranchPath, + IsWiki: form.Wiki, + } + + re.RenderMarkup(ctx.Base, ctx.Repo) } // Markdown render markdown document to HTML @@ -76,7 +85,14 @@ func Markdown(ctx *context.APIContext) { mode = form.Mode } - common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, "", form.Wiki) + re := common.Renderer{ + Mode: mode, + Text: form.Text, + URLPrefix: form.Context, + IsWiki: form.Wiki, + } + + re.RenderMarkup(ctx.Base, ctx.Repo) } // MarkdownRaw render raw markdown HTML diff --git a/routers/common/markup.go b/routers/common/markup.go index 2d5638ef61..ce3a8acdb0 100644 --- a/routers/common/markup.go +++ b/routers/common/markup.go @@ -18,26 +18,31 @@ import ( "mvdan.cc/xurls/v2" ) +type Renderer struct { + Mode, Text, URLPrefix, FilePath, BranchPath string + IsWiki bool +} + // RenderMarkup renders markup text for the /markup and /markdown endpoints -func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPrefix, filePath string, wiki bool) { +func (re *Renderer) RenderMarkup(ctx *context.Base, repo *context.Repository) { var markupType string relativePath := "" - if len(text) == 0 { + if len(re.Text) == 0 { _, _ = ctx.Write([]byte("")) return } - switch mode { + switch re.Mode { case "markdown": // Raw markdown if err := markdown.RenderRaw(&markup.RenderContext{ Ctx: ctx, Links: markup.Links{ AbsolutePrefix: true, - Base: urlPrefix, + Base: re.URLPrefix, }, - }, strings.NewReader(text), ctx.Resp); err != nil { + }, strings.NewReader(re.Text), ctx.Resp); err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) } return @@ -50,30 +55,30 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr case "file": // File as document based on file extension markupType = "" - relativePath = filePath + relativePath = re.FilePath default: - ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", mode)) + ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", re.Mode)) return } - if !strings.HasPrefix(setting.AppSubURL+"/", urlPrefix) { + if !strings.HasPrefix(setting.AppSubURL+"/", re.URLPrefix) { // check if urlPrefix is already set to a URL linkRegex, _ := xurls.StrictMatchingScheme("https?://") - m := linkRegex.FindStringIndex(urlPrefix) + m := linkRegex.FindStringIndex(re.URLPrefix) if m == nil { - urlPrefix = util.URLJoin(setting.AppURL, urlPrefix) + re.URLPrefix = util.URLJoin(setting.AppURL, re.URLPrefix) } } meta := map[string]string{} if repo != nil && repo.Repository != nil { - if mode == "comment" { + if re.Mode == "comment" { meta = repo.Repository.ComposeMetas(ctx) } else { meta = repo.Repository.ComposeDocumentMetas(ctx) } } - if mode != "comment" { + if re.Mode != "comment" { meta["mode"] = "document" } @@ -81,13 +86,14 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr Ctx: ctx, Links: markup.Links{ AbsolutePrefix: true, - Base: urlPrefix, + Base: re.URLPrefix, + BranchPath: re.BranchPath, }, Metas: meta, - IsWiki: wiki, + IsWiki: re.IsWiki, Type: markupType, RelativePath: relativePath, - }, strings.NewReader(text), ctx.Resp); err != nil { + }, strings.NewReader(re.Text), ctx.Resp); err != nil { if markup.IsErrUnsupportedRenderExtension(err) { ctx.Error(http.StatusUnprocessableEntity, err.Error()) } else { diff --git a/routers/web/misc/markup.go b/routers/web/misc/markup.go index 2dbbd6fc09..2bae122b91 100644 --- a/routers/web/misc/markup.go +++ b/routers/web/misc/markup.go @@ -14,5 +14,15 @@ import ( // Markup render markup document to HTML func Markup(ctx *context.Context) { form := web.GetForm(ctx).(*api.MarkupOption) - common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki) + + re := common.Renderer{ + Mode: form.Mode, + Text: form.Text, + URLPrefix: form.Context, + FilePath: form.FilePath, + BranchPath: form.BranchPath, + IsWiki: form.Wiki, + } + + re.RenderMarkup(ctx.Base, ctx.Repo) } diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 00c3d880a9..f27ad62982 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -211,6 +211,7 @@ func editFile(ctx *context.Context, isNewFile bool) { ctx.Data["TreeNames"] = treeNames ctx.Data["TreePaths"] = treePaths ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() + ctx.Data["BranchPath"] = ctx.Repo.BranchNameSubURL() ctx.Data["commit_summary"] = "" ctx.Data["commit_message"] = "" if canCommit { diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index 71cecf1514..39379a977c 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -28,7 +28,7 @@ <div class="field"> <div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff"> <a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a> - <a class="item" data-tab="preview" data-url="{{.Repository.Link}}/markup" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-markup-mode="file">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a> + <a class="item" data-tab="preview" data-url="{{.Repository.Link}}/markup" data-context="{{.RepoLink}}" data-branch-path="{{.BranchNameSubURL}}" data-markup-mode="file">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a> {{if not .IsNewFile}} <a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a> {{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index fd147f44a2..c714ff0927 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -24322,6 +24322,10 @@ "description": "MarkupOption markup options", "type": "object", "properties": { + "BranchPath": { + "description": "The current branch path where the form gets posted\n\nin: body", + "type": "string" + }, "Context": { "description": "Context to render\n\nin: body", "type": "string" diff --git a/tests/e2e/markdown-editor.test.e2e.ts b/tests/e2e/markdown-editor.test.e2e.ts index 4cdf4644f7..582ea7eceb 100644 --- a/tests/e2e/markdown-editor.test.e2e.ts +++ b/tests/e2e/markdown-editor.test.e2e.ts @@ -11,6 +11,37 @@ test.beforeAll(async ({browser}, workerInfo) => { await login_user(browser, workerInfo, 'user2'); }); +test('Markdown image preview behaviour', async ({browser}, workerInfo) => { + test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari;'); + + const context = await load_logged_in_context(browser, workerInfo, 'user2'); + + // Editing the root README.md file for image preview + const editPath = '/user2/repo1/src/branch/master/README.md'; + + const page = await context.newPage(); + const response = await page.goto(editPath, {waitUntil: 'domcontentloaded'}); + expect(response?.status()).toBe(200); + + // Click 'Edit file' tab + await page.locator('[data-tooltip-content="Edit file"]').click(); + + // This yields the monaco editor + const editor = page.getByRole('presentation').nth(0); + await editor.click(); + // Clear all the content + await page.keyboard.press('ControlOrMeta+KeyA'); + // Add the image + await page.keyboard.type(''); + + // Click 'Preview' tab + await page.locator('a[data-tab="preview"]').click(); + + // Check for the image preview via the expected attribute + const preview = page.locator('div[data-tab="preview"] p[dir="auto"] a'); + await expect(preview).toHaveAttribute('href', 'http://localhost:3003/user2/repo1/media/branch/master/assets/logo.svg'); +}); + test('markdown indentation', async ({browser}, workerInfo) => { const context = await load_logged_in_context(browser, workerInfo, 'user2'); diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js index faf8ba1f84..5b59b66f80 100644 --- a/web_src/js/features/repo-editor.js +++ b/web_src/js/features/repo-editor.js @@ -26,6 +26,7 @@ function initEditPreviewTab($form) { const formData = new FormData(); formData.append('mode', mode); formData.append('context', context); + formData.append('branch_path', $this.data('branch-path')); formData.append( 'text', $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val(),