mirror of
https://github.com/filebrowser/filebrowser.git
synced 2024-06-07 23:00:43 +00:00
updates and restructure
This commit is contained in:
parent
b43d8e2465
commit
72c7abe469
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -1,37 +0,0 @@
|
||||
# hugo - a caddy plugin
|
||||
|
||||
[![Build](https://img.shields.io/travis/hacdias/caddy-hugo.svg?style=flat-square)](https://travis-ci.org/hacdias/caddy-hugo)
|
||||
[![community](https://img.shields.io/badge/community-forum-ff69b4.svg?style=flat-square)](https://forum.caddyserver.com)
|
||||
[![Documentation](https://img.shields.io/badge/caddy-doc-F06292.svg?style=flat-square)](https://caddyserver.com/docs/hugo)
|
||||
[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/hacdias/caddy-hugo)
|
||||
|
||||
[Hugo](http://gohugo.io/) is an easy to use and fast command line static website generator, while [Caddy](http://caddyserver.com) is a lightweight, fast, general-purpose, cross-platform HTTP/2 web server with automatic HTTPS. This extension is able to bring a web interface to Caddy to manage Hugo generated websites. This plugin provides you an web interface to manage your websites made with Hugo.
|
||||
|
||||
**If you're not developer go to the [documentation](https://caddyserver.com/docs/hugo)**.
|
||||
|
||||
## Build from source
|
||||
|
||||
Requirements:
|
||||
|
||||
+ [Go 1.6 or higher][1]
|
||||
+ [caddydev][2]
|
||||
+ [go-bindata][3]
|
||||
+ [Node.js w/ npm][4] (optional)
|
||||
|
||||
Instructions:
|
||||
|
||||
1. ```go get github.com/hacdias/caddy-hugo```
|
||||
2. ```cd $GOPATH/github.com/hacdias/caddy-hugo```
|
||||
1. If you want to modify the CSS/JS:
|
||||
2. Change the third comment to ```//go:generate go-bindata -debug -pkg assets -o assets/assets.go templates/ assets/css/ assets/js/ assets/fonts/```
|
||||
3. ```npm install```
|
||||
4. ```grunt watch```
|
||||
3. ```go generate```
|
||||
4. ```cd $YOUR_WEBSITE_PATH```
|
||||
5. ```caddydev --source $GOPATH/github.com/hacdias/caddy-hugo hugo```
|
||||
6. Go to ```http://domain:port```
|
||||
|
||||
[1]: https://golang.org/dl/
|
||||
[2]: https://github.com/caddyserver/caddydev
|
||||
[3]: https://github.com/jteeuwen/go-bindata
|
||||
[4]: https://nodejs.org
|
@ -1,66 +0,0 @@
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HandleGit handles the POST method on GIT page which is only an API.
|
||||
func HandleGit(w http.ResponseWriter, r *http.Request, c *Config) (int, error) {
|
||||
response := &Response{
|
||||
Code: http.StatusOK,
|
||||
Err: nil,
|
||||
Content: "OK",
|
||||
}
|
||||
|
||||
// Check if git is installed on the computer
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
response.Code = http.StatusNotImplemented
|
||||
response.Content = "Git is not installed on your computer."
|
||||
return response.Send(w)
|
||||
}
|
||||
|
||||
// Get the JSON information sent using a buffer
|
||||
buff := new(bytes.Buffer)
|
||||
buff.ReadFrom(r.Body)
|
||||
|
||||
// Creates the raw file "map" using the JSON
|
||||
var info map[string]interface{}
|
||||
json.Unmarshal(buff.Bytes(), &info)
|
||||
|
||||
// Check if command was sent
|
||||
if _, ok := info["command"]; !ok {
|
||||
response.Code = http.StatusBadRequest
|
||||
response.Content = "Command not specified."
|
||||
return response.Send(w)
|
||||
}
|
||||
|
||||
command := info["command"].(string)
|
||||
args := strings.Split(command, " ")
|
||||
|
||||
if len(args) > 0 && args[0] == "git" {
|
||||
args = append(args[:0], args[1:]...)
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
response.Code = http.StatusBadRequest
|
||||
response.Content = "Command not specified."
|
||||
return response.Send(w)
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", args...)
|
||||
cmd.Dir = c.Path
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
response.Code = http.StatusInternalServerError
|
||||
response.Content = err.Error()
|
||||
return response.Send(w)
|
||||
}
|
||||
|
||||
response.Content = string(output)
|
||||
return response.Send(w)
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
// Package hugo makes the bridge between the static website generator Hugo
|
||||
// and the webserver Caddy, also providing an administrative user interface.
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hacdias/caddy-filemanager"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
// Hugo contais the next middleware to be run and the configuration
|
||||
// of the current one.
|
||||
type Hugo struct {
|
||||
FileManager *filemanager.FileManager
|
||||
Next httpserver.Handler
|
||||
Config *Config
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Check if the current request if for this plugin
|
||||
if httpserver.Path(r.URL.Path).Matches(h.Config.Admin) {
|
||||
// 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 h.FileManager.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
if val, ok := query["download"]; ok && val[0] == "true" {
|
||||
return h.FileManager.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// If the url matches exactly with /{admin}/settings/, redirect
|
||||
// to the page of the configuration file
|
||||
if r.URL.Path == h.Config.Admin+"/settings/" {
|
||||
var frontmatter string
|
||||
|
||||
if _, err := os.Stat(h.Config.Path + "config.yaml"); err == nil {
|
||||
frontmatter = "yaml"
|
||||
}
|
||||
|
||||
if _, err := os.Stat(h.Config.Path + "config.json"); err == nil {
|
||||
frontmatter = "json"
|
||||
}
|
||||
|
||||
if _, err := os.Stat(h.Config.Path + "config.toml"); err == nil {
|
||||
frontmatter = "toml"
|
||||
}
|
||||
|
||||
http.Redirect(w, r, h.Config.Admin+"/config."+frontmatter, http.StatusTemporaryRedirect)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
filename := strings.Replace(r.URL.Path, c.Admin+"/edit/", "", 1)
|
||||
filename = c.Path + filename
|
||||
|
||||
if strings.HasPrefix(r.URL.Path, h.Config.Admin+"/api/git/") && r.Method == http.MethodPost {
|
||||
return HandleGit(w, r, h.Config)
|
||||
}
|
||||
|
||||
if h.ShouldHandle(r.URL) {
|
||||
// return editor
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return h.FileManager.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
return h.Next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
var extensions = []string{
|
||||
"md", "markdown", "mdown", "mmark",
|
||||
"asciidoc", "adoc", "ad",
|
||||
"rst",
|
||||
"html", "htm",
|
||||
"js",
|
||||
"toml", "yaml", "json",
|
||||
}
|
||||
|
||||
// ShouldHandle checks if this extension should be handled by this plugin
|
||||
func (h Hugo) ShouldHandle(url *url.URL) bool {
|
||||
extension := strings.TrimPrefix(filepath.Ext(url.Path), ".")
|
||||
|
||||
for _, ext := range extensions {
|
||||
if ext == extension {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
119
_stuff/page.go
119
_stuff/page.go
@ -1,119 +0,0 @@
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Page contains the informations and functions needed to show the page
|
||||
type Page struct {
|
||||
Info *PageInfo
|
||||
}
|
||||
|
||||
// PageInfo contains the information of a page
|
||||
type PageInfo struct {
|
||||
Name string
|
||||
Path string
|
||||
IsDir bool
|
||||
Config *Config
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// BreadcrumbMap returns p.Path where every element is a map
|
||||
// of URLs and path segment names.
|
||||
func (p PageInfo) BreadcrumbMap() map[string]string {
|
||||
result := map[string]string{}
|
||||
|
||||
if len(p.Path) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
// skip trailing slash
|
||||
lpath := p.Path
|
||||
if lpath[len(lpath)-1] == '/' {
|
||||
lpath = lpath[:len(lpath)-1]
|
||||
}
|
||||
|
||||
parts := strings.Split(lpath, "/")
|
||||
for i, part := range parts {
|
||||
if i == 0 && part == "" {
|
||||
// Leading slash (root)
|
||||
result["/"] = "/"
|
||||
continue
|
||||
}
|
||||
result[strings.Join(parts[:i+1], "/")] = part
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// PreviousLink returns the path of the previous folder
|
||||
func (p PageInfo) PreviousLink() string {
|
||||
parts := strings.Split(strings.TrimSuffix(p.Path, "/"), "/")
|
||||
if len(parts) <= 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
if parts[len(parts)-2] == "" {
|
||||
if p.Config.BaseURL == "" {
|
||||
return "/"
|
||||
}
|
||||
return p.Config.BaseURL
|
||||
}
|
||||
|
||||
return parts[len(parts)-2]
|
||||
}
|
||||
|
||||
// PrintAsHTML formats the page in HTML and executes the template
|
||||
func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, error) {
|
||||
templates = append(templates, "actions", "base")
|
||||
var tpl *template.Template
|
||||
|
||||
// For each template, add it to the the tpl variable
|
||||
for i, t := range templates {
|
||||
// Get the template from the assets
|
||||
page, err := Asset("templates/" + t + ".tmpl")
|
||||
|
||||
// Check if there is some error. If so, the template doesn't exist
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
// If it's the first iteration, creates a new template and add the
|
||||
// functions map
|
||||
if i == 0 {
|
||||
tpl, err = template.New(t).Parse(string(page))
|
||||
} else {
|
||||
tpl, err = tpl.Parse(string(page))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
err := tpl.Execute(w, p.Info)
|
||||
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
// PrintAsJSON prints the current page infromation in JSON
|
||||
func (p Page) PrintAsJSON(w http.ResponseWriter) (int, error) {
|
||||
marsh, err := json.Marshal(p.Info.Data)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
return w.Write(marsh)
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Response conta
|
||||
type Response struct {
|
||||
Code int
|
||||
Err error
|
||||
Content string
|
||||
}
|
||||
|
||||
// Send used to send JSON responses to the web server
|
||||
func (r *Response) Send(w http.ResponseWriter) (int, error) {
|
||||
content := map[string]string{"message": r.Content}
|
||||
msg, msgErr := json.Marshal(content)
|
||||
|
||||
if msgErr != nil {
|
||||
return 500, msgErr
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(r.Code)
|
||||
w.Write(msg)
|
||||
return 0, r.Err
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hacdias/caddy-hugo/tools/commands"
|
||||
"github.com/hacdias/caddy-hugo/tools/variables"
|
||||
)
|
||||
|
||||
// Run is used to run the static website generator
|
||||
func Run(c *Config, force bool) {
|
||||
os.RemoveAll(c.Path + "public")
|
||||
|
||||
// Prevent running if watching is enabled
|
||||
if b, pos := variables.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 := commands.Run(c.Hugo, c.Args, c.Path); err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
145
_stuff/setup.go
145
_stuff/setup.go
@ -1,145 +0,0 @@
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hacdias/caddy-filemanager"
|
||||
"github.com/hacdias/caddy-hugo/tools/commands"
|
||||
"github.com/hacdias/caddy-hugo/tools/installer"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("hugo", caddy.Plugin{
|
||||
ServerType: "http",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
// Setup is the init function of Caddy plugins and it configures the whole
|
||||
// middleware thing.
|
||||
func setup(c *caddy.Controller) error {
|
||||
cnf := httpserver.GetConfig(c.Key)
|
||||
conf, _ := ParseHugo(c, cnf.Root)
|
||||
|
||||
// Checks if there is an Hugo website in the path that is provided.
|
||||
// If not, a new website will be created.
|
||||
create := true
|
||||
|
||||
if _, err := os.Stat(conf.Path + "config.yaml"); err == nil {
|
||||
create = false
|
||||
}
|
||||
|
||||
if _, err := os.Stat(conf.Path + "config.json"); err == nil {
|
||||
create = false
|
||||
}
|
||||
|
||||
if _, err := os.Stat(conf.Path + "config.toml"); err == nil {
|
||||
create = false
|
||||
}
|
||||
|
||||
if create {
|
||||
err := commands.Run(conf.Hugo, []string{"new", "site", conf.Path, "--force"}, ".")
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Generates the Hugo website for the first time the plugin is activated.
|
||||
go Run(conf, true)
|
||||
|
||||
mid := func(next httpserver.Handler) httpserver.Handler {
|
||||
return &Hugo{
|
||||
Next: next,
|
||||
FileManager: &filemanager.FileManager{
|
||||
Next: next,
|
||||
Configs: []filemanager.Config{
|
||||
filemanager.Config{
|
||||
PathScope: conf.Path,
|
||||
Root: http.Dir(conf.Path),
|
||||
BaseURL: conf.Admin,
|
||||
},
|
||||
},
|
||||
},
|
||||
Config: conf,
|
||||
}
|
||||
}
|
||||
|
||||
cnf.AddMiddleware(mid)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Config is the add-on configuration set on Caddyfile
|
||||
type Config struct {
|
||||
Public string // Public content path
|
||||
Path string // Hugo files path
|
||||
Styles string // Admin styles path
|
||||
Args []string // Hugo arguments
|
||||
Hugo string // Hugo executable path
|
||||
Admin string // Hugo admin URL
|
||||
Git bool // Is this site a git repository
|
||||
}
|
||||
|
||||
// ParseHugo parses the configuration file
|
||||
func ParseHugo(c *caddy.Controller, root string) (*Config, error) {
|
||||
conf := &Config{
|
||||
Public: strings.Replace(root, "./", "", -1),
|
||||
Admin: "/admin",
|
||||
Path: "./",
|
||||
Git: false,
|
||||
}
|
||||
|
||||
conf.Hugo = installer.GetPath()
|
||||
|
||||
for c.Next() {
|
||||
args := c.RemainingArgs()
|
||||
|
||||
switch len(args) {
|
||||
case 1:
|
||||
conf.Path = args[0]
|
||||
conf.Path = strings.TrimSuffix(conf.Path, "/")
|
||||
conf.Path += "/"
|
||||
}
|
||||
|
||||
for c.NextBlock() {
|
||||
switch c.Val() {
|
||||
case "styles":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
conf.Styles = c.Val()
|
||||
// Remove the beginning slash if it exists or not
|
||||
conf.Styles = strings.TrimPrefix(conf.Styles, "/")
|
||||
// Add a beginning slash to make a
|
||||
conf.Styles = "/" + conf.Styles
|
||||
case "admin":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
conf.Admin = c.Val()
|
||||
conf.Admin = strings.TrimPrefix(conf.Admin, "/")
|
||||
conf.Admin = "/" + conf.Admin
|
||||
default:
|
||||
key := "--" + c.Val()
|
||||
value := "true"
|
||||
|
||||
if c.NextArg() {
|
||||
value = c.Val()
|
||||
}
|
||||
|
||||
conf.Args = append(conf.Args, key+"="+value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(conf.Path, ".git")); err == nil {
|
||||
conf.Git = true
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package installer
|
||||
|
||||
var (
|
||||
sha256Hash = map[string]string{
|
||||
"hugo_0.16_darwin-arm32.tgz": "683d5d4b4e0ac03a183ca5eb9019981ba696569445c7d6d1efc7e6706bd273a5",
|
||||
"hugo_0.16_dragonfly-64bit.tgz": "63a3ee9a36d4d2166c77b96bb8bf39b2239affe118e44a83b3d0a44374a8921d",
|
||||
"hugo_0.16_freebsd-32bit.tgz": "ea3f84900feeeb9d89573dea49a4349753116e70de561eeec4858f7ffc74f8f9",
|
||||
"hugo_0.16_freebsd-64bit.tgz": "8d9320bb660090a77a4f922ca30b1582593bc6d87c3fd8bd6f5ecbe49cf1d2f2",
|
||||
"hugo_0.16_freebsd-arm32.tgz": "b4c21296e01ea68709ac50d7eb1d314b738f1c8408ff2be223d06ae76604dbea",
|
||||
"hugo_0.16_linux-32bit.tgz": "aed82d156f01a4562c39bd1af41aa81699009140da965e0369c370ba874725c9",
|
||||
"hugo_0.16_linux-64bit.tgz": "13e299dc45bea4fad5bdf8c2640305a5926e2acd02c3aa03b7864403e513920e",
|
||||
"hugo_0.16_linux-arm32.tgz": "bc836def127d93e2457da9994f9c09b0100523e46d61074cd724ef092b11714f",
|
||||
"hugo_0.16_linux-arm64.tgz": "d04486918f43f89f1e0359eebedd8a05d96f7ca40f93e7fd8d7c3f2dac115a8d",
|
||||
"hugo_0.16_netbsd-32bit.tgz": "cb578eebec5b6364b0afd5bb208d94317acab0a3e033b81f04b1511af0669b63",
|
||||
"hugo_0.16_netbsd-64bit.tgz": "d3c766d9800d7fdd268ffd2f28b7af451f13a4de63901bfdae2ee5c96528b8cc",
|
||||
"hugo_0.16_netbsd-arm32.tgz": "51162b2637e71b786582af715a44b778f62bdc62a9a354ccc4a7c8384afe194c",
|
||||
"hugo_0.16_openbsd-32bit.tgz": "2d1e112a7346850897ea77da868c0d987ef90efb7f49c917659437a5a67f89f8",
|
||||
"hugo_0.16_openbsd-64bit.tgz": "7b33ff2565df5a6253c3e4308813d947e34af04c633fb4e01cac83751066e16e",
|
||||
"hugo_0.16_osx-32bit.tgz": "6155dda548bbd1e26c26a4a00472e4c0e55fad9fcd46991ce90987385bd5fd0a",
|
||||
"hugo_0.16_osx-64bit.tgz": "b0cba8f6996946ef34a664184d6461567d79fc2a3e793145d34379902eda0ad9",
|
||||
"hugo_0.16_solaris-64bit.tgz": "af9557403af5e16eb7faf965c04540417a70699efbbbc4e0a7ae4c4703ad1ae8",
|
||||
"hugo_0.16_windows-32bit.zip": "1c72d06843fe02cb62348660d87a523c885ed684a683271fc8762e7234c4210b",
|
||||
"hugo_0.16_windows-64bit.zip": "a3fda0bd30592e4eb3bdde85c8a8ce23a7433073536466d16fd0e97bf7794067",
|
||||
}
|
||||
)
|
@ -1,216 +0,0 @@
|
||||
package installer
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
||||
"github.com/hacdias/caddy-hugo/tools/files"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/pivotal-golang/archiver/extractor"
|
||||
)
|
||||
|
||||
const (
|
||||
version = "0.16"
|
||||
baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/"
|
||||
)
|
||||
|
||||
var caddy, bin, temp, hugo, tempfile, zipname, exename string
|
||||
|
||||
// GetPath retrives the Hugo path for the user or install it if it's not found
|
||||
func GetPath() string {
|
||||
initializeVariables()
|
||||
|
||||
var err error
|
||||
|
||||
// Check if Hugo is already on $PATH
|
||||
if hugo, err = exec.LookPath("hugo"); err == nil {
|
||||
if checkVersion() {
|
||||
return hugo
|
||||
}
|
||||
}
|
||||
|
||||
// Check if Hugo is on $HOME/.caddy/bin
|
||||
if _, err = os.Stat(hugo); err == nil {
|
||||
if checkVersion() {
|
||||
return hugo
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Unable to find Hugo on your computer.")
|
||||
|
||||
// Create the neccessary folders
|
||||
os.MkdirAll(caddy, 0774)
|
||||
os.Mkdir(bin, 0774)
|
||||
|
||||
if temp, err = ioutil.TempDir("", "caddy-hugo"); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
downloadHugo()
|
||||
checkSHA256()
|
||||
|
||||
fmt.Print("Unzipping... ")
|
||||
|
||||
// Unzip or Ungzip the file
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "windows":
|
||||
zp := extractor.NewZip()
|
||||
err = zp.Extract(tempfile, temp)
|
||||
default:
|
||||
gz := extractor.NewTgz()
|
||||
err = gz.Extract(tempfile, temp)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
fmt.Println("done.")
|
||||
|
||||
var exetorename string
|
||||
|
||||
err = filepath.Walk(temp, func(path string, f os.FileInfo, err error) error {
|
||||
if f.Name() == exename {
|
||||
exetorename = path
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
// Copy the file
|
||||
fmt.Print("Moving Hugo executable... ")
|
||||
err = files.CopyFile(exetorename, hugo)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
err = os.Chmod(hugo, 0755)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
fmt.Println("done.")
|
||||
fmt.Println("Hugo installed at " + hugo)
|
||||
defer os.RemoveAll(temp)
|
||||
return hugo
|
||||
}
|
||||
|
||||
func initializeVariables() {
|
||||
var arch string
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
arch = "64bit"
|
||||
case "386":
|
||||
arch = "32bit"
|
||||
case "arm":
|
||||
arch = "arm32"
|
||||
default:
|
||||
arch = runtime.GOARCH
|
||||
}
|
||||
|
||||
var ops = runtime.GOOS
|
||||
if runtime.GOOS == "darwin" && runtime.GOARCH != "arm" {
|
||||
ops = "osx"
|
||||
}
|
||||
|
||||
exename = "hugo"
|
||||
zipname = "hugo_" + version + "_" + ops + "-" + arch
|
||||
|
||||
homedir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
caddy = filepath.Join(homedir, ".caddy")
|
||||
bin = filepath.Join(caddy, "bin")
|
||||
hugo = filepath.Join(bin, "hugo")
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
zipname += ".zip"
|
||||
exename += ".exe"
|
||||
hugo += ".exe"
|
||||
default:
|
||||
zipname += ".tgz"
|
||||
}
|
||||
}
|
||||
|
||||
func checkVersion() bool {
|
||||
out, _ := exec.Command("hugo", "version").Output()
|
||||
|
||||
r := regexp.MustCompile(`v\d\.\d{2}`)
|
||||
v := r.FindStringSubmatch(string(out))[0]
|
||||
v = v[1:len(v)]
|
||||
|
||||
return (v == version)
|
||||
}
|
||||
|
||||
func downloadHugo() {
|
||||
tempfile = filepath.Join(temp, zipname)
|
||||
|
||||
fmt.Print("Downloading Hugo from GitHub releases... ")
|
||||
|
||||
// Create the file
|
||||
out, err := os.Create(tempfile)
|
||||
out.Chmod(0774)
|
||||
if err != nil {
|
||||
defer os.RemoveAll(temp)
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// Get the data
|
||||
resp, err := http.Get(baseurl + zipname)
|
||||
if err != nil {
|
||||
fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.")
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Writer the body to file
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
fmt.Println("downloaded.")
|
||||
}
|
||||
|
||||
func checkSHA256() {
|
||||
fmt.Print("Checking SHA256...")
|
||||
|
||||
hasher := sha256.New()
|
||||
f, err := os.Open(tempfile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := io.Copy(hasher, f); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[zipname] {
|
||||
fmt.Println("can't verify SHA256.")
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
fmt.Println("checked!")
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Code int
|
||||
Err error
|
||||
Content interface{}
|
||||
}
|
||||
|
||||
// RespondJSON used to send JSON responses to the web server
|
||||
func RespondJSON(w http.ResponseWriter, r *Response) (int, error) {
|
||||
if r.Content == nil {
|
||||
r.Content = map[string]string{}
|
||||
}
|
||||
|
||||
msg, msgErr := json.Marshal(r.Content)
|
||||
|
||||
if msgErr != nil {
|
||||
return 500, msgErr
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(r.Code)
|
||||
w.Write(msg)
|
||||
return 0, r.Err
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseURLComponents parses the components of an URL creating an array
|
||||
func ParseURLComponents(r *http.Request) []string {
|
||||
//The URL that the user queried.
|
||||
path := r.URL.Path
|
||||
path = strings.TrimSpace(path)
|
||||
//Cut off the leading and trailing forward slashes, if they exist.
|
||||
//This cuts off the leading forward slash.
|
||||
if strings.HasPrefix(path, "/") {
|
||||
path = path[1:]
|
||||
}
|
||||
//This cuts off the trailing forward slash.
|
||||
if strings.HasSuffix(path, "/") {
|
||||
cutOffLastCharLen := len(path) - 1
|
||||
path = path[:cutOffLastCharLen]
|
||||
}
|
||||
//We need to isolate the individual components of the path.
|
||||
components := strings.Split(path, "/")
|
||||
return components
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/hacdias/caddy-hugo/routes/assets"
|
||||
)
|
||||
|
||||
// CanBeEdited checks if the extension of a file is supported by the editor
|
||||
func CanBeEdited(filename string) bool {
|
||||
extensions := [...]string{
|
||||
"md", "markdown", "mdown", "mmark",
|
||||
"asciidoc", "adoc", "ad",
|
||||
"rst",
|
||||
".json", ".toml", ".yaml",
|
||||
".css", ".sass", ".scss",
|
||||
".js",
|
||||
".html",
|
||||
".txt",
|
||||
}
|
||||
|
||||
for _, extension := range extensions {
|
||||
if strings.HasSuffix(filename, extension) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Get is used to get a ready to use template based on the url and on
|
||||
// other sent templates
|
||||
func Get(r *http.Request, functions template.FuncMap, templates ...string) (*template.Template, error) {
|
||||
// If this is a pjax request, use the minimal template to send only
|
||||
// the main content
|
||||
if r.Header.Get("X-PJAX") == "true" {
|
||||
templates = append(templates, "base_minimal")
|
||||
} else {
|
||||
templates = append(templates, "base_full")
|
||||
}
|
||||
|
||||
var tpl *template.Template
|
||||
|
||||
// For each template, add it to the the tpl variable
|
||||
for i, t := range templates {
|
||||
// Get the template from the assets
|
||||
page, err := assets.Asset("templates/" + t + ".tmpl")
|
||||
|
||||
// Check if there is some error. If so, the template doesn't exist
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return new(template.Template), err
|
||||
}
|
||||
|
||||
// If it's the first iteration, creates a new template and add the
|
||||
// functions map
|
||||
if i == 0 {
|
||||
tpl, err = template.New(t).Funcs(functions).Parse(string(page))
|
||||
} else {
|
||||
tpl, err = tpl.Parse(string(page))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return new(template.Template), err
|
||||
}
|
||||
}
|
||||
|
||||
return tpl, nil
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package templates
|
||||
|
||||
import "testing"
|
||||
|
||||
type canBeEdited struct {
|
||||
file string
|
||||
result bool
|
||||
}
|
||||
|
||||
var canBeEditedPairs = []canBeEdited{
|
||||
{"file.markdown", true},
|
||||
{"file.md", true},
|
||||
{"file.json", true},
|
||||
{"file.toml", true},
|
||||
{"file.yaml", true},
|
||||
{"file.css", true},
|
||||
{"file.sass", true},
|
||||
{"file.scss", true},
|
||||
{"file.js", true},
|
||||
{"file.html", true},
|
||||
{"file.git", false},
|
||||
{"file.log", false},
|
||||
{"file.sh", false},
|
||||
{"file.png", false},
|
||||
{"file.jpg", false},
|
||||
}
|
||||
|
||||
func TestCanBeEdited(t *testing.T) {
|
||||
for _, pair := range canBeEditedPairs {
|
||||
v := CanBeEdited(pair.file)
|
||||
if v != pair.result {
|
||||
t.Error(
|
||||
"For", pair.file,
|
||||
"expected", pair.result,
|
||||
"got", v,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package variables
|
||||
|
||||
func StringInSlice(a string, list []string) (bool, int) {
|
||||
for i, b := range list {
|
||||
if b == a {
|
||||
return true, i
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package variables
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package variables
|
||||
|
||||
import "testing"
|
||||
|
||||
type testSplitCapitalize struct {
|
||||
name string
|
||||
result string
|
||||
}
|
||||
|
||||
var testSplitCapitalizeCases = []testSplitCapitalize{
|
||||
{"loremIpsum", "Lorem ipsum"},
|
||||
{"LoremIpsum", "Lorem ipsum"},
|
||||
{"loremipsum", "Loremipsum"},
|
||||
{"YouTube", "YouTube"},
|
||||
{"GitHub", "GitHub"},
|
||||
{"GooglePlus", "Google Plus"},
|
||||
{"Facebook", "Facebook"},
|
||||
}
|
||||
|
||||
func TestSplitCapitalize(t *testing.T) {
|
||||
for _, pair := range testSplitCapitalizeCases {
|
||||
v := SplitCapitalize(pair.name)
|
||||
if v != pair.result {
|
||||
t.Error(
|
||||
"For", pair.name,
|
||||
"expected", pair.result,
|
||||
"got", v,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package variables
|
||||
|
||||
import "reflect"
|
||||
|
||||
// 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
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package variables
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package variables
|
||||
|
||||
import "testing"
|
||||
|
||||
type testDefinedData struct {
|
||||
f1 string
|
||||
f2 bool
|
||||
f3 int
|
||||
f4 func()
|
||||
}
|
||||
|
||||
type testDefined struct {
|
||||
data interface{}
|
||||
field string
|
||||
result bool
|
||||
}
|
||||
|
||||
var testDefinedCases = []testDefined{
|
||||
{testDefinedData{}, "f1", true},
|
||||
{testDefinedData{}, "f2", true},
|
||||
{testDefinedData{}, "f3", true},
|
||||
{testDefinedData{}, "f4", true},
|
||||
{testDefinedData{}, "f5", false},
|
||||
{[]string{}, "", false},
|
||||
{map[string]int{"oi": 4}, "", false},
|
||||
{"asa", "", false},
|
||||
{"int", "", false},
|
||||
}
|
||||
|
||||
func TestDefined(t *testing.T) {
|
||||
for _, pair := range testDefinedCases {
|
||||
v := Defined(pair.data, pair.field)
|
||||
if v != pair.result {
|
||||
t.Error(
|
||||
"For", pair.data,
|
||||
"expected", pair.result,
|
||||
"got", v,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RespondJSON used to send JSON responses to the web server
|
||||
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
|
||||
}
|
58
assets/templates/editor.tmpl
Normal file
58
assets/templates/editor.tmpl
Normal file
@ -0,0 +1,58 @@
|
||||
{{ define "content" }}
|
||||
<div class="editor container {{ .Class }}">
|
||||
{{ if eq .Class "complete" }}
|
||||
<h1><textarea id="site-title">{{ .Name }}</textarea></h1>
|
||||
{{ end }}
|
||||
<form method="POST" action="">
|
||||
<main>
|
||||
{{ if not (eq .Class "complete") }}
|
||||
<h1 id="site-title">{{ .Name }}</h1>
|
||||
{{ end }}
|
||||
|
||||
{{ if eq .Class "frontmatter-only" }}
|
||||
<div class="frontmatter blocks">
|
||||
{{ template "frontmatter" .FrontMatter }}
|
||||
<p class="actions">
|
||||
<button class="add">Add field</button>
|
||||
</p>
|
||||
</div>
|
||||
{{ else if eq .Class "content-only" }}
|
||||
<div class="content">
|
||||
<div id="editor-source" data-mode="{{ .Mode }}"></div>
|
||||
<textarea name="content">{{ .Content }}</textarea>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="frontmatter">
|
||||
<div class="blocks">
|
||||
{{ template "frontmatter" .FrontMatter }}
|
||||
</div>
|
||||
|
||||
<p class="actions">
|
||||
<button class="add">Add field</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
{{ 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>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<p class="toolbar">
|
||||
<input type="submit" data-type="{{ .Class }}" data-regenerate="false" data-schedule="false" data-message="{{ if eq .Class "frontmatter-only" }}The fields were put on their way.{{ else if eq .Class "content-only" }}Every byte was saved.{{ else }}Post saved with pomp and circumstance.{{ end }}" value="Save">
|
||||
<span class="right">
|
||||
{{ if and (eq .Class "complete") ( .IsPost ) }}<input type="submit" data-type="{{ .Class }}" data-schedule="true" data-regenerate="false" data-message="Post scheduled." value="Schedule"> {{ end }}
|
||||
<input type="submit" data-type="{{ .Class }}" data-regenerate="true" data-schedule="false" data-message="{{ if eq .Class "frontmatter-only" }}Saved and regenerated.{{ else if eq .Class "content-only" }}Done. What do you want more?{{ else }}Post published. Go and share it!{{ end }}" class="default" value="Publish">
|
||||
</span>
|
||||
</p>
|
||||
</main>
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
44
assets/templates/frontmatter.tmpl
Normal file
44
assets/templates/frontmatter.tmpl
Normal file
@ -0,0 +1,44 @@
|
||||
{{ define "frontmatter" }}
|
||||
{{ range $key, $value := . }}
|
||||
|
||||
{{ if or (eq $value.Type "object") (eq $value.Type "array") }}
|
||||
<fieldset id="{{ $value.Name }}" data-type="{{ $value.Type }}">
|
||||
<h3>{{ SplitCapitalize $value.Title }}</h3>
|
||||
<span class="actions">
|
||||
<button class="add">+</button>
|
||||
<button class="delete">−</button>
|
||||
</span>
|
||||
{{ template "frontmatter" $value.Content }}
|
||||
</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>
|
||||
<span class="actions">
|
||||
<button class="delete">−</button>
|
||||
</span>
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ if eq $value.Parent.Type "array" }}
|
||||
<div 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" }}
|
||||
<span class="actions"><button class="delete">−</button></span></div>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
260
binary.go
Normal file
260
binary.go
Normal file
@ -0,0 +1,260 @@
|
||||
// Code generated by go-bindata.
|
||||
// sources:
|
||||
// assets/templates/editor.tmpl
|
||||
// assets/templates/frontmatter.tmpl
|
||||
// DO NOT EDIT!
|
||||
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _templatesEditorTmpl = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xc4\x56\x4d\x6f\xdc\x38\x0c\xbd\x07\xc8\x7f\xe0\xea\x94\x3d\x8c\x8d\xbd\x7b\xbc\x58\xec\x6e\x8b\x1e\xd2\x0e\x30\x01\x7a\xd6\x58\x9c\x58\x88\x6d\xb9\x12\x3d\xd3\x41\x90\xff\x5e\xca\xf2\x97\x32\xcd\x07\x82\xa0\xf5\xc9\x96\xc8\xc7\x47\xf2\x51\xf2\xfd\x3d\x28\xdc\xeb\x06\x41\x14\xa6\x21\x6c\x48\xc0\xc3\xc3\xe5\x45\xa6\xf4\x01\x8a\x4a\x3a\xb7\x16\xa8\x34\x19\x0b\x7e\x5f\xb2\xa5\x05\x76\x4a\xfe\xf5\x7b\x6c\x2a\xf2\xcb\x0b\xf0\x2b\x7a\x0f\xf8\x6d\x5c\x67\xb0\xba\xad\x90\x30\xa0\x01\x64\xe5\x5f\x79\x46\xf8\x9d\xa4\x45\x09\x5a\xad\x85\xd3\x84\x2b\xd2\x54\xa1\xc8\x3d\xe0\x67\x59\x23\x1b\x67\xe9\x68\x95\x67\x29\x3b\x0d\xe8\xd8\xa8\x11\x69\x6f\x6c\x0d\x35\x52\x69\x18\x65\xf3\x65\x7b\x23\x40\x16\xa4\x4d\xb3\x16\x81\x0c\xdb\xd4\x4c\x74\x78\x1f\xc9\x35\x86\xe0\xea\x67\x0c\xff\x1c\x80\xc3\xc3\x44\x9f\xa7\x37\x70\x1a\x80\x47\x5e\x71\xac\x45\x98\xbd\xe5\xb2\xd5\x92\x08\xed\xca\x34\xd5\x49\x2c\xa2\x2d\x8b\xbc\xb0\x83\x5d\x65\x8a\x3b\x27\xf2\x99\x15\xc3\x12\x32\x5f\x49\x18\x41\x0a\x48\x3e\xf8\xaf\xeb\xe0\x18\x25\xd2\x8e\xd0\xa1\x3a\x11\x1e\x6f\xef\x3a\x22\xd3\x4c\x36\x4a\x89\xfc\x1f\xa5\x60\xaf\xb1\x52\x59\x1a\x76\x17\x1e\x59\xda\x4e\x5f\x59\xca\xc4\xa3\x2a\x54\x0e\xcf\x04\xd0\xab\xe9\xd9\x9c\x47\xc5\x2d\xc3\xf8\x6d\x5f\xff\x20\xba\x95\x33\x9d\x2d\x58\x45\x4a\x92\x5c\xd5\x46\xe1\x5a\xf8\x6e\x5c\xf3\x5b\x2f\xbe\x98\x0a\xfb\x4f\x1a\x6b\xb8\x61\x8b\x10\xbd\x66\xc3\x47\xac\xb2\xe7\x93\x7a\xb1\x59\x67\xe4\x07\x93\xf7\xe9\xe1\xc0\xe9\x97\x76\xf5\x95\x9d\x9a\x95\xde\x37\x43\xd4\xd2\xde\x29\x73\x6c\x44\x9c\x41\x23\x0f\x31\xc3\x61\xfa\x11\xa7\xde\x2e\x12\x3a\xf0\xb4\x65\x7a\x2a\xb3\x84\xbd\x5c\x15\x0c\xef\x1b\xad\x73\xd8\xf6\x1e\x59\x2a\x9f\x82\x6c\x2d\x1e\x34\x1e\xcf\x41\xf0\x34\x62\x6c\x82\x49\x0c\x92\xa5\x31\xcf\x30\xd9\x51\x22\xbf\x4d\x98\xe7\xb1\xe7\x2c\xa3\x20\xe7\x0a\x7e\x74\x38\xcd\xe2\x21\x63\xaa\x9d\x8c\xc5\xab\x9b\xb6\x23\xa0\x53\xcb\xec\x5c\xb7\xab\x35\x0d\xd9\x85\xa5\xe8\xd0\x0f\x1b\x16\x6f\x91\xef\x03\x16\xb4\x2f\x33\x8f\xcb\xb0\xee\x8a\x12\x55\x57\xcd\xab\x43\x95\xd0\x39\x79\x1b\xa0\x5e\x3e\x24\x6f\x4a\x0c\xb2\x75\x70\x44\x8b\xe0\xc9\xb1\xb2\xa9\x44\x6d\xe1\x28\x4f\xc9\xeb\xce\x9d\xff\x0f\x68\x4f\xb0\x3b\xf1\xd0\x1d\xa5\x03\x27\x0f\xa8\x92\x79\xba\x37\xc6\x51\x58\x84\xa3\xa6\x12\x5a\xbe\x15\x40\x72\xdd\x0a\x6d\x8b\xae\x76\x24\x9b\x02\x93\xa9\x96\x02\x0e\xb2\xea\x38\x85\xad\xf4\x52\x5d\x54\xcf\xb5\x72\x1a\x3b\xab\x6f\x4b\x8a\x07\x33\xa4\xec\x81\x9f\xb8\x83\xae\x20\xf9\xe4\x7a\x36\xfe\x3e\x7a\x43\x33\xe6\xa2\x93\xed\xf0\x85\x0e\x4d\xad\x08\xf9\x0f\xae\x2a\xe1\x56\x8d\x09\x0e\x6b\x22\x8f\xaf\xdf\x77\x92\xcb\x82\xe3\x99\x5a\xde\x20\x96\x6d\xdf\x41\x5f\xde\x39\x88\x7a\xa5\x42\xfe\x33\x0d\x26\xf0\xb5\x94\x04\xca\xc0\xc9\x74\x2c\x14\x9e\xc5\xda\x58\xfc\xfb\x91\x50\xda\x6e\x57\x69\xc7\x74\x13\xf8\x68\xfa\x70\xae\xe4\x59\x05\x4d\x7f\x2c\x24\x32\xa8\x80\xff\xab\x64\x57\xd1\x24\x99\x4d\x70\x8e\x54\x93\x7a\xd9\x2c\x06\x78\x3c\x8d\xb3\x74\xfa\x83\xc9\x52\xff\xc3\xc3\xaf\xe3\x78\x2f\xda\xf1\x23\x00\x00\xff\xff\x32\x91\x30\x45\xbe\x09\x00\x00")
|
||||
|
||||
func templatesEditorTmplBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_templatesEditorTmpl,
|
||||
"templates/editor.tmpl",
|
||||
)
|
||||
}
|
||||
|
||||
func templatesEditorTmpl() (*asset, error) {
|
||||
bytes, err := templatesEditorTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/editor.tmpl", size: 2494, mode: os.FileMode(438), modTime: time.Unix(1466520293, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesFrontmatterTmpl = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x54\xdd\x6e\xd3\x4c\x10\xbd\xaf\xd4\x77\x18\xed\x57\x7d\x02\x09\xc7\x69\x5a\x7e\x14\x1c\xdf\x54\x42\x5c\x00\x42\x22\x2f\xb0\xb1\xc7\xb0\x74\xb3\x6b\xd6\x93\x8a\x50\xf5\xdd\x19\xaf\xbd\xa9\xb1\x9d\xca\xad\x10\x77\x59\x9f\xd9\xd9\x73\xce\xcc\xc9\xed\x2d\xe4\x58\x28\x83\x20\x0a\x67\x0d\x6d\x25\x11\x3a\x01\x77\x77\xa7\x27\x8c\x39\x69\xbe\x22\x9c\x5d\xe3\xfe\x05\x9c\xdd\x48\xbd\x43\x58\xae\x60\xe6\xe1\xd3\x13\x00\x2e\x51\x05\x58\x07\xcf\xf0\x47\x5b\x30\x5b\xef\x4b\xee\x66\x37\xdf\x31\x23\xf1\x7c\x88\x48\xe7\xe4\x9e\x81\xba\x07\x40\x52\x28\xd4\x79\x85\x04\x2a\x5f\x09\xee\xd7\xd6\x7e\x92\x5b\xe4\x12\x01\xb9\x24\x19\x11\xdf\xec\xa2\xbe\x13\xa3\x69\xdd\x82\x9b\x7c\xbb\x48\x19\xfc\x52\x6a\x45\x57\xb2\x54\x24\xb5\xfa\x85\x87\x62\x45\xba\xae\x4e\x62\x2e\x6b\x2f\x54\xa5\x34\x90\x69\x59\x55\x2b\x21\x33\x52\xd6\x54\xa1\x19\xa3\x9b\x1d\x91\xbd\xc7\xf3\x5c\xa4\xff\xff\x77\x79\xf1\x36\x89\x1b\xe4\x58\x65\x8e\x1a\x09\xeb\xe2\x37\xaf\x17\x8b\x7e\x79\x12\xd7\xaf\xb6\x07\xa6\x4b\xb8\x2d\xb5\xa4\xbe\xf5\x2d\xeb\x2b\xfe\x84\x86\x82\x4d\x71\xf0\x29\x6d\x7d\x47\x5d\x61\x6f\x0e\xc6\x52\xd7\xee\xcf\xd2\x71\x83\x71\xd7\x73\x75\x13\x48\x6f\xb4\xcd\xae\x85\xb7\xdf\xff\x8c\x8e\x0d\x21\x6b\x18\x8d\x4d\x29\x28\xd4\x72\x83\x1a\x0a\xeb\x46\x8b\xa6\x8c\xc8\x77\x78\xca\x94\x26\x7a\x1f\xdc\x33\x79\xcf\xbc\x07\x7d\xeb\xda\x36\xba\xa7\xde\x34\x8e\x49\x7f\x65\xfd\xf5\x48\xf1\xa8\x45\x3a\xe1\xe9\xf7\xeb\x8f\x1f\x9a\x77\x09\x7f\x12\xf3\x90\x87\xa7\xc3\x87\xa0\xb7\xca\x9c\xd5\x5a\x80\x61\x02\x23\x84\x96\xc3\xb0\x3c\x98\xb0\xd2\x8b\x1e\x04\xad\xeb\x45\x3b\xc2\xc1\x7e\x26\x71\xe0\xf6\xc7\x6e\x1e\x11\xc6\xaf\x21\xa9\x2d\x1e\x84\x29\x53\xee\xe8\x2f\xc8\xf0\xc7\x2e\xd2\x32\x9c\xbd\xb3\x8e\xb3\x05\x62\x31\x9f\xbf\x8a\xe6\xe7\xd1\x7c\xb1\x3e\x7f\xb9\x9c\x5f\x0a\x7f\xad\x51\x1c\x58\x45\x9c\x00\xa9\x1f\xe7\x49\x12\x7b\x09\x83\x60\xfe\x03\x6d\x1d\x05\xf7\xd8\xc1\xeb\xc7\x0e\xb7\x2f\x64\xb0\xa8\xd3\xfe\x60\x92\x98\x63\x92\x3e\x3d\x64\xa3\xa9\x9f\x98\xf6\x36\xe7\x0d\x85\x81\x8e\xce\x61\xfc\xe7\xef\x00\x00\x00\xff\xff\x30\xc2\x3e\x0c\x0f\x07\x00\x00")
|
||||
|
||||
func templatesFrontmatterTmplBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_templatesFrontmatterTmpl,
|
||||
"templates/frontmatter.tmpl",
|
||||
)
|
||||
}
|
||||
|
||||
func templatesFrontmatterTmpl() (*asset, error) {
|
||||
bytes, err := templatesFrontmatterTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/frontmatter.tmpl", size: 1807, mode: os.FileMode(438), modTime: time.Unix(1466520317, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"templates/editor.tmpl": templatesEditorTmpl,
|
||||
"templates/frontmatter.tmpl": templatesFrontmatterTmpl,
|
||||
}
|
||||
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
// For example if you run go-bindata on data/... and data contains the
|
||||
// following hierarchy:
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(cannonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"templates": &bintree{nil, map[string]*bintree{
|
||||
"editor.tmpl": &bintree{templatesEditorTmpl, map[string]*bintree{}},
|
||||
"frontmatter.tmpl": &bintree{templatesFrontmatterTmpl, map[string]*bintree{}},
|
||||
}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||
}
|
||||
|
@ -2,17 +2,14 @@ package hugo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/hacdias/caddy-hugo/tools/frontmatter"
|
||||
"github.com/hacdias/caddy-hugo/tools/templates"
|
||||
"github.com/hacdias/caddy-hugo/tools/variables"
|
||||
"github.com/hacdias/caddy-filemanager"
|
||||
"github.com/spf13/hugo/parser"
|
||||
)
|
||||
|
||||
@ -23,17 +20,10 @@ type editor struct {
|
||||
Mode string
|
||||
Content string
|
||||
FrontMatter interface{}
|
||||
Config *Config
|
||||
}
|
||||
|
||||
// GET handles the GET method on editor page
|
||||
func GET(w http.ResponseWriter, r *http.Request, c *Config, filename string) (int, error) {
|
||||
// Check if the file format is supported. If not, send a "Not Acceptable"
|
||||
// header and an error
|
||||
if !templates.CanBeEdited(filename) {
|
||||
return http.StatusNotAcceptable, errors.New("File format not supported.")
|
||||
}
|
||||
|
||||
func (h Hugo) GET(w http.ResponseWriter, r *http.Request, c *Config, filename string) (int, error) {
|
||||
// Check if the file exists.
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
return http.StatusNotFound, err
|
||||
@ -51,51 +41,56 @@ func GET(w http.ResponseWriter, r *http.Request, c *Config, filename string) (in
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
page := &filemanager.Page{
|
||||
Info: &filemanager.PageInfo{
|
||||
IsDir: false,
|
||||
Config: &h.FileManager.Configs[0],
|
||||
Name: strings.Replace(filename, c.Root, "", 1),
|
||||
},
|
||||
}
|
||||
|
||||
// Create a new editor variable and set the extension
|
||||
page := new(editor)
|
||||
page.Mode = strings.TrimPrefix(filepath.Ext(filename), ".")
|
||||
page.Name = strings.Replace(filename, c.Path, "", 1)
|
||||
page.Config = c
|
||||
page.IsPost = false
|
||||
data := new(editor)
|
||||
data.Mode = strings.TrimPrefix(filepath.Ext(filename), ".")
|
||||
data.Name = strings.Replace(filename, c.Root, "", 1)
|
||||
data.IsPost = false
|
||||
data.Mode = sanitizeMode(data.Mode)
|
||||
|
||||
// Sanitize the extension
|
||||
page.Mode = sanitizeMode(page.Mode)
|
||||
|
||||
var ppage parser.Page
|
||||
var parserPage parser.Page
|
||||
|
||||
// Handle the content depending on the file extension
|
||||
switch page.Mode {
|
||||
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)
|
||||
ppage, err = parser.ReadFrom(buffer)
|
||||
parserPage, err = parser.ReadFrom(buffer)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
if strings.Contains(string(ppage.FrontMatter()), "date") {
|
||||
page.IsPost = true
|
||||
if strings.Contains(string(parserPage.FrontMatter()), "date") {
|
||||
data.IsPost = true
|
||||
}
|
||||
|
||||
// Parses the page content and the frontmatter
|
||||
page.Content = strings.TrimSpace(string(ppage.Content()))
|
||||
page.FrontMatter, page.Name, err = frontmatter.Pretty(ppage.FrontMatter())
|
||||
page.Class = "complete"
|
||||
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
|
||||
page.Class = "content-only"
|
||||
page.Content = string(file)
|
||||
data.Class = "content-only"
|
||||
data.Content = string(file)
|
||||
}
|
||||
case "json", "toml", "yaml":
|
||||
// Defines the class and declares an error
|
||||
page.Class = "frontmatter-only"
|
||||
data.Class = "frontmatter-only"
|
||||
|
||||
// Checks if the file already has the frontmatter rune and parses it
|
||||
if hasFrontMatterRune(file) {
|
||||
page.FrontMatter, _, err = frontmatter.Pretty(file)
|
||||
data.FrontMatter, _, err = Pretty(file)
|
||||
} else {
|
||||
page.FrontMatter, _, err = frontmatter.Pretty(appendFrontMatterRune(file, page.Mode))
|
||||
data.FrontMatter, _, err = Pretty(appendFrontMatterRune(file, data.Mode))
|
||||
}
|
||||
|
||||
// Check if there were any errors
|
||||
@ -104,24 +99,35 @@ func GET(w http.ResponseWriter, r *http.Request, c *Config, filename string) (in
|
||||
}
|
||||
default:
|
||||
// The editor will handle only content
|
||||
page.Class = "content-only"
|
||||
page.Content = string(file)
|
||||
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": variables.SplitCapitalize,
|
||||
"Defined": variables.Defined,
|
||||
"SplitCapitalize": SplitCapitalize,
|
||||
"Defined": Defined,
|
||||
}
|
||||
|
||||
tpl, err := templates.Get(r, functions, "editor", "frontmatter")
|
||||
var code int
|
||||
|
||||
page.Info.Data = data
|
||||
code, err = page.AddTemplate("base", filemanager.Asset, functions)
|
||||
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
return code, err
|
||||
}
|
||||
|
||||
return http.StatusOK, tpl.Execute(w, page)
|
||||
templates := []string{"listing", "actions"}
|
||||
for _, t := range templates {
|
||||
code, err = page.AddTemplate(t, Asset, nil)
|
||||
if err != nil {
|
||||
return code, err
|
||||
}
|
||||
}
|
||||
|
||||
return page.PrintAsHTML(w)
|
||||
}
|
||||
|
||||
func hasFrontMatterRune(file []byte) bool {
|
@ -1,4 +1,4 @@
|
||||
package frontmatter
|
||||
package hugo
|
||||
|
||||
import (
|
||||
"log"
|
||||
@ -6,7 +6,6 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hacdias/caddy-hugo/tools/variables"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/hugo/parser"
|
||||
)
|
||||
@ -64,9 +63,9 @@ func rawToPretty(config interface{}, parent *frontmatter) interface{} {
|
||||
}
|
||||
|
||||
for name, element := range cnf {
|
||||
if variables.IsMap(element) {
|
||||
if IsMap(element) {
|
||||
objects = append(objects, handleObjects(element, parent, name))
|
||||
} else if variables.IsSlice(element) {
|
||||
} else if IsSlice(element) {
|
||||
arrays = append(arrays, handleArrays(element, parent, name))
|
||||
} else {
|
||||
if name == "title" && parent.Name == mainName {
|
4
hugo.go
4
hugo.go
@ -1,3 +1,7 @@
|
||||
//go:generate go get github.com/jteeuwen/go-bindata
|
||||
//go:generate go install github.com/jteeuwen/go-bindata/go-bindata
|
||||
//go:generate go-bindata -pkg hugo -prefix "assets" -o binary.go assets/...
|
||||
|
||||
// Package hugo makes the bridge between the static website generator Hugo
|
||||
// and the webserver Caddy, also providing an administrative user interface.
|
||||
package hugo
|
||||
|
Loading…
Reference in New Issue
Block a user