github.com/turgay/mattermost-server@v5.3.2-0.20181002173352-2945e8a2b0ce+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 := ParseDateFilterToTime(p.AfterDate) 31 // travel forward 1 day 32 oneDay := time.Hour * 24 33 afterDate := date.Add(oneDay) 34 return GetStartOfDayMillis(afterDate, p.TimeZoneOffset) 35 } 36 37 // Returns the epoch timestamp of the end of the day specified by SearchParams.BeforeDate 38 func (p *SearchParams) GetBeforeDateMillis() int64 { 39 date := ParseDateFilterToTime(p.BeforeDate) 40 // travel back 1 day 41 oneDay := time.Hour * -24 42 beforeDate := date.Add(oneDay) 43 return GetEndOfDayMillis(beforeDate, p.TimeZoneOffset) 44 } 45 46 // Returns the epoch timestamps of the start and end of the day specified by SearchParams.OnDate 47 func (p *SearchParams) GetOnDateMillis() (int64, int64) { 48 date := ParseDateFilterToTime(p.OnDate) 49 return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset) 50 } 51 52 var searchFlags = [...]string{"from", "channel", "in", "before", "after", "on"} 53 54 func splitWords(text string) []string { 55 words := []string{} 56 57 foundQuote := false 58 location := 0 59 for i, char := range text { 60 if char == '"' { 61 if foundQuote { 62 // Grab the quoted section 63 word := text[location : i+1] 64 words = append(words, word) 65 foundQuote = false 66 location = i + 1 67 } else { 68 words = append(words, strings.Fields(text[location:i])...) 69 foundQuote = true 70 location = i 71 } 72 } 73 } 74 75 words = append(words, strings.Fields(text[location:])...) 76 77 return words 78 } 79 80 func parseSearchFlags(input []string) ([]string, [][2]string) { 81 words := []string{} 82 flags := [][2]string{} 83 84 skipNextWord := false 85 for i, word := range input { 86 if skipNextWord { 87 skipNextWord = false 88 continue 89 } 90 91 isFlag := false 92 93 if colon := strings.Index(word, ":"); colon != -1 { 94 flag := word[:colon] 95 value := word[colon+1:] 96 97 for _, searchFlag := range searchFlags { 98 // check for case insensitive equality 99 if strings.EqualFold(flag, searchFlag) { 100 if value != "" { 101 flags = append(flags, [2]string{searchFlag, value}) 102 isFlag = true 103 } else if i < len(input)-1 { 104 flags = append(flags, [2]string{searchFlag, input[i+1]}) 105 skipNextWord = true 106 isFlag = true 107 } 108 109 if isFlag { 110 break 111 } 112 } 113 } 114 } 115 116 if !isFlag { 117 // trim off surrounding punctuation (note that we leave trailing asterisks to allow wildcards) 118 word = searchTermPuncStart.ReplaceAllString(word, "") 119 word = searchTermPuncEnd.ReplaceAllString(word, "") 120 121 // and remove extra pound #s 122 word = hashtagStart.ReplaceAllString(word, "#") 123 124 if len(word) != 0 { 125 words = append(words, word) 126 } 127 } 128 } 129 130 return words, flags 131 } 132 133 func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams { 134 words, flags := parseSearchFlags(splitWords(text)) 135 136 hashtagTermList := []string{} 137 plainTermList := []string{} 138 139 for _, word := range words { 140 if validHashtag.MatchString(word) { 141 hashtagTermList = append(hashtagTermList, word) 142 } else { 143 plainTermList = append(plainTermList, word) 144 } 145 } 146 147 hashtagTerms := strings.Join(hashtagTermList, " ") 148 plainTerms := strings.Join(plainTermList, " ") 149 150 inChannels := []string{} 151 fromUsers := []string{} 152 afterDate := "" 153 beforeDate := "" 154 onDate := "" 155 156 for _, flagPair := range flags { 157 flag := flagPair[0] 158 value := flagPair[1] 159 160 if flag == "in" || flag == "channel" { 161 inChannels = append(inChannels, value) 162 } else if flag == "from" { 163 fromUsers = append(fromUsers, value) 164 } else if flag == "after" { 165 afterDate = value 166 } else if flag == "before" { 167 beforeDate = value 168 } else if flag == "on" { 169 onDate = value 170 } 171 } 172 173 paramsList := []*SearchParams{} 174 175 if len(plainTerms) > 0 { 176 paramsList = append(paramsList, &SearchParams{ 177 Terms: plainTerms, 178 IsHashtag: false, 179 InChannels: inChannels, 180 FromUsers: fromUsers, 181 AfterDate: afterDate, 182 BeforeDate: beforeDate, 183 OnDate: onDate, 184 TimeZoneOffset: timeZoneOffset, 185 }) 186 } 187 188 if len(hashtagTerms) > 0 { 189 paramsList = append(paramsList, &SearchParams{ 190 Terms: hashtagTerms, 191 IsHashtag: true, 192 InChannels: inChannels, 193 FromUsers: fromUsers, 194 AfterDate: afterDate, 195 BeforeDate: beforeDate, 196 OnDate: onDate, 197 TimeZoneOffset: timeZoneOffset, 198 }) 199 } 200 201 // special case for when no terms are specified but we still have a filter 202 if len(plainTerms) == 0 && len(hashtagTerms) == 0 && (len(inChannels) != 0 || len(fromUsers) != 0 || len(afterDate) != 0 || len(beforeDate) != 0 || len(onDate) != 0) { 203 paramsList = append(paramsList, &SearchParams{ 204 Terms: "", 205 IsHashtag: false, 206 InChannels: inChannels, 207 FromUsers: fromUsers, 208 AfterDate: afterDate, 209 BeforeDate: beforeDate, 210 OnDate: onDate, 211 TimeZoneOffset: timeZoneOffset, 212 }) 213 } 214 215 return paramsList 216 }