| 1 | package gallery |
| 2 | |
| 3 | import ( |
| 4 | "sort" |
| 5 | "strings" |
| 6 | |
| 7 | "amadan.net/rastrillo/fiesta" |
| 8 | ) |
| 9 | |
| 10 | // formerRTL is the directory a theme's right-to-left twin had before |
| 11 | // every page moved under a locale: <theme>/rtl/<page>, beside the |
| 12 | // English <theme>/<page>. Those addresses were published, so they are |
| 13 | // redirected rather than left to 404. |
| 14 | const formerRTL = "rtl" |
| 15 | |
| 16 | // Redirects are the _redirects rules, for a tree rendered for mount, |
| 17 | // that send each address the gallery had before its pages moved under |
| 18 | // a locale to the page that is there now, with a 301: |
| 19 | // |
| 20 | // <theme>/ <theme>/en/ |
| 21 | // <theme>/index.html <theme>/en/ |
| 22 | // <theme>/<page>.html <theme>/en/<page>.html |
| 23 | // <theme>/frames/<example>/<state> <theme>/en/frames/<example>/<state> |
| 24 | // <theme>/rtl/... the same under <theme>/ar/ |
| 25 | // |
| 26 | // An index goes to its directory, the address the edge serves it at, |
| 27 | // since the edge sends .../index.html on to ./ and that would be a |
| 28 | // second hop. The rules are written so that no source can match a |
| 29 | // file of the tree, and no destination can match a source, so none |
| 30 | // shadows a page and none leads to another: every file of today's tree |
| 31 | // sits under <theme>/<locale>/ or assets/, and no locale is "frames", |
| 32 | // "rtl", "index.html" or a page's name. So <theme>/rtl/:page can be one |
| 33 | // rule, but an English page is named rule by rule: <theme>/:page would |
| 34 | // also match <theme>/en, the English tree's own directory. The pages |
| 35 | // named are the ones tree holds at <theme>/en/ in every theme, so each |
| 36 | // destination is a page whatever the theme. The order is fixed, so the |
| 37 | // same tree gives the same rules. |
| 38 | func Redirects(mount string, tree map[string][]byte) []string { |
| 39 | m := cleanMount(mount) |
| 40 | en, ar := Locales[0], rtlLocale() |
| 41 | var out []string |
| 42 | rule := func(from, to string) { out = append(out, m+from+" "+m+to+" 301") } |
| 43 | |
| 44 | themes := fiesta.ThemeNames() |
| 45 | for _, theme := range themes { |
| 46 | rule("/"+theme, "/"+theme+"/"+en+"/") |
| 47 | } |
| 48 | rule("/:theme/index.html", "/:theme/"+en+"/") |
| 49 | rule("/:theme/frames/:example/:state", "/:theme/"+en+"/frames/:example/:state") |
| 50 | for _, page := range everyThemesPages(tree, themes, en) { |
| 51 | rule("/:theme/"+page, "/:theme/"+en+"/"+page) |
| 52 | } |
| 53 | |
| 54 | twin := "/:theme/" + formerRTL |
| 55 | rule(twin, "/:theme/"+ar+"/") |
| 56 | rule(twin+"/index.html", "/:theme/"+ar+"/") |
| 57 | rule(twin+"/frames/:example/:state", "/:theme/"+ar+"/frames/:example/:state") |
| 58 | rule(twin+"/:page", "/:theme/"+ar+"/:page") |
| 59 | return out |
| 60 | } |
| 61 | |
| 62 | // rtlLocale is the locale that renders right to left, where the former |
| 63 | // twins now live. |
| 64 | func rtlLocale() string { |
| 65 | for _, l := range Locales { |
| 66 | if Dir(l) == "rtl" { |
| 67 | return l |
| 68 | } |
| 69 | } |
| 70 | panic("gallery: no locale renders right to left") |
| 71 | } |
| 72 | |
| 73 | // everyThemesPages are the names of the HTML pages directly under |
| 74 | // <theme>/<locale>/ for every theme, the index aside, sorted. |
| 75 | func everyThemesPages(tree map[string][]byte, themes []string, locale string) []string { |
| 76 | count := map[string]int{} |
| 77 | for name := range tree { |
| 78 | for _, theme := range themes { |
| 79 | page, ok := strings.CutPrefix(name, theme+"/"+locale+"/") |
| 80 | if ok && !strings.Contains(page, "/") && strings.HasSuffix(page, ".html") && page != "index.html" { |
| 81 | count[page]++ |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | var pages []string |
| 86 | for page, n := range count { |
| 87 | if n == len(themes) { |
| 88 | pages = append(pages, page) |
| 89 | } |
| 90 | } |
| 91 | sort.Strings(pages) |
| 92 | return pages |
| 93 | } |
| 94 | |