Some checks failed
requirements / merge-conditions (pull_request) Has been cancelled
testing / backend-checks (pull_request) Has been cancelled
testing / frontend-checks (pull_request) Has been cancelled
issue-labels / release-notes (pull_request_target) Has been cancelled
issue-labels / backporting (pull_request_target) Has been cancelled
milestone / set (pull_request_target) Has been cancelled
testing / test-unit (pull_request) Has been cancelled
testing / test-e2e (pull_request) Has been cancelled
testing / test-remote-cacher (redis) (pull_request) Has been cancelled
testing / test-remote-cacher (valkey) (pull_request) Has been cancelled
testing / test-remote-cacher (garnet) (pull_request) Has been cancelled
testing / test-remote-cacher (redict) (pull_request) Has been cancelled
testing / test-mysql (pull_request) Has been cancelled
testing / test-pgsql (pull_request) Has been cancelled
testing / test-sqlite (pull_request) Has been cancelled
testing / security-check (pull_request) Has been cancelled
108 lines
No EOL
2.6 KiB
Go
108 lines
No EOL
2.6 KiB
Go
package theme
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"forgejo.org/modules/assetfs"
|
|
)
|
|
|
|
// The Theme struct represents the metadata of a theme. Most fields are allowed to be null / blank.
|
|
type Theme struct {
|
|
// ID is the theme id, e.g., forgejo-dark
|
|
ID string
|
|
// Name is the theme's readable name, e.g., Forgejo. Note that the scheme is excluded in favor of the Scheme property.
|
|
Name string
|
|
// Family is the theme's family, e.g. Catppuccin.
|
|
Family string
|
|
// Author is the theme's author. It may be an invididual, a group, or an entity.
|
|
Author string
|
|
// Url is the URL to the theme's website, e.g. https://forgejo.org.
|
|
Url string
|
|
// Description is the theme's short description.
|
|
Description string
|
|
// Scheme is the theme's color scheme. It must be either: dark, light, or auto.
|
|
Scheme string
|
|
// Recommended means the theme is guaranteed to be bug-free, usually a first-party theme.
|
|
Recommended bool
|
|
}
|
|
|
|
// loadThemeMeta loads theme metadata from a file in a layered FS.
|
|
//
|
|
// Theme ID is expected to be prefilled. The metadata will be assigned to the passed `theme` object.
|
|
func loadThemeMeta(assetFs *assetfs.LayeredFS, filename string, theme *Theme) error {
|
|
file, err := assetFs.Open("assets/css/" + filename)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open file: %w", err)
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
kv, err := scanThemeForKeyValueMetaMap(file, theme.ID) // ID is expected to be prefilled
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("error scanning theme for meta: %w", err)
|
|
}
|
|
|
|
theme.Name = kv["name"]
|
|
theme.Family = kv["family"]
|
|
theme.Author = kv["author"]
|
|
theme.Url = kv["url"]
|
|
theme.Description = kv["description"]
|
|
theme.Scheme = strings.ToLower(kv["scheme"])
|
|
theme.Recommended = kv["recommended"] == "yes"
|
|
|
|
return nil
|
|
}
|
|
|
|
// scanThemeForKeyValueMetaMap finds raw key-value metadata in an open file.
|
|
func scanThemeForKeyValueMetaMap(file io.Reader, id string) (map[string]string, error) {
|
|
var kv = make(map[string]string)
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
ok := false
|
|
|
|
// webpack adds headers
|
|
for scanner.Scan() {
|
|
if strings.TrimSpace(scanner.Text()) == "/* theme "+id {
|
|
ok = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !ok {
|
|
return nil, errors.New("theme has no meta")
|
|
}
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
|
|
parts := strings.Fields(line)
|
|
|
|
if len(parts) == 0 {
|
|
continue
|
|
}
|
|
|
|
key := strings.ToLower(parts[0])
|
|
|
|
if key == "*/" {
|
|
break
|
|
}
|
|
|
|
if len(parts) == 1 {
|
|
kv[key] = "yes"
|
|
} else {
|
|
kv[key] = strings.Join(parts[1:], " ")
|
|
}
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, fmt.Errorf("scanner error: %w", err)
|
|
}
|
|
|
|
return kv, nil
|
|
} |