notatio/editor.go

224 lines
5.9 KiB
Go
Raw Normal View History

2023-11-21 04:10:40 +00:00
package main
import (
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
md "github.com/JohannesKaufmann/html-to-markdown"
"github.com/JohannesKaufmann/html-to-markdown/plugin"
"github.com/gomarkdown/markdown"
"github.com/microcosm-cc/bluemonday"
)
type Template struct {
TemplateName string `json:"templateName"`
Filename string `json:"filename"`
}
func EditFile(w http.ResponseWriter, r *http.Request) {
userSession, err := validateSession(w, r)
if err != nil {
handleError(w, "Error validating session", err)
return
}
// Get template and filename from query parameters
templateName := r.URL.Query().Get("template")
filename := r.URL.Query().Get("filename")
filePath := filepath.Join(fileUploadPath, userSession.username, filename)
// Read file content
fileContent, err := readFileContent(filePath)
if err != nil {
handleError(w, "Error reading file content", err)
return
}
// Apply template content if specified
if templateName != "" {
templatePath := filepath.Join(templateFilePath, templateName)
templateContent, err := readFileContent(templatePath)
if err != nil {
handleError(w, "Error reading template content", err)
return
}
// overwrites file content. Could also change this to append.
//TODO: Give user setting to choose which method of usage.
fileContent = markdown.ToHTML([]byte(templateContent), nil, nil)
}
// Sanitize file content
sanitizedContent := sanitizeHTML(fileContent)
// Convert file content based on file type
if filepath.Ext(filename) == ".md" {
// Convert Markdown to HTML
htmlContent := markdown.ToHTML([]byte(sanitizedContent), nil, nil)
fileContent = []byte(htmlContent)
} else if filepath.Ext(filename) == ".html" {
// Sanitize HTML content to prevent any malicious code
fileContent = []byte(sanitizedContent)
}
templates, err := getTemplateList()
if err != nil {
handleError(w, "Error getting template list", err)
return
}
// Update the Templates slice to assign the current filename
for i := range templates {
templates[i].Filename = filename
}
data := struct {
Filename string
FileContent string
Templates []Template // Update type to []Template
}{
Filename: filename,
FileContent: string(fileContent),
Templates: templates, // Assign the template list
}
renderTemplate(w, "edit.html", data)
}
func SaveFile(w http.ResponseWriter, r *http.Request) {
userSession, err := validateSession(w, r)
if err != nil {
handleError(w, "Error validating session", err)
return
}
// Get filename from query parameters
filename := r.URL.Query().Get("filename")
newFilename := r.FormValue("filename") // Retrieve the new filename from the form
filePath := filepath.Join(fileUploadPath, userSession.username, filename)
newFilePath := filepath.Join(fileUploadPath, userSession.username, newFilename) // Create the new file path
// If the new filename is different from the expected filename
if newFilename != filename {
// Check if the file with the new filename already exists
_, err = os.Stat(newFilePath)
if err == nil {
// File with new filename already exists, generate a new unique filename
count := 1
extension := filepath.Ext(newFilename)
filenameWithoutExt := strings.TrimSuffix(newFilename, extension)
// Keep incrementing the count until a unique filename is found
for err == nil {
newFilename = fmt.Sprintf("%s_%d%s", filenameWithoutExt, count, extension)
newFilePath = filepath.Join(fileUploadPath, userSession.username, newFilename)
_, err = os.Stat(newFilePath)
count++
}
} else if !os.IsNotExist(err) {
// Error accessing the file, handle the error or return an error response
handleError(w, "Error: Unable to access file", err)
return
}
err := updateFilename(userSession.username, filename, newFilename)
if err != nil {
handleError(w, "Error updating filename", err)
return
}
err = os.Rename(filePath, newFilePath) // Rename the file to the new filename
if err != nil {
handleError(w, "Error renaming file", err)
return
}
}
// Create or open the file
file, err := os.Create(newFilePath)
if err != nil {
handleError(w, "Error creating file", err)
return
}
defer file.Close()
// Read the edited content from the request form
editedContent := r.FormValue("editor")
// Convert edited content from HTML to Markdown if the file extension is .md
if strings.HasSuffix(newFilename, ".md") {
editedContent = convertHTMLtoMarkdown(editedContent)
}
// Write the edited content to the file
_, err = file.WriteString(editedContent)
if err != nil {
handleError(w, "Error writing edited content to file", err)
return
}
// Update the edited timestamp for the user and file
UpdateEditedTimestamp(userSession.username, newFilename)
http.Redirect(w, r, "/home", http.StatusSeeOther)
}
func handleError(w http.ResponseWriter, errMsg string, err error) {
log.Printf("%s: %v", errMsg, err)
}
func readFileContent(filePath string) ([]byte, error) {
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close()
return io.ReadAll(file)
}
func sanitizeHTML(htmlContent []byte) []byte {
return bluemonday.UGCPolicy().SanitizeBytes(htmlContent)
}
func convertHTMLtoMarkdown(html string) string {
options := md.Options{
EscapeMode: "disabled",
}
converter := md.NewConverter("", true, &options)
converter.Use(plugin.Table())
converter.Use(plugin.Strikethrough("~~"))
markdown, err := converter.ConvertString(html)
if err != nil {
log.Fatal(err)
}
return markdown
}
func getTemplateList() ([]Template, error) {
dir, err := os.Open(templateFilePath)
if err != nil {
return nil, err
}
defer dir.Close()
fileInfos, err := dir.Readdir(-1)
if err != nil {
return nil, err
}
templates := make([]Template, 0)
for _, fileInfo := range fileInfos {
if !fileInfo.IsDir() {
template := Template{
TemplateName: fileInfo.Name(),
Filename: "",
}
templates = append(templates, template)
}
}
return templates, nil
}