bootstrap new version
This commit is contained in:
		
							parent
							
								
									1675403010
								
							
						
					
					
						commit
						83d9462ca1
					
				|  | @ -1,7 +0,0 @@ | ||||||
| assets.go |  | ||||||
| node_modules |  | ||||||
| .sass-cache |  | ||||||
| temp |  | ||||||
| copied_file.txt |  | ||||||
| builds |  | ||||||
| release |  | ||||||
|  | @ -1,97 +0,0 @@ | ||||||
| .editor input[type="text"], .editor input[type="datetime-local"], .editor textarea { |  | ||||||
|     background-color: #f5f3f3; |  | ||||||
|     border-radius: .5em; |  | ||||||
|     border: 1px solid #ccc; |  | ||||||
|     outline: none; |  | ||||||
|     padding: .5em; |  | ||||||
|     line-height: 1.2em; |  | ||||||
|     width: 100%; |  | ||||||
|     font-size: 1rem; |  | ||||||
|     margin: 0; |  | ||||||
|     transition: all 0.3s; |  | ||||||
| } |  | ||||||
| input[type="text"]:focus { |  | ||||||
|     border-bottom: 1px solid #26a69a; |  | ||||||
| } |  | ||||||
| .editor .block { |  | ||||||
|     margin-bottom: 1em; |  | ||||||
| } |  | ||||||
| .editor fieldset { |  | ||||||
|     border: 0; |  | ||||||
|     margin: 0; |  | ||||||
|     padding: 0; |  | ||||||
| } |  | ||||||
| .editor h1 textarea { |  | ||||||
|     font-size: 2em; |  | ||||||
|     font-weight: 400; |  | ||||||
|     resize: none; |  | ||||||
|     overflow: hidden; |  | ||||||
|     padding: 0; |  | ||||||
|     line-height: 1em; |  | ||||||
|     height: 1em; |  | ||||||
|     background-color: transparent; |  | ||||||
|     border: 0; |  | ||||||
| } |  | ||||||
| .editor fieldset h3 { |  | ||||||
|     display: inline-block |  | ||||||
| } |  | ||||||
| .editor label { |  | ||||||
|     font-size: .8em; |  | ||||||
|     color: #8b8b8b; |  | ||||||
|     font-weight: 500; |  | ||||||
| } |  | ||||||
| .editor .options { |  | ||||||
|     column-count: 2; |  | ||||||
|     column-gap: 1em; |  | ||||||
| } |  | ||||||
| .editor .block { |  | ||||||
|     break-inside: avoid; |  | ||||||
|     position: relative; |  | ||||||
|     z-index: 0; |  | ||||||
| } |  | ||||||
| .editor .block .delete { |  | ||||||
|     position: absolute; |  | ||||||
|     right: 0; |  | ||||||
|     bottom: 1px; |  | ||||||
|     height: 2.5em; |  | ||||||
|     padding: 0 .7em; |  | ||||||
|     background: transparent; |  | ||||||
|     color: rgba(0, 0, 0, 0.5); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* BUTTONS */ |  | ||||||
| 
 |  | ||||||
| .editor button, .editor input[type="submit"] { |  | ||||||
|     box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); |  | ||||||
|     border: none; |  | ||||||
|     border-radius: 2px; |  | ||||||
|     height: 36px; |  | ||||||
|     line-height: 36px; |  | ||||||
|     outline: 0; |  | ||||||
|     padding: 0 2rem; |  | ||||||
|     text-transform: uppercase; |  | ||||||
|     text-decoration: none; |  | ||||||
|     color: #fff; |  | ||||||
|     background-color: #26a69a; |  | ||||||
|     text-align: center; |  | ||||||
|     letter-spacing: .5px; |  | ||||||
|     position: relative; |  | ||||||
|     cursor: pointer; |  | ||||||
|     display: inline-block; |  | ||||||
|     overflow: hidden; |  | ||||||
|     vertical-align: middle; |  | ||||||
|     transition: all .3s ease-out; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* TOOLBAR */ |  | ||||||
| 
 |  | ||||||
| .editor #actions { |  | ||||||
|     display: flex; |  | ||||||
|     margin: 1em 0; |  | ||||||
| } |  | ||||||
| .editor #actions> div { |  | ||||||
|     flex-basis: 50%; |  | ||||||
| } |  | ||||||
| .editor #actions div:nth-child(2) { |  | ||||||
|     text-align: right; |  | ||||||
| } |  | ||||||
|  | @ -1,5 +1 @@ | ||||||
| "use strict"; | 'use strict'; | ||||||
| 
 |  | ||||||
| var ace = document.createElement('script'); |  | ||||||
| ace.src = 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.3/ace.js'; |  | ||||||
| document.head.appendChild(ace); |  | ||||||
|  |  | ||||||
|  | @ -1,62 +0,0 @@ | ||||||
| {{ define "content" }} |  | ||||||
| <div class="container editor {{ .Class }}"> |  | ||||||
| {{ if eq .Class "complete" }} |  | ||||||
| <h1> |  | ||||||
| <textarea id="site-title">{{ .Name }}</textarea> |  | ||||||
| </h1> |  | ||||||
| 
 |  | ||||||
| <form method="POST" action="."> |  | ||||||
| <div class="options"> |  | ||||||
| {{ template "frontmatter" .FrontMatter }} |  | ||||||
| <button id="add-field">Add field</button> |  | ||||||
| </div> |  | ||||||
| 
 |  | ||||||
| <div class="main"> |  | ||||||
| {{ if eq .Mode "markdown" }} |  | ||||||
| <nav> |  | ||||||
| <a id="see-source" class="active"> |  | ||||||
| <i class="fa fa-code"></i> |  | ||||||
| Source</a> |  | ||||||
| <a id="see-preview"> |  | ||||||
| <i class="fa fa-eye"></i> |  | ||||||
| Preview</a> |  | ||||||
| </nav> |  | ||||||
| {{ end}} |  | ||||||
| 
 |  | ||||||
| <div id="editor-source" data-mode="{{ .Mode }}"></div> |  | ||||||
| <textarea name="content">{{ .Content }}</textarea> |  | ||||||
| <div id="editor-preview"></div> |  | ||||||
| </form> |  | ||||||
| {{ else if eq .Class "content-only" }} |  | ||||||
| <form method="POST" action=""> |  | ||||||
| <h1 id="site-title">{{ .Name }}</h1> |  | ||||||
| 
 |  | ||||||
| <div class="main"> |  | ||||||
| <div id="editor-source" data-mode="{{ .Mode }}"></div> |  | ||||||
| <textarea name="content">{{ .Content }}</textarea> |  | ||||||
| </div> |  | ||||||
| </form> |  | ||||||
| {{ else }} |  | ||||||
| <form method="POST" action=""> |  | ||||||
| <h1 id="site-title">{{ .Name }}</h1> |  | ||||||
| 
 |  | ||||||
| <div class="options"> |  | ||||||
| {{ template "frontmatter" .FrontMatter }} |  | ||||||
| <button id="add-field">Add field</button> |  | ||||||
| </div> |  | ||||||
| </form> |  | ||||||
| {{ end }} |  | ||||||
| 
 |  | ||||||
| <div id="actions"> |  | ||||||
| <div> |  | ||||||
| <input type="submit" data-type="{{ .Class }}" data-regenerate="false" data-schedule="false" value="Save"> |  | ||||||
| </div> |  | ||||||
| <div> |  | ||||||
| {{ if and (eq .Class "complete") ( .IsPost ) }}<input type="submit" data-type="{{ .Class }}" data-schedule="true" data-regenerate="false" value="Schedule"> |  | ||||||
| {{ end }} |  | ||||||
| <input type="submit" data-type="{{ .Class }}" data-regenerate="true" data-schedule="false" class="default" value="Publish"> |  | ||||||
| </div> |  | ||||||
| </div> |  | ||||||
| </div> |  | ||||||
| 
 |  | ||||||
| {{ end }} |  | ||||||
|  | @ -1,50 +0,0 @@ | ||||||
| {{ define "frontmatter" }} |  | ||||||
|  {{ range $key, $value := . }} |  | ||||||
| 
 |  | ||||||
|   {{ if or (eq $value.Type "object") (eq $value.Type "array") }} |  | ||||||
|    <fieldset id="{{ $value.Name }}" data-type="{{ $value.Type }}"> |  | ||||||
|     <div class="title"> |  | ||||||
|      <h3>{{ SplitCapitalize $value.Title }}</h3> |  | ||||||
| 
 |  | ||||||
|      <button class="delete" data-delete="{{ $value.Name }}">−</button> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <div class="columns"> |  | ||||||
|      {{ template "frontmatter" $value.Content }} |  | ||||||
|      <button class="add">Add field</button> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|    </fieldset> |  | ||||||
|    {{ else }} |  | ||||||
| 
 |  | ||||||
|    {{ if not (eq $value.Parent.Type "array") }} |  | ||||||
|     <div class="block" id="block-{{ $value.Name }}" data-content="{{ $value.Name }}"> |  | ||||||
|      <label for="{{ $value.Name }}">{{ SplitCapitalize $value.Title }}</label> |  | ||||||
|      <button class="delete" data-delete="block-{{ $value.Name }}">×</button> |  | ||||||
| 
 |  | ||||||
|     {{ end }} |  | ||||||
| 
 |  | ||||||
|     {{ if eq $value.Parent.Type "array" }} |  | ||||||
|      <div class="block" id="{{ $value.Name }}-{{ $key }}" data-type="array-item"> |  | ||||||
|      {{ end }} |  | ||||||
| 
 |  | ||||||
|      {{ if eq $value.HTMLType "textarea" }} |  | ||||||
|       <textarea class="scroll" name="{{ $value.Name }}:{{ $value.Type }}" id="{{ $value.Name }}" data-parent-type="{{ $value.Parent.Type }}">{{ $value.Content }}</textarea> |  | ||||||
|       {{ else if eq $value.HTMLType "datetime" }} |  | ||||||
|       <input name="{{ $value.Name }}:{{ $value.Type }}" id="{{ $value.Name }}" value="{{ $value.Content.Format " 2006-01-02T15:04" }}" type="datetime-local" data-parent-type="{{ $value.Parent.Type }}"></input> |  | ||||||
|       {{ else }} |  | ||||||
|       <input name="{{ $value.Name }}:{{ $value.Type }}" id="{{ $value.Name }}" value="{{ $value.Content }}" type="{{ $value.HTMLType }}" data-parent-type="{{ $value.Parent.Type }}"></input> |  | ||||||
|      {{ end }} |  | ||||||
| 
 |  | ||||||
|      {{ if not (eq $value.Parent.Type "array") }} |  | ||||||
|      </div> |  | ||||||
|     {{ end }} |  | ||||||
| 
 |  | ||||||
|     {{ if eq $value.Parent.Type "array" }} |  | ||||||
|      <button class="delete" data-delete="{{ $value.Name }}">×</button> |  | ||||||
|     </div> |  | ||||||
|    {{ end }} |  | ||||||
| 
 |  | ||||||
|   {{ end }} |  | ||||||
|  {{ end }} |  | ||||||
| {{ end }} |  | ||||||
							
								
								
									
										67
									
								
								binary.go
								
								
								
								
							
							
						
						
									
										67
									
								
								binary.go
								
								
								
								
							|  | @ -1,9 +1,6 @@ | ||||||
| // Code generated by go-bindata.
 | // Code generated by go-bindata.
 | ||||||
| // sources:
 | // sources:
 | ||||||
| // assets/public/css/styles.css
 |  | ||||||
| // assets/public/js/application.js
 | // assets/public/js/application.js
 | ||||||
| // assets/templates/editor.tmpl
 |  | ||||||
| // assets/templates/options.tmpl
 |  | ||||||
| // DO NOT EDIT!
 | // DO NOT EDIT!
 | ||||||
| 
 | 
 | ||||||
| package hugo | package hugo | ||||||
|  | @ -30,24 +27,6 @@ type asset struct { | ||||||
| 	info  os.FileInfo | 	info  os.FileInfo | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // publicCssStylesCss reads file data from disk. It returns an error on failure.
 |  | ||||||
| func publicCssStylesCss() (*asset, error) { |  | ||||||
| 	path := "D:\\Code\\Go\\src\\github.com\\hacdias\\caddy-hugo\\assets\\public\\css\\styles.css" |  | ||||||
| 	name := "public/css/styles.css" |  | ||||||
| 	bytes, err := bindataRead(path, name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	fi, err := os.Stat(path) |  | ||||||
| 	if err != nil { |  | ||||||
| 		err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	a := &asset{bytes: bytes, info: fi} |  | ||||||
| 	return a, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // publicJsApplicationJs reads file data from disk. It returns an error on failure.
 | // publicJsApplicationJs reads file data from disk. It returns an error on failure.
 | ||||||
| func publicJsApplicationJs() (*asset, error) { | func publicJsApplicationJs() (*asset, error) { | ||||||
| 	path := "D:\\Code\\Go\\src\\github.com\\hacdias\\caddy-hugo\\assets\\public\\js\\application.js" | 	path := "D:\\Code\\Go\\src\\github.com\\hacdias\\caddy-hugo\\assets\\public\\js\\application.js" | ||||||
|  | @ -66,42 +45,6 @@ func publicJsApplicationJs() (*asset, error) { | ||||||
| 	return a, err | 	return a, err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // templatesEditorTmpl reads file data from disk. It returns an error on failure.
 |  | ||||||
| func templatesEditorTmpl() (*asset, error) { |  | ||||||
| 	path := "D:\\Code\\Go\\src\\github.com\\hacdias\\caddy-hugo\\assets\\templates\\editor.tmpl" |  | ||||||
| 	name := "templates/editor.tmpl" |  | ||||||
| 	bytes, err := bindataRead(path, name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	fi, err := os.Stat(path) |  | ||||||
| 	if err != nil { |  | ||||||
| 		err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	a := &asset{bytes: bytes, info: fi} |  | ||||||
| 	return a, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // templatesOptionsTmpl reads file data from disk. It returns an error on failure.
 |  | ||||||
| func templatesOptionsTmpl() (*asset, error) { |  | ||||||
| 	path := "D:\\Code\\Go\\src\\github.com\\hacdias\\caddy-hugo\\assets\\templates\\options.tmpl" |  | ||||||
| 	name := "templates/options.tmpl" |  | ||||||
| 	bytes, err := bindataRead(path, name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	fi, err := os.Stat(path) |  | ||||||
| 	if err != nil { |  | ||||||
| 		err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	a := &asset{bytes: bytes, info: fi} |  | ||||||
| 	return a, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Asset loads and returns the asset for the given name.
 | // Asset loads and returns the asset for the given name.
 | ||||||
| // It returns an error if the asset could not be found or
 | // It returns an error if the asset could not be found or
 | ||||||
| // could not be loaded.
 | // could not be loaded.
 | ||||||
|  | @ -154,10 +97,7 @@ func AssetNames() []string { | ||||||
| 
 | 
 | ||||||
| // _bindata is a table, holding each asset generator, mapped to its name.
 | // _bindata is a table, holding each asset generator, mapped to its name.
 | ||||||
| var _bindata = map[string]func() (*asset, error){ | var _bindata = map[string]func() (*asset, error){ | ||||||
| 	"public/css/styles.css": publicCssStylesCss, |  | ||||||
| 	"public/js/application.js": publicJsApplicationJs, | 	"public/js/application.js": publicJsApplicationJs, | ||||||
| 	"templates/editor.tmpl": templatesEditorTmpl, |  | ||||||
| 	"templates/options.tmpl": templatesOptionsTmpl, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AssetDir returns the file names below a certain
 | // AssetDir returns the file names below a certain
 | ||||||
|  | @ -201,17 +141,10 @@ type bintree struct { | ||||||
| } | } | ||||||
| var _bintree = &bintree{nil, map[string]*bintree{ | var _bintree = &bintree{nil, map[string]*bintree{ | ||||||
| 	"public": &bintree{nil, map[string]*bintree{ | 	"public": &bintree{nil, map[string]*bintree{ | ||||||
| 		"css": &bintree{nil, map[string]*bintree{ |  | ||||||
| 			"styles.css": &bintree{publicCssStylesCss, map[string]*bintree{}}, |  | ||||||
| 		}}, |  | ||||||
| 		"js": &bintree{nil, map[string]*bintree{ | 		"js": &bintree{nil, map[string]*bintree{ | ||||||
| 			"application.js": &bintree{publicJsApplicationJs, map[string]*bintree{}}, | 			"application.js": &bintree{publicJsApplicationJs, map[string]*bintree{}}, | ||||||
| 		}}, | 		}}, | ||||||
| 	}}, | 	}}, | ||||||
| 	"templates": &bintree{nil, map[string]*bintree{ |  | ||||||
| 		"editor.tmpl": &bintree{templatesEditorTmpl, map[string]*bintree{}}, |  | ||||||
| 		"options.tmpl": &bintree{templatesOptionsTmpl, map[string]*bintree{}}, |  | ||||||
| 	}}, |  | ||||||
| }} | }} | ||||||
| 
 | 
 | ||||||
| // RestoreAsset restores an asset under the given directory
 | // RestoreAsset restores an asset under the given directory
 | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								commands.go
								
								
								
								
							
							
						
						
									
										36
									
								
								commands.go
								
								
								
								
							|  | @ -1,36 +0,0 @@ | ||||||
| package hugo |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"log" |  | ||||||
| 	"os" |  | ||||||
| 	"os/exec" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Run executes an external command
 |  | ||||||
| func Run(command string, args []string, path string) error { |  | ||||||
| 	cmd := exec.Command(command, args...) |  | ||||||
| 	cmd.Dir = path |  | ||||||
| 	cmd.Stdout = os.Stderr |  | ||||||
| 	cmd.Stderr = os.Stderr |  | ||||||
| 	return cmd.Run() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RunHugo is used to run the static website generator
 |  | ||||||
| func RunHugo(c *Config, force bool) { |  | ||||||
| 	os.RemoveAll(c.Public) |  | ||||||
| 
 |  | ||||||
| 	// Prevent running if watching is enabled
 |  | ||||||
| 	if b, pos := StringInSlice("--watch", c.Args); b && !force { |  | ||||||
| 		if len(c.Args) > pos && c.Args[pos+1] != "false" { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if len(c.Args) == pos+1 { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := Run(c.Hugo, c.Args, c.Root); err != nil { |  | ||||||
| 		log.Panic(err) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										172
									
								
								frontmatter.go
								
								
								
								
							
							
						
						
									
										172
									
								
								frontmatter.go
								
								
								
								
							|  | @ -1,172 +0,0 @@ | ||||||
| package hugo |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"log" |  | ||||||
| 	"reflect" |  | ||||||
| 	"sort" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"github.com/spf13/cast" |  | ||||||
| 	"github.com/spf13/hugo/parser" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	mainName   = "#MAIN#" |  | ||||||
| 	objectType = "object" |  | ||||||
| 	arrayType  = "array" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var mainTitle = "" |  | ||||||
| 
 |  | ||||||
| // Pretty creates a new FrontMatter object
 |  | ||||||
| func Pretty(content []byte) (interface{}, string, error) { |  | ||||||
| 	frontType := parser.DetectFrontMatter(rune(content[0])) |  | ||||||
| 	front, err := frontType.Parse(content) |  | ||||||
| 
 |  | ||||||
| 	if err != nil { |  | ||||||
| 		return []string{}, mainTitle, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	object := new(frontmatter) |  | ||||||
| 	object.Type = objectType |  | ||||||
| 	object.Name = mainName |  | ||||||
| 
 |  | ||||||
| 	return rawToPretty(front, object), mainTitle, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type frontmatter struct { |  | ||||||
| 	Name     string |  | ||||||
| 	Title    string |  | ||||||
| 	Content  interface{} |  | ||||||
| 	Type     string |  | ||||||
| 	HTMLType string |  | ||||||
| 	Parent   *frontmatter |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func rawToPretty(config interface{}, parent *frontmatter) interface{} { |  | ||||||
| 	objects := []*frontmatter{} |  | ||||||
| 	arrays := []*frontmatter{} |  | ||||||
| 	fields := []*frontmatter{} |  | ||||||
| 
 |  | ||||||
| 	cnf := map[string]interface{}{} |  | ||||||
| 
 |  | ||||||
| 	if reflect.TypeOf(config) == reflect.TypeOf(map[interface{}]interface{}{}) { |  | ||||||
| 		for key, value := range config.(map[interface{}]interface{}) { |  | ||||||
| 			cnf[key.(string)] = value |  | ||||||
| 		} |  | ||||||
| 	} else if reflect.TypeOf(config) == reflect.TypeOf([]interface{}{}) { |  | ||||||
| 		for key, value := range config.([]interface{}) { |  | ||||||
| 			cnf[string(key)] = value |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		cnf = config.(map[string]interface{}) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for name, element := range cnf { |  | ||||||
| 		if IsMap(element) { |  | ||||||
| 			objects = append(objects, handleObjects(element, parent, name)) |  | ||||||
| 		} else if IsSlice(element) { |  | ||||||
| 			arrays = append(arrays, handleArrays(element, parent, name)) |  | ||||||
| 		} else { |  | ||||||
| 			if name == "title" && parent.Name == mainName { |  | ||||||
| 				mainTitle = element.(string) |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			fields = append(fields, handleFlatValues(element, parent, name)) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	sort.Sort(sortByTitle(objects)) |  | ||||||
| 	sort.Sort(sortByTitle(arrays)) |  | ||||||
| 	sort.Sort(sortByTitle(fields)) |  | ||||||
| 
 |  | ||||||
| 	settings := []*frontmatter{} |  | ||||||
| 	settings = append(settings, fields...) |  | ||||||
| 	settings = append(settings, arrays...) |  | ||||||
| 	settings = append(settings, objects...) |  | ||||||
| 	return settings |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type sortByTitle []*frontmatter |  | ||||||
| 
 |  | ||||||
| func (f sortByTitle) Len() int      { return len(f) } |  | ||||||
| func (f sortByTitle) Swap(i, j int) { f[i], f[j] = f[j], f[i] } |  | ||||||
| func (f sortByTitle) Less(i, j int) bool { |  | ||||||
| 	return strings.ToLower(f[i].Name) < strings.ToLower(f[j].Name) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func handleObjects(content interface{}, parent *frontmatter, name string) *frontmatter { |  | ||||||
| 	c := new(frontmatter) |  | ||||||
| 	c.Parent = parent |  | ||||||
| 	c.Type = objectType |  | ||||||
| 	c.Title = name |  | ||||||
| 
 |  | ||||||
| 	if parent.Name == mainName { |  | ||||||
| 		c.Name = c.Title |  | ||||||
| 	} else if parent.Type == arrayType { |  | ||||||
| 		c.Name = parent.Name + "[]" |  | ||||||
| 	} else { |  | ||||||
| 		c.Name = parent.Name + "[" + c.Title + "]" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	c.Content = rawToPretty(content, c) |  | ||||||
| 	return c |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func handleArrays(content interface{}, parent *frontmatter, name string) *frontmatter { |  | ||||||
| 	c := new(frontmatter) |  | ||||||
| 	c.Parent = parent |  | ||||||
| 	c.Type = arrayType |  | ||||||
| 	c.Title = name |  | ||||||
| 
 |  | ||||||
| 	if parent.Name == mainName { |  | ||||||
| 		c.Name = name |  | ||||||
| 	} else { |  | ||||||
| 		c.Name = parent.Name + "[" + name + "]" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	c.Content = rawToPretty(content, c) |  | ||||||
| 	return c |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func handleFlatValues(content interface{}, parent *frontmatter, name string) *frontmatter { |  | ||||||
| 	c := new(frontmatter) |  | ||||||
| 	c.Parent = parent |  | ||||||
| 
 |  | ||||||
| 	switch reflect.ValueOf(content).Kind() { |  | ||||||
| 	case reflect.Bool: |  | ||||||
| 		c.Type = "boolean" |  | ||||||
| 	case reflect.Int, reflect.Float32, reflect.Float64: |  | ||||||
| 		c.Type = "number" |  | ||||||
| 	default: |  | ||||||
| 		c.Type = "string" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	c.Content = content |  | ||||||
| 
 |  | ||||||
| 	switch strings.ToLower(name) { |  | ||||||
| 	case "description": |  | ||||||
| 		c.HTMLType = "textarea" |  | ||||||
| 	case "date", "publishdate": |  | ||||||
| 		c.HTMLType = "datetime" |  | ||||||
| 		c.Content = cast.ToTime(content) |  | ||||||
| 	default: |  | ||||||
| 		c.HTMLType = "text" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if parent.Type == arrayType { |  | ||||||
| 		c.Name = parent.Name + "[]" |  | ||||||
| 		c.Title = content.(string) |  | ||||||
| 	} else if parent.Type == objectType { |  | ||||||
| 		c.Title = name |  | ||||||
| 		c.Name = parent.Name + "[" + name + "]" |  | ||||||
| 
 |  | ||||||
| 		if parent.Name == mainName { |  | ||||||
| 			c.Name = name |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		log.Panic("Parent type not allowed in handleFlatValues.") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return c |  | ||||||
| } |  | ||||||
							
								
								
									
										174
									
								
								get.go
								
								
								
								
							
							
						
						
									
										174
									
								
								get.go
								
								
								
								
							|  | @ -1,174 +0,0 @@ | ||||||
| package hugo |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"fmt" |  | ||||||
| 	"html/template" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"net/http" |  | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"github.com/hacdias/caddy-filemanager" |  | ||||||
| 	"github.com/spf13/hugo/parser" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type editor struct { |  | ||||||
| 	Name        string |  | ||||||
| 	Class       string |  | ||||||
| 	IsPost      bool |  | ||||||
| 	Mode        string |  | ||||||
| 	Content     string |  | ||||||
| 	BaseURL     string |  | ||||||
| 	FrontMatter interface{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // GET handles the GET method on editor page
 |  | ||||||
| func (h Hugo) GET(w http.ResponseWriter, r *http.Request, filename string) (int, error) { |  | ||||||
| 	// Check if the file exists.
 |  | ||||||
| 	if _, err := os.Stat(filename); os.IsNotExist(err) { |  | ||||||
| 		return http.StatusNotFound, err |  | ||||||
| 	} else if os.IsPermission(err) { |  | ||||||
| 		return http.StatusForbidden, err |  | ||||||
| 	} else if err != nil { |  | ||||||
| 		return http.StatusInternalServerError, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Open the file and check if there was some error while opening
 |  | ||||||
| 	file, err := ioutil.ReadFile(filename) |  | ||||||
| 	if os.IsPermission(err) { |  | ||||||
| 		return http.StatusForbidden, err |  | ||||||
| 	} else if err != nil { |  | ||||||
| 		return http.StatusInternalServerError, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Create a new editor variable and set the extension
 |  | ||||||
| 	data := new(editor) |  | ||||||
| 	data.Mode = strings.TrimPrefix(filepath.Ext(filename), ".") |  | ||||||
| 	data.Name = strings.Replace(filename, h.Config.Root, "", 1) |  | ||||||
| 	data.IsPost = false |  | ||||||
| 	data.BaseURL = h.Config.BaseURL |  | ||||||
| 	data.Mode = sanitizeMode(data.Mode) |  | ||||||
| 
 |  | ||||||
| 	var parserPage parser.Page |  | ||||||
| 
 |  | ||||||
| 	// Handle the content depending on the file extension
 |  | ||||||
| 	switch data.Mode { |  | ||||||
| 	case "markdown", "asciidoc", "rst": |  | ||||||
| 		if hasFrontMatterRune(file) { |  | ||||||
| 			// Starts a new buffer and parses the file using Hugo's functions
 |  | ||||||
| 			buffer := bytes.NewBuffer(file) |  | ||||||
| 			parserPage, err = parser.ReadFrom(buffer) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return http.StatusInternalServerError, err |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if strings.Contains(string(parserPage.FrontMatter()), "date") { |  | ||||||
| 				data.IsPost = true |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// Parses the page content and the frontmatter
 |  | ||||||
| 			data.Content = strings.TrimSpace(string(parserPage.Content())) |  | ||||||
| 			data.FrontMatter, data.Name, err = Pretty(parserPage.FrontMatter()) |  | ||||||
| 			data.Class = "complete" |  | ||||||
| 		} else { |  | ||||||
| 			// The editor will handle only content
 |  | ||||||
| 			data.Class = "content-only" |  | ||||||
| 			data.Content = string(file) |  | ||||||
| 		} |  | ||||||
| 	case "json", "toml", "yaml": |  | ||||||
| 		// Defines the class and declares an error
 |  | ||||||
| 		data.Class = "frontmatter-only" |  | ||||||
| 
 |  | ||||||
| 		// Checks if the file already has the frontmatter rune and parses it
 |  | ||||||
| 		if hasFrontMatterRune(file) { |  | ||||||
| 			data.FrontMatter, _, err = Pretty(file) |  | ||||||
| 		} else { |  | ||||||
| 			data.FrontMatter, _, err = Pretty(appendFrontMatterRune(file, data.Mode)) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Check if there were any errors
 |  | ||||||
| 		if err != nil { |  | ||||||
| 			return http.StatusInternalServerError, err |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		// The editor will handle only content
 |  | ||||||
| 		data.Class = "content-only" |  | ||||||
| 		data.Content = string(file) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Create the functions map, then the template, check for erros and
 |  | ||||||
| 	// execute the template if there aren't errors
 |  | ||||||
| 	functions := template.FuncMap{ |  | ||||||
| 		"SplitCapitalize": SplitCapitalize, |  | ||||||
| 		"Defined":         Defined, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var code int |  | ||||||
| 
 |  | ||||||
| 	page := &filemanager.Page{ |  | ||||||
| 		Info: &filemanager.PageInfo{ |  | ||||||
| 			IsDir:  false, |  | ||||||
| 			Config: &h.FileManager.Configs[0], |  | ||||||
| 			Name:   data.Name, |  | ||||||
| 			Data:   data, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	templates := []string{"options", "editor"} |  | ||||||
| 	for _, t := range templates { |  | ||||||
| 		code, err = page.AddTemplate(t, Asset, functions) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return code, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	templates = []string{"actions", "base"} |  | ||||||
| 	for _, t := range templates { |  | ||||||
| 		code, err = page.AddTemplate(t, filemanager.Asset, nil) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return code, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	code, err = page.PrintAsHTML(w) |  | ||||||
| 	fmt.Println(err) |  | ||||||
| 	return code, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func hasFrontMatterRune(file []byte) bool { |  | ||||||
| 	return strings.HasPrefix(string(file), "---") || |  | ||||||
| 		strings.HasPrefix(string(file), "+++") || |  | ||||||
| 		strings.HasPrefix(string(file), "{") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func appendFrontMatterRune(frontmatter []byte, language string) []byte { |  | ||||||
| 	switch language { |  | ||||||
| 	case "yaml": |  | ||||||
| 		return []byte("---\n" + string(frontmatter) + "\n---") |  | ||||||
| 	case "toml": |  | ||||||
| 		return []byte("+++\n" + string(frontmatter) + "\n+++") |  | ||||||
| 	case "json": |  | ||||||
| 		return frontmatter |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return frontmatter |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func sanitizeMode(extension string) string { |  | ||||||
| 	switch extension { |  | ||||||
| 	case "md", "markdown", "mdown", "mmark": |  | ||||||
| 		return "markdown" |  | ||||||
| 	case "asciidoc", "adoc", "ad": |  | ||||||
| 		return "asciidoc" |  | ||||||
| 	case "rst": |  | ||||||
| 		return "rst" |  | ||||||
| 	case "html", "htm": |  | ||||||
| 		return "html" |  | ||||||
| 	case "js": |  | ||||||
| 		return "javascript" |  | ||||||
| 	default: |  | ||||||
| 		return extension |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										109
									
								
								hugo.go
								
								
								
								
							
							
						
						
									
										109
									
								
								hugo.go
								
								
								
								
							|  | @ -7,6 +7,7 @@ | ||||||
| package hugo | package hugo | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"log" | ||||||
| 	"mime" | 	"mime" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
|  | @ -14,120 +15,54 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/hacdias/caddy-filemanager" | 	"github.com/hacdias/caddy-filemanager" | ||||||
|  | 	"github.com/hacdias/caddy-filemanager/utils/variables" | ||||||
|  | 	"github.com/hacdias/caddy-hugo/utils/commands" | ||||||
| 	"github.com/mholt/caddy/caddyhttp/httpserver" | 	"github.com/mholt/caddy/caddyhttp/httpserver" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // AssetsURL is the base url of the assets
 | // Hugo is hugo
 | ||||||
| const AssetsURL = "/_hugointernal" |  | ||||||
| 
 |  | ||||||
| // Hugo contais the next middleware to be run and the configuration
 |  | ||||||
| // of the current one.
 |  | ||||||
| type Hugo struct { | type Hugo struct { | ||||||
| 	FileManager *filemanager.FileManager |  | ||||||
| 	Next        httpserver.Handler | 	Next        httpserver.Handler | ||||||
| 	Config      *Config | 	Config      *Config | ||||||
|  | 	FileManager *filemanager.FileManager | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ServeHTTP is the main function of the whole plugin that routes every single
 |  | ||||||
| // request to its function.
 |  | ||||||
| func (h Hugo) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { | func (h Hugo) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { | ||||||
| 	// Check if the current request if for this plugin
 |  | ||||||
| 	if httpserver.Path(r.URL.Path).Matches(h.Config.BaseURL) { | 	if httpserver.Path(r.URL.Path).Matches(h.Config.BaseURL) { | ||||||
| 		// Check if we are asking for the assets
 |  | ||||||
| 		if httpserver.Path(r.URL.Path).Matches(h.Config.BaseURL + AssetsURL) { | 		if httpserver.Path(r.URL.Path).Matches(h.Config.BaseURL + AssetsURL) { | ||||||
| 			return h.ServeAssets(w, r) |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		// If the url matches exactly with /{admin}/settings/, redirect
 |  | ||||||
| 		// to the page of the configuration file
 |  | ||||||
| 		if r.URL.Path == h.Config.BaseURL+"/settings/" { |  | ||||||
| 			var frontmatter string |  | ||||||
| 
 |  | ||||||
| 			if _, err := os.Stat(h.Config.Root + "config.yaml"); err == nil { |  | ||||||
| 				frontmatter = "yaml" |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if _, err := os.Stat(h.Config.Root + "config.json"); err == nil { |  | ||||||
| 				frontmatter = "json" |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if _, err := os.Stat(h.Config.Root + "config.toml"); err == nil { |  | ||||||
| 				frontmatter = "toml" |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			http.Redirect(w, r, h.Config.BaseURL+"/config."+frontmatter, http.StatusTemporaryRedirect) |  | ||||||
| 			return 0, nil |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if strings.HasPrefix(r.URL.Path, h.Config.BaseURL+"/api/git/") && r.Method == http.MethodPost { |  | ||||||
| 			//return HandleGit(w, r, h.Config)
 |  | ||||||
| 			return 0, nil |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if h.ShouldHandle(r) { |  | ||||||
| 			filename := strings.Replace(r.URL.Path, h.Config.BaseURL, h.Config.Root, 1) |  | ||||||
| 			switch r.Method { |  | ||||||
| 			case http.MethodGet: |  | ||||||
| 				return h.GET(w, r, filename) |  | ||||||
| 			case http.MethodPost: |  | ||||||
| 				return h.POST(w, r, filename) |  | ||||||
| 			default: |  | ||||||
| 				return h.FileManager.ServeHTTP(w, r) |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return h.FileManager.ServeHTTP(w, r) | 		return h.FileManager.ServeHTTP(w, r) | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	return h.Next.ServeHTTP(w, r) | 	return h.Next.ServeHTTP(w, r) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var extensions = []string{ | // RunHugo is used to run the static website generator
 | ||||||
| 	"md", "markdown", "mdown", "mmark", | func RunHugo(c *Config, force bool) { | ||||||
| 	"asciidoc", "adoc", "ad", | 	os.RemoveAll(c.Root + "public") | ||||||
| 	"rst", |  | ||||||
| 	"html", "htm", |  | ||||||
| 	"js", |  | ||||||
| 	"toml", "yaml", "json", |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // ShouldHandle checks if this extension should be handled by this plugin
 | 	// Prevent running if watching is enabled
 | ||||||
| func (h Hugo) ShouldHandle(r *http.Request) bool { | 	if b, pos := variables.StringInSlice("--watch", c.Args); b && !force { | ||||||
| 	// Checks if the method is get or post
 | 		if len(c.Args) > pos && c.Args[pos+1] != "false" { | ||||||
| 	if r.Method != http.MethodGet && r.Method != http.MethodPost { | 			return | ||||||
| 		return false | 		} | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	// Check if this request is for FileManager assets
 | 		if len(c.Args) == pos+1 { | ||||||
| 	if httpserver.Path(r.URL.Path).Matches(h.Config.BaseURL + filemanager.AssetsURL) { | 			return | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// If this request requires a raw file or a download, return the FileManager
 |  | ||||||
| 	query := r.URL.Query() |  | ||||||
| 	if val, ok := query["raw"]; ok && val[0] == "true" { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if val, ok := query["download"]; ok && val[0] == "true" { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Check by file extension
 |  | ||||||
| 	extension := strings.TrimPrefix(filepath.Ext(r.URL.Path), ".") |  | ||||||
| 
 |  | ||||||
| 	for _, ext := range extensions { |  | ||||||
| 		if ext == extension { |  | ||||||
| 			return true |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return false | 	if err := commands.Run(c.Hugo, c.Args, c.Root); err != nil { | ||||||
|  | 		log.Panic(err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ServeAssets provides the needed assets for the front-end
 | // serveAssets provides the needed assets for the front-end
 | ||||||
| func (h Hugo) ServeAssets(w http.ResponseWriter, r *http.Request) (int, error) { | func serveAssets(w http.ResponseWriter, r *http.Request, c *Config) (int, error) { | ||||||
| 	// gets the filename to be used with Assets function
 | 	// gets the filename to be used with Assets function
 | ||||||
| 	filename := strings.Replace(r.URL.Path, h.Config.BaseURL+AssetsURL, "public", 1) | 	filename := strings.Replace(r.URL.Path, c.BaseURL+AssetsURL, "public", 1) | ||||||
| 	file, err := Asset(filename) | 	file, err := Asset(filename) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return http.StatusNotFound, nil | 		return http.StatusNotFound, nil | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ import ( | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/hacdias/caddy-hugo/utils/files" | ||||||
| 	"github.com/mitchellh/go-homedir" | 	"github.com/mitchellh/go-homedir" | ||||||
| 	"github.com/pivotal-golang/archiver/extractor" | 	"github.com/pivotal-golang/archiver/extractor" | ||||||
| ) | ) | ||||||
|  | @ -90,7 +91,7 @@ func GetPath() string { | ||||||
| 
 | 
 | ||||||
| 	// Copy the file
 | 	// Copy the file
 | ||||||
| 	fmt.Print("Moving Hugo executable... ") | 	fmt.Print("Moving Hugo executable... ") | ||||||
| 	err = CopyFile(exetorename, hugo) | 	err = files.CopyFile(exetorename, hugo) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		fmt.Println(err) | 		fmt.Println(err) | ||||||
| 		os.Exit(-1) | 		os.Exit(-1) | ||||||
|  | @ -213,27 +214,3 @@ func checkSHA256() { | ||||||
| 
 | 
 | ||||||
| 	fmt.Println("checked!") | 	fmt.Println("checked!") | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // CopyFile is used to copy a file
 |  | ||||||
| func CopyFile(old, new string) error { |  | ||||||
| 	// Open the file and create a new one
 |  | ||||||
| 	r, err := os.Open(old) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer r.Close() |  | ||||||
| 
 |  | ||||||
| 	w, err := os.Create(new) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer w.Close() |  | ||||||
| 
 |  | ||||||
| 	// Copy the content
 |  | ||||||
| 	_, err = io.Copy(w, r) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
							
								
								
									
										200
									
								
								post.go
								
								
								
								
							
							
						
						
									
										200
									
								
								post.go
								
								
								
								
							|  | @ -1,200 +0,0 @@ | ||||||
| package hugo |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"net/http" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"github.com/robfig/cron" |  | ||||||
| 	"github.com/spf13/cast" |  | ||||||
| 	"github.com/spf13/hugo/parser" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type info struct { |  | ||||||
| 	ContentType string |  | ||||||
| 	Schedule    bool |  | ||||||
| 	Regenerate  bool |  | ||||||
| 	Content     map[string]interface{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type response struct { |  | ||||||
| 	Message string `json:"message"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // POST handles the POST method on editor page
 |  | ||||||
| func (h Hugo) POST(w http.ResponseWriter, r *http.Request, filename string) (int, error) { |  | ||||||
| 	var data info |  | ||||||
| 
 |  | ||||||
| 	// Get the JSON information sent using a buffer
 |  | ||||||
| 	rawBuffer := new(bytes.Buffer) |  | ||||||
| 	rawBuffer.ReadFrom(r.Body) |  | ||||||
| 	err := json.Unmarshal(rawBuffer.Bytes(), &data) |  | ||||||
| 
 |  | ||||||
| 	if err != nil { |  | ||||||
| 		return RespondJSON(w, &response{"Error decrypting json."}, http.StatusInternalServerError, err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Initializes the file content to write
 |  | ||||||
| 	var file []byte |  | ||||||
| 	var code int |  | ||||||
| 
 |  | ||||||
| 	switch data.ContentType { |  | ||||||
| 	case "frontmatter-only": |  | ||||||
| 		file, code, err = parseFrontMatterOnlyFile(data, filename) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return RespondJSON(w, &response{err.Error()}, code, err) |  | ||||||
| 		} |  | ||||||
| 	case "content-only": |  | ||||||
| 		// The main content of the file
 |  | ||||||
| 		mainContent := data.Content["content"].(string) |  | ||||||
| 		mainContent = strings.TrimSpace(mainContent) |  | ||||||
| 
 |  | ||||||
| 		file = []byte(mainContent) |  | ||||||
| 	case "complete": |  | ||||||
| 		file, code, err = parseCompleteFile(data, filename, h.Config) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return RespondJSON(w, &response{err.Error()}, code, err) |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		return RespondJSON(w, &response{"Invalid content type."}, http.StatusBadRequest, nil) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Write the file
 |  | ||||||
| 	err = ioutil.WriteFile(filename, file, 0666) |  | ||||||
| 
 |  | ||||||
| 	if err != nil { |  | ||||||
| 		return RespondJSON(w, &response{err.Error()}, http.StatusInternalServerError, err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if data.Regenerate { |  | ||||||
| 		go RunHugo(h.Config, false) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return RespondJSON(w, nil, http.StatusOK, nil) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parseFrontMatterOnlyFile(data info, filename string) ([]byte, int, error) { |  | ||||||
| 	frontmatter := strings.TrimPrefix(filepath.Ext(filename), ".") |  | ||||||
| 	var mark rune |  | ||||||
| 
 |  | ||||||
| 	switch frontmatter { |  | ||||||
| 	case "toml": |  | ||||||
| 		mark = rune('+') |  | ||||||
| 	case "json": |  | ||||||
| 		mark = rune('{') |  | ||||||
| 	case "yaml": |  | ||||||
| 		mark = rune('-') |  | ||||||
| 	default: |  | ||||||
| 		return []byte{}, http.StatusBadRequest, errors.New("Can't define the frontmatter.") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	f, err := parser.InterfaceToFrontMatter(data.Content, mark) |  | ||||||
| 	fString := string(f) |  | ||||||
| 
 |  | ||||||
| 	// If it's toml or yaml, strip frontmatter identifier
 |  | ||||||
| 	if frontmatter == "toml" { |  | ||||||
| 		fString = strings.TrimSuffix(fString, "+++\n") |  | ||||||
| 		fString = strings.TrimPrefix(fString, "+++\n") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if frontmatter == "yaml" { |  | ||||||
| 		fString = strings.TrimSuffix(fString, "---\n") |  | ||||||
| 		fString = strings.TrimPrefix(fString, "---\n") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	f = []byte(fString) |  | ||||||
| 
 |  | ||||||
| 	if err != nil { |  | ||||||
| 		return []byte{}, http.StatusInternalServerError, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return f, http.StatusOK, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parseCompleteFile(data info, filename string, c *Config) ([]byte, int, error) { |  | ||||||
| 	// The main content of the file
 |  | ||||||
| 	mainContent := data.Content["content"].(string) |  | ||||||
| 	mainContent = "\n\n" + strings.TrimSpace(mainContent) + "\n" |  | ||||||
| 
 |  | ||||||
| 	// Removes the main content from the rest of the frontmatter
 |  | ||||||
| 	delete(data.Content, "content") |  | ||||||
| 
 |  | ||||||
| 	if _, ok := data.Content["date"]; ok { |  | ||||||
| 		data.Content["date"] = data.Content["date"].(string) + ":00" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Schedule the post
 |  | ||||||
| 	if data.Schedule { |  | ||||||
| 		t := cast.ToTime(data.Content["date"]) |  | ||||||
| 
 |  | ||||||
| 		scheduler := cron.New() |  | ||||||
| 		scheduler.AddFunc(t.In(time.Now().Location()).Format("05 04 15 02 01 *"), func() { |  | ||||||
| 			// Set draft to false
 |  | ||||||
| 			data.Content["draft"] = false |  | ||||||
| 
 |  | ||||||
| 			// Converts the frontmatter in JSON
 |  | ||||||
| 			jsonFrontmatter, err := json.Marshal(data.Content) |  | ||||||
| 
 |  | ||||||
| 			if err != nil { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// Indents the json
 |  | ||||||
| 			frontMatterBuffer := new(bytes.Buffer) |  | ||||||
| 			json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ") |  | ||||||
| 
 |  | ||||||
| 			// Generates the final file
 |  | ||||||
| 			f := new(bytes.Buffer) |  | ||||||
| 			f.Write(frontMatterBuffer.Bytes()) |  | ||||||
| 			f.Write([]byte(mainContent)) |  | ||||||
| 			file := f.Bytes() |  | ||||||
| 
 |  | ||||||
| 			// Write the file
 |  | ||||||
| 			if err = ioutil.WriteFile(filename, file, 0666); err != nil { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			go RunHugo(c, false) |  | ||||||
| 		}) |  | ||||||
| 		scheduler.Start() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Converts the frontmatter in JSON
 |  | ||||||
| 	jsonFrontmatter, err := json.Marshal(data.Content) |  | ||||||
| 
 |  | ||||||
| 	if err != nil { |  | ||||||
| 		return []byte{}, http.StatusInternalServerError, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Indents the json
 |  | ||||||
| 	frontMatterBuffer := new(bytes.Buffer) |  | ||||||
| 	json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ") |  | ||||||
| 
 |  | ||||||
| 	// Generates the final file
 |  | ||||||
| 	f := new(bytes.Buffer) |  | ||||||
| 	f.Write(frontMatterBuffer.Bytes()) |  | ||||||
| 	f.Write([]byte(mainContent)) |  | ||||||
| 	return f.Bytes(), http.StatusOK, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func RespondJSON(w http.ResponseWriter, message interface{}, code int, err error) (int, error) { |  | ||||||
| 	if message == nil { |  | ||||||
| 		message = map[string]string{} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	msg, msgErr := json.Marshal(message) |  | ||||||
| 
 |  | ||||||
| 	if msgErr != nil { |  | ||||||
| 		return 500, msgErr |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	w.Header().Set("Content-Type", "application/json") |  | ||||||
| 	w.WriteHeader(code) |  | ||||||
| 	w.Write(msg) |  | ||||||
| 	return 0, err |  | ||||||
| } |  | ||||||
							
								
								
									
										57
									
								
								setup.go
								
								
								
								
							
							
						
						
									
										57
									
								
								setup.go
								
								
								
								
							|  | @ -5,15 +5,18 @@ import ( | ||||||
| 	"log" | 	"log" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/hacdias/caddy-filemanager" | 	"github.com/hacdias/caddy-filemanager" | ||||||
|  | 	"github.com/hacdias/caddy-filemanager/config" | ||||||
| 	"github.com/hacdias/caddy-hugo/installer" | 	"github.com/hacdias/caddy-hugo/installer" | ||||||
|  | 	"github.com/hacdias/caddy-hugo/utils/commands" | ||||||
| 	"github.com/mholt/caddy" | 	"github.com/mholt/caddy" | ||||||
| 	"github.com/mholt/caddy/caddyhttp/httpserver" | 	"github.com/mholt/caddy/caddyhttp/httpserver" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | const AssetsURL = "/_hugointernal" | ||||||
|  | 
 | ||||||
| func init() { | func init() { | ||||||
| 	caddy.RegisterPlugin("hugo", caddy.Plugin{ | 	caddy.RegisterPlugin("hugo", caddy.Plugin{ | ||||||
| 		ServerType: "http", | 		ServerType: "http", | ||||||
|  | @ -25,7 +28,7 @@ func init() { | ||||||
| // middleware thing.
 | // middleware thing.
 | ||||||
| func setup(c *caddy.Controller) error { | func setup(c *caddy.Controller) error { | ||||||
| 	cnf := httpserver.GetConfig(c) | 	cnf := httpserver.GetConfig(c) | ||||||
| 	conf, _ := ParseHugo(c, cnf.Root) | 	conf, _ := parse(c, cnf.Root) | ||||||
| 
 | 
 | ||||||
| 	// Checks if there is an Hugo website in the path that is provided.
 | 	// Checks if there is an Hugo website in the path that is provided.
 | ||||||
| 	// If not, a new website will be created.
 | 	// If not, a new website will be created.
 | ||||||
|  | @ -44,7 +47,7 @@ func setup(c *caddy.Controller) error { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if create { | 	if create { | ||||||
| 		err := Run(conf.Hugo, []string{"new", "site", conf.Root, "--force"}, ".") | 		err := commands.Run(conf.Hugo, []string{"new", "site", conf.Root, "--force"}, ".") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Panic(err) | 			log.Panic(err) | ||||||
| 		} | 		} | ||||||
|  | @ -59,8 +62,8 @@ func setup(c *caddy.Controller) error { | ||||||
| 			Config: conf, | 			Config: conf, | ||||||
| 			FileManager: &filemanager.FileManager{ | 			FileManager: &filemanager.FileManager{ | ||||||
| 				Next: next, | 				Next: next, | ||||||
| 				Configs: []filemanager.Config{ | 				Configs: []config.Config{ | ||||||
| 					filemanager.Config{ | 					config.Config{ | ||||||
| 						HugoEnabled: true, | 						HugoEnabled: true, | ||||||
| 						PathScope:   conf.Root, | 						PathScope:   conf.Root, | ||||||
| 						Root:        http.Dir(conf.Root), | 						Root:        http.Dir(conf.Root), | ||||||
|  | @ -76,35 +79,27 @@ func setup(c *caddy.Controller) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Config contains the configuration of hugo plugin
 | // Config is a configuration for managing a particular hugo website.
 | ||||||
| type Config struct { | type Config struct { | ||||||
| 	Args    []string // Hugo arguments
 | 	Public      string   // Public content path
 | ||||||
| 	Git     bool     // Is this site a git repository
 | 	Root        string   // Hugo files path
 | ||||||
| 	BaseURL string   // Admin URL to listen on
 | 	Hugo        string   // Hugo executable location
 | ||||||
| 	Hugo    string   // Hugo executable path
 | 	Styles      string   // Admin styles path
 | ||||||
| 	Root    string   // Hugo website path
 | 	Args        []string // Hugo arguments
 | ||||||
| 	Public  string   // Public content path
 | 	BaseURL     string   // BaseURL of admin interface
 | ||||||
| 	Styles  string   // Admin stylesheet
 | 	FileManager *filemanager.FileManager | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ParseHugo parses the configuration file
 | // Parse parses the configuration set by the user so it can be
 | ||||||
| func ParseHugo(c *caddy.Controller, root string) (*Config, error) { | // used by the middleware
 | ||||||
|  | func parse(c *caddy.Controller, root string) (*Config, error) { | ||||||
| 	conf := &Config{ | 	conf := &Config{ | ||||||
| 		Public:  strings.Replace(root, "./", "", -1), | 		Public:  strings.Replace(root, "./", "", -1), | ||||||
| 		BaseURL: "/admin", | 		BaseURL: "/admin", | ||||||
| 		Root:    "./", | 		Root:    "./", | ||||||
| 		Git:     false, |  | ||||||
| 		Hugo:    installer.GetPath(), |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	stlsbytes, err := Asset("public/css/styles.css") | 	conf.Hugo = installer.GetPath() | ||||||
| 
 |  | ||||||
| 	if err != nil { |  | ||||||
| 		return conf, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	conf.Styles = string(stlsbytes) |  | ||||||
| 
 |  | ||||||
| 	for c.Next() { | 	for c.Next() { | ||||||
| 		args := c.RemainingArgs() | 		args := c.RemainingArgs() | ||||||
| 
 | 
 | ||||||
|  | @ -121,19 +116,17 @@ func ParseHugo(c *caddy.Controller, root string) (*Config, error) { | ||||||
| 				if !c.NextArg() { | 				if !c.NextArg() { | ||||||
| 					return conf, c.ArgErr() | 					return conf, c.ArgErr() | ||||||
| 				} | 				} | ||||||
| 				stylesheet, err := ioutil.ReadFile(c.Val()) | 				tplBytes, err := ioutil.ReadFile(c.Val()) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					return conf, err | 					return conf, err | ||||||
| 				} | 				} | ||||||
| 				conf.Styles += string(stylesheet) | 				conf.Styles = string(tplBytes) | ||||||
| 			case "admin": | 			case "admin": | ||||||
| 				if !c.NextArg() { | 				if !c.NextArg() { | ||||||
| 					return nil, c.ArgErr() | 					return conf, c.ArgErr() | ||||||
| 				} | 				} | ||||||
| 				conf.BaseURL = c.Val() | 				conf.BaseURL = c.Val() | ||||||
| 				// Remove the beginning slash if it exists or not
 |  | ||||||
| 				conf.BaseURL = strings.TrimPrefix(conf.BaseURL, "/") | 				conf.BaseURL = strings.TrimPrefix(conf.BaseURL, "/") | ||||||
| 				// Add a beginning slash to make a
 |  | ||||||
| 				conf.BaseURL = "/" + conf.BaseURL | 				conf.BaseURL = "/" + conf.BaseURL | ||||||
| 			default: | 			default: | ||||||
| 				key := "--" + c.Val() | 				key := "--" + c.Val() | ||||||
|  | @ -148,9 +141,5 @@ func ParseHugo(c *caddy.Controller, root string) (*Config, error) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if _, err := os.Stat(filepath.Join(conf.Root, ".git")); err == nil { |  | ||||||
| 		conf.Git = true |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return conf, nil | 	return conf, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,15 @@ | ||||||
|  | package commands | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Run executes an external command
 | ||||||
|  | func Run(command string, args []string, path string) error { | ||||||
|  | 	cmd := exec.Command(command, args...) | ||||||
|  | 	cmd.Dir = path | ||||||
|  | 	cmd.Stdout = os.Stderr | ||||||
|  | 	cmd.Stderr = os.Stderr | ||||||
|  | 	return cmd.Run() | ||||||
|  | } | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | package files | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // CopyFile is used to copy a file
 | ||||||
|  | func CopyFile(old, new string) error { | ||||||
|  | 	// Open the file and create a new one
 | ||||||
|  | 	r, err := os.Open(old) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer r.Close() | ||||||
|  | 
 | ||||||
|  | 	w, err := os.Create(new) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer w.Close() | ||||||
|  | 
 | ||||||
|  | 	// Copy the content
 | ||||||
|  | 	_, err = io.Copy(w, r) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										95
									
								
								variables.go
								
								
								
								
							
							
						
						
									
										95
									
								
								variables.go
								
								
								
								
							|  | @ -1,95 +0,0 @@ | ||||||
| package hugo |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"log" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strings" |  | ||||||
| 	"unicode" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Defined checks if variable is defined in a struct
 |  | ||||||
| func Defined(data interface{}, field string) bool { |  | ||||||
| 	t := reflect.Indirect(reflect.ValueOf(data)).Type() |  | ||||||
| 
 |  | ||||||
| 	if t.Kind() != reflect.Struct { |  | ||||||
| 		log.Print("Non-struct type not allowed.") |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	_, b := t.FieldByName(field) |  | ||||||
| 	return b |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Dict allows to send more than one variable into a template
 |  | ||||||
| func Dict(values ...interface{}) (map[string]interface{}, error) { |  | ||||||
| 	if len(values)%2 != 0 { |  | ||||||
| 		return nil, errors.New("invalid dict call") |  | ||||||
| 	} |  | ||||||
| 	dict := make(map[string]interface{}, len(values)/2) |  | ||||||
| 	for i := 0; i < len(values); i += 2 { |  | ||||||
| 		key, ok := values[i].(string) |  | ||||||
| 		if !ok { |  | ||||||
| 			return nil, errors.New("dict keys must be strings") |  | ||||||
| 		} |  | ||||||
| 		dict[key] = values[i+1] |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return dict, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsMap checks if some variable is a map
 |  | ||||||
| func IsMap(sth interface{}) bool { |  | ||||||
| 	return reflect.ValueOf(sth).Kind() == reflect.Map |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsSlice checks if some variable is a slice
 |  | ||||||
| func IsSlice(sth interface{}) bool { |  | ||||||
| 	return reflect.ValueOf(sth).Kind() == reflect.Slice |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // StringInSlice checks if a slice contains a string
 |  | ||||||
| func StringInSlice(a string, list []string) (bool, int) { |  | ||||||
| 	for i, b := range list { |  | ||||||
| 		if b == a { |  | ||||||
| 			return true, i |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false, 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var splitCapitalizeExceptions = map[string]string{ |  | ||||||
| 	"youtube":    "YouTube", |  | ||||||
| 	"github":     "GitHub", |  | ||||||
| 	"googleplus": "Google Plus", |  | ||||||
| 	"linkedin":   "LinkedIn", |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // SplitCapitalize splits a string by its uppercase letters and capitalize the
 |  | ||||||
| // first letter of the string
 |  | ||||||
| func SplitCapitalize(name string) string { |  | ||||||
| 	if val, ok := splitCapitalizeExceptions[strings.ToLower(name)]; ok { |  | ||||||
| 		return val |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var words []string |  | ||||||
| 	l := 0 |  | ||||||
| 	for s := name; s != ""; s = s[l:] { |  | ||||||
| 		l = strings.IndexFunc(s[1:], unicode.IsUpper) + 1 |  | ||||||
| 		if l <= 0 { |  | ||||||
| 			l = len(s) |  | ||||||
| 		} |  | ||||||
| 		words = append(words, s[:l]) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	name = "" |  | ||||||
| 
 |  | ||||||
| 	for _, element := range words { |  | ||||||
| 		name += element + " " |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	name = strings.ToLower(name[:len(name)-1]) |  | ||||||
| 	name = strings.ToUpper(string(name[0])) + name[1:] |  | ||||||
| 
 |  | ||||||
| 	return name |  | ||||||
| } |  | ||||||
		Loading…
	
		Reference in New Issue