github.com/jfrerich/mattermost-server@v5.8.0-rc2+incompatible/model/search_params.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package model 5 6 import ( 7 "regexp" 8 "strings" 9 "time" 10 ) 11 12 var searchTermPuncStart = regexp.MustCompile(`^[^\pL\d\s#"]+`) 13 var searchTermPuncEnd = regexp.MustCompile(`[^\pL\d\s*"]+$`) 14 15 type SearchParams struct { 16 Terms string 17 IsHashtag bool 18 InChannels []string 19 FromUsers []string 20 AfterDate string 21 BeforeDate string 22 OnDate string 23 OrTerms bool 24 IncludeDeletedChannels bool 25 TimeZoneOffset int 26 } 27 28 // Returns the epoch timestamp of the start of the day specified by SearchParams.AfterDate 29 func (p *SearchParams) GetAfterDateMillis() int64 { 30 date, err := time.Parse("2006-01-02", PadDateStringZeros(p.AfterDate)) 31 if err != nil { 32 date = time.Now() 33 } 34 35 // travel forward 1 day 36 oneDay := time.Hour * 24 37 afterDate := date.Add(oneDay) 38 return GetStartOfDayMillis(afterDate, p.TimeZoneOffset) 39 } 40 41 // Returns the epoch timestamp of the end of the day specified by SearchParams.BeforeDate 42 func (p *SearchParams) GetBeforeDateMillis() int64 { 43 date, err := time.Parse("2006-01-02", PadDateStringZeros(p.BeforeDate)) 44 if err != nil { 45 return 0 46 } 47 48 // travel back 1 day 49 oneDay := time.Hour * -24 50 beforeDate := date.Add(oneDay) 51 return GetEndOfDayMillis(beforeDate, p.TimeZoneOffset) 52 } 53 54 // Returns the epoch timestamps of the start and end of the day specified by SearchParams.OnDate 55 func (p *SearchParams) GetOnDateMillis() (int64, int64) { 56 date, err := time.Parse("2006-01-02", PadDateStringZeros(p.OnDate)) 57 if err != nil { 58 return 0, 0 59 } 60 61 return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset) 62 } 63 64 var searchFlags = [...]string{"from", "channel", "in", "before", "after", "on"} 65 66 func splitWords(text string) []string { 67 words := []string{} 68 69 foundQuote := false 70 location := 0 71 for i, char := range text { 72 if char == '"' { 73 if foundQuote { 74 // Grab the quoted section 75 word := text[location : i+1] 76 words = append(words, word) 77 foundQuote = false 78 location = i + 1 79 } else { 80 words = append(words, strings.Fields(text[location:i])...) 81 foundQuote = true 82 location = i 83 } 84 } 85 } 86 87 words = append(words, strings.Fields(text[location:])...) 88 89 return words 90 } 91 92 func parseSearchFlags(input []string) ([]string, [][2]string) { 93 words := []string{} 94 flags := [][2]string{} 95 96 skipNextWord := false 97 for i, word := range input { 98 if skipNextWord { 99 skipNextWord = false 100 continue 101 } 102 103 isFlag := false 104 105 if colon := strings.Index(word, ":"); colon != -1 { 106 flag := word[:colon] 107 value := word[colon+1:] 108 109 for _, searchFlag := range searchFlags { 110 // check for case insensitive equality 111 if strings.EqualFold(flag, searchFlag) { 112 if value != "" { 113 flags = append(flags, [2]string{searchFlag, value}) 114 isFlag = true 115 } else if i < len(input)-1 { 116 flags = append(flags, [2]string{searchFlag, input[i+1]}) 117 skipNextWord = true 118 isFlag = true 119 } 120 121 if isFlag { 122 break 123 } 124 } 125 } 126 } 127 128 if !isFlag { 129 // trim off surrounding punctuation (note that we leave trailing asterisks to allow wildcards) 130 word = searchTermPuncStart.ReplaceAllString(word, "") 131 word = searchTermPuncEnd.ReplaceAllString(word, "") 132 133 // and remove extra pound #s 134 word = hashtagStart.ReplaceAllString(word, "#") 135 136 if len(word) != 0 { 137 words = append(words, word) 138 } 139 } 140 } 141 142 return words, flags 143 } 144 145 func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams { 146 words, flags := parseSearchFlags(splitWords(text)) 147 148 hashtagTermList := []string{} 149 plainTermList := []string{} 150 151 for _, word := range words { 152 if validHashtag.MatchString(word) { 153 hashtagTermList = append(hashtagTermList, word) 154 } else { 155 plainTermList = append(plainTermList, word) 156 } 157 } 158 159 hashtagTerms := strings.Join(hashtagTermList, " ") 160 plainTerms := strings.Join(plainTermList, " ") 161 162 inChannels := []string{} 163 fromUsers := []string{} 164 afterDate := "" 165 beforeDate := "" 166 onDate := "" 167 168 for _, flagPair := range flags { 169 flag := flagPair[0] 170 value := flagPair[1] 171 172 if flag == "in" || flag == "channel" { 173 inChannels = append(inChannels, value) 174 } else if flag == "from" { 175 fromUsers = append(fromUsers, value) 176 } else if flag == "after" { 177 afterDate = value 178 } else if flag == "before" { 179 beforeDate = value 180 } else if flag == "on" { 181 onDate = value 182 } 183 } 184 185 paramsList := []*SearchParams{} 186 187 if len(plainTerms) > 0 { 188 paramsList = append(paramsList, &SearchParams{ 189 Terms: plainTerms, 190 IsHashtag: false, 191 InChannels: inChannels, 192 FromUsers: fromUsers, 193 AfterDate: afterDate, 194 BeforeDate: beforeDate, 195 OnDate: onDate, 196 TimeZoneOffset: timeZoneOffset, 197 }) 198 } 199 200 if len(hashtagTerms) > 0 { 201 paramsList = append(paramsList, &SearchParams{ 202 Terms: hashtagTerms, 203 IsHashtag: true, 204 InChannels: inChannels, 205 FromUsers: fromUsers, 206 AfterDate: afterDate, 207 BeforeDate: beforeDate, 208 OnDate: onDate, 209 TimeZoneOffset: timeZoneOffset, 210 }) 211 } 212 213 // special case for when no terms are specified but we still have a filter 214 if len(plainTerms) == 0 && len(hashtagTerms) == 0 && (len(inChannels) != 0 || len(fromUsers) != 0 || len(afterDate) != 0 || len(beforeDate) != 0 || len(onDate) != 0) { 215 paramsList = append(paramsList, &SearchParams{ 216 Terms: "", 217 IsHashtag: false, 218 InChannels: inChannels, 219 FromUsers: fromUsers, 220 AfterDate: afterDate, 221 BeforeDate: beforeDate, 222 OnDate: onDate, 223 TimeZoneOffset: timeZoneOffset, 224 }) 225 } 226 227 return paramsList 228 }