From b068666a99d845a23364ff06c1bf03bcb1aeb325 Mon Sep 17 00:00:00 2001 From: Robert Wang Date: Mon, 23 Apr 2018 22:05:26 -0700 Subject: [PATCH] Add template functions for search shortcuts. --- pttbbs/search_predicate.go | 7 +++++++ pttbbs/string.go | 30 ++++++++++++++++++++++++++++++ pttweb.go | 27 +++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/pttbbs/search_predicate.go b/pttbbs/search_predicate.go index d1566cc..3487c64 100644 --- a/pttbbs/search_predicate.go +++ b/pttbbs/search_predicate.go @@ -13,6 +13,13 @@ func WithTitle(title string) SearchPredicate { }} } +func WithExactTitle(title string) SearchPredicate { + return &searchPredicate{&apipb.SearchFilter{ + Type: apipb.SearchFilter_TYPE_EXACT_TITLE, + StringData: title, + }} +} + func WithAuthor(author string) SearchPredicate { return &searchPredicate{&apipb.SearchFilter{ Type: apipb.SearchFilter_TYPE_AUTHOR, diff --git a/pttbbs/string.go b/pttbbs/string.go index 48842b8..c367e16 100644 --- a/pttbbs/string.go +++ b/pttbbs/string.go @@ -5,6 +5,7 @@ import ( "errors" "regexp" "strconv" + "strings" "time" ) @@ -16,6 +17,8 @@ var ( SignaturePrefixStrings = []string{"※", "==>"} ArticlePushPrefixStrings = []string{"推 ", "噓 ", "→ "} + + subjectPrefixStrings = []string{"re:", "fw:", "[轉錄]"} ) const ( @@ -26,6 +29,7 @@ const ( var ( validBrdNameRegexp = regexp.MustCompile(`^[0-9a-zA-Z][0-9a-zA-Z_\.\-]+$`) validFileNameRegexp = regexp.MustCompile(`^[MG]\.\d+\.A(\.[0-9A-F]+)?$`) + validUserIDRegexp = regexp.MustCompile(`^[a-zA-Z][0-9a-zA-Z]{1,11}$`) fileNameTimeRegexp = regexp.MustCompile(`^[MG]\.(\d+)\.A(\.[0-9A-F]+)?$`) ) @@ -37,6 +41,10 @@ func IsValidArticleFileName(filename string) bool { return validFileNameRegexp.MatchString(filename) } +func IsValidUserID(userID string) bool { + return validUserIDRegexp.MatchString(userID) +} + func ParseArticleFirstLine(line []byte) (tag1, val1, tag2, val2 []byte, ok bool) { m := ArticleFirstLineRegexp.FindSubmatch(line) if m == nil { @@ -79,3 +87,25 @@ func ParseFileNameTime(filename string) (time.Time, error) { } return time.Unix(int64(unix), 0), nil } + +func Subject(subject string) string { + lower := strings.ToLower(subject) + off := 0 + for _, p := range subjectPrefixStrings { + for strings.HasPrefix(lower[off:], p) { + off += len(p) + off += countPrefixSpaces(lower[off:]) + } + off += countPrefixSpaces(lower[off:]) + } + return subject[off:] +} + +func countPrefixSpaces(s string) int { + for i, c := range s { + if c != ' ' { + return i + } + } + return 0 +} diff --git a/pttweb.go b/pttweb.go index 7b21fc9..f3da4b1 100644 --- a/pttweb.go +++ b/pttweb.go @@ -293,6 +293,15 @@ func templateFuncMap() template.FuncMap { "route": func(where string, attrs ...string) (*url.URL, error) { return router.Get(where).URLPath(attrs...) }, + "route_search_author": func(b pttbbs.Board, author string) (*url.URL, error) { + if !pttbbs.IsValidUserID(author) { + return nil, nil + } + return bbsSearchURL(b, "author:"+author) + }, + "route_search_thread": func(b pttbbs.Board, title string) (*url.URL, error) { + return bbsSearchURL(b, "thread:"+pttbbs.Subject(title)) + }, "static_prefix": func() string { return config.StaticPrefix }, @@ -475,6 +484,17 @@ func handleBbs(c *Context, w http.ResponseWriter) error { return page.ExecutePage(w, (*page.BbsIndex)(bbsindex)) } +func bbsSearchURL(b pttbbs.Board, query string) (*url.URL, error) { + u, err := router.Get("bbssearch").URLPath("brdname", b.BrdName) + if err != nil { + return nil, err + } + q := url.Values{} + q.Set("q", query) + u.RawQuery = q.Encode() + return u, nil +} + func parseKeyValueTerm(term string) (pttbbs.SearchPredicate, bool) { kv := strings.SplitN(term, ":", 2) if len(kv) != 2 { @@ -495,6 +515,13 @@ func parseKeyValueTerm(term string) (pttbbs.SearchPredicate, bool) { } func parseQuery(query string) ([]pttbbs.SearchPredicate, error) { + // Special case, thread takes up all the query. + if strings.HasPrefix(query, "thread:") { + return []pttbbs.SearchPredicate{ + pttbbs.WithExactTitle(strings.TrimSpace(strings.TrimPrefix(query, "thread:"))), + }, nil + } + segs := strings.Split(query, " ") var titleSegs []string var preds []pttbbs.SearchPredicate