diff options
Diffstat (limited to 'commands/undraft.go')
-rw-r--r-- | commands/undraft.go | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/commands/undraft.go b/commands/undraft.go new file mode 100644 index 000000000..2a3b85360 --- /dev/null +++ b/commands/undraft.go @@ -0,0 +1,157 @@ +// Copyright 2015 The Hugo Authors. All rights reserved. +// +// 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. + +package commands + +import ( + "bytes" + "errors" + "os" + "time" + + "github.com/gohugoio/hugo/parser" + "github.com/spf13/cobra" +) + +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.`, + RunE: Undraft, +} + +// Undraft 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) error { + cfg, err := InitializeConfig() + + if err != nil { + return err + } + + if len(args) < 1 { + return newUserError("a piece of content needs to be specified") + } + + location := args[0] + // open the file + f, err := cfg.Fs.Source.Open(location) + if err != nil { + return err + } + + // get the page from file + p, err := parser.ReadFrom(f) + f.Close() + if err != nil { + return err + } + + w, err := undraftContent(p) + if err != nil { + return newSystemErrorF("an error occurred while undrafting %q: %s", location, err) + } + + f, err = cfg.Fs.Source.OpenFile(location, os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + return newSystemErrorF("%q not be undrafted due to error opening file to save changes: %q\n", location, err) + } + defer f.Close() + _, err = w.WriteTo(f) + if err != nil { + return newSystemErrorF("%q not be undrafted due to save error: %q\n", location, err) + } + return nil +} + +// undraftContent: if the content is a draft, change its 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 { + return buff, errors.New("Front Matter was found, nothing was finalized") + } + + 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, errors.New("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, errors.New("not a Draft: nothing was done") + } + + // get the front matter as bytes and split it into lines + var lineEnding []byte + fmLines := bytes.Split(fm, []byte("\n")) + if len(fmLines) == 1 { // if the result is only 1 element, try to split on dos line endings + fmLines = bytes.Split(fm, []byte("\r\n")) + if len(fmLines) == 1 { + return buff, errors.New("unable to split FrontMatter into lines") + } + lineEnding = append(lineEnding, []byte("\r\n")...) + } else { + lineEnding = append(lineEnding, []byte("\n")...) + } + + // 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(p.Content()) + + return buff, nil +} |