From d71df01077fbd9366e38150e0b037008c3f808de Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 27 Apr 2022 04:31:15 +0800 Subject: [PATCH] Refactor readme file renderer (#19502) * Refactor readme file renderer * improve --- routers/web/repo/view.go | 233 +++++++++++++++++++++------------------ 1 file changed, 123 insertions(+), 110 deletions(-) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 0faa01d57..168927d10 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -146,6 +146,21 @@ func renderDirectory(ctx *context.Context, treeLink string) { ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) } + // Check permission to add or upload new file. + if ctx.Repo.CanWrite(unit_model.TypeCode) && ctx.Repo.IsViewBranch { + ctx.Data["CanAddFile"] = !ctx.Repo.Repository.IsArchived + ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived + } + + readmeFile, readmeTreelink := findReadmeFile(ctx, entries, treeLink) + if ctx.Written() || readmeFile == nil { + return + } + + renderReadmeFile(ctx, readmeFile, readmeTreelink) +} + +func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string) (*namedBlob, string) { // 3 for the extensions in exts[] in order // the last one is for a readme that doesn't // strictly match an extension @@ -183,7 +198,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { target, err = entry.FollowLinks() if err != nil && !git.IsErrBadLink(err) { ctx.ServerError("FollowLinks", err) - return + return nil, "" } } log.Debug("%t", target == nil) @@ -205,7 +220,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { entry, err = entry.FollowLinks() if err != nil && !git.IsErrBadLink(err) { ctx.ServerError("FollowLinks", err) - return + return nil, "" } } if entry != nil && (entry.IsExecutable() || entry.IsRegular()) { @@ -236,7 +251,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { readmeFile, err = getReadmeFileFromPath(ctx.Repo.Commit, entry.GetSubJumpablePathName()) if err != nil { ctx.ServerError("getReadmeFileFromPath", err) - return + return nil, "" } if readmeFile != nil { readmeFile.name = entry.Name() + "/" + readmeFile.name @@ -245,129 +260,127 @@ func renderDirectory(ctx *context.Context, treeLink string) { } } } + return readmeFile, readmeTreelink +} - if readmeFile != nil { - ctx.Data["RawFileLink"] = "" - ctx.Data["ReadmeInList"] = true - ctx.Data["ReadmeExist"] = true - ctx.Data["FileIsSymlink"] = readmeFile.isSymlink +func renderReadmeFile(ctx *context.Context, readmeFile *namedBlob, readmeTreelink string) { + ctx.Data["RawFileLink"] = "" + ctx.Data["ReadmeInList"] = true + ctx.Data["ReadmeExist"] = true + ctx.Data["FileIsSymlink"] = readmeFile.isSymlink - dataRc, err := readmeFile.blob.DataAsync() - if err != nil { - ctx.ServerError("Data", err) - return - } - defer dataRc.Close() + dataRc, err := readmeFile.blob.DataAsync() + if err != nil { + ctx.ServerError("Data", err) + return + } + defer dataRc.Close() - buf := make([]byte, 1024) - n, _ := util.ReadAtMost(dataRc, buf) - buf = buf[:n] + buf := make([]byte, 1024) + n, _ := util.ReadAtMost(dataRc, buf) + buf = buf[:n] - st := typesniffer.DetectContentType(buf) - isTextFile := st.IsText() + st := typesniffer.DetectContentType(buf) + isTextFile := st.IsText() - ctx.Data["FileIsText"] = isTextFile - ctx.Data["FileName"] = readmeFile.name - fileSize := int64(0) - isLFSFile := false - ctx.Data["IsLFSFile"] = false + ctx.Data["FileIsText"] = isTextFile + ctx.Data["FileName"] = readmeFile.name + fileSize := int64(0) + isLFSFile := false + ctx.Data["IsLFSFile"] = false - // FIXME: what happens when README file is an image? - if isTextFile && setting.LFS.StartServer { - pointer, _ := lfs.ReadPointerFromBuffer(buf) - if pointer.IsValid() { - meta, err := models.GetLFSMetaObjectByOid(ctx.Repo.Repository.ID, pointer.Oid) - if err != nil && err != models.ErrLFSObjectNotExist { - ctx.ServerError("GetLFSMetaObject", err) + // FIXME: what happens when README file is an image? + if isTextFile && setting.LFS.StartServer { + pointer, _ := lfs.ReadPointerFromBuffer(buf) + if pointer.IsValid() { + meta, err := models.GetLFSMetaObjectByOid(ctx.Repo.Repository.ID, pointer.Oid) + if err != nil && err != models.ErrLFSObjectNotExist { + ctx.ServerError("GetLFSMetaObject", err) + return + } + if meta != nil { + ctx.Data["IsLFSFile"] = true + isLFSFile = true + + // OK read the lfs object + var err error + dataRc, err = lfs.ReadMetaObject(pointer) + if err != nil { + ctx.ServerError("ReadMetaObject", err) return } - if meta != nil { - ctx.Data["IsLFSFile"] = true - isLFSFile = true + defer dataRc.Close() - // OK read the lfs object - var err error - dataRc, err = lfs.ReadMetaObject(pointer) - if err != nil { - ctx.ServerError("ReadMetaObject", err) - return - } - defer dataRc.Close() - - buf = make([]byte, 1024) - n, err = util.ReadAtMost(dataRc, buf) - if err != nil { - ctx.ServerError("Data", err) - return - } - buf = buf[:n] - - st = typesniffer.DetectContentType(buf) - isTextFile = st.IsText() - ctx.Data["IsTextFile"] = isTextFile - - fileSize = meta.Size - ctx.Data["FileSize"] = meta.Size - filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.name)) - ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.HTMLURL(), url.PathEscape(meta.Oid), url.PathEscape(filenameBase64)) + buf = make([]byte, 1024) + n, err = util.ReadAtMost(dataRc, buf) + if err != nil { + ctx.ServerError("Data", err) + return } - } - } + buf = buf[:n] - if !isLFSFile { - fileSize = readmeFile.blob.Size() - } + st = typesniffer.DetectContentType(buf) + isTextFile = st.IsText() + ctx.Data["IsTextFile"] = isTextFile - if isTextFile { - if fileSize >= setting.UI.MaxDisplayFileSize { - // Pretend that this is a normal text file to display 'This file is too large to be shown' - ctx.Data["IsFileTooLarge"] = true - ctx.Data["IsTextFile"] = true - ctx.Data["FileSize"] = fileSize - } else { - rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc)) - - if markupType := markup.Type(readmeFile.name); markupType != "" { - ctx.Data["IsMarkup"] = true - ctx.Data["MarkupType"] = string(markupType) - var result strings.Builder - err := markup.Render(&markup.RenderContext{ - Ctx: ctx, - Filename: readmeFile.name, - URLPrefix: readmeTreelink, - Metas: ctx.Repo.Repository.ComposeDocumentMetas(), - GitRepo: ctx.Repo.GitRepo, - }, rd, &result) - if err != nil { - log.Error("Render failed: %v then fallback", err) - buf := &bytes.Buffer{} - ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf) - ctx.Data["FileContent"] = strings.ReplaceAll( - gotemplate.HTMLEscapeString(buf.String()), "\n", `
`, - ) - } else { - ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String()) - } - } else { - ctx.Data["IsRenderedHTML"] = true - buf := &bytes.Buffer{} - ctx.Data["EscapeStatus"], err = charset.EscapeControlReader(rd, buf) - if err != nil { - log.Error("Read failed: %v", err) - } - - ctx.Data["FileContent"] = strings.ReplaceAll( - gotemplate.HTMLEscapeString(buf.String()), "\n", `
`, - ) - } + fileSize = meta.Size + ctx.Data["FileSize"] = meta.Size + filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.name)) + ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.HTMLURL(), url.PathEscape(meta.Oid), url.PathEscape(filenameBase64)) } } } - // Check permission to add or upload new file. - if ctx.Repo.CanWrite(unit_model.TypeCode) && ctx.Repo.IsViewBranch { - ctx.Data["CanAddFile"] = !ctx.Repo.Repository.IsArchived - ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived + if !isTextFile { + return + } + + if !isLFSFile { + fileSize = readmeFile.blob.Size() + } + + if fileSize >= setting.UI.MaxDisplayFileSize { + // Pretend that this is a normal text file to display 'This file is too large to be shown' + ctx.Data["IsFileTooLarge"] = true + ctx.Data["IsTextFile"] = true + ctx.Data["FileSize"] = fileSize + return + } + + rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc)) + + if markupType := markup.Type(readmeFile.name); markupType != "" { + ctx.Data["IsMarkup"] = true + ctx.Data["MarkupType"] = string(markupType) + var result strings.Builder + err := markup.Render(&markup.RenderContext{ + Ctx: ctx, + Filename: readmeFile.name, + URLPrefix: readmeTreelink, + Metas: ctx.Repo.Repository.ComposeDocumentMetas(), + GitRepo: ctx.Repo.GitRepo, + }, rd, &result) + if err != nil { + log.Error("Render failed: %v then fallback", err) + buf := &bytes.Buffer{} + ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf) + ctx.Data["FileContent"] = strings.ReplaceAll( + gotemplate.HTMLEscapeString(buf.String()), "\n", `
`, + ) + } else { + ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String()) + } + } else { + ctx.Data["IsRenderedHTML"] = true + buf := &bytes.Buffer{} + ctx.Data["EscapeStatus"], err = charset.EscapeControlReader(rd, buf) + if err != nil { + log.Error("Read failed: %v", err) + } + + ctx.Data["FileContent"] = strings.ReplaceAll( + gotemplate.HTMLEscapeString(buf.String()), "\n", `
`, + ) } }