shithub: hugo

Download patch

ref: b4871787f02d6660da8bc82b951178dc3964d9a9
parent: 563a6302a0a27f1b8412d4ab621e336f83159727
author: Joel Scoble <[email protected]>
date: Sun Mar 15 13:32:41 EDT 2015

add undraft command

--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -75,6 +75,7 @@
 	HugoCmd.AddCommand(convertCmd)
 	HugoCmd.AddCommand(newCmd)
 	HugoCmd.AddCommand(listCmd)
+	HugoCmd.AddCommand(undraftCmd)
 }
 
 //Initializes flags
--- /dev/null
+++ b/commands/undraft.go
@@ -1,0 +1,157 @@
+// Copyright © 2013 Steve Francia <[email protected]>.
+//
+// Licensed under the Simple Public 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://opensource.org/licenses/Simple-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.
+package commands
+
+import (
+	"bytes"
+	"fmt"
+	"os"
+	"time"
+
+	"github.com/spf13/cobra"
+	"github.com/spf13/hugo/parser"
+	jww "github.com/spf13/jwalterweatherman"
+)
+
+var undraftCmd = &cobra.Command{
+	Use:   "undraft path/to/content",
+	Short: "Undraft changes the content's draft status from 'True' to 'False'",
+	Long:  `Undraft changes the content's draft status from 'True' to 'False' and updates the date to the current date and time. If the content's draft status is 'False', nothing is done`,
+	Run:   Undraft,
+}
+
+// Publish publishes the specified content by setting its draft status
+// to false and setting its publish date to now. If the specified content is
+// not a draft, it will log an error.
+func Undraft(cmd *cobra.Command, args []string) {
+	InitializeConfig()
+
+	if len(args) < 1 {
+		cmd.Usage()
+		jww.FATAL.Fatalln("a piece of content needs to be specified")
+	}
+
+	location := args[0]
+	// open the file
+	f, err := os.Open(location)
+	if err != nil {
+		jww.ERROR.Print(err)
+		return
+	}
+
+	// get the page from file
+	p, err := parser.ReadFrom(f)
+	f.Close()
+	if err != nil {
+		jww.ERROR.Print(err)
+		return
+	}
+
+	w, err := undraftContent(p)
+	if err != nil {
+		jww.ERROR.Printf("an error occurred while undrafting %q: %s", location, err)
+		return
+	}
+
+	f, err = os.OpenFile(location, os.O_WRONLY|os.O_TRUNC, 0644)
+	if err != nil {
+		jww.ERROR.Printf("%q not be undrafted due to error opening file to save changes: %q\n", location, err)
+		return
+	}
+	defer f.Close()
+	_, err = w.WriteTo(f)
+	if err != nil {
+		jww.ERROR.Printf("%q not be undrafted due to save error: %q\n", location, err)
+	}
+	return
+}
+
+// undraftContent: if the content is a draft, change it's draft status to
+// 'false' and set the date to time.Now(). If the draft status is already
+// 'false', don't do anything.
+func undraftContent(p parser.Page) (bytes.Buffer, error) {
+	var buff bytes.Buffer
+	// get the metadata; easiest way to see if it's a draft
+	meta, err := p.Metadata()
+	if err != nil {
+		return buff, err
+	}
+	// since the metadata was obtainable, we can also get the key/value separator for
+	// Front Matter
+	fm := p.FrontMatter()
+	if fm == nil {
+		err := fmt.Errorf("Front Matter was found, nothing was finalized")
+		return buff, err
+	}
+
+	var isDraft, gotDate bool
+	var date string
+L:
+	for k, v := range meta.(map[string]interface{}) {
+		switch k {
+		case "draft":
+			if !v.(bool) {
+				return buff, fmt.Errorf("not a Draft: nothing was done")
+			}
+			isDraft = true
+			if gotDate {
+				break L
+			}
+		case "date":
+			date = v.(string) // capture the value to make replacement easier
+			gotDate = true
+			if isDraft {
+				break L
+			}
+		}
+	}
+
+	// if draft wasn't found in FrontMatter, it isn't a draft.
+	if !isDraft {
+		return buff, fmt.Errorf("not a Draft: nothing was done")
+	}
+
+	// get the front matter as bytes and split it into lines
+	var lineEnding []byte
+	fmLines := bytes.Split(fm, parser.UnixEnding)
+	if len(fmLines) == 1 { // if the result is only 1 element, try to split on dos line endings
+		fmLines = bytes.Split(fm, parser.DosEnding)
+		if len(fmLines) == 1 {
+			return buff, fmt.Errorf("unable to split FrontMatter into lines")
+		}
+		lineEnding = append(lineEnding, parser.DosEnding...)
+	} else {
+		lineEnding = append(lineEnding, parser.UnixEnding...)
+	}
+
+	// Write the front matter lines to the buffer, replacing as necessary
+	for _, v := range fmLines {
+		pos := bytes.Index(v, []byte("draft"))
+		if pos != -1 {
+			v = bytes.Replace(v, []byte("true"), []byte("false"), 1)
+			goto write
+		}
+		pos = bytes.Index(v, []byte("date"))
+		if pos != -1 { // if date field wasn't found, add it
+			v = bytes.Replace(v, []byte(date), []byte(time.Now().Format(time.RFC3339)), 1)
+		}
+	write:
+		buff.Write(v)
+		buff.Write(lineEnding)
+	}
+
+	// append the actual content
+	buff.Write([]byte(p.Content()))
+
+	return buff, nil
+}
--- /dev/null
+++ b/commands/undraft_test.go
@@ -1,0 +1,72 @@
+package commands
+
+// TODO Support Mac Encoding (\r)
+
+import (
+	"bytes"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/spf13/hugo/parser"
+)
+
+var (
+	jsonFM      = "{\n \"date\": \"12-04-06\",\n \"title\": \"test json\"\n}"
+	jsonDraftFM = "{\n \"draft\": true,\n \"date\": \"12-04-06\",\n \"title\":\"test json\"\n}"
+	tomlFM      = "+++\n date= \"12-04-06\"\n title= \"test toml\"\n+++"
+	tomlDraftFM = "+++\n draft= true\n date= \"12-04-06\"\n title=\"test toml\"\n+++"
+	yamlFM      = "---\n date: \"12-04-06\"\n title: \"test yaml\"\n---"
+	yamlDraftFM = "---\n draft: true\n date: \"12-04-06\"\n title: \"test yaml\"\n---"
+)
+
+func TestUndraftContent(t *testing.T) {
+	tests := []struct {
+		fm          string
+		expectedErr string
+	}{
+		{jsonFM, "not a Draft: nothing was done"},
+		{jsonDraftFM, ""},
+		{tomlFM, "not a Draft: nothing was done"},
+		{tomlDraftFM, ""},
+		{yamlFM, "not a Draft: nothing was done"},
+		{yamlDraftFM, ""},
+	}
+
+	for _, test := range tests {
+		r := bytes.NewReader([]byte(test.fm))
+		p, _ := parser.ReadFrom(r)
+		res, err := undraftContent(p)
+		if test.expectedErr != "" {
+			if err == nil {
+				t.Error("Expected error, got none")
+				continue
+			}
+			if err.Error() != test.expectedErr {
+				t.Errorf("Expected %q, got %q", test.expectedErr, err)
+				continue
+			}
+		} else {
+			r = bytes.NewReader(res.Bytes())
+			p, _ = parser.ReadFrom(r)
+			meta, err := p.Metadata()
+			if err != nil {
+				t.Errorf("unexpected error %q", err)
+				continue
+			}
+			for k, v := range meta.(map[string]interface{}) {
+				if k == "draft" {
+					if v.(bool) {
+						t.Errorf("Expected %q to be \"false\", got \"true\"", k)
+						continue
+					}
+				}
+				if k == "date" {
+					if !strings.HasPrefix(v.(string), time.Now().Format("2006-01-02")) {
+						t.Errorf("Expected %v to start with %v", v.(string), time.Now().Format("2006-01-02"))
+					}
+				}
+			}
+		}
+	}
+}
--- a/parser/page.go
+++ b/parser/page.go
@@ -30,8 +30,8 @@
 		[]byte(JSON_LEAD),
 	}
 
-	unixEnding = []byte("\n")
-	dosEnding  = []byte("\r\n")
+	UnixEnding = []byte("\n")
+	DosEnding  = []byte("\r\n")
 )
 
 type FrontMatter []byte