Files
sm-journal-plugin/classes/media_names.go
Jakub Zych 391435b4bf feat: stored HTML keeps link rel and media names are slugged
- the HTML policy keeps rel on a when it holds only nofollow, sponsored
  and ugc tokens (D-83); any other rel value is removed
- classes.MediaFileName slugs uploaded image names (lowercase, Polish
  letters transliterated, hyphens, at most 80 characters, image when
  empty) for the editor upload and POST /media/upload (D-85)
- the editor corpus gains an external link with rel and a decorative
  image, pinned by TestEditorOutputPassesPolicy
2026-11-04 10:32:25 +01:00

62 lines
1.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package classes
import (
"path"
"regexp"
"strings"
)
// MaxMediaStem is the longest file name stem MediaFileName keeps (D-85).
const MaxMediaStem = 80
// mediaExtPattern is an extension MediaFileName keeps.
var mediaExtPattern = regexp.MustCompile(`^\.[a-z0-9]+$`)
// polishLetters transliterates the Polish diacritics (lower case).
var polishLetters = map[rune]rune{'ą': 'a', 'ć': 'c', 'ę': 'e', 'ł': 'l', 'ń': 'n', 'ó': 'o', 'ś': 's', 'ź': 'z', 'ż': 'z'}
// MediaFileName is the stored name of an uploaded image (D-85, the Blog
// Editor's J4 rule, applied on the server whatever the client sends): the
// base name's stem lowercased, Polish diacritics transliterated, every other
// run of characters outside a-z and 0-9 turned into one hyphen, trimmed and
// cut to MaxMediaStem characters, "image" when nothing is left; and its
// extension lowercased when it is a dot and letters or digits (dropped
// otherwise). "Lwia Grzywa – w lesie!.JPG" is "lwia-grzywa-w-lesie.jpg".
// The result needs no escaping in a URL or an src attribute.
func MediaFileName(name string) string {
base := path.Base(strings.ReplaceAll(name, `\`, "/"))
if base == "." || base == "/" {
base = ""
}
stem, ext := base, ""
if dot := strings.LastIndexByte(base, '.'); dot >= 0 {
if e := strings.ToLower(base[dot:]); mediaExtPattern.MatchString(e) {
stem, ext = base[:dot], e
}
}
var b strings.Builder
dash := false
for _, r := range strings.ToLower(stem) {
if t, ok := polishLetters[r]; ok {
r = t
}
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
b.WriteRune(r)
dash = false
continue
}
if !dash {
b.WriteByte('-')
dash = true
}
}
out := strings.Trim(b.String(), "-")
if len(out) > MaxMediaStem {
out = strings.TrimRight(out[:MaxMediaStem], "-")
}
if out == "" {
out = "image"
}
return out + ext
}