Refactor readme file renderer (#19502)
* Refactor readme file renderer * improve
This commit is contained in:
		
							parent
							
								
									e4274f640c
								
							
						
					
					
						commit
						d71df01077
					
				|  | @ -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", `<br>`, | ||||
| 						) | ||||
| 					} 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", `<br>`, | ||||
| 					) | ||||
| 				} | ||||
| 				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", `<br>`, | ||||
| 			) | ||||
| 		} 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", `<br>`, | ||||
| 		) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue