From aecca64e255c2efb3150517bce91bc1a86d21b44 Mon Sep 17 00:00:00 2001 From: Junno Tantra Date: Mon, 10 Jun 2019 15:17:52 +0700 Subject: [PATCH 1/6] make slug editable --- management/editor/editor.go | 3 +- system/db/content.go | 63 +++++++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/management/editor/editor.go b/management/editor/editor.go index 39bb25f2..3d4f27d8 100644 --- a/management/editor/editor.go +++ b/management/editor/editor.go @@ -238,8 +238,7 @@ func addPostDefaultFieldsToEditorView(p Editable, e *Editor) error { View: Input("Slug", p, map[string]string{ "label": "URL Slug", "type": "text", - "disabled": "true", - "placeholder": "Will be set automatically", + "placeholder": "Can be left empty for new item", }), }, { diff --git a/system/db/content.go b/system/db/content.go index 43d60239..bcba4295 100644 --- a/system/db/content.go +++ b/system/db/content.go @@ -108,6 +108,37 @@ func update(ns, id string, data url.Values, existingContent *[]byte) (int, error return err } + // update the slug,type:id in contentIndex if public content + if specifier == "" { + if len(data["slug"]) > 1 { + slug := data["slug"][0] + customSlug := data["slug"][1] + + target := fmt.Sprintf("%s:%d", ns, cid) + // if slug changed + if slug != customSlug { + ci := tx.Bucket([]byte("__contentIndex")) + if ci == nil { + return bolt.ErrBucketNotFound + } + + // remove existing slug from __contentIndex + err = ci.Delete([]byte(fmt.Sprintf("%s", slug))) + if err != nil { + return err + } + + // insert new slug to __contentIndex + k := []byte(customSlug) + v := []byte(target) + err := ci.Put(k, v) + if err != nil { + return err + } + } + } + } + return nil }) if err != nil { @@ -773,18 +804,30 @@ func postToJSON(ns string, data url.Values) ([]byte, error) { // if the content has no slug, and has no specifier, create a slug, check it // for duplicates, and add it to our values if data.Get("slug") == "" && data.Get("__specifier") == "" { - slug, err := item.Slug(post.(item.Identifiable)) - if err != nil { - return nil, err - } + // check custom slug + slugsData := data["slug"] + if len(slugsData) > 1 && data["slug"][1] != "" { + customSlug := data["slug"][1] + + if customSlug != "" { + post.(item.Sluggable).SetSlug(customSlug) + data.Set("slug", customSlug) + } + } else { + // generate default slug + slug, err := item.Slug(post.(item.Identifiable)) + if err != nil { + return nil, err + } - slug, err = checkSlugForDuplicate(slug) - if err != nil { - return nil, err - } + slug, err = checkSlugForDuplicate(slug) + if err != nil { + return nil, err + } - post.(item.Sluggable).SetSlug(slug) - data.Set("slug", slug) + post.(item.Sluggable).SetSlug(slug) + data.Set("slug", slug) + } } // marshall content struct to json for db storage From 63bfb8f68fd2f5fe247395dcdb2fa3f2c0a2f397 Mon Sep 17 00:00:00 2001 From: Junno Tantra Date: Thu, 8 Aug 2019 10:24:35 +0700 Subject: [PATCH 2/6] Add validation on slug edit - validation on frontend using js, prevent submit empty slug - validation on backend, use existing slug if new slug is empty string - update code to be more readable --- management/editor/editor.go | 14 +++++- system/db/content.go | 89 ++++++++++++++++++++++--------------- 2 files changed, 65 insertions(+), 38 deletions(-) diff --git a/management/editor/editor.go b/management/editor/editor.go index 3d4f27d8..8f0185e8 100644 --- a/management/editor/editor.go +++ b/management/editor/editor.go @@ -150,12 +150,14 @@ func Form(post Editable, fields ...Field) ([]byte, error) { external = form.find('.post-controls.external'), id = form.find('input[name=id]'), timestamp = $('.__ponzu.content-only'), - slug = $('input[name=slug]'); + slug = $('input[name=slug]'), + allowEmptySlug = false; // hide if this is a new post, or a non-post editor page if (id.val() === '-1' || form.attr('action') !== '/admin/edit') { del.hide(); external.hide(); + allowEmptySlug = true; } // hide approval if not on a pending content item @@ -167,11 +169,19 @@ func Form(post Editable, fields ...Field) ([]byte, error) { if (form.attr('action') === '/admin/addon') { timestamp.hide(); slug.parent().hide(); + allowEmptySlug = true; } save.on('click', function(e) { e.preventDefault(); + if (!allowEmptySlug) { + if (slug.length > 1 && slug.eq(1).val() === "") { + alert("Slug cannot be empty"); + return; + } + } + if (getParam('status') === 'pending') { var action = form.attr('action'); form.attr('action', action + '?status=pending') @@ -238,7 +248,7 @@ func addPostDefaultFieldsToEditorView(p Editable, e *Editor) error { View: Input("Slug", p, map[string]string{ "label": "URL Slug", "type": "text", - "placeholder": "Can be left empty for new item", + "placeholder": "Can be left empty, only for new item", }), }, { diff --git a/system/db/content.go b/system/db/content.go index bcba4295..e57e8734 100644 --- a/system/db/content.go +++ b/system/db/content.go @@ -108,33 +108,38 @@ func update(ns, id string, data url.Values, existingContent *[]byte) (int, error return err } - // update the slug,type:id in contentIndex if public content + // get slugs data + var ( + existingSlug, newSlug string + ) + existingSlug = data.Get("slug") + if len(data["slug"]) > 1 { + newSlug = data["slug"][1] + } + + // update the slug (type:id) in contentIndex if public content if specifier == "" { - if len(data["slug"]) > 1 { - slug := data["slug"][0] - customSlug := data["slug"][1] - - target := fmt.Sprintf("%s:%d", ns, cid) - // if slug changed - if slug != customSlug { - ci := tx.Bucket([]byte("__contentIndex")) - if ci == nil { - return bolt.ErrBucketNotFound - } - // remove existing slug from __contentIndex - err = ci.Delete([]byte(fmt.Sprintf("%s", slug))) - if err != nil { - return err - } + target := fmt.Sprintf("%s:%d", ns, cid) + // if slug changed and valid + if existingSlug != newSlug && newSlug != "" { + ci := tx.Bucket([]byte("__contentIndex")) + if ci == nil { + return bolt.ErrBucketNotFound + } - // insert new slug to __contentIndex - k := []byte(customSlug) - v := []byte(target) - err := ci.Put(k, v) - if err != nil { - return err - } + // remove existing slug from __contentIndex + err = ci.Delete([]byte(fmt.Sprintf("%s", existingSlug))) + if err != nil { + return err + } + + // insert new slug to __contentIndex + k := []byte(newSlug) + v := []byte(target) + err := ci.Put(k, v) + if err != nil { + return err } } } @@ -801,20 +806,22 @@ func postToJSON(ns string, data url.Values) ([]byte, error) { return nil, err } + // get slugs data + var ( + existingSlug, newSlug string + ) + existingSlug = data.Get("slug") + if len(data["slug"]) > 1 { + newSlug = data["slug"][1] + } + + // for new item // if the content has no slug, and has no specifier, create a slug, check it // for duplicates, and add it to our values - if data.Get("slug") == "" && data.Get("__specifier") == "" { - // check custom slug - slugsData := data["slug"] - if len(slugsData) > 1 && data["slug"][1] != "" { - customSlug := data["slug"][1] - - if customSlug != "" { - post.(item.Sluggable).SetSlug(customSlug) - data.Set("slug", customSlug) - } - } else { - // generate default slug + if existingSlug == "" && data.Get("__specifier") == "" { + + // if no new slug specified, generate slug + if newSlug == "" { slug, err := item.Slug(post.(item.Identifiable)) if err != nil { return nil, err @@ -827,9 +834,19 @@ func postToJSON(ns string, data url.Values) ([]byte, error) { post.(item.Sluggable).SetSlug(slug) data.Set("slug", slug) + } else { + post.(item.Sluggable).SetSlug(newSlug) + data.Set("slug", newSlug) } } + // for editing item + // prevent edit to empty slug + if existingSlug != "" && newSlug == "" { + post.(item.Sluggable).SetSlug(existingSlug) + data.Set("slug", existingSlug) + } + // marshall content struct to json for db storage j, err := json.Marshal(post) if err != nil { From 6720c0ff9351e840ac6b99575b098f54309cc99d Mon Sep 17 00:00:00 2001 From: Igor Ljubuncic Date: Tue, 13 Aug 2019 15:17:38 +0100 Subject: [PATCH 3/6] Add support for building snaps --- snapcraft.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 snapcraft.yaml diff --git a/snapcraft.yaml b/snapcraft.yaml new file mode 100644 index 00000000..8e11d095 --- /dev/null +++ b/snapcraft.yaml @@ -0,0 +1,26 @@ +name: ponzu +version: '1.0' +summary: Headless CMS with automatic JSON API +description: | + Headless CMS with automatic JSON API. Featuring auto-HTTPS from + Lets Encrypt, HTTP/2 Server Push, and flexible server framework + written in Go. +grade: stable +confinement: strict +base: core18 +parts: + ponzu: + plugin: go + source: https://github.com/ponzu-cms/ponzu.git + go-importpath: github.com/ponzu-cms/ponzu + build-packages: + - build-essential +apps: + ponzu: + command: bin/ponzu + daemon: simple + restart-condition: on-abnormal + plugs: + - home + - network + - network-bind From 5b662ff7091f9d74a8f8d78c127f0bb65b8f91ca Mon Sep 17 00:00:00 2001 From: Ollie Phillips <7113347+olliephillips@users.noreply.github.com> Date: Fri, 6 Sep 2019 11:33:42 +0100 Subject: [PATCH 4/6] Added go dependency As per #313 just adding code to have go be a dependency --- snapcraft.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/snapcraft.yaml b/snapcraft.yaml index 8e11d095..ae39a072 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -15,6 +15,8 @@ parts: go-importpath: github.com/ponzu-cms/ponzu build-packages: - build-essential + stage-packages: + - go apps: ponzu: command: bin/ponzu From 0a8c77a0e4cde3d34687c04d0eb2cd09dea3b815 Mon Sep 17 00:00:00 2001 From: junnotantra Date: Fri, 18 Oct 2019 16:27:55 +0700 Subject: [PATCH 5/6] Sorting results in Select and SelectRepeater --- .../bosssauce/reference/reference.go | 26 +++++++++++++++--- management/editor/elements.go | 27 ++++++++++++++++--- management/editor/repeaters.go | 26 +++++++++++++++--- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/addons/github.com/bosssauce/reference/reference.go b/addons/github.com/bosssauce/reference/reference.go index 12d77695..9fe9f0e4 100644 --- a/addons/github.com/bosssauce/reference/reference.go +++ b/addons/github.com/bosssauce/reference/reference.go @@ -10,6 +10,7 @@ import ( "html" "html/template" "log" + "sort" "strings" "github.com/ponzu-cms/ponzu/management/editor" @@ -83,15 +84,34 @@ func SelectRepeater(fieldName string, p interface{}, attrs map[string]string, co opts = append(opts, cta, reset) + // order the options by value + orderedOptions := make([]string, 0, len(options)) for k, v := range options { - optAttrs := map[string]string{"value": k} - if k == val { + orderedOptions = append(orderedOptions, fmt.Sprintf("%s__ponzu__order%s", v, k)) + } + sort.Strings(orderedOptions) + + // construct editor elements array from option items + for _, v := range orderedOptions { + // get key value pair from ordered string + vkPair := strings.Split(v, "__ponzu__order") + if len(vkPair) < 2 { + continue + } + key := vkPair[1] + value := vkPair[0] + + // check for selected item + optAttrs := map[string]string{"value": key} + if key == val { optAttrs["selected"] = "true" } + + // construct editor elements item opt := &editor.Element{ TagName: "option", Attrs: optAttrs, - Data: v, + Data: value, ViewBuf: &bytes.Buffer{}, } diff --git a/management/editor/elements.go b/management/editor/elements.go index b279fa9b..25bbdaa5 100644 --- a/management/editor/elements.go +++ b/management/editor/elements.go @@ -2,7 +2,9 @@ package editor import ( "bytes" + "fmt" "html" + "sort" "strings" ) @@ -355,15 +357,34 @@ func Select(fieldName string, p interface{}, attrs, options map[string]string) [ opts = append(opts, cta, reset) + // order the options by value + orderedOptions := make([]string, 0, len(options)) for k, v := range options { - optAttrs := map[string]string{"value": k} - if k == fieldVal { + orderedOptions = append(orderedOptions, fmt.Sprintf("%s__ponzu__order%s", v, k)) + } + sort.Strings(orderedOptions) + + // construct editor elements array from option items + for _, v := range orderedOptions { + // get key value pair from ordered string + vkPair := strings.Split(v, "__ponzu__order") + if len(vkPair) < 2 { + continue + } + key := vkPair[1] + value := vkPair[0] + + // check for selected item + optAttrs := map[string]string{"value": key} + if key == fieldVal { optAttrs["selected"] = "true" } + + // construct editor elements item opt := &Element{ TagName: "option", Attrs: optAttrs, - Data: v, + Data: value, ViewBuf: &bytes.Buffer{}, } diff --git a/management/editor/repeaters.go b/management/editor/repeaters.go index bfdd3351..a374e14b 100644 --- a/management/editor/repeaters.go +++ b/management/editor/repeaters.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "log" + "sort" "strings" ) @@ -138,15 +139,34 @@ func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]s opts = append(opts, cta, reset) + // order the options by value + orderedOptions := make([]string, 0, len(options)) for k, v := range options { - optAttrs := map[string]string{"value": k} - if k == val { + orderedOptions = append(orderedOptions, fmt.Sprintf("%s__ponzu__order%s", v, k)) + } + sort.Strings(orderedOptions) + + // construct editor elements array from option items + for _, v := range orderedOptions { + // get key value pair from ordered string + vkPair := strings.Split(v, "__ponzu__order") + if len(vkPair) < 2 { + continue + } + key := vkPair[1] + value := vkPair[0] + + // check for selected item + optAttrs := map[string]string{"value": key} + if key == val { optAttrs["selected"] = "true" } + + // construct editor elements item opt := &Element{ TagName: "option", Attrs: optAttrs, - Data: v, + Data: value, ViewBuf: &bytes.Buffer{}, } From b9cd3da300b2e6e05d415ac8dd5a05ad9389ebab Mon Sep 17 00:00:00 2001 From: junnotantra Date: Thu, 31 Oct 2019 12:06:13 +0700 Subject: [PATCH 6/6] Enable generate content from file changes: - Add optional flag on generate content to read write JSON template file - Update documentation --- cmd/ponzu/generate.go | 123 ++++++++++++++++++++++++++++++- docs/src/CLI/General-Usage.md | 135 +++++++++++++++++++++++----------- 2 files changed, 213 insertions(+), 45 deletions(-) diff --git a/cmd/ponzu/generate.go b/cmd/ponzu/generate.go index bc94913d..f7e7628e 100644 --- a/cmd/ponzu/generate.go +++ b/cmd/ponzu/generate.go @@ -2,8 +2,10 @@ package main import ( "bytes" + "encoding/json" "fmt" "go/format" + "io/ioutil" "os" "path/filepath" "strings" @@ -359,6 +361,118 @@ func isHyphen(char rune) bool { return char == '-' } +func generate(args []string, fromSource, writeOutput bool) error { + var err error + + if fromSource { + args, err = parseSourceFile(args) + if err != nil { + return err + } + } + + err = generateContentType(args) + if err != nil { + return err + } + + if writeOutput && !fromSource { + err = generateOutputFile(args) + if err != nil { + return err + } + } + + return nil +} + +func parseSourceFile(args []string) ([]string, error) { + name := args[0] + fileName := strings.ToLower(name) + ".json" + + // open file in ./content/ dir + // if not exists, alert user + pwd, err := os.Getwd() + if err != nil { + return args, err + } + + contentDir := filepath.Join(pwd, "content") + filePath := filepath.Join(contentDir, fileName) + + if _, err := os.Stat(filePath); os.IsNotExist(err) { + localFile := filepath.Join("content", fileName) + return args, fmt.Errorf("Please create '%s' before executing this command", localFile) + } + + // read data from file + file, err := ioutil.ReadFile(filePath) + if err != nil { + return args, err + } + + data := make(map[string]string) + err = json.Unmarshal(file, &data) + if err != nil { + return args, err + } + + // add data to args + argsFromSource := []string{args[0]} + for k, v := range data { + argsFromSource = append(argsFromSource, k+":"+v) + } + + return argsFromSource, nil +} + +func generateOutputFile(args []string) error { + name := args[0] + fileName := strings.ToLower(name) + ".json" + + // open file in ./content/ dir + // if exists, alert user of conflict + pwd, err := os.Getwd() + if err != nil { + return err + } + + contentDir := filepath.Join(pwd, "content") + filePath := filepath.Join(contentDir, fileName) + + if _, err := os.Stat(filePath); !os.IsNotExist(err) { + localFile := filepath.Join("content", fileName) + return fmt.Errorf("Please remove '%s' before executing this command", localFile) + } + + // write args to file + fields := args[1:] + output := make(map[string]string, len(fields)) + for _, field := range fields { + data := strings.Split(field, ":") + output[data[0]] = strings.Join(data[1:], ":") + } + + jsonOutput, err := json.Marshal(output) + if err != nil { + return err + } + + // no file exists.. ok to write new one + file, err := os.Create(filePath) + defer file.Close() + if err != nil { + return err + } + + _, err = file.Write(jsonOutput) + if err != nil { + return fmt.Errorf("Failed to write generated file buffer: %s", err.Error()) + } + + return nil +} + func generateContentType(args []string) error { name := args[0] fileName := strings.ToLower(name) + ".go" @@ -444,11 +558,18 @@ var contentCmd = &cobra.Command{ Aliases: []string{"c"}, Short: "generates a new content type", RunE: func(cmd *cobra.Command, args []string) error { - return generateContentType(args) + return generate(args, fromSource, writeOutput) }, } +var ( + fromSource bool + writeOutput bool +) + func init() { generateCmd.AddCommand(contentCmd) + contentCmd.Flags().BoolVar(&fromSource, "source", false, "define whether to generate content from source file") + contentCmd.Flags().BoolVar(&writeOutput, "output", false, "define whether to write content template to file") RegisterCmdlineCommand(generateCmd) } diff --git a/docs/src/CLI/General-Usage.md b/docs/src/CLI/General-Usage.md index 993ff18d..6ff95f4d 100644 --- a/docs/src/CLI/General-Usage.md +++ b/docs/src/CLI/General-Usage.md @@ -11,14 +11,16 @@ $ ponzu [flags] command Creates a project directory of the name supplied as a parameter immediately following the 'new' option in the $GOPATH/src directory. Note: 'new' depends on the program 'git' and possibly a network connection. If there is no local -repository to clone from at the local machine's $GOPATH, 'new' will attempt to +repository to clone from at the local machine's $GOPATH, 'new' will attempt to clone the 'github.com/ponzu-cms/ponzu' package from over the network. Example: + ```bash $ ponzu new github.com/nilslice/proj > New ponzu project created at $GOPATH/src/github.com/nilslice/proj ``` + --- ### generate, gen, g @@ -26,10 +28,11 @@ $ ponzu new github.com/nilslice/proj Generate boilerplate code for various Ponzu components, such as `content`. Example: + ```bash generator struct fields and built-in types... | | - v v + v v $ ponzu gen content review title:"string" body:"string":richtext rating:"int" ^ ^ | | @@ -42,7 +45,7 @@ methods, as well as struct definition, and corresponding field tags like: ```go type Review struct { item.Item - + Title string `json:"title"` Body string `json:"body"` Rating int `json:"rating"` @@ -51,30 +54,30 @@ type Review struct { ``` The generate command will intelligently parse more sophisticated field names -such as 'field_name' and convert it to 'FieldName' and vice versa, only where -appropriate as per common Go idioms. Errors will be reported, but successful +such as 'field_name' and convert it to 'FieldName' and vice versa, only where +appropriate as per common Go idioms. Errors will be reported, but successful generate commands return nothing. **Input View Specifiers** _(optional)_ -The CLI can optionally parse a third parameter on the fields provided to generate +The CLI can optionally parse a third parameter on the fields provided to generate the type of HTML view an editor field is presented within. If no third parameter -is added, a plain text HTML input will be generated. In the example above, the +is added, a plain text HTML input will be generated. In the example above, the argument shown as `body:string:richtext` would show the Richtext input instead of a plain text HTML input (as shown in the screenshot). The following input view specifiers are implemented: -| CLI parameter | Generates | -|---------------|-----------| -| checkbox | [`editor.Checkbox()`](/Form-Fields/HTML-Inputs/#editorcheckbox) | -| custom | generates a pre-styled empty div to fill with HTML | -| file | [`editor.File()`](/Form-Fields/HTML-Inputs/#editorfile) | -| hidden | [`editor.Input()`](/Form-Fields/HTML-Inputs/#editorinput) + uses type=hidden | -| input, text | [`editor.Input()`](/Form-Fields/HTML-Inputs/#editorinput) | -| richtext | [`editor.Richtext()`](/Form-Fields/HTML-Inputs/#editorrichtext) | -| select | [`editor.Select()`](/Form-Fields/HTML-Inputs/#editorselect) | -| textarea | [`editor.Textarea()`](/Form-Fields/HTML-Inputs/#editortextarea) | -| tags | [`editor.Tags()`](/Form-Fields/HTML-Inputs/#editortags) | +| CLI parameter | Generates | +| ------------- | ---------------------------------------------------------------------------- | +| checkbox | [`editor.Checkbox()`](/Form-Fields/HTML-Inputs/#editorcheckbox) | +| custom | generates a pre-styled empty div to fill with HTML | +| file | [`editor.File()`](/Form-Fields/HTML-Inputs/#editorfile) | +| hidden | [`editor.Input()`](/Form-Fields/HTML-Inputs/#editorinput) + uses type=hidden | +| input, text | [`editor.Input()`](/Form-Fields/HTML-Inputs/#editorinput) | +| richtext | [`editor.Richtext()`](/Form-Fields/HTML-Inputs/#editorrichtext) | +| select | [`editor.Select()`](/Form-Fields/HTML-Inputs/#editorselect) | +| textarea | [`editor.Textarea()`](/Form-Fields/HTML-Inputs/#editortextarea) | +| tags | [`editor.Tags()`](/Form-Fields/HTML-Inputs/#editortags) | **Generate Content References** @@ -84,24 +87,61 @@ for more details: ```bash $ ponzu gen c author name:string genre:string:select -$ ponzu gen c book title:string author:@author,name,genre +$ ponzu gen c book title:string author:@author,name,genre ``` + The commands above will generate a `Book` Content type with a reference to an `Author` item, by also generating a [`reference.Select`](/Form-Fields/HTML-Inputs/#referenceselect) as the view for the `author` field. +**Writing Content Fields Template to JSON Template File** + +When you generating content, you can also write the fields definition to a JSON file that can be used later to regenerate the content boilerplate code. You can write the the JSON template file when generating the content by passring `--output` flag. + +Example: + +```bash +$ ponzu gen content review title:"string" body:"string":richtext rating:"int" --output +``` + +Above command will generated JSON template file on `content/review.json` and the content would look like this: + +```json +{ "body": "string:richtext", "rating": "int", "title": "string" } +``` + +_Note:_ +The order of the fileds on the generated json template file are random. You might need to rearrange the order manually before regenerating the content to get desired order. + +**Generate Content From JSON Template File** + +You can pass flag `--source` to generate content from template file. + +Example: + +```bash +$ ponzu gen content review --source +``` + +Using above command, ponzu will try to load a json file on `content/review.json` as the input to define content fields. + +_Note:_ +If you manually change the content code, it will not be updated on template file. Regenerating the content from template file will remove your changes. + --- ### build -From within your Ponzu project directory, running build will copy and move -the necessary files from your workspace into the vendored directory, and -will build/compile the project to then be run. +From within your Ponzu project directory, running build will copy and move +the necessary files from your workspace into the vendored directory, and +will build/compile the project to then be run. Optional flags: + - `--gocmd` sets the binary used when executing `go build` within `ponzu` build step Example: + ```bash $ ponzu build (or) @@ -115,10 +155,10 @@ Errors will be reported, but successful build commands return nothing. ### run Starts the HTTP server for the JSON API, Admin System, or both. -The segments, separated by a comma, describe which services to start, either -'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, +The segments, separated by a comma, describe which services to start, either +'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, if the server should utilize TLS encryption - served over HTTPS, which is -automatically managed using Let's Encrypt (https://letsencrypt.org) +automatically managed using Let's Encrypt (https://letsencrypt.org) Optional flags: @@ -130,23 +170,25 @@ Optional flags: - `--docs` runs a local documentation server in case of no network connection - `--docs-port` sets the port on which the docs server listens for HTTP requests [defaults to 1234] -Example: +Example: + ```bash $ ponzu run (or) $ ponzu run --bind=0.0.0.0 (or) $ ponzu run --port=8080 --https admin,api -(or) +(or) $ ponzu run admin (or) $ ponzu run --port=8888 api (or) $ ponzu run --dev-https ``` + Defaults to `$ ponzu run --port=8080 admin,api` (running Admin & API on port 8080, without TLS) -*Note:* +_Note:_ Admin and API cannot run on separate processes unless you use a copy of the database, since the first process to open it receives a lock. If you intend to run the Admin and API on separate processes, you must call them with the @@ -157,11 +199,12 @@ to run the Admin and API on separate processes, you must call them with the ### upgrade Will backup your own custom project code (like content, addons, uploads, etc) so -we can safely re-clone Ponzu from the latest version you have or from the network +we can safely re-clone Ponzu from the latest version you have or from the network if necessary. Before running `$ ponzu upgrade`, you should update the `ponzu` -package by running `$ go get -u github.com/ponzu-cms/ponzu/...` +package by running `$ go get -u github.com/ponzu-cms/ponzu/...` Example: + ```bash $ ponzu upgrade ``` @@ -174,6 +217,7 @@ Downloads an addon to GOPATH/src and copies it to the current Ponzu project's `/addons` directory. Example: + ```bash $ ponzu add github.com/bosssauce/fbscheduler ``` @@ -184,11 +228,12 @@ Errors will be reported, but successful add commands return nothing. ### version, v -Prints the version of Ponzu your project is using. Must be called from within a -Ponzu project directory. By passing the `--cli` flag, the `version` command will +Prints the version of Ponzu your project is using. Must be called from within a +Ponzu project directory. By passing the `--cli` flag, the `version` command will print the version of the Ponzu CLI you have installed. Example: + ```bash $ ponzu version Ponzu v0.8.2 @@ -204,17 +249,18 @@ Ponzu v0.9.2 1. Checkout branch ponzu-dev 2. Make code changes 3. Test changes to ponzu-dev branch - - make a commit to ponzu-dev - - to manually test, you will need to use a new copy (ponzu new path/to/code), - but pass the `--dev` flag so that ponzu generates a new copy from the `ponzu-dev` - branch, not master by default (i.e. `$ponzu new --dev /path/to/code`) - - build and run with `$ ponzu build` and `$ ponzu run` -4. To add back to master: - - first push to origin ponzu-dev - - create a pull request - - will then be merged into master + - make a commit to ponzu-dev + - to manually test, you will need to use a new copy (ponzu new path/to/code), + but pass the `--dev` flag so that ponzu generates a new copy from the `ponzu-dev` + branch, not master by default (i.e. `$ponzu new --dev /path/to/code`) + - build and run with `$ ponzu build` and `$ ponzu run` +4. To add back to master: + - first push to origin ponzu-dev + - create a pull request + - will then be merged into master _A typical contribution workflow might look like:_ + ```bash # clone the repository and checkout ponzu-dev $ git clone https://github.com/ponzu-cms/ponzu path/to/local/ponzu # (or your fork) @@ -223,7 +269,7 @@ $ git checkout ponzu-dev # install ponzu with go get or from your own local path $ go get github.com/ponzu-cms/ponzu/... # or -$ cd /path/to/local/ponzu +$ cd /path/to/local/ponzu $ go install ./... # edit files, add features, etc @@ -245,9 +291,10 @@ $ git push origin ponzu-dev **Note:** if you intend to work on your own fork and contribute from it, you will need to also pass `--fork=path/to/your/fork` (using OS-standard filepath structure), where `path/to/your/fork` _must_ be within `$GOPATH/src`, and you are working from a branch -called `ponzu-dev`. +called `ponzu-dev`. + +For example: -For example: ```bash # ($GOPATH/src is implied in the fork path, do not add it yourself) $ ponzu new --dev --fork=github.com/nilslice/ponzu /path/to/new/project