2020-06-18 10:23:44 +00:00
package main
import (
2020-06-19 13:03:31 +00:00
"io/ioutil"
2020-06-18 10:23:44 +00:00
"log"
"net/http"
2020-06-19 13:03:31 +00:00
"path/filepath"
"strconv"
"strings"
"time"
2020-06-19 14:30:19 +00:00
2020-06-23 17:53:05 +00:00
"github.com/bouncepaw/mycorrhiza/cfg"
2020-06-19 14:30:19 +00:00
"github.com/gorilla/mux"
2020-06-18 10:23:44 +00:00
)
2020-06-19 18:11:47 +00:00
// There are handlers below. See main() for their usage.
2020-06-18 10:23:44 +00:00
// Boilerplate code present in many handlers. Good to have it.
2020-06-19 18:11:47 +00:00
func HandlerBase ( w http . ResponseWriter , rq * http . Request ) ( Revision , bool ) {
vars := mux . Vars ( rq )
2020-06-18 10:23:44 +00:00
revno := RevInMap ( vars )
2020-06-19 18:11:47 +00:00
return GetRevision ( vars [ "hypha" ] , revno )
2020-06-18 10:23:44 +00:00
}
2020-06-19 18:11:47 +00:00
func HandlerGetBinary ( w http . ResponseWriter , rq * http . Request ) {
if rev , ok := HandlerBase ( w , rq ) ; ok {
2020-06-18 10:23:44 +00:00
rev . ActionGetBinary ( w )
}
}
2020-06-19 18:11:47 +00:00
func HandlerRaw ( w http . ResponseWriter , rq * http . Request ) {
if rev , ok := HandlerBase ( w , rq ) ; ok {
2020-06-18 10:23:44 +00:00
rev . ActionRaw ( w )
}
}
2020-06-19 18:11:47 +00:00
func HandlerZen ( w http . ResponseWriter , rq * http . Request ) {
if rev , ok := HandlerBase ( w , rq ) ; ok {
2020-06-18 10:23:44 +00:00
rev . ActionZen ( w )
}
}
2020-06-19 18:11:47 +00:00
func HandlerView ( w http . ResponseWriter , rq * http . Request ) {
if rev , ok := HandlerBase ( w , rq ) ; ok {
2020-06-18 10:23:44 +00:00
rev . ActionView ( w , HyphaPage )
2020-06-22 15:04:19 +00:00
} else { // Hypha does not exist
log . Println ( "Hypha does not exist, showing 404" )
w . WriteHeader ( http . StatusNotFound )
w . Write ( [ ] byte ( Hypha404 ( mux . Vars ( rq ) [ "hypha" ] ) ) )
2020-06-18 10:23:44 +00:00
}
}
2020-06-19 18:11:47 +00:00
func HandlerHistory ( w http . ResponseWriter , rq * http . Request ) {
2020-06-18 10:23:44 +00:00
w . WriteHeader ( http . StatusNotImplemented )
log . Println ( "Attempt to access an unimplemented thing" )
}
2020-06-19 18:11:47 +00:00
func HandlerEdit ( w http . ResponseWriter , rq * http . Request ) {
vars := mux . Vars ( rq )
2020-06-18 10:23:44 +00:00
ActionEdit ( vars [ "hypha" ] , w )
}
2020-06-19 18:11:47 +00:00
func HandlerRewind ( w http . ResponseWriter , rq * http . Request ) {
2020-06-18 10:23:44 +00:00
w . WriteHeader ( http . StatusNotImplemented )
log . Println ( "Attempt to access an unimplemented thing" )
}
2020-06-19 18:11:47 +00:00
func HandlerDelete ( w http . ResponseWriter , rq * http . Request ) {
2020-06-18 10:23:44 +00:00
w . WriteHeader ( http . StatusNotImplemented )
log . Println ( "Attempt to access an unimplemented thing" )
}
2020-06-19 18:11:47 +00:00
func HandlerRename ( w http . ResponseWriter , rq * http . Request ) {
2020-06-18 10:23:44 +00:00
w . WriteHeader ( http . StatusNotImplemented )
log . Println ( "Attempt to access an unimplemented thing" )
}
2020-06-19 18:11:47 +00:00
// makeTagsSlice turns strings like `"foo,, bar,kek"` to slice of strings that represent tag names. Whitespace around commas is insignificant.
// Expected output for string above: []string{"foo", "bar", "kek"}
2020-06-19 13:03:31 +00:00
func makeTagsSlice ( responseTagsString string ) ( ret [ ] string ) {
for _ , tag := range strings . Split ( responseTagsString , "," ) {
if trimmed := strings . TrimSpace ( tag ) ; "" == trimmed {
ret = append ( ret , trimmed )
}
}
return ret
}
2020-06-19 18:11:47 +00:00
// getHypha returns an existing hypha if it exists in `hyphae` or creates a new one. If it `isNew`, you'll have to insert it to `hyphae` yourself.
2020-06-19 13:03:31 +00:00
func getHypha ( name string ) ( * Hypha , bool ) {
log . Println ( "Accessing hypha" , name )
if h , ok := hyphae [ name ] ; ok {
log . Println ( "Got hypha" , name )
return h , false
}
log . Println ( "Create hypha" , name )
h := & Hypha {
FullName : name ,
2020-06-23 17:53:05 +00:00
Path : filepath . Join ( cfg . WikiDir , name ) ,
2020-06-19 13:03:31 +00:00
Revisions : make ( map [ string ] * Revision ) ,
parentName : filepath . Dir ( name ) ,
}
return h , true
}
2020-06-19 18:11:47 +00:00
// revisionFromHttpData creates a new revison for hypha `h`. All data is fetched from `rq`, except for BinaryMime and BinaryPath which require additional processing. You'll have te insert the revision to `h` yourself.
func revisionFromHttpData ( h * Hypha , rq * http . Request ) * Revision {
2020-06-19 13:03:31 +00:00
idStr := strconv . Itoa ( h . NewestRevisionInt ( ) + 1 )
2020-06-20 15:17:13 +00:00
log . Printf ( "Creating revision %s from http data" , idStr )
2020-06-19 13:03:31 +00:00
rev := & Revision {
2020-06-20 15:17:13 +00:00
Id : h . NewestRevisionInt ( ) + 1 ,
FullName : h . FullName ,
ShortName : filepath . Base ( h . FullName ) ,
Tags : makeTagsSlice ( rq . PostFormValue ( "tags" ) ) ,
Comment : rq . PostFormValue ( "comment" ) ,
Author : rq . PostFormValue ( "author" ) ,
Time : int ( time . Now ( ) . Unix ( ) ) ,
TextMime : rq . PostFormValue ( "text_mime" ) ,
// Fields left: BinaryMime, BinaryPath, BinaryName, TextName, TextPath
}
rev . desiredTextFilename ( ) // TextName is set now
rev . TextPath = filepath . Join ( h . Path , rev . TextName )
2020-06-19 13:03:31 +00:00
return rev
}
2020-06-19 18:11:47 +00:00
// writeTextFileFromHttpData tries to fetch text content from `rq` for revision `rev` and write it to a corresponding text file. It used in `HandlerUpdate`.
func writeTextFileFromHttpData ( rev * Revision , rq * http . Request ) error {
data := [ ] byte ( rq . PostFormValue ( "text" ) )
2020-06-19 13:03:31 +00:00
err := ioutil . WriteFile ( rev . TextPath , data , 0644 )
if err != nil {
log . Println ( "Failed to write" , len ( data ) , "bytes to" , rev . TextPath )
}
return err
}
2020-06-19 18:11:47 +00:00
// writeBinaryFileFromHttpData tries to fetch binary content from `rq` for revision `newRev` and write it to a corresponding binary file. If there is no content, it is taken from `oldRev`.
func writeBinaryFileFromHttpData ( h * Hypha , oldRev Revision , newRev * Revision , rq * http . Request ) error {
2020-06-19 13:03:31 +00:00
// 10 MB file size limit
2020-06-19 18:11:47 +00:00
rq . ParseMultipartForm ( 10 << 20 )
2020-06-19 13:03:31 +00:00
// Read file
2020-06-19 18:11:47 +00:00
file , handler , err := rq . FormFile ( "binary" )
2020-06-19 13:03:31 +00:00
if file != nil {
defer file . Close ( )
}
if err != nil {
log . Println ( "No binary data passed for" , newRev . FullName )
newRev . BinaryMime = oldRev . BinaryMime
newRev . BinaryPath = oldRev . BinaryPath
2020-06-20 15:17:13 +00:00
newRev . BinaryName = oldRev . BinaryName
2020-06-19 13:03:31 +00:00
log . Println ( "Set previous revision's binary data" )
return nil
}
newRev . BinaryMime = handler . Header . Get ( "Content-Type" )
newRev . BinaryPath = filepath . Join ( h . Path , newRev . IdAsStr ( ) + ".bin" )
2020-06-20 15:17:13 +00:00
newRev . BinaryName = newRev . desiredBinaryFilename ( )
2020-06-19 13:03:31 +00:00
data , err := ioutil . ReadAll ( file )
if err != nil {
log . Println ( err )
return err
}
log . Println ( "Got" , len ( data ) , "of binary data for" , newRev . FullName )
err = ioutil . WriteFile ( newRev . BinaryPath , data , 0644 )
if err != nil {
log . Println ( "Failed to write" , len ( data ) , "bytes to" , newRev . TextPath )
return err
}
log . Println ( "Written" , len ( data ) , "of binary data for" , newRev . FullName )
return nil
}
2020-06-19 18:11:47 +00:00
func HandlerUpdate ( w http . ResponseWriter , rq * http . Request ) {
vars := mux . Vars ( rq )
log . Println ( "Attempt to update hypha" , mux . Vars ( rq ) [ "hypha" ] )
2020-06-19 13:03:31 +00:00
h , isNew := getHypha ( vars [ "hypha" ] )
2020-06-22 13:58:12 +00:00
var oldRev Revision
if ! isNew {
oldRev = h . GetNewestRevision ( )
} else {
h = & Hypha {
FullName : vars [ "hypha" ] ,
2020-06-23 17:53:05 +00:00
Path : filepath . Join ( cfg . WikiDir , vars [ "hypha" ] ) ,
2020-06-22 13:58:12 +00:00
Revisions : make ( map [ string ] * Revision ) ,
parentName : filepath . Dir ( vars [ "hypha" ] ) ,
}
2020-06-19 13:03:31 +00:00
h . CreateDir ( )
2020-06-22 13:58:12 +00:00
oldRev = Revision { }
2020-06-19 13:03:31 +00:00
}
2020-06-22 13:58:12 +00:00
newRev := revisionFromHttpData ( h , rq )
2020-06-19 18:11:47 +00:00
err := writeTextFileFromHttpData ( newRev , rq )
2020-06-19 13:03:31 +00:00
if err != nil {
log . Println ( err )
return
}
2020-06-19 18:11:47 +00:00
err = writeBinaryFileFromHttpData ( h , oldRev , newRev , rq )
2020-06-19 13:03:31 +00:00
if err != nil {
log . Println ( err )
return
}
h . Revisions [ newRev . IdAsStr ( ) ] = newRev
h . SaveJson ( )
2020-06-22 13:58:12 +00:00
if isNew {
hyphae [ h . FullName ] = h
}
2020-06-19 13:03:31 +00:00
log . Println ( "Current hyphae storage is" , hyphae )
w . WriteHeader ( http . StatusOK )
2020-06-20 15:17:13 +00:00
w . Header ( ) . Set ( "Content-Type" , "text/html; charset=utf-8" )
d := map [ string ] string { "Name" : h . FullName }
w . Write ( [ ] byte ( renderFromMap ( d , "updateOk.html" ) ) )
2020-06-18 10:23:44 +00:00
}