diff options
author | KN4CK3R <KN4CK3R@users.noreply.github.com> | 2021-06-05 14:32:19 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-05 15:32:19 +0300 |
commit | 8e262104c25d1c2578f683109e1b373aade3a17c (patch) | |
tree | 04b8fda8516498b74350bb695f230e0e1089a48d /routers | |
parent | 7979c3654eb91adce4fd9717d9ff891496a56ff3 (diff) | |
download | gitea-8e262104c25d1c2578f683109e1b373aade3a17c.tar.gz gitea-8e262104c25d1c2578f683109e1b373aade3a17c.zip |
Add Image Diff for SVG files (#14867)
* Added type sniffer.
* Switched content detection from base to typesniffer.
* Added GuessContentType to Blob.
* Moved image info logic to client.
Added support for SVG images in diff.
* Restore old blocked svg behaviour.
* Added missing image formats.
* Execute image diff only when container is visible.
* add margin to spinner
* improve BIN tag on image diffs
* Default to render view.
* Show image diff on incomplete diff.
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Lauris BH <lauris@nix.lv>
Diffstat (limited to 'routers')
-rw-r--r-- | routers/repo/compare.go | 41 | ||||
-rw-r--r-- | routers/repo/download.go | 34 | ||||
-rw-r--r-- | routers/repo/editor.go | 5 | ||||
-rw-r--r-- | routers/repo/lfs.go | 17 | ||||
-rw-r--r-- | routers/repo/setting.go | 4 | ||||
-rw-r--r-- | routers/repo/view.go | 29 | ||||
-rw-r--r-- | routers/user/setting/profile.go | 5 |
7 files changed, 78 insertions, 57 deletions
diff --git a/routers/repo/compare.go b/routers/repo/compare.go index d02ea0b160..f53a31769d 100644 --- a/routers/repo/compare.go +++ b/routers/repo/compare.go @@ -37,8 +37,20 @@ func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, ctx.Data["BaseCommit"] = base ctx.Data["HeadCommit"] = head + ctx.Data["GetBlobByPathForCommit"] = func(commit *git.Commit, path string) *git.Blob { + if commit == nil { + return nil + } + + blob, err := commit.GetBlobByPath(path) + if err != nil { + return nil + } + return blob + } + setPathsCompareContext(ctx, base, head, headTarget) - setImageCompareContext(ctx, base, head) + setImageCompareContext(ctx) setCsvCompareContext(ctx) } @@ -57,27 +69,18 @@ func setPathsCompareContext(ctx *context.Context, base *git.Commit, head *git.Co } // setImageCompareContext sets context data that is required by image compare template -func setImageCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit) { - ctx.Data["IsImageFileInHead"] = head.IsImageFile - ctx.Data["IsImageFileInBase"] = base.IsImageFile - ctx.Data["ImageInfoBase"] = func(name string) *git.ImageMetaData { - if base == nil { - return nil - } - result, err := base.ImageInfo(name) - if err != nil { - log.Error("ImageInfo failed: %v", err) - return nil +func setImageCompareContext(ctx *context.Context) { + ctx.Data["IsBlobAnImage"] = func(blob *git.Blob) bool { + if blob == nil { + return false } - return result - } - ctx.Data["ImageInfo"] = func(name string) *git.ImageMetaData { - result, err := head.ImageInfo(name) + + st, err := blob.GuessContentType() if err != nil { - log.Error("ImageInfo failed: %v", err) - return nil + log.Error("GuessContentType failed: %v", err) + return false } - return result + return st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()) } } diff --git a/routers/repo/download.go b/routers/repo/download.go index 4917c233ae..bbf4684b2e 100644 --- a/routers/repo/download.go +++ b/routers/repo/download.go @@ -12,7 +12,6 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" @@ -20,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" ) // ServeData download file from io.Reader @@ -45,28 +45,32 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) // Google Chrome dislike commas in filenames, so let's change it to a space name = strings.ReplaceAll(name, ",", " ") - if base.IsTextFile(buf) || ctx.QueryBool("render") { + st := typesniffer.DetectContentType(buf) + + if st.IsText() || ctx.QueryBool("render") { cs, err := charset.DetectEncoding(buf) if err != nil { log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err) cs = "utf-8" } ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs)) - } else if base.IsImageFile(buf) || base.IsPDFFile(buf) { - ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) - ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") - if base.IsSVGImageFile(buf) { - ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") - ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") - ctx.Resp.Header().Set("Content-Type", base.SVGMimeType) - } } else { - ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") - if setting.MimeTypeMap.Enabled { - fileExtension := strings.ToLower(filepath.Ext(name)) - if mimetype, ok := setting.MimeTypeMap.Map[fileExtension]; ok { - ctx.Resp.Header().Set("Content-Type", mimetype) + + if (st.IsImage() || st.IsPDF()) && (setting.UI.SVG.Enabled || !st.IsSvgImage()) { + ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) + if st.IsSvgImage() { + ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") + ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") + ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType) + } + } else { + ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) + if setting.MimeTypeMap.Enabled { + fileExtension := strings.ToLower(filepath.Ext(name)) + if mimetype, ok := setting.MimeTypeMap.Map[fileExtension]; ok { + ctx.Resp.Header().Set("Content-Type", mimetype) + } } } } diff --git a/routers/repo/editor.go b/routers/repo/editor.go index 2a2c56952d..0f978c7b01 100644 --- a/routers/repo/editor.go +++ b/routers/repo/editor.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/repofiles" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" @@ -117,8 +118,8 @@ func editFile(ctx *context.Context, isNewFile bool) { buf = buf[:n] // Only some file types are editable online as text. - if !base.IsRepresentableAsText(buf) { - ctx.NotFound("base.IsRepresentableAsText", nil) + if !typesniffer.DetectContentType(buf).IsRepresentableAsText() { + ctx.NotFound("typesniffer.IsRepresentableAsText", nil) return } diff --git a/routers/repo/lfs.go b/routers/repo/lfs.go index c17bd2f87a..173ffb773f 100644 --- a/routers/repo/lfs.go +++ b/routers/repo/lfs.go @@ -25,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/modules/typesniffer" ) const ( @@ -278,16 +279,16 @@ func LFSFileGet(ctx *context.Context) { } buf = buf[:n] - ctx.Data["IsTextFile"] = base.IsTextFile(buf) - isRepresentableAsText := base.IsRepresentableAsText(buf) + st := typesniffer.DetectContentType(buf) + ctx.Data["IsTextFile"] = st.IsText() + isRepresentableAsText := st.IsRepresentableAsText() fileSize := meta.Size ctx.Data["FileSize"] = meta.Size ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s.git/info/lfs/objects/%s/%s", setting.AppURL, ctx.Repo.Repository.FullName(), meta.Oid, "direct") switch { case isRepresentableAsText: - // This will be true for SVGs. - if base.IsImageFile(buf) { + if st.IsSvgImage() { ctx.Data["IsImageFile"] = true } @@ -322,13 +323,13 @@ func LFSFileGet(ctx *context.Context) { } ctx.Data["LineNums"] = gotemplate.HTML(output.String()) - case base.IsPDFFile(buf): + case st.IsPDF(): ctx.Data["IsPDFFile"] = true - case base.IsVideoFile(buf): + case st.IsVideo(): ctx.Data["IsVideoFile"] = true - case base.IsAudioFile(buf): + case st.IsAudio(): ctx.Data["IsAudioFile"] = true - case base.IsImageFile(buf): + case st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()): ctx.Data["IsImageFile"] = true } ctx.HTML(http.StatusOK, tplSettingsLFSFile) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 51a0e01164..21a82491fe 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" @@ -1021,7 +1022,8 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error { if err != nil { return fmt.Errorf("ioutil.ReadAll: %v", err) } - if !base.IsImageFile(data) { + st := typesniffer.DetectContentType(data) + if !(st.IsImage() && !st.IsSvgImage()) { return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image")) } if err = ctxRepo.UploadAvatar(data); err != nil { diff --git a/routers/repo/view.go b/routers/repo/view.go index 285cacc2df..30d7de4078 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -29,6 +29,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" ) const ( @@ -265,7 +266,9 @@ func renderDirectory(ctx *context.Context, treeLink string) { n, _ := dataRc.Read(buf) buf = buf[:n] - isTextFile := base.IsTextFile(buf) + st := typesniffer.DetectContentType(buf) + isTextFile := st.IsText() + ctx.Data["FileIsText"] = isTextFile ctx.Data["FileName"] = readmeFile.name fileSize := int64(0) @@ -302,7 +305,8 @@ func renderDirectory(ctx *context.Context, treeLink string) { } buf = buf[:n] - isTextFile = base.IsTextFile(buf) + st = typesniffer.DetectContentType(buf) + isTextFile = st.IsText() ctx.Data["IsTextFile"] = isTextFile fileSize = meta.Size @@ -405,7 +409,9 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st n, _ := dataRc.Read(buf) buf = buf[:n] - isTextFile := base.IsTextFile(buf) + st := typesniffer.DetectContentType(buf) + isTextFile := st.IsText() + isLFSFile := false isDisplayingSource := ctx.Query("display") == "source" isDisplayingRendered := !isDisplayingSource @@ -441,14 +447,16 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st } buf = buf[:n] - isTextFile = base.IsTextFile(buf) + st = typesniffer.DetectContentType(buf) + isTextFile = st.IsText() + fileSize = meta.Size ctx.Data["RawFileLink"] = fmt.Sprintf("%s/media/%s/%s", ctx.Repo.RepoLink, ctx.Repo.BranchNameSubURL(), ctx.Repo.TreePath) } } } - isRepresentableAsText := base.IsRepresentableAsText(buf) + isRepresentableAsText := st.IsRepresentableAsText() if !isRepresentableAsText { // If we can't show plain text, always try to render. isDisplayingSource = false @@ -483,8 +491,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st switch { case isRepresentableAsText: - // This will be true for SVGs. - if base.IsImageFile(buf) { + if st.IsSvgImage() { ctx.Data["IsImageFile"] = true ctx.Data["HasSourceRenderedToggle"] = true } @@ -540,13 +547,13 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st } } - case base.IsPDFFile(buf): + case st.IsPDF(): ctx.Data["IsPDFFile"] = true - case base.IsVideoFile(buf): + case st.IsVideo(): ctx.Data["IsVideoFile"] = true - case base.IsAudioFile(buf): + case st.IsAudio(): ctx.Data["IsAudioFile"] = true - case base.IsImageFile(buf): + case st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()): ctx.Data["IsImageFile"] = true default: if fileSize >= setting.UI.MaxDisplayFileSize { diff --git a/routers/user/setting/profile.go b/routers/user/setting/profile.go index 8cde81f295..20042caca4 100644 --- a/routers/user/setting/profile.go +++ b/routers/user/setting/profile.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" @@ -159,7 +160,9 @@ func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser * if err != nil { return fmt.Errorf("ioutil.ReadAll: %v", err) } - if !base.IsImageFile(data) { + + st := typesniffer.DetectContentType(data) + if !(st.IsImage() && !st.IsSvgImage()) { return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image")) } if err = ctxUser.UploadAvatar(data); err != nil { |