- golem15.menu plugin, tables golem15_menu_menus and golem15_menu_menu_entries with the PHP columns plus a nullable url_pattern (D-02) - MenuEntry BeforeSave ports PHP beforeSave: nest columns 0, menu from the parent, sort_order after the menu's last - entry type dropdown: url, divider and the signpost registry types; reference depends on type through signpost.ReferenceOptions - URL scheme allowlist (errors.url_scheme), target _self or _blank, menu and parent cycle checks - menu filter, toolbar reorder/create/delete and config_reorder.yaml partitioned by menu - navigation Menus with Menus and Menu entries, permission golem15.menu.manage_menu, en and pl phrasebooks - TestMenuAdminSmoke
47 lines
1.2 KiB
Go
47 lines
1.2 KiB
Go
// Package classes holds the menu plugin's link rules and the tree the public
|
|
// API serves.
|
|
package classes
|
|
|
|
import "strings"
|
|
|
|
// AllowedURL reports whether an entry URL may be stored and served: empty, a
|
|
// path starting with "/", a fragment starting with "#", or an absolute
|
|
// http, https, mailto or tel URL (case-insensitive). Anything else, such as
|
|
// javascript: or data:, is refused, and so is a URL with surrounding
|
|
// whitespace or control characters, which browsers strip before reading the
|
|
// scheme (RESEARCH Pitfall 15).
|
|
func AllowedURL(url string) bool {
|
|
if url == "" {
|
|
return true
|
|
}
|
|
for _, r := range url {
|
|
if r < 0x20 || r == 0x7f {
|
|
return false
|
|
}
|
|
}
|
|
if strings.TrimSpace(url) != url {
|
|
return false
|
|
}
|
|
if url[0] == '/' || url[0] == '#' {
|
|
return true
|
|
}
|
|
lower := strings.ToLower(url)
|
|
for _, prefix := range []string{"http://", "https://", "mailto:", "tel:"} {
|
|
if strings.HasPrefix(lower, prefix) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// The link targets an entry may open in.
|
|
const (
|
|
TargetSelf = "_self"
|
|
TargetBlank = "_blank"
|
|
)
|
|
|
|
// AllowedTarget reports whether target is _self, _blank or empty.
|
|
func AllowedTarget(target string) bool {
|
|
return target == "" || target == TargetSelf || target == TargetBlank
|
|
}
|