package main import ( "errors" "fmt" "git.openprivacy.ca/openprivacy/libricochet-go/log" "git.openprivacy.ca/sarah/kdb/citing" "github.com/nickng/bibtex" "html/template" "io/ioutil" "net/http" "os" "os/exec" "path" "path/filepath" "regexp" "strings" ) type Page struct { Title string Body template.HTML BodyOrig string Concept template.HTML References map[string]*bibtex.BibEntry } var templates = template.Must(template.ParseFiles("templates/view.html")) func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { err := templates.ExecuteTemplate(w, tmpl+".html", p) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } func (p *Page) save() error { filename := path.Join("data", p.Title+".kdb") return ioutil.WriteFile(filename, []byte(p.Body), 0600) } var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9\\-]+)(/?)$") func getTitle(w http.ResponseWriter, r *http.Request) (string, error) { m := validPath.FindStringSubmatch(r.URL.Path) if m == nil { http.NotFound(w, r) return "", errors.New("Invalid Page Title") } return m[2], nil // The title is the second subexpression. } func hasPage(word string) bool { if _, err := os.Stat(path.Join("data/", normalize(word)+".kdb")); err != nil { return false } return true } func loadPage(title string) (*Page, error) { filename := title + ".kdb" bodyb, err := ioutil.ReadFile(path.Join("data", filename)) if err != nil { return nil, err } body := template.HTMLEscapeString(string(bodyb)) concept := template.HTML(construct_graph(title)) linked := make(map[string]bool) references := make(map[string]*bibtex.BibEntry) for _, word := range strings.Fields(body) { if !linked[normalize(word)] { if hasPage(word) { body = strings.ReplaceAll(body, word, fmt.Sprintf("%v", normalize(word), normalize(word))) } if strings.HasPrefix(word, "cite;") { log.Infof("Extracting Citation") cite, link, img, rec := citing.ExtractPDF(word) if rec != nil { if references[rec.CiteName] == nil { references[rec.CiteName] = rec } } body = strings.ReplaceAll(body, word, fmt.Sprintf("[X]%v", cite, link, img)) } if strings.HasPrefix(word, "http") { body = strings.ReplaceAll(body, word, fmt.Sprintf("%v", word, word)) } if strings.HasPrefix(word, "data:image/png;base64,") { body = strings.ReplaceAll(body, word, fmt.Sprintf("", word)) } linked[normalize(word)] = true } } return &Page{Title: title, Body: template.HTML(body), BodyOrig: string(bodyb), Concept: concept, References: references}, nil } func normalize(word string) string { if strings.HasPrefix(word, "typeof:") { word = word[7:] } else if strings.HasPrefix(word, "subtype:") { word = word[8:] } else if strings.HasPrefix(word, "instance:") { word = word[9:] } word = strings.TrimSpace(word) return word } func viewHandler(w http.ResponseWriter, r *http.Request) { title, err := getTitle(w, r) if err != nil { return } p, err := loadPage(title) if err != nil { p = &Page{} p.Title = title } renderTemplate(w, "view", p) } func saveHandler(w http.ResponseWriter, r *http.Request) { title, err := getTitle(w, r) if err != nil { return } body := r.FormValue("body") p := &Page{Title: title, Body: template.HTML(body)} err = p.save() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } http.Redirect(w, r, "/view/"+title, http.StatusFound) } func get_links(name string) (links []string) { filename := name + ".kdb" bodyb, err := ioutil.ReadFile(path.Join("data", filename)) if err != nil { return []string{} } body := template.HTMLEscapeString(string(bodyb)) for _, word := range strings.Fields(body) { if hasPage(word) { links = append(links, word) } } return } func construct_graph(word string) string { seen := make(map[string]bool) graph := "digraph {\n ratio=\"compress\";size=\"8,4!\";splines=true;\"" + word + "\";\n" graph = construct_graph_sub(graph, word, seen, 1) graph += "}" ioutil.WriteFile("tmp.dot", []byte(graph), 0640) subProcess := exec.Command("dot", "-Tsvg", "-otmp.svg", "-Nfontname=Chilanka", "tmp.dot") subProcess.Run() subProcess.Wait() data, _ := ioutil.ReadFile("tmp.svg") return "
" + strings.ReplaceAll(string(data), ` `, "") } func construct_graph_sub(graph string, word string, seen map[string]bool, depth int) string { if depth < 0 { return graph } links := get_links(normalize(word)) if !seen[normalize(word)] { col := "fillcolor=\"white\"" if depth == 1 { col = "fillcolor=\"#b09cbc\"" } graph += fmt.Sprintf("\"%v\" [href=\"/view/%v\",%v,style=filled];\n", normalize(word), normalize(word), col) } seen[normalize(word)] = true seen[word] = true graph += "# " + word + "\n" for _, link := range links { if strings.HasPrefix(word, "typeof:") || strings.HasPrefix(word, "instance:") || strings.HasPrefix(word, "subtype:") { word = normalize(word) } if !seen[normalize(link)] { graph += fmt.Sprintf("\"%v\" [href=\"/view/%v\"];\n", normalize(link), normalize(link)) } if normalize(word) != normalize(link) && (!seen[normalize(word)+"++"+normalize(link)] || !seen[normalize(link)+"++"+normalize(word)]) { if strings.HasPrefix(link, "typeof:") { graph += fmt.Sprintf("\"%v\" -> \"%v\" [style=dotted]\n", link[7:], word) seen[word+"++"+link[7:]] = true seen[link[7:]+"++"+word] = true } else if strings.HasPrefix(link, "subtype:") { graph += fmt.Sprintf("\"%v\" -> \"%v\" [style=dotted]\n", word, link[8:]) seen[word+"++"+link[8:]] = true seen[link[8:]+"++"+word] = true } else if strings.HasPrefix(link, "instance:") { graph += fmt.Sprintf("\"%v\" -> \"%v\" [style=dotted]\n", word, link[9:]) seen[word+"++"+link[9:]] = true seen[link[9:]+"++"+word] = true } else { graph += fmt.Sprintf("\"%v\" -> \"%v\" []\n", word, link) } seen[normalize(word)+"++"+normalize(link)] = true seen[normalize(link)+"++"+normalize(word)] = true } //} if !seen[link] { graph = construct_graph_sub(graph, link, seen, depth-1) } } return graph } func indexHandler(w http.ResponseWriter, r *http.Request) { root := "./data/" var files []string filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if path != "./data/" { files = append(files, path[5:]) } return nil }) body := "" for _, file := range files { name := strings.ReplaceAll(file, ".kdb", "") body += fmt.Sprintf("%v
", name, name) } renderTemplate(w, "view", &Page{Title: "Sigil KDB", Body: template.HTML(body), BodyOrig: ""}) } func main() { log.SetLevel(log.LevelInfo) http.HandleFunc("/", indexHandler) http.HandleFunc("/view/", viewHandler) http.HandleFunc("/save/", saveHandler) log.Errorln(http.ListenAndServe(":8080", nil)) }