github.com/vlifesystems/rulehunter@v0.0.0-20180501090014-673078aa4a83/html/html.go (about) 1 // Copyright (C) 2016-2018 vLife Systems Ltd <http://vlifesystems.com> 2 // Licensed under an MIT licence. Please see LICENSE.md for details. 3 4 package html 5 6 import ( 7 "bytes" 8 "fmt" 9 "html/template" 10 "os" 11 "path/filepath" 12 "reflect" 13 "regexp" 14 "strings" 15 16 "github.com/vlifesystems/rulehunter/config" 17 "github.com/vlifesystems/rulehunter/report" 18 ) 19 20 // File mode permission used as standard for the html content: 21 // No special permission bits 22 // User: Read, Write Execute 23 // Group: Read 24 // Other: None 25 const modePerm = 0740 26 27 // The number of times to try to load a report. This is useful because 28 // sometimes a report will be being written while trying to load it 29 // and therefore the load will fail. 30 const maxReportLoadAttempts = 5 31 32 var nonAlphaNumOrSpaceRegexp = regexp.MustCompile("[^[:alnum:] ]") 33 var spaceRegexp = regexp.MustCompile(" ") 34 var multipleHyphenRegexp = regexp.MustCompile("-+") 35 36 func escapeString(s string) string { 37 newS := nonAlphaNumOrSpaceRegexp.ReplaceAllString(s, "") 38 newS = spaceRegexp.ReplaceAllString(newS, "-") 39 newS = multipleHyphenRegexp.ReplaceAllString(newS, "-") 40 newS = strings.Trim(newS, " -") 41 return strings.ToLower(newS) 42 } 43 44 // CreatePageError indicates that an html page can't be created 45 type CreatePageError struct { 46 Filename string 47 Op string 48 Err error 49 } 50 51 func (wpe CreatePageError) Error() string { 52 return fmt.Sprintf( 53 "can't create html page for filename: %s, %s (%s)", 54 wpe.Filename, wpe.Err, wpe.Op) 55 } 56 57 func writeTemplate( 58 cfg *config.Config, 59 filename string, 60 tpl string, 61 tplData interface{}, 62 ) error { 63 funcMap := template.FuncMap{ 64 "ToTitle": strings.Title, 65 "IsLast": func(x int, a interface{}) bool { 66 return x == reflect.ValueOf(a).Len()-1 67 }, 68 } 69 t, err := template.New("webpage").Funcs(funcMap).Parse(tpl) 70 if err != nil { 71 return CreatePageError{Filename: filename, Op: "parse", Err: err} 72 } 73 74 fullFilename := filepath.Join(cfg.WWWDir, filename) 75 dir := filepath.Dir(fullFilename) 76 if err := os.MkdirAll(dir, modePerm); err != nil { 77 return CreatePageError{Filename: filename, Op: "mkdir", Err: err} 78 } 79 80 f, err := os.Create(fullFilename) 81 if err != nil { 82 return CreatePageError{Filename: filename, Op: "create", Err: err} 83 } 84 defer f.Close() 85 86 if err := t.Execute(f, tplData); err != nil { 87 return CreatePageError{Filename: filename, Op: "execute", Err: err} 88 } 89 return nil 90 } 91 92 func genReportURLDir( 93 mode report.ModeKind, 94 category string, 95 title string, 96 ) string { 97 escapedTitle := escapeString(title) 98 escapedCategory := escapeString(category) 99 if category != "" { 100 return fmt.Sprintf("reports/category/%s/%s/%s/", 101 escapedCategory, escapedTitle, mode.String()) 102 } 103 return fmt.Sprintf("reports/nocategory/%s/%s/", escapedTitle, mode.String()) 104 } 105 106 func countFiles(files []os.FileInfo) int { 107 numFiles := 0 108 for _, file := range files { 109 if !file.IsDir() { 110 numFiles++ 111 } 112 } 113 return numFiles 114 } 115 116 func makeHtmlNav(menuItem string) template.HTML { 117 var doc bytes.Buffer 118 validMenuItems := []string{ 119 "front", 120 "reports", 121 "tag", 122 "category", 123 "activity", 124 } 125 126 foundValidItem := false 127 for _, validMenuItem := range validMenuItems { 128 if validMenuItem == menuItem { 129 foundValidItem = true 130 } 131 } 132 if !foundValidItem { 133 panic(fmt.Sprintf("menuItem not valid: %s", menuItem)) 134 } 135 136 t, err := template.New("webpage").Parse(navTpl) 137 if err != nil { 138 panic(fmt.Sprintf("Couldn't create nav html: %s", err)) 139 } 140 141 tplData := struct{ MenuItem string }{menuItem} 142 143 if err := t.Execute(&doc, tplData); err != nil { 144 panic(fmt.Sprintf("Couldn't create nav html: %s", err)) 145 } 146 return template.HTML(doc.String()) 147 } 148 149 func makeHtmlHead(cfg *config.Config) template.HTML { 150 var doc bytes.Buffer 151 t, err := template.New("webpage").Parse(headTpl) 152 if err != nil { 153 panic(fmt.Sprintf("Couldn't create head html: %s", err)) 154 } 155 156 tplData := struct { 157 BaseURL string 158 JSComment template.HTML 159 }{BaseURL: cfg.BaseURL, JSComment: template.HTML(headJSComment)} 160 161 if err := t.Execute(&doc, tplData); err != nil { 162 panic(fmt.Sprintf("Couldn't create head html: %s", err)) 163 } 164 return template.HTML(doc.String()) 165 } 166 167 func makeHtml(cfg *config.Config, menuItem string) map[string]template.HTML { 168 r := make(map[string]template.HTML) 169 r["head"] = makeHtmlHead(cfg) 170 r["nav"] = makeHtmlNav(menuItem) 171 r["footer"] = template.HTML(footerHtml) 172 r["bootstrapJS"] = template.HTML(bootstrapJSHtml) 173 return r 174 }