From 78de7ed98abff93fe5fef94907bcfa4f76dcef07 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Sat, 27 May 2017 10:27:51 -0700 Subject: adding docs to repo --- docs/src/CLI/General-Usage.md | 248 ++++++++++++++ docs/src/CLI/Generating-References.md | 123 +++++++ docs/src/Content/An-Overview.md | 87 +++++ docs/src/Content/Extending-Content.md | 44 +++ docs/src/Form-Fields/HTML-Inputs.md | 391 ++++++++++++++++++++++ docs/src/HTTP-APIs/Content.md | 196 +++++++++++ docs/src/HTTP-APIs/File-Metadata.md | 29 ++ docs/src/HTTP-APIs/Search.md | 47 +++ docs/src/Interfaces/API.md | 110 ++++++ docs/src/Interfaces/Editor.md | 70 ++++ docs/src/Interfaces/Format.md | 48 +++ docs/src/Interfaces/Item.md | 517 +++++++++++++++++++++++++++++ docs/src/Interfaces/Search.md | 44 +++ docs/src/Ponzu-Addons/Creating-Addons.md | 6 + docs/src/Ponzu-Addons/Using-Addons.md | 6 + docs/src/Quickstart/Overview.md | 38 +++ docs/src/References/Overview.md | 218 ++++++++++++ docs/src/Running-Backups/Backups.md | 26 ++ docs/src/System-Configuration/Settings.md | 124 +++++++ docs/src/System-Deployment/Docker.md | 34 ++ docs/src/System-Deployment/SysV-Style.md | 76 +++++ docs/src/images/editor-checkbox.png | Bin 0 -> 4759 bytes docs/src/images/editor-file-repeater.png | Bin 0 -> 4976 bytes docs/src/images/editor-file.png | Bin 0 -> 4485 bytes docs/src/images/editor-input-repeater.png | Bin 0 -> 2682 bytes docs/src/images/editor-input.png | Bin 0 -> 1847 bytes docs/src/images/editor-richtext.png | Bin 0 -> 9381 bytes docs/src/images/editor-select-repeater.png | Bin 0 -> 4695 bytes docs/src/images/editor-select.png | Bin 0 -> 3997 bytes docs/src/images/editor-tags.png | Bin 0 -> 5977 bytes docs/src/images/editor-textarea.png | Bin 0 -> 2747 bytes docs/src/images/logo.png | Bin 0 -> 19734 bytes docs/src/images/ponzu-banner.png | Bin 0 -> 909819 bytes docs/src/index.md | 41 +++ 34 files changed, 2523 insertions(+) create mode 100644 docs/src/CLI/General-Usage.md create mode 100644 docs/src/CLI/Generating-References.md create mode 100644 docs/src/Content/An-Overview.md create mode 100644 docs/src/Content/Extending-Content.md create mode 100644 docs/src/Form-Fields/HTML-Inputs.md create mode 100644 docs/src/HTTP-APIs/Content.md create mode 100644 docs/src/HTTP-APIs/File-Metadata.md create mode 100644 docs/src/HTTP-APIs/Search.md create mode 100644 docs/src/Interfaces/API.md create mode 100644 docs/src/Interfaces/Editor.md create mode 100644 docs/src/Interfaces/Format.md create mode 100644 docs/src/Interfaces/Item.md create mode 100644 docs/src/Interfaces/Search.md create mode 100644 docs/src/Ponzu-Addons/Creating-Addons.md create mode 100644 docs/src/Ponzu-Addons/Using-Addons.md create mode 100644 docs/src/Quickstart/Overview.md create mode 100644 docs/src/References/Overview.md create mode 100644 docs/src/Running-Backups/Backups.md create mode 100644 docs/src/System-Configuration/Settings.md create mode 100644 docs/src/System-Deployment/Docker.md create mode 100644 docs/src/System-Deployment/SysV-Style.md create mode 100644 docs/src/images/editor-checkbox.png create mode 100644 docs/src/images/editor-file-repeater.png create mode 100644 docs/src/images/editor-file.png create mode 100644 docs/src/images/editor-input-repeater.png create mode 100644 docs/src/images/editor-input.png create mode 100644 docs/src/images/editor-richtext.png create mode 100644 docs/src/images/editor-select-repeater.png create mode 100644 docs/src/images/editor-select.png create mode 100644 docs/src/images/editor-tags.png create mode 100644 docs/src/images/editor-textarea.png create mode 100644 docs/src/images/logo.png create mode 100644 docs/src/images/ponzu-banner.png create mode 100644 docs/src/index.md (limited to 'docs/src') diff --git a/docs/src/CLI/General-Usage.md b/docs/src/CLI/General-Usage.md new file mode 100644 index 0000000..3a4544f --- /dev/null +++ b/docs/src/CLI/General-Usage.md @@ -0,0 +1,248 @@ +title: Using the Ponzu Command Line Interface (CLI) + +```bash +$ ponzu [flags] command +``` + +## Commands + +### new + +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 +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 + +Generate boilerplate code for various Ponzu components, such as `content`. + +Example: +```bash + generator struct fields and built-in types... + | | + v v +$ ponzu gen content review title:"string" body:"string":richtext rating:"int" + ^ ^ + | | + struct type (optional) input view specifier +``` + +The command above will generate the file `content/review.go` with boilerplate +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"` + Tags []string `json:"tags"` +} +``` + +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 +generate commands return nothing. + +**Input View Specifiers** _(optional)_ + +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 +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) | + +**Generate Content References** + +It's also possible to generate all of the code needed to create references between +your content types. The syntax to do so is below, but refer to the [documentation](/CLI/Generating-References) +for more details: + +```bash +$ ponzu gen c author name:string genre:string:select +$ 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. + +--- + +### 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. + +Optional flags: +- `--gocmd` sets the binary used when executing `go build` within `ponzu` build step + +Example: +```bash +$ ponzu build +(or) +$ ponzu build --gocmd=go1.8rc1 # useful for testing +``` + +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, +if the server should utilize TLS encryption - served over HTTPS, which is +automatically managed using Let's Encrypt (https://letsencrypt.org) + +Optional flags: +- `--port` sets the port on which the server listens for HTTP requests [defaults to 8080] +- `--https-port` sets the port on which the server listens for HTTPS requests [defaults to 443] +- `--https` enables auto HTTPS management via Let's Encrypt (port is always 443) +- `--dev-https` generates self-signed SSL certificates for development-only (port is 10443) + +Example: +```bash +$ ponzu run +(or) +$ ponzu run --port=8080 --https admin,api +(or) +$ ponzu run admin +(or) +$ ponzu run --port=8888 api +(or) +$ ponzu --dev-https run +``` +Defaults to `$ ponzu run --port=8080 admin,api` (running Admin & API on port 8080, without TLS) + +*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 +'ponzu' command independently. + +--- + +### 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 +if necessary. Before running `$ ponzu upgrade`, you should update the `ponzu` +package by running `$ go get -u github.com/ponzu-cms/ponzu/...` + +Example: +```bash +$ ponzu upgrade +``` + +--- + +### add, a + +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 +``` + +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 +print the version of the Ponzu CLI you have installed. + +Example: +```bash +$ ponzu version +Ponzu v0.8.2 +# (or) +$ ponzu version --cli +Ponzu v0.9.2 +``` + +--- + +## Contributing + +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 + +_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) +$ 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 +$ go install ./... + +# edit files, add features, etc +$ git add -A +$ git commit -m 'edited files, added features, etc' + +# now you need to test the feature.. make a new ponzu project, but pass --dev flag +$ ponzu --dev new /path/to/new/project # will create $GOPATH/src/path/to/new/project + +# build & run ponzu from the new project directory +$ cd /path/to/new/project +$ ponzu build && ponzu run + +# push to your origin:ponzu-dev branch and create a PR at ponzu-cms/ponzu +$ git push origin ponzu-dev +# ... go to https://github.com/ponzu-cms/ponzu and create a PR +``` + +**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`. + +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 +``` diff --git a/docs/src/CLI/Generating-References.md b/docs/src/CLI/Generating-References.md new file mode 100644 index 0000000..77cd16a --- /dev/null +++ b/docs/src/CLI/Generating-References.md @@ -0,0 +1,123 @@ +title: How to Generate References using Ponzu CLI + +In Ponzu, users make connections between Content types using references. In order +to use the CLI to generate these references, a slightly different syntax is required. +In all cases, the Content type you wish to reference does not need to exist prior +to the "parent" type referencing it at generate-time, but in the following examples, +the referenced "child" type will be shown before the parent type for clarity. + +## Syntax + +### @ + +The **@** symbol is used to declare that the following name is a reference. The +CLI will take care to parse the name and treat it as a Content type to which the +current type refers. + +### [] + +The `[]`, which if used, is always in front of the **@** symbol. It signifies +that the reference type is a slice or a collection of references. When `[]` +is used, the CLI will automatically generate a `reference.SelectRepeater()` view +for you. + +### ,arg1,arg2,argN + +Immediately following the reference name (after the @ symbol), users may optionally +pass arguments to specify how the reference is displayed in the parent type's +editor. References are included in the parent types editor as a dropdown menu, with +each possible reference as an option. These arguments define what goes inside the +`` text node, as would be seen by an Admin. + +The arguments must be valid JSON struct tag names from the reference type's fields. +Notice in the example below, the `title` and `price` are formatted exactly as they +were in the generate command for the `product` type. + +--- +### + +##### Example + +```bash +$ ponzu gen content product title:string price:int description:string:textarea +$ ponzu gen content catalog year:int products:"[]@product",title,price +``` + +The commands above output the following. For demonstration, we will omit the full +code generated for the `Product`, since the reference is in the `Catalog` type. + +```go +// content/product.go +package content +... + +type Product struct { + item.Item + + Title string `json:"title"` + Price int `json:"price"` + Description string `json:"description"` +} + +... +``` + +```go +package content + +import ( + "fmt" + + "github.com/bosssauce/reference" + + "github.com/ponzu-cms/ponzu/management/editor" + "github.com/ponzu-cms/ponzu/system/item" +) + +type Catalog struct { + item.Item + + Year int `json:"year"` + // all references are stored as []string or string types + Products []string `json:"products"` +} + +func (c *Catalog) MarshalEditor() ([]byte, error) { + view, err := editor.Form(c, + editor.Field{ + View: editor.Input("Year", c, map[string]string{ + "label": "Year", + "type": "text", + "placeholder": "Enter the Year here", + }), + }, + editor.Field{ + // reference.SelectRepeater since []@product was used + View: reference.SelectRepeater("Products", c, map[string]string{ + "label": "Products", + }, + "Product", // generated from @product + `{{ .title }} {{ .price }} `, // generated from ,title,price args + ), + }, + ) + + if err != nil { + return nil, fmt.Errorf("Failed to render Catalog editor view: %s", err.Error()) + } + + return view, nil +} + +func init() { + item.Types["Catalog"] = func() interface{} { return new(Catalog) } +} +``` + +**Note:** +If the reference should be only a single item, rather than a slice (or collection) +of items, omit the `[]`, changing the command to: + +```bash +$ ponzu gen content catalog year:int product:@product,title,price +``` diff --git a/docs/src/Content/An-Overview.md b/docs/src/Content/An-Overview.md new file mode 100644 index 0000000..1e9d53e --- /dev/null +++ b/docs/src/Content/An-Overview.md @@ -0,0 +1,87 @@ +title: Content Overview + +Nearly everything you work on in Ponzu is inside content files on the content types you create. These types must all reside in the `content` package and are the fundamental core of your CMS. In order for Content types to be rendered and managed by the CMS, they must implement the `editor.Editable` interface, and add their own `interface{}` container to the global `item.Types` map. + +Sound like a lot? Don't worry, all of this can be done for you by using the code-generating command line tools that come with Ponzu. + +It is rare to hand-write a new Content type, and should be generated instead! + +### Generating Content types + +To generate content types and boilerplate code, use the Ponzu CLI `generate` command as such: +```bash +$ ponzu generate content post title:string body:string:richtext author:string +``` + +The command above will create a file at `content/post.go` and will generate the following code: +```go +package content + +import ( + "fmt" + + "github.com/ponzu-cms/ponzu/management/editor" + "github.com/ponzu-cms/ponzu/system/item" +) + +type Post struct { + item.Item + + Title string `json:"title"` + Body string `json:"body"` + Author string `json:"author"` +} + +// MarshalEditor writes a buffer of html to edit a Post within the CMS +// and implements editor.Editable +func (p *Post) MarshalEditor() ([]byte, error) { + view, err := editor.Form(p, + // Take note that the first argument to these Input-like functions + // is the string version of each Post field, and must follow + // this pattern for auto-decoding and auto-encoding reasons: + editor.Field{ + View: editor.Input("Title", p, map[string]string{ + "label": "Title", + "type": "text", + "placeholder": "Enter the Title here", + }), + }, + editor.Field{ + View: editor.Richtext("Body", p, map[string]string{ + "label": "Body", + "placeholder": "Enter the Body here", + }), + }, + editor.Field{ + View: editor.Input("Author", p, map[string]string{ + "label": "Author", + "type": "text", + "placeholder": "Enter the Author here", + }), + }, + ) + + if err != nil { + return nil, fmt.Errorf("Failed to render Post editor view: %s", err.Error()) + } + + return view, nil +} + +func init() { + item.Types["Post"] = func() interface{} { return new(Post) } +} +``` + +The code above is the baseline amount required to manage content for the `Post` type from within the CMS. See [Extending Content](/Content/Extending-Content) for information about how to add more functionality to your Content types. + +All content managed by the CMS and exposed via the API is considered an "item", and thus should embed the `item.Item` type. There are many benefits to this, such as becoming automatically sortable by time, and being given default methods that are useful inside and out of the CMS. All content types that are created by the `generate` command via Ponzu CLI will embed Item. + +### Related packages + +The `item` package has a number of useful interfaces, which make it simple to add functionality to all content types and other types that embed Item. + +The `editor` package has the Editable interface, which allows types to create an editor for their fields within the CMS. Additionally, there is a helper function `editor.Form` which simplifies defining the editor's input layout and input types using `editor.Input` and various other functions to make HTML input elements like Select, Checkbox, Richtext, Textarea and more. + +The `api` package has interfaces including `api.Createable` and `api.Mergeable` which make it trivial to accept and approve or reject content submitted from 3rd parties (POST from HTML forms, mobile clients, etc). + diff --git a/docs/src/Content/Extending-Content.md b/docs/src/Content/Extending-Content.md new file mode 100644 index 0000000..91a32a0 --- /dev/null +++ b/docs/src/Content/Extending-Content.md @@ -0,0 +1,44 @@ +title: Extending Content through built-in Interfaces and optional Addons + +Extending your Content types with more features and functionality within the system +is done by implementing the various built-in interfaces provided by Ponzu. To learn +more about interfaces, see [A Tour of Go - Interfaces](https://tour.golang.org/methods/10). + +It is also common to add more advanced functionality to Content types using Addons. Refer to the [Addon documentation](/Ponzu-Addons) for more information about how to use and create Ponzu Addons. + +## [Item Interfaces](/Interfaces/Item) + +All Content types which embed an `item.Item` will implicitly [implement](#) its many +interfaces. In Ponzu, the following interfaces are exported from the `system/item` +package and have a default implementation which can be overridden to change your +content types' functionality within the system. + +- [`item.Pushable`](/Interfaces/Item#itempushable) +- [`item.Hideable`](/Interfaces/Item#itemhideable) +- [`item.Omittable`](/Interfaces/Item#itemomittable) +- [`item.Hookable`](/Interfaces/Item#itemhookable) +- [`item.Identifiable`](/Interfaces/Item#itemidentifiable) +- [`item.Sortable`](/Interfaces/Item#itemsortable) +- [`item.Sluggable`](/Interfaces/Item#itemsluggable) + +## [API Interfaces](/Interfaces/API) + +To enable 3rd-party clients to interact with your Content types, you can extend your types with the API interfaces: + +- [`api.Createable`](/Interfaces/API/#apicreateable) +- [`api.Updateable`](/Interfaces/API/#apiupdateable) +- [`api.Deleteable`](/Interfaces/API/#apideleteable) +- [`api.Trustable`](/Interfaces/API/#apitrustable) + +## [Editor Interfaces](/Interfaces/Editor) + +To manage how content is edited and handled in the CMS, use the following Editor interfaces: + +- [`editor.Editable`](/Interfaces/Editor/#editoreditable) +- [`editor.Mergeable`](/Interfaces/Editor/#editormergeable) + +## [Search Interfaces](/Interfaces/Search) + +To enable and customize full-text search on your content types, use the following interfaces: + +- [`search.Searchable`](/Interfaces/Search/#searchsearchable) \ No newline at end of file diff --git a/docs/src/Form-Fields/HTML-Inputs.md b/docs/src/Form-Fields/HTML-Inputs.md new file mode 100644 index 0000000..b79c5dd --- /dev/null +++ b/docs/src/Form-Fields/HTML-Inputs.md @@ -0,0 +1,391 @@ +title: HTML Input Elements for Ponzu Editor Forms + +Ponzu provides a number of helpful HTML Inputs to create forms which CMS admins +use to manage content. The input functions are typically used inside a Content +type's `MarshalEditor()` func from within an `editor.Form()` - for example: + +```go +// MarshalEditor writes a buffer of html to edit a Post within the CMS +// and implements editor.Editable +func (p *Post) MarshalEditor() ([]byte, error) { + view, err := editor.Form(p, + editor.Field{ // <- editor.Fields contain input-like funcs + View: editor.Input("Title", p, map[string]string{ // <- makes a text input + "label": "Title", + "type": "text", + "placeholder": "Enter the Title here", + }), + }, + editor.Field{ + View: editor.Richtext("Body", p, map[string]string{ // <- makes a WYSIWIG editor + "label": "Body", + "placeholder": "Enter the Body here", + }), + }, + editor.Field{ + View: editor.Input("Author", p, map[string]string{ + "label": "Author", + "type": "text", + "placeholder": "Enter the Author here", + }), + }, + ) + + if err != nil { + return nil, fmt.Errorf("Failed to render Post editor view: %s", err.Error()) + } + + return view, nil +} +``` +--- + +## Field Input Functions + +There are many of these input-like HTML view funcs exported from Ponzu's +`management/editor` package. Below is a list of the built-in options: + +### `editor.Input` +The `editor.Input` function produces a standard text input. + +##### Screenshot +![HTML Input](/images/editor-input.png) + +##### Function Signature +```go +Input(fieldName string, p interface{}, attrs, options map[string]string) []byte +``` + +##### Example + +```go +... +editor.Field{ + View: editor.Input("Title", s, map[string]string{ + "label": "Title", + "type": "text", + "placeholder": "Enter the Title here", + }), +}, +... +``` + +--- + +### `editor.InputRepeater` +The `editor.InputRepeater` function applies a controller UI to the `editor.Input` +view so any arbitrary number of inputs can be added for your field. + +!!! warning "Using Repeaters" + When using the `editor.InputRepeater` make sure it's corresponding field is a **slice `[]T`** + type. You will experience errors if it is not. + +##### Screenshot +![HTML Input](/images/editor-input-repeater.png) + +##### Function Signature +```go +InputRepeater(fieldName string, p interface{}, attrs, options map[string]string) []byte +``` + +##### Example + +```go +... +editor.Field{ + View: editor.InputRepeater("Title", s, map[string]string{ + "label": "Titles", + "type": "text", + "placeholder": "Enter the Title here", + }), +}, +... +``` + +--- + +### `editor.Checkbox` +The `editor.Checkbox` function returns any number of checkboxes in a collection, +defined by the value:name map of options. + +##### Screenshot +![HTML Checkbox](/images/editor-checkbox.png) + +##### Function Signature +```go +Checkbox(fieldName string, p interface{}, attrs, options map[string]string) []byte +``` + +##### Example + +```go +... +editor.Field{ + View: editor.Checkbox("Options", s, map[string]string{ + "label": "Options", + }, map[string]string{ + // "value": "Display Name", + "1": "First", + "2": "Second", + "3": "Third", + }), +}, +... +``` + +--- + +### `editor.Richtext` +The `editor.Richetext` function displays an HTML5 rich text / WYSYWIG editor which +supports text formatting and styling, images, quotes, arbitrary HTML, and more. + +The rich text editor is a modified version of [Summernote](http://summernote.org/) +using a theme called [MaterialNote](https://github.com/Cerealkillerway/materialNote) + +##### Screenshot +![HTML Richtext Input](/images/editor-richtext.png) + +##### Function Signature +```go +Richtext(fieldName string, p interface{}, attrs map[string]string) []byte +``` + +##### Example +```go +... +editor.Field{ + View: editor.Richtext("Opinion", s, map[string]string{ + "label": "Rich Text Editor", + "placeholder": "Enter the Opinion here", + }), +}, +... +``` + +--- + +### `editor.Tags` +The `editor.Tags` function returns a container input element for lists of arbitrary +bits of information. + +##### Screenshot +![HTML Tags Input](/images/editor-tags.png) + +##### Function Signature +```go +Tags(fieldName string, p interface{}, attrs map[string]string) []byte +``` + +##### Example +```go +... +editor.Field{ + View: editor.Tags("Category", s, map[string]string{ + "label": "Tags", + "placeholder": "+Category", + }), +}, +... +``` + +--- + +### `editor.File` +The `editor.File` function returns an HTML file upload element, which saves files +into the `/uploads` directory, and can be viewed from the "Uploads" section in the +Admin dashboard. See also the [File Metadata API](/HTTP-APIs/File-Metadata.md). + +!!! warning "Field Type" + When using the `editor.File` function, its corresponding field type must be + a **`string`**, as files will be stored as URL paths in the database. + +##### Screenshot +![HTML File Input](/images/editor-file.png) + +##### Function Signature +```go +File(fieldName string, p interface{}, attrs map[string]string) []byte +``` + +##### Example +```go +... +editor.Field{ + View: editor.File("Photo", s, map[string]string{ + "label": "File Upload", + "placeholder": "Upload the Photo here", + }), +}, +... +``` + +--- + +### `editor.FileRepeater` +The `editor.FileRepeater` function applies a controller UI to the `editor.File` +view so any arbitrary number of uploads can be added for your field. + +!!! warning "Using Repeaters" + When using the `editor.FileRepeater` make sure it's corresponding field is a **slice `[]string`** + type. You will experience errors if it is not. + +##### Screenshot +![HTML File Input](/images/editor-file-repeater.png) + +##### Function Signature +```go +FileRepeater(fieldName string, p interface{}, attrs map[string]string) []byte +``` + +##### Example +```go +... +editor.Field{ + View: editor.FileRepeater("Photo", s, map[string]string{ + "label": "File Upload Repeater", + "placeholder": "Upload the Photo here", + }), +}, +... +``` + +--- + +### `editor.Select` +The `editor.Select` function returns a single HTML select input with options +as defined in the `options map[string]string` parameter of the function call. + +##### Screenshot +![HTML Select Input](/images/editor-select.png) + +##### Function Signature +```go +func Select(fieldName string, p interface{}, attrs, options map[string]string) []byte +``` + +##### Example +```go +... +editor.Field{ + View: editor.Select("Rating", s, map[string]string{ + "label": "Select Dropdown", + }, map[string]string{ + // "value": "Display Name", + "G": "G", + "PG": "PG", + "PG-13": "PG-13", + "R": "R", + }), +}, +... +``` + +--- + +### `editor.SelectRepeater` +The `editor.SelectRepeater` function applies a controller UI to the `editor.Select` +view so any arbitrary number of dropdowns can be added for your field. + +##### Screenshot +![HTML Select Input](/images/editor-select-repeater.png) + +##### Function Signature +```go +func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]string) []byte +``` + +##### Example +```go +... +editor.Field{ + View: editor.SelectRepeater("Rating", s, map[string]string{ + "label": "Select Dropdown Repeater", + }, map[string]string{ + // "value": "Display Name", + "G": "G", + "PG": "PG", + "PG-13": "PG-13", + "R": "R", + }), +}, +... +``` + +--- + +### `editor.Textarea` +The `editor.Textarea` function returns an HTML textarea input to add unstyled text +blocks. Newlines in the textarea are preserved. + +##### Screenshot +![HTML Textarea Input](/images/editor-textarea.png) + +##### Function Signature +```go +Textarea(fieldName string, p interface{}, attrs map[string]string) []byte +``` + +##### Example +```go +... +editor.Field{ + View: editor.Textarea("Readme", s, map[string]string{ + "label": "Textarea", + "placeholder": "Enter the Readme here", + }), +}, +... +``` + +--- + +## Data References +It is common to want to keep a reference from one Content type to another. To do +this in Ponzu, use the [`bosssauce/reference`](https://github.com/bosssauce/reference) +package. It comes pre-installed with Ponzu as an ["Addon"](/Ponzu-Addons/Using-Addons). + +### `reference.Select` + +##### Screenshot +![HTML Select Input](/images/editor-select.png) + +##### Function Signature +```go +func Select(fieldName string, p interface{}, attrs map[string]string, contentType, tmplString string) []byte +``` + +##### Example +```go +... +editor.Field{ + View: reference.Select("DirectedBy", s, map[string]string{ + "label": "Select Dropdown", + }, "Director", `{{.last-name}}, {{.first_name}}`), +}, +... +``` + +--- + +### `reference.SelectRepeater` + +##### Screenshot +![HTML Select Input](/images/editor-select-repeater.png) + +##### Function Signature +```go +func SelectRepeater(fieldName string, p interface{}, attrs map[string]string, contentType, tmplString string) []byte +``` + +##### Example +```go +... +editor.Field{ + View: reference.SelectRepeater("PlacesFilmed", s, map[string]string{ + "label": "Select Dropdown Repeater", + }, "Location", `{{.name}}, {{.region}}`), +}, +... +``` + +--- diff --git a/docs/src/HTTP-APIs/Content.md b/docs/src/HTTP-APIs/Content.md new file mode 100644 index 0000000..6a3e387 --- /dev/null +++ b/docs/src/HTTP-APIs/Content.md @@ -0,0 +1,196 @@ +title: Content HTTP API + + +Ponzu provides a read & write HTTP API to access and interact with content on a +system. By default, write access (including create, update and delete) and search +are disabled. See the section on Ponzu's [API Interfaces](/Interfaces/API) to learn +more about how to enable these endpoints. + +--- + +## Endpoints + +### Get Content by Type +GET `/api/content?type=&id=` + +##### Sample Response +```javascript +{ + "data": [ + { + "uuid": "024a5797-e064-4ee0-abe3-415cb6d3ed18", + "id": 6, + "slug": "item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18" // customizable + "timestamp": 1493926453826, // milliseconds since Unix epoch + "updated": 1493926453826, + // your content data..., + } + ] +} +``` + +--- + +### Get Contents by Type +GET `/api/contents?type=` + + - optional params: + 1. `order` (string: ASC / DESC, default: DESC) + 2. `count` (int: -1 - N, default: 10, -1 returns all) + 3. `offset` (int: 0 - N, default: 0) +##### Sample Response +```javascript +{ + "data": [ + { + "uuid": "024a5797-e064-4ee0-abe3-415cb6d3ed18", + "id": 6, + "slug": "item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18", // customizable + "timestamp": 1493926453826, // milliseconds since Unix epoch + "updated": 1493926453826, + // your content data..., + }, + { + "uuid": "5a9177c7-634d-4fb1-88a6-ef6c45de797c", + "id": 7, + "slug": "item-id-5a9177c7-634d-4fb1-88a6-ef6c45de797c", // customizable + "timestamp": 1493926453826, // milliseconds since Unix epoch + "updated": 1493926453826, + // your content data..., + }, + // more objects... + ] +} +``` + +--- + +### Get Content by Slug +GET `/api/content?slug=` + +##### Sample Response +```javascript +{ + "data": [ + { + "uuid": "024a5797-e064-4ee0-abe3-415cb6d3ed18", + "id": 6, + "slug": "item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18", // customizable + "timestamp": 1493926453826, // milliseconds since Unix epoch + "updated": 1493926453826, + // your content data..., + } + ] +} +``` + +--- + +### New Content +POST `/api/content/create?type=` + + - Type must implement [`api.Createable`](/Interfaces/API#apicreateable) interface +!!! note "Request Data Encoding" + Request must be `multipart/form-data` encoded. If not, a `400 Bad Request` + Response will be returned. + +##### Sample Response +```javascript +{ + "data": [ + { + "id": 6, // will be omitted if status is pending + "type": "Review", + "status": "public" + } + ] +} +``` + +--- + +### Update Content +POST `/api/content/update?type=&id=` + + - Type must implement [`api.Updateable`](/Interfaces/API#apiupdateable) interface +!!! note "Request Data Encoding" + Request must be `multipart/form-data` encoded. If not, a `400 Bad Request` + Response will be returned. + +##### Sample Response +```javascript +{ + "data": [ + { + "id": 6, + "type": "Review", + "status": "public" + } + ] +} +``` + +--- + +### Delete Content +POST `/api/content/delete?type=&id=` + + - Type must implement [`api.Deleteable`](/Interfaces/API#apideleteable) interface +!!! note "Request Data Encoding" + Request must be `multipart/form-data` encoded. If not, a `400 Bad Request` + Response will be returned. + +##### Sample Response +```javascript +{ + "data": [ + { + "id": 6, + "type": "Review", + "status": "deleted" + } + ] +} +``` + +--- + +### Additional Information + +All API endpoints are CORS-enabled (can be disabled in configuration at run-time) and API requests are recorded by your system to generate graphs of total requests and unique client requests within the Admin dashboard. + +#### Response Headers +The following headers are common across all Ponzu API responses. Some of them can be modified +in the [system configuration](/System-Configuration/Settings) while your system is running. + +##### HTTP/1.1 +``` +HTTP/1.1 200 OK +Access-Control-Allow-Headers: Accept, Authorization, Content-Type +Access-Control-Allow-Origin: * +Cache-Control: max-age=2592000, public +Content-Encoding: gzip +Content-Type: application/json +Etag: MTQ5Mzk0NTYzNQ== +Vary: Accept-Encoding +Date: Fri, 05 May 2017 01:15:49 GMT +Content-Length: 199 +``` + +##### HTTP/2 +``` +access-control-allow-headers: Accept, Authorization, Content-Type +access-control-allow-origin: * +cache-control: max-age=2592000, public +content-encoding: gzip +content-length: 199 +content-type: application/json +date: Fri, 05 May 2017 01:38:11 GMT +etag: MTQ5Mzk0ODI4MA== +status: 200 +vary: Accept-Encoding +``` + +#### Helpful links +[Typewriter](https://github.com/natdm/typewriter) +Generate & sync front-end data structures from Ponzu content types. ([Ponzu example](https://github.com/natdm/typewriter/blob/master/EXAMPLES.md#example-use-in-a-package-like-ponzu)) diff --git a/docs/src/HTTP-APIs/File-Metadata.md b/docs/src/HTTP-APIs/File-Metadata.md new file mode 100644 index 0000000..19d6ab6 --- /dev/null +++ b/docs/src/HTTP-APIs/File-Metadata.md @@ -0,0 +1,29 @@ +title: File Metadata HTTP API + +Ponzu provides a read-only HTTP API to get metadata about the files that have been uploaded to your system. As a security and bandwidth abuse precaution, the API is only queryable by "slug" which is the normalized filename of the uploaded file. + +--- + +### Endpoints + +#### Get File by Slug (single item) +GET `/api/uploads?slug=` + +##### Sample Response +```javascript +{ + "data": [ + { + "uuid": "024a5797-e064-4ee0-abe3-415cb6d3ed18", + "id": 6, + "slug": "filename.jpg", + "timestamp": 1493926453826, // milliseconds since Unix epoch + "updated": 1493926453826, + "name": "filename.jpg", + "path": "/api/uploads/2017/05/filename.jpg", + "content_length": 357557, + "content_type": "image/jpeg", + } + ] +} +``` \ No newline at end of file diff --git a/docs/src/HTTP-APIs/Search.md b/docs/src/HTTP-APIs/Search.md new file mode 100644 index 0000000..2939cce --- /dev/null +++ b/docs/src/HTTP-APIs/Search.md @@ -0,0 +1,47 @@ +title: Full-text Search HTTP API + +Ponzu provides a read-only HTTP API to search the contents of your system's database. +Full-text search is made possible by the use of [Bleve](http://blevesearch.com), +which handles the indexing and querying. + +--- + +### Endpoints + +#### Search Content + +GET `/api/search?type=&q=` + +!!! warning "Search must be enabled individually for each Content type" + - Search is not on by default to protect your data in case it shouldn't be indexed and published via the API. + - `SearchMapping()` is implemented with default mapping (ideal for 99% of use cases). + - To enable search, add a `IndexContent() bool` method to your content type and return `true` (default implementation returns false). + +- `` must implement [db.Searchable](/Interfaces/Search/#searchsearchable) + +- Search is currently limited to single `` per request + +- `` documentation here: [Bleve Docs - Query String](http://www.blevesearch.com/docs/Query-String-Query/) + +- Search results are formatted exactly the same as standard Content API calls, so you don't need to change your client data model + +- Search handler will respect other interface implementations on your content, including: + - [`item.Hideable`](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Hideable) + - [`item.Omittable`](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Omittable) + - [`item.Pushable`](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Pushable) _(Note: only the first search result will be pushed)_ + +##### Sample Response +```javascript +{ + "data": [ + { + "uuid": "024a5797-e064-4ee0-abe3-415cb6d3ed18", + "id": 6, + "slug": "item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18", // customizable + "timestamp": 1493926453826, // milliseconds since Unix epoch + "updated": 1493926453826, + // your content data..., + } + ] +} +``` diff --git a/docs/src/Interfaces/API.md b/docs/src/Interfaces/API.md new file mode 100644 index 0000000..1d9ab82 --- /dev/null +++ b/docs/src/Interfaces/API.md @@ -0,0 +1,110 @@ +title: API Package Interfaces + +Ponzu provides a set of interfaces from the `system/api` package which enable +richer interaction with your system from external clients. If you need to allow +3rd-party apps to manage content, use the following interfaces. + +The API interfaces adhere to a common function signature, expecting an +`http.ResponseWriter` and `*http.Request` as arguments and returning an `error`. +This provides Ponzu developers with full control over the request/response +life-cycle. + +--- + +## Interfaces + +### [api.Createable](https://godoc.org/github.com/ponzu-cms/ponzu/system/api#Createable) +Externalable enables 3rd-party clients (outside the CMS) to send content via a +`multipart/form-data` encoded `POST` request to a specific endpoint: +`/api/content/create?type=`. When `api.Createable` is implemented, content +will be saved from the request in a "Pending" section which will is visible only +within the CMS. + +To work with "Pending" data, implement the [`editor.Mergeable`](/Interfaces/Editor#editormergeable) +interface, which will add "Approve" and "Reject" buttons to your Content types' +editor -- or implement [`api.Trustable`](#apitrustable) to bypass +the "Pending" section altogether and become "Public" immediately. + +##### Method Set + +```go +type Createable interface { + Create(http.ResponseWriter, *http.Request) error +} +``` + +##### Implementation +```go +func (p *Post) Create(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +--- + +### [api.Updateable](https://godoc.org/github.com/ponzu-cms/ponzu/system/api#Updateable) +Updateable enables 3rd-party clients (outside the CMS) to update existing content +via a `multipart/form-data` encoded `POST` request to a specific endpoint: +`/api/content/update?type=&id=`. Request validation should be employed +otherwise any client could change data in your database. + +##### Method Set + +```go +type Updateable interface { + Update(http.ResponseWriter, *http.Request) error +} +``` + +##### Implementation +```go +func (p *Post) Update(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +--- + +### [api.Deleteable](https://godoc.org/github.com/ponzu-cms/ponzu/system/api#Deleteable) +Updateable enables 3rd-party clients (outside the CMS) to delete existing content +via a `multipart/form-data` encoded `POST` request to a specific endpoint: +`/api/content/delete?type=&id=`. Request validation should be employed +otherwise any client could delete data from your database. + +##### Method Set + +```go +type Deleteable interface { + Delete(http.ResponseWriter, *http.Request) error +} +``` + +##### Implementation +```go +func (p *Post) Delete(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +--- + +### [api.Trustable](https://godoc.org/github.com/ponzu-cms/ponzu/system/api#Trustable) +Trustable provides a way for submitted content (via `api.Createable`) to bypass +the `editor.Mergeable` step in which CMS end-users must manually click the +"Approve" button in order for content to be put in the "Public" section and access +via the content API endpoints. `api.Trustable` has a single method: `AutoApprove` +which will automatically approve content, bypassing the "Pending" section +altogether. + +```go +type Trustable interface { + AutoApprove(http.ResponseWriter, *http.Request) error +} +``` + +##### Implementation +```go +func (p *Post) AutoApprove(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` \ No newline at end of file diff --git a/docs/src/Interfaces/Editor.md b/docs/src/Interfaces/Editor.md new file mode 100644 index 0000000..63d3ceb --- /dev/null +++ b/docs/src/Interfaces/Editor.md @@ -0,0 +1,70 @@ +title: Editor Package Interfaces + +Ponzu provides a set of interfaces from the `management/editor` package which +extend the system's functionality and determine how content editors are rendered +within the CMS. + +--- + +## Interfaces + +### [editor.Editable](https://godoc.org/github.com/ponzu-cms/ponzu/management/editor#Editable) + +Editable determines what `[]bytes` are rendered inside the editor page. Use +Edtiable on anything inside your CMS that you want to provide configuration, editable +fields, or any HTML/markup to display to an end-user. + +!!! note "Implementing `editor.Editable`" + Most of the time, Ponzu developers generate the majority of this code using + the Ponzu CLI [`generate` command](/CLI/Usage). + +##### Method Set + +```go +type Editable interface { + MarshalEditor() ([]byte, error) +} +``` + +##### Implementation + +```go +func (p *Post) MarshalEditor() ([]byte, error) { + // The editor.Form func sets up a structured UI with default styles and form + // elements based on the fields provided. Most often, Ponzu developers will + // have the `$ ponzu generate` command generate the MarshalEditor func and + // its internal form fields + view, err := editor.Form(p, + editor.Field{ + View: editor.Input("Name", p, map[string]string{ + "label": "Name", + "type": "text", + "placeholder": "Enter the Name here", + }), + }, + ) +} +``` + +!!! note "MarshalEditor() & View Rendering" + Although it is common to use the `editor.Form` and `editor.Fields` to structure your content editor inside `MarshalEditor()`, the method signature defines that its return value needs only to be `[]byte, error`. Keep in mind that you can return a `[]byte` of any raw HTML or other markup to be rendered in the editor view. + +--- + +### [editor.Mergeable](https://godoc.org/github.com/ponzu-cms/ponzu/management/editor#Mergeable) + +Mergable enables a CMS end-user to merge the "Pending" content from an outside source into the "Public" section, and thus making it visible via the public content API. It also allows the end-user to reject content. "Approve" and "Reject" buttons will be visible on the edit page for content submitted. + +##### Method Set +```go +type Mergeable interface { + Approve(http.ResponseWriter, *http.Request) error +} +``` + +##### Example +```go +func (p *Post) Approve(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` diff --git a/docs/src/Interfaces/Format.md b/docs/src/Interfaces/Format.md new file mode 100644 index 0000000..8a259d3 --- /dev/null +++ b/docs/src/Interfaces/Format.md @@ -0,0 +1,48 @@ +title: Format Package Interfaces + +Ponzu provides a set of interfaces from the `management/format` package which +determine how content data should be converted and formatted for exporting via +the Admin interface. + +--- + +## Interfaces + +### [format.CSVFormattable](https://godoc.org/github.com/ponzu-cms/ponzu/management/format#CSVFormattable) + +CSVFormattable controls if an "Export" button is added to the contents view for +a Content type in the CMS to export the data to CSV. If it is implemented, a +button will be present beneath the "New" button per Content type. + +##### Method Set + +```go +type CSVFormattable interface { + FormatCSV() []string +} +``` + +##### Implementation + +```go +func (p *Post) FormatCSV() []string { + // []string contains the JSON struct tags generated for your Content type + // implementing the interface + return []string{ + "id", + "timestamp", + "slug", + "title", + "photos", + "body", + "written_by", + } +} +``` + +!!! note "FormatCSV() []string" + Just like other Ponzu content extension interfaces, like `Push()`, you will + return the JSON struct tags for the fields you want exported to the CSV file. + These will also be the "header" row in the CSV file to give titles to the file + columns. Keep in mind that all of item.Item's fields are available here as well. + diff --git a/docs/src/Interfaces/Item.md b/docs/src/Interfaces/Item.md new file mode 100644 index 0000000..32f250b --- /dev/null +++ b/docs/src/Interfaces/Item.md @@ -0,0 +1,517 @@ +title: Item Package Interfaces + +Ponzu provides a set of interfaces from the `system/item` package which extend +the functionality of the content in your system and how it interacts with other +components inside and outside of Ponzu. + +--- + +## Interfaces + +### [item.Pushable](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Pushable) +Pushable, if [HTTP/2 Server Push](https://http2.github.io/http2-spec/#PushResources) +is supported by the client, can tell a handler which resources it would like to +have "pushed" preemptively to the client. This saves follow-on roundtrip requests +for other items which are referenced by the Pushable item. The `Push` method, the +only method in Pushable, must return a `[]string` containing the `json` field tags +of the referenced items within the type. + +##### Method Set +```go +type Pushable interface { + // the values contained in fields returned by Push must be URL paths + Push() []string +} +``` + +##### Implementation +The `Push` method returns a `[]string` containing the `json` tag field names for +which you want to have pushed to a supported client. The values for the field +names **must** be URL paths, and cannot be from another origin. + +```go +type Post struct { + item.Item + + HeaderPhoto string `json:"header_photo"` + Author string `json:"author"` // reference `/api/content/?type=Author&id=2` + // ... +} + +func (p *Post) Push() []string { + return []string{ + "header_photo", + "author", + } +} +``` + +--- + +### [item.Hideable](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Hideable) +Hideable tells an API handler that data of this type shouldn’t be exposed outside +the system. Hideable types cannot be used as references (relations in Content types). +The `Hide` method, the only method in Hideable, takes an `http.ResponseWriter, *http.Request` +and returns an `error`. A special error in the `items` package, `ErrAllowHiddenItem` +can be returned as the error from Hide to instruct handlers to show hidden +content in specific cases. + +##### Method Set +```go +type Hideable interface { + Hide(http.ResponseWriter, *http.Request) error +} +``` + +##### Implementation +```go +func (p *Post) Hide(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +--- + +### [item.Omittable](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Omittable) +Omittable tells a content API handler to keep certain fields from being exposed +through the JSON response. It's single method, `Omit` takes no arguments and +returns a `[]string` which must be made up of the JSON struct tags for the type +containing fields to be omitted. + +##### Method Set +```go +type Omittable interface { + Omit() []string +} +``` + +##### Implementation +```go +type Post struct { + item.Item + + HeaderPhoto string `json:"header_photo"` + Author string `json:"author"` + // ... +} + +func (p *Post) Omit() []string { + return []string{ + "header_photo", + "author", + } +} +``` + +--- + +### [item.Hookable](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Hookable) +Hookable provides lifecycle hooks into the http handlers which manage Save, Delete, +Approve, and Reject routines. All methods in its set take an +`http.ResponseWriter, *http.Request` and return an `error`. + +##### Method Set + +```go +type Hookable interface { + BeforeAPICreate(http.ResponseWriter, *http.Request) error + AfterAPICreate(http.ResponseWriter, *http.Request) error + + BeforeAPIUpdate(http.ResponseWriter, *http.Request) error + AfterAPIUpdate(http.ResponseWriter, *http.Request) error + + BeforeAPIDelete(http.ResponseWriter, *http.Request) error + AfterAPIDelete(http.ResponseWriter, *http.Request) error + + BeforeAdminCreate(http.ResponseWriter, *http.Request) error + AfterAdminCreate(http.ResponseWriter, *http.Request) error + + BeforeAdminUpdate(http.ResponseWriter, *http.Request) error + AfterAdminUpdate(http.ResponseWriter, *http.Request) error + + BeforeAdminDelete(http.ResponseWriter, *http.Request) error + AfterAdminDelete(http.ResponseWriter, *http.Request) error + + BeforeSave(http.ResponseWriter, *http.Request) error + AfterSave(http.ResponseWriter, *http.Request) error + + BeforeDelete(http.ResponseWriter, *http.Request) error + AfterDelete(http.ResponseWriter, *http.Request) error + + BeforeApprove(http.ResponseWriter, *http.Request) error + AfterApprove(http.ResponseWriter, *http.Request) error + + BeforeReject(http.ResponseWriter, *http.Request) error + AfterReject(http.ResponseWriter, *http.Request) error + + // Enable/Disable used exclusively for addons + BeforeEnable(http.ResponseWriter, *http.Request) error + AfterEnable(http.ResponseWriter, *http.Request) error + + BeforeDisable(http.ResponseWriter, *http.Request) error + AfterDisable(http.ResponseWriter, *http.Request) error +} +``` + +##### Implementations + +#### BeforeAPICreate +BeforeAPICreate is called before an item is created via a 3rd-party client. If a +non-nil `error` value is returned, the item will not be created/saved. + +```go +func (p *Post) BeforeAPICreate(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterAPICreate +AfterAPICreate is called after an item has been created via a 3rd-party client. +At this point, the item has been saved to the database. If a non-nil `error` is +returned, it will respond to the client with an empty response, so be sure to +use the `http.ResponseWriter` from within your hook appropriately. + +```go +func (p *Post) AfterAPICreate(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeApprove +BeforeApprove is called before an item is merged as "Public" from its prior +status as "Pending". If a non-nil `error` value is returned, the item will not be +appproved, and an error message is displayed to the Admin. + +```go +func (p *Post) BeforeApprove(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterApprove +AfterApprove is called after an item has been merged as "Public" from its prior +status as "Pending". If a non-nil `error` is returned, an error message is +displayed to the Admin, however the item will already be irreversibly merged. + +```go +func (p *Post) AfterApprove(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeReject +BeforeReject is called before an item is rejected and deleted by default. To reject +an item, but not delete it, return a non-nil `error` from this hook - doing so +will allow the hook to do what you want it to do prior to the return, but the item +will remain in the "Pending" section. + +```go +func (p *Post) BeforeReject(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterReject +AfterReject is called after an item is rejected and has been deleted. + +```go +func (p *Post) AfterReject(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeSave +BeforeSave is called before any CMS Admin or 3rd-party client triggers a save to +the database. This could be done by clicking the 'Save' button on a Content editor, +or by a API call to Create or Update the Content item. By returning a non-nil +`error` value, the item will not be saved. + +```go +func (p *Post) BeforeSave(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterSave +AfterSave is called after any CMS Admin or 3rd-party client triggers a save to +the database. This could be done by clicking the 'Save' button on a Content editor, +or by a API call to Create or Update the Content item. + +```go +func (p *Post) AfterSave(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeDelete +BeforeDelete is called before any CMS Admin or 3rd-party client triggers a delete to +the database. This could be done by clicking the 'Delete' button on a Content editor, +or by a API call to Delete the Content item. By returning a non-nil `error` value, +the item will not be deleted. + +```go +func (p *Post) BeforeDelete(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterDelete +AfterSave is called after any CMS Admin or 3rd-party client triggers a delete to +the database. This could be done by clicking the 'Delete' button on a Content editor, +or by a API call to Delete the Content item. + +```go +func (p *Post) AfterDelete(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeAPIDelete +BeforeDelete is only called before a 3rd-party client triggers a delete to the +database. By returning a non-nil `error` value, the item will not be deleted. + +```go +func (p *Post) BeforeAPIDelete(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterAPIDelete +AfterAPIDelete is only called after a 3rd-party client triggers a delete to the +database. + +```go +func (p *Post) AfterAPIDelete(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeAPIUpdate +BeforeAPIUpdate is only called before a 3rd-party client triggers an update to +the database. By returning a non-nil `error` value, the item will not be updated. + +```go +func (p *Post) BeforeAPIUpdate(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterAPIUpdate +AfterAPIUpdate is only called after a 3rd-party client triggers an update to +the database. + +```go +func (p *Post) AfterAPIUpdate(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeAdminCreate +BeforeAdminCreate is only called before a CMS Admin creates a new Content item. +It is not called for subsequent saves to the item once it has been created and +assigned an ID. By returning a non-nil `error` value, the item will not be created. + +```go +func (p *Post) BeforeAdminCreate(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterAdminCreate +AfterAdminCreate is only called after a CMS Admin creates a new Content item. +It is not called for subsequent saves to the item once it has been created and +assigned an ID. + +```go +func (p *Post) AfterAdminCreate(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeAdminUpdate +BeforeAdminUpdate is only called before a CMS Admin updates a Content item. By +returning a non-nil `error`, the item will not be updated. + +```go +func (p *Post) BeforeAdminUpdate(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterAdminUpdate +AfterAdminUpdate is only called after a CMS Admin updates a Content item. + +```go +func (p *Post) AfterAdminUpdate(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeAdminDelete +BeforeAdminDelete is only called before a CMS Admin deletes a Content item. By +returning a non-nil `error` value, the item will not be deleted. + +```go +func (p *Post) BeforeAdminDelete(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### AfterAdminDelete +AfterAdminDelete is only called after a CMS Admin deletes a Content item. + +```go +func (p *Post) AfterAdminDelete(res http.ResponseWriter, req *http.Request) error { + return nil +} +``` + +#### BeforeEnable +BeforeEnable is only applicable to Addon items, and is called before the addon +changes status to "Enabled". By returning a non-nil `error` value, the addon +will not become enabled. + +```go +func (p *Post) BeforeEnable(http.ResponseWriter, *http.Request) error { + return nil +} +``` + +#### AfterEnable +AfterEnable is only applicable to Addon items, and is called after the addon +changes status to "Enabled". +```go +func (p *Post) AfterEnable(http.ResponseWriter, *http.Request) error { + return nil +} +``` + +#### BeforeDisable +BeforeDisable is only applicable to Addon items, and is called before the addon +changes status to "Disabled". By returning a non-nil `error` value, the addon +will not become disabled. +```go +func (p *Post) BeforeDisable(http.ResponseWriter, *http.Request) error { + return nil +} +``` + +#### AfterDisable +AfterDisable is only applicable to Addon items, and is called after the addon +changes status to "Disabled". +```go +func (p *Post) AfterDisable(http.ResponseWriter, *http.Request) error { + return nil +} +``` + +Hookable is implemented by Item by default as no-ops which are expected to be overridden. + +!!! note "Note" + returning an error from any of these `Hookable` methods will end the request, + causing it to halt immediately after the hook. For example, returning an `error` + from `BeforeDelete` will result in the content being kept in the database. + The same logic applies to all of these interface methods that return an error + - **the error defines the behavior**. + +--- + +### [item.Identifiable](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Identifiable) +Identifiable enables a struct to have its ID set/get. Typically this is done to set an ID to -1 indicating it is new for DB inserts, since by default a newly initialized struct would have an ID of 0, the int zero-value, and BoltDB's starting key per bucket is 0, thus overwriting the first record. +Most notable, Identifiable’s `String` method is used to set a meaningful display name for an Item. `String` is called by default in the Admin dashboard to show the Items of certain types, and in the default creation of an Item’s slug. +Identifiable is implemented by Item by default. + +##### Method Set +```go +type Identifiable interface { + ItemID() int + SetItemID(int) + UniqueID() uuid.UUID + String() string +} +``` + +##### Implementation +`item.Identifiable` has a default implementation in the `system/item` package. +It is not advised to override these methods, with the exception of `String()`, +which is commonly used to set the display name of Content items when listed in +the CMS, and to customize slugs. + +```go +func (i Item) ItemID() int { + return i.ID +} + +func (i *Item) SetItemID(id int) { + i.ID = id +} + +func (i Item) UniqueID() uuid.UUID { + return i.UUID +} + +func (i Item) String() string { + return fmt.Sprintf("Item ID: %s", i.UniqueID()) +} +``` +--- + +### [item.Sluggable](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Sluggable) +Sluggable makes a struct locatable by URL with it's own path. As an Item implementing Sluggable, slugs may overlap. If this is an issue, make your content struct (or one which embeds Item) implement Sluggable and it will override the slug created by Item's `SetSlug` method with your own. +It is not recommended to override `SetSlug`, but rather the `String` method on your content struct, which will have a similar, more predictable effect. +Sluggable is implemented by Item by default. + +##### Method Set +```go +type Sluggable interface { + SetSlug(string) + ItemSlug() string +} +``` + +##### Implementation +`item.Sluggable` has a default implementation in the `system/item` package. It is +possible to override these methods on your own Content types, but beware, behavior +is undefined. It is tempting to override the `SetSlug()` method to customize your +Content item slug, but try first to override the `String()` method found in the +`item.Identifiable` interface instead. If you don't get the desired results, try +`SetSlug()`. + +```go +func (i *Item) SetSlug(slug string) { + i.Slug = slug +} + +func (i *Item) ItemSlug() string { + return i.Slug +} +``` +--- + + +### [item.Sortable](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Sortable) +Sortable enables items to be sorted by time, as per the sort.Interface interface. Sortable is implemented by Item by default. + +##### Method Set +```go +type Sortable interface { + Time() int64 + Touch() int64 +} +``` + +##### Implementation +`item.Sortable` has a default implementation in the `system/item` package. It is +possible to override these methods on your own Content type, but beware, behavior +is undefined. + +```go +func (i Item) Time() int64 { + return i.Timestamp +} + +func (i Item) Touch() int64 { + return i.Updated +} +``` + diff --git a/docs/src/Interfaces/Search.md b/docs/src/Interfaces/Search.md new file mode 100644 index 0000000..a7cd0d1 --- /dev/null +++ b/docs/src/Interfaces/Search.md @@ -0,0 +1,44 @@ +title: Search Package Interfaces + +Ponzu provides a set of interfaces from the `system/search` package to enable and customize full-text search access to content in your system. **Search is not enabled by default**, and must be enabled per Content type individually. + +## Interfaces + +### [search.Searchable](https://godoc.org/github.com/ponzu-cms/ponzu/system/search#Searchable) +Searchable determines how content is indexed and whether the system should index the content when it is created and updated or be removed from the index when content is deleted. + +!!! warning "" + Search is **disabled** for all Content items by default. Each Content item that should be indexed and searchable must implement the `search.Searchable` interface. + +##### Method Set + +```go +type Searchable interface { + SearchMapping() (*mapping.IndexMappingImpl, error) + IndexContent() bool +} +``` + +By default, Ponzu sets up the [Bleve's](http://blevesearch.com) "default mapping", which is typically what you want for most content-based systems. This can be overridden by implementing your own `SearchMapping() (*mapping.IndexMappingImpl, error)` method on your Content type. + +This way, all you need to do to get full-text search is to add the `IndexContent() bool` method to each Content type you want search enabled. Return `true` from this method to enable search. + + +##### Example +```go +// ... + +type Song struct { + item.Item + + Name string `json:"name"` + // ... +} + +func (s *Song) IndexContent() bool { + return true +} +``` + +!!! tip "Indexing Existing Content" + If you previously had search disabled and had already added content to your system, you will need to re-index old content items in your CMS. Otherwise, they will not show up in search queries.. This requires you to manually open each item and click 'Save'. This could be scripted and Ponzu _might_ ship with a re-indexing function at some point in the fututre. diff --git a/docs/src/Ponzu-Addons/Creating-Addons.md b/docs/src/Ponzu-Addons/Creating-Addons.md new file mode 100644 index 0000000..c6aed40 --- /dev/null +++ b/docs/src/Ponzu-Addons/Creating-Addons.md @@ -0,0 +1,6 @@ +title: How to create Ponzu Addons + +# Coming soon + +For a reference to creating your own addons, see: +[https://github.com/bosssauce/fbscheduler](https://github.com/bosssauce/fbscheduler) diff --git a/docs/src/Ponzu-Addons/Using-Addons.md b/docs/src/Ponzu-Addons/Using-Addons.md new file mode 100644 index 0000000..78200c5 --- /dev/null +++ b/docs/src/Ponzu-Addons/Using-Addons.md @@ -0,0 +1,6 @@ +title: How to use Ponzu Addons + +# Coming soon + +For a reference to creating your own addons, see: +[https://github.com/bosssauce/fbscheduler](https://github.com/bosssauce/fbscheduler) diff --git a/docs/src/Quickstart/Overview.md b/docs/src/Quickstart/Overview.md new file mode 100644 index 0000000..6c4bec5 --- /dev/null +++ b/docs/src/Quickstart/Overview.md @@ -0,0 +1,38 @@ +### Quickstart Steps +1) Install [Go 1.8+](https://golang.org/dl/) + +2) Install Ponzu CLI: +```bash +$ go get github.com/ponzu-cms/ponzu/… +``` + +3) Create a new project (path is created in your GOPATH): +```bash +$ ponzu new github.com/nilslice/reviews +``` + +4) Enter your new project directory: +```bash +$ cd $GOPATH/src/github.com/nilslice/reviews +``` + +5) Generate content type file and boilerplate code (creates `content/review.go`): +```bash +$ ponzu generate content review title:"string" author:"string" rating:"float64" body:"string":richtext website_url:"string" items:"[]string" photo:string:file` +``` + +6) Build your project: +```bash +$ ponzu build +``` + +7) Run your project with defaults: +```bash +$ ponzu run +``` + +8) Open browser to [`http://localhost:8080/admin`](http://localhost:8080/admin) + +### Notes +- One-time initialization to set configuration +- All fields can be changed in Configuration afterward diff --git a/docs/src/References/Overview.md b/docs/src/References/Overview.md new file mode 100644 index 0000000..8f5e40e --- /dev/null +++ b/docs/src/References/Overview.md @@ -0,0 +1,218 @@ +title: References in Ponzu + +References in Ponzu allow you to create relationships between your Content types. +Ponzu uses an embedded database, rather than a more traditional relational database +with SQL support. This may seem unnatural since there is no native concept of +"foreign keys" or "joins" like you may be used to. Instead, Ponzu wires up your +data using references, which are simply URL paths, like `/api/content?type=Post&id=1` + +A foreign key as a URL path?! Am I crazy? No! For the purpose Ponzu serves, +this structure works quite well, especially given its creation was specifically +tuned for HTTP/2 features such as "Request/Response Multiplexing" and "Server Push." + +There is a deeper dive into the HTTP/2 concepts [below](/References/Overview/#designed-for-http2), but first we'll walk through +a quick tutorial on Ponzu's references. + +To generate references from the CLI, please [read through the documentation](/CLI/Generating-References). +The example below assumes you understand the syntax. + +--- + +### Create Your Content Types + +Here we are creating two Content types, `Author` and `Book`. A `Book` will keep +a reference to an `Author` in the sense that an author wrote the book. + +```bash +$ ponzu gen c author name:string photo:string:file bio:string:textarea +$ ponzu gen c book title:string author:@author,name pages:int year:int +``` + +The structs generated for each look like: + +`content/author.go` +```go +type Author struct { + item.Item + + Name string `json:"name"` + Photo string `json:"photo"` + Bio string `json:"bio"` +} +``` + +`content/book.go` +```go +type Book struct { + item.Item + + Title string `json:"title"` + Author string `json:"author"` + Pages int `json:"pages"` + Year int `json:"year"` +} +``` + +Notice how the `Author` field within the `Book` struct is a `string` type, not +an `Author` type. This is because the `Author` is stored as a `string` in our +database, as a reference to the `Author`, instead of embedding the `Author` data +inside the `Book`. + +Some example JSON data for the two structs looks like: + +GET `/api/content?type=Author&id=1` (`Author`) +```json +{ + "data": [ + { + "uuid": "024a5797-e064-4ee0-abe3-415cb6d3ed18", + "id": 1, + "slug": "item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18", + "timestamp": 1493926453826, + "updated": 1493926453826, + "name": "Shel Silverstein", + "photo": "/api/uploads/2017/05/shel-silverstein.jpg", + "bio": "Sheldon Allan Silverstein was an American poet..." + } + ] +} +``` + +GET `/api/content?type=Book&id=1` (`Book`) +```json +{ + "data": [ + { + "uuid": "024a5797-e064-4ee0-abe3-415cb6d3ed18", + "id": 1, + "slug": "item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18", + "timestamp": 1493926453826, + "updated": 1493926453826, + "title": "The Giving Tree", + "author": "/api/content?type=Author&id=1", + "pages": 57, + "year": 1964 + } + ] +} +``` + +As you can see, the `Author` is a reference as the `author` field in the JSON +response for a `Book`. When you're building your client, you need to make a second +request for the `Author`, to the URL path found in the `author` field of the `Book` +response. + +For example, in pseudo-code: +```bash +# Request 1: +$book = GET /api/content?type=Book&id=1 + +# Request 2: +$author = GET $book.author # where author = /api/content?type=Author&id=1 +``` + +Until recently, this would be considered bad practice and would be costly to do +over HTTP. However, with the wide availability of HTTP/2 clients, including all +modern web browsers, mobile devices, and HTTP/2 libraries in practically every +programming language, this pattern is fast and scalable. + +--- + +### Designed For HTTP/2 + +At this point, you've likely noticed that you're still making two independent +HTTP requests to your Ponzu server. Further, if there are multiple references or more +than one item, you'll be making many requests -- _how can that be efficient?_ + +There are two main concepts at play: Request/Response Multiplexing and Server Push. + +#### Request/Response Multiplexing + +With HTTP/2, a client and server (peers) transfer data over a single TCP connection, +and can send data back and forth at the same time. No longer does a request need +to wait to be sent until after an expected response is read. This means that HTTP +requests can be sent much faster and at the _same time_ on a single connection. +Where previously, a client would open up several TCP connections, the re-use of a +single connection reduces CPU overhead and makes the server more efficient. + +This feature is automatically provided to you when using HTTP/2 - the only +requirement is that you connect via HTTPS and have active TLS certificates, which +you can get for free by running Ponzu with the `--https` flag and configuring it +with a properly set, active domain name of your own. + +#### Server Push + +Another impactful feature of HTTP/2 is "Server Push": the ability to preemptively +send a response from the server to a client without waiting for a request. This +is where Ponzu's reference design really shows it's power. Let's revisit the +example from above: + +```bash +# Request 1: +$book = GET /api/content?type=Book&id=1 + +# Request 2: +$author = GET $book.author # where author = /api/content?type=Author&id=1 +``` + +Instead of waiting for the server to respond with the data for `$book.author`, +the response data is already in the client's cache before we even make the request! +Now there is no round-trip made to the server and back, and the client reads the +pushed response from cache in fractions of a millisecond. + +But, how does the server know which response to push and when? You'll need to +specify which fields of the type you've requested should be pushed. This is done +by implementing the [`item.Pushable` interface](/Interfaces/Item#itempushable). +See the example below which demonstrates a complete implementation on the `Book` +struct, which has a reference to an `Author`. + +##### Example + +`content/book.go` +```go +... +type Book struct { + item.Item + + Title string `json:"title"` + Author string `json:"author"` + Pages int `json:"pages"` + Year int `json:"year"` +} + + +func (b *Book) Push() []string { + return []string{ + // the json struct tag is used to tell the server which + // field(s) it should push - only URL paths originating + // from your server can be pushed! + "author", + } +} +... +``` + +Now, whenever a single `Book` is requested, the server will preemptively push the +`Author` referenced by the book. The response for the `Author` will _already be +on the client_ and will remain there until a request for the referenced `Author` +has been made. + +!!! note "What else can I Push?" + Only fields that are URL paths originating from your server can be pushed. + This means that you could also implement `item.Pushable` on the `Author` + type, and return `[]string{"photo"}` to push the Author's image! + +--- + +### Other Considerations + +HTTP/2 Server Push is a powerful feature, but it can be abused just like anything +else. To try and help mitigate potential issues, Ponzu has put some "stop-gaps" +in place. Server Push is only activated on **single item** API responses, so you +shouldn't expect to see references or files pushed from the `/api/contents` endpoint. +An exception to this is the `/api/search` endpoint, which only the **first** +result is pushed (if applicable) no matter how many items are in the response. + +You should take advantage of HTTP/2 in Ponzu and get the most out of the system. +With the automatic HTTPS feature, there is no reason not to and you gain the +additional benefit of encrypting your traffic - which your users will appreciate! diff --git a/docs/src/Running-Backups/Backups.md b/docs/src/Running-Backups/Backups.md new file mode 100644 index 0000000..15632a0 --- /dev/null +++ b/docs/src/Running-Backups/Backups.md @@ -0,0 +1,26 @@ +title: Running Backups on Ponzu systems + +Both the databases `system.db` & `analytics.db`, and the `/uploads` directory can be backed up over HTTP using `wget`, `curl`, etc. All of which are located at the `/admin/backup` route and require HTTP Basic Auth. In order to enable backups, you must add a user/password pair inside the CMS Configuration at `/admin/configure` near the bottom of the page. + +All backups are made using a `GET` request to the `/admin/backup` path with a query parameter of `?source={system,analytics,uploads}` (only one source can be included in the URL). + +Here are some full backup scripts to use or modify to fit your needs: +[https://github.com/ponzu-cms/backup-scripts](https://github.com/ponzu-cms/backup-scripts) + +## System & Analytics +The `system.db` & `analytics.db` data files are sent uncompressed in their original form as they exist on your server. No temporary copy is stored on the origin server, and it is possible that the backup could fail so checking for successful backups is recommended. See https://github.com/boltdb/bolt#database-backups for more information about how BoltDB handles HTTP backups. + +An example backup request for the `system.db` data file would look like: +```bash +$ curl --user user:pass "https://example.com/admin/backup?source=system" > system.db.bak +``` + +## Uploads +The `/uploads` directory is gzip compressed and archived as a tar file, stored in the temporary directory (typically `/tmp` on Linux) on your origin server with a timestamp in the file name. + +An example backup request for the `/uploads` directory would look like: +```bash +$ curl --user user:pass "https://example.com/admin/backup?source=uploads" > uploads.tar.gz +# unarchive the tarball with gzip +$ tar xzf uploads.tar.gz +``` \ No newline at end of file diff --git a/docs/src/System-Configuration/Settings.md b/docs/src/System-Configuration/Settings.md new file mode 100644 index 0000000..71fd2ec --- /dev/null +++ b/docs/src/System-Configuration/Settings.md @@ -0,0 +1,124 @@ +title: Configuring Your Ponzu System Settings + +Ponzu has several options which can be configured at run-time. To view these +configuration settings, visit the `/admin/configure` page of your Ponzu CMS. + +--- + +#### Site Name +The Site Name setting changes the displayed name on your admin dashboard. This is +visible publicly on the `/admin/login` page. + +--- + +#### Domain Name +Internally, Ponzu needs to know where its canonical HTTP access origin is, and +requires you to add the qualified domain name you are using. In development, use +`localhost` or some other name mapped to the loopback address (`127.0.0.1`). + +Once you have deployed your Ponzu server to a remote host and pointed a public +domain at it, you need to change the Domain Name setting to match. This is +especially important when fetching TLS (SSL) certificates from [Let's Encrypt](https://letsencrypt.org) +- since the process requires an active, verifiable domain. To set up your server +with TLS over HTTPS connections, follow these steps: + +1. Set your Domain Name in the system configuration +2. Set the Administrator Email to register with Let's Encrypt +2. Stop your Ponzu server +3. Run your Ponzu server with the `--https` flag e.g. `$ ponzu run --https` +4. Visit your CMS admin with `https://` prepended to your URL + +!!! success "Verifying HTTPS / TLS Connections" + If successful, your APIs and CMS will be accessible via HTTPS, and you will + see a green indicator near the URL bar of most browsers. This also enables + your server to use the HTTP/2 protocol. + +##### Development Environment + +You can test HTTPS & HTTP/2 connections in your development environment on `localhost`, +by running Ponzu with the `--devhttps` flag e.g. `$ ponzu --devhttps run` + +If you're greeted with a warning from the browser saying the connection is not +secure, follow the steps outlined in the CLI message, or here: +``` +If your browser rejects HTTPS requests, try allowing insecure connections on localhost. +on Chrome, visit chrome://flags/#allow-insecure-localhost +``` + +--- + +#### Administrator Email +The Administrator Email is the contact email for the person who is the main admin +of your Ponzu CMS. This can be changed at any point, but once a Let's Encrypt +certificate has been fetched using an Administrator Email, it will remain the +contact until a new certificate is requested. + +--- + +#### Client Secret +The Client Secret is a secure value used by the server to sign tokens and authenticate requests. +**Do not share this** value with any untrusted party. + +!!! danger "Security and the Client Secret" + HTTP requests with a valid token, signed with the Client Secret, can take any + action an Admin can within the CMS. Be cautious of this when sharing account + logins or details with anyone. + +--- + +#### Etag Header +The Etag Header value is automatically created when content is changed and serves +as a caching validation mechanism. + +--- + +#### CORS +CORS, or "Cross-Origin Resource Sharing" is a security setting which defines how +resources (or URLs) can be accessed from outside clients / domains. By default, +Ponzu HTTP APIs can be accessed from any origin, meaning a script from an unknown +website could fetch data. + +By disabling CORS, you limit API requests to only the Domain Name you set. + +--- + +#### GZIP +GZIP is a popular codec which when applied to most HTTP responses, decreases data +transmission size and response times. The GZIP setting on Ponzu has a minor +side-effect of using more CPU, so you can disable it if you notice your system +is CPU-constrained. However, traffic levels would need to be extremely demanding +for this to be noticeable. + +--- + +#### HTTP Cache +The HTTP Cache configuration allows a system to disable the default HTTP cache, +which saves the server from repeating API queries and sending responses -- it's +generally advised to keep this enabled unless you have _frequently_ changing data. + +The `Max-Age` value setting overrides the default 2592000-second (30 day) cache +`max-age` duration set in API response headers. The `0` value is an alias to +`2592000`, so check the `Disable HTTP Cache` box if you don't want any caching. + + +--- + +#### Invalidate Cache +If this box is checked and then the configuration is saved, the server will +re-generate an Etag to send in responses. By doing so, the cache becomes invalidated +and reset so new content or assets will be included in previously cached responses. + +The cache is invalidated when content changes, so this is typically not a widely +used setting. + +--- + +#### Database Backup Credentials +In order to enable HTTP backups of the components that make up your system, you +will need to add an HTTP Basic Auth user and password pair. When used to +[run backups](/Running-Backups/Backups), the `user:password` pair tells your server +that the backup request is made from a trusted party. + +!!! danger "Backup Access with Credentials" + This `user:password` pair should not be shared outside of your organization as + it allows full database downloads and archives of your system's uploads. diff --git a/docs/src/System-Deployment/Docker.md b/docs/src/System-Deployment/Docker.md new file mode 100644 index 0000000..a998a38 --- /dev/null +++ b/docs/src/System-Deployment/Docker.md @@ -0,0 +1,34 @@ +## Ponzu Docker build + +Ponzu is distributed as a [docker image](https://hub.docker.com/r/ponzu/ponzu/), +which aids in ponzu deployment. The Dockerfile in this directory is used by Ponzu +to generate the docker image which contains the ponzu executable. + +If you are deploying your own Ponzu project, you can write a new Dockerfile that +is based from the `ponzu/ponzu` image of your choice. For example: +```docker +FROM ponzu/ponzu:latest + +# your project set up ... +# ... +# ... +``` + +### The following are convenient commands during development of Ponzu core: + +#### Build the docker image. Run from the root of the project. +```bash +# from the root of ponzu: +docker build -t ponzu-dev +``` + +#### Start the image, share the local directory and pseudo terminal (tty) into for debugging: +```bash +docker run -v $(pwd):/go/src/github.com/ponzu-cms/ponzu -it ponzu-dev +pwd # will output the go src directory for ponzu +ponzu version # will output the ponzu version +# make an edit on your local and rebuild +go install ./... +``` + +Special thanks to [**@krismeister**](https://github.com/krismeister) for contributing this! \ No newline at end of file diff --git a/docs/src/System-Deployment/SysV-Style.md b/docs/src/System-Deployment/SysV-Style.md new file mode 100644 index 0000000..565b399 --- /dev/null +++ b/docs/src/System-Deployment/SysV-Style.md @@ -0,0 +1,76 @@ +title: Deploying Ponzu on Linux with System-V style init + +For reference, here is an example init script to run Ponzu servers. You must +define the `PROJECT_DIR` & `RUNAS` variables by replacing `` +& `` in the script below: + +```bash +#!/bin/sh +### BEGIN INIT INFO +# Provides: ponzu-server +# Required-Start: $local_fs $network $named $time $syslog +# Required-Stop: $local_fs $network $named $time $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Description: Ponzu API & Admin server +### END INIT INFO + +PROJECT_DIR= +SCRIPT='cd $PROJECT_DIR && ponzu run --port=80' # add --https here to get TLS/HTTPS +RUNAS= + +PIDFILE=/var/run/ponzu-server.pid +LOGFILE=/var/log/ponzu-server.log + +start() { + if [ -f /var/run/$PIDNAME ] && kill -0 $(cat /var/run/$PIDNAME); then + echo 'Service already running' >&2 + return 1 + fi + echo 'Starting service…' >&2 + local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!" + su -c "$CMD" $RUNAS > "$PIDFILE" + echo 'Service started' >&2 +} + +stop() { + if [ ! -f "$PIDFILE" ] || ! kill -0 $(cat "$PIDFILE"); then + echo 'Service not running' >&2 + return 1 + fi + echo 'Stopping service…' >&2 + kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE" + echo 'Service stopped' >&2 +} + +uninstall() { + echo -n "Are you really sure you want to uninstall this service? That cannot be undone. [yes|No] " + local SURE + read SURE + if [ "$SURE" = "yes" ]; then + stop + rm -f "$PIDFILE" + echo "Notice: log file is not be removed: '$LOGFILE'" >&2 + update-rc.d -f remove + rm -fv "$0" + fi +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + uninstall) + uninstall + ;; + restart) + stop + start + ;; + *) + echo "Usage: $0 {start|stop|restart|uninstall}" +esac +``` \ No newline at end of file diff --git a/docs/src/images/editor-checkbox.png b/docs/src/images/editor-checkbox.png new file mode 100644 index 0000000..dcea71c Binary files /dev/null and b/docs/src/images/editor-checkbox.png differ diff --git a/docs/src/images/editor-file-repeater.png b/docs/src/images/editor-file-repeater.png new file mode 100644 index 0000000..ec4d372 Binary files /dev/null and b/docs/src/images/editor-file-repeater.png differ diff --git a/docs/src/images/editor-file.png b/docs/src/images/editor-file.png new file mode 100644 index 0000000..93ab56f Binary files /dev/null and b/docs/src/images/editor-file.png differ diff --git a/docs/src/images/editor-input-repeater.png b/docs/src/images/editor-input-repeater.png new file mode 100644 index 0000000..5ab4256 Binary files /dev/null and b/docs/src/images/editor-input-repeater.png differ diff --git a/docs/src/images/editor-input.png b/docs/src/images/editor-input.png new file mode 100644 index 0000000..eea009a Binary files /dev/null and b/docs/src/images/editor-input.png differ diff --git a/docs/src/images/editor-richtext.png b/docs/src/images/editor-richtext.png new file mode 100644 index 0000000..a953eda Binary files /dev/null and b/docs/src/images/editor-richtext.png differ diff --git a/docs/src/images/editor-select-repeater.png b/docs/src/images/editor-select-repeater.png new file mode 100644 index 0000000..a175e5c Binary files /dev/null and b/docs/src/images/editor-select-repeater.png differ diff --git a/docs/src/images/editor-select.png b/docs/src/images/editor-select.png new file mode 100644 index 0000000..ffd0ca6 Binary files /dev/null and b/docs/src/images/editor-select.png differ diff --git a/docs/src/images/editor-tags.png b/docs/src/images/editor-tags.png new file mode 100644 index 0000000..ea7c994 Binary files /dev/null and b/docs/src/images/editor-tags.png differ diff --git a/docs/src/images/editor-textarea.png b/docs/src/images/editor-textarea.png new file mode 100644 index 0000000..f714b48 Binary files /dev/null and b/docs/src/images/editor-textarea.png differ diff --git a/docs/src/images/logo.png b/docs/src/images/logo.png new file mode 100644 index 0000000..40b7b21 Binary files /dev/null and b/docs/src/images/logo.png differ diff --git a/docs/src/images/ponzu-banner.png b/docs/src/images/ponzu-banner.png new file mode 100644 index 0000000..855f97e Binary files /dev/null and b/docs/src/images/ponzu-banner.png differ diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..944a710 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,41 @@ +# Ponzu CMS + Server Framework Docs + +![Ponzu](/images/ponzu-banner.png) + +## What is Ponzu? + +> Watch the [**video introduction**](https://www.youtube.com/watch?v=T_1ncPoLgrg) + +Ponzu is a powerful and efficient open-source HTTP server framework and CMS. It +provides automatic, free, and secure HTTP/2 over TLS (certificates obtained via +[Let's Encrypt](https://letsencrypt.org)), a useful CMS and scaffolding to generate +content editors, and a fast HTTP API on which to build modern applications. + +Want to jump in right away? Try the [Quickstart](/Quickstart/Overview) + +### Table of Contents + +1. [CLI](/CLI/General-Usage/) +2. [Content](/Content/An-Overview) +3. [Form Fields](/Form-Fields/HTML-Inputs) +4. [HTTP API - Content](/HTTP-APIs/Content) +5. [HTTP API - File Metadata](/HTTP-APIs/File-Metadata) +6. [HTTP API - Search](/HTTP-APIs/Search) +7. [Interfaces - API](/Interfaces/API) +8. [Interfaces - Editor](/Interfaces/Editor) +9. [Interfaces - Item](/Interfaces/Item) +10. [Interfaces - Search](/Interfaces/Search) +11. [Creating Ponzu Addons](/Ponzu-Addons/Creating-Addons) +12. [Using Ponzu Addons](/Ponzu-Addons/Using-Addons) +13. [Quickstart](/Quickstart/Overview) +14. [Backups](/Running-Backups/Backups) +15. [System Configuration](/System-Configuration/Settings) + + +### Need help? Get in touch +- Chat: [#ponzu on Slack](https://gophers.slack.com/messages/C3TBV356D) +- Reach out on Twitter: [@ponzu_cms](https://twitter.com/ponzu_cms) +- File an [issue](https://github.com/ponzu-cms/ponzu/issues) + +--- +current version: `v0.9.2` @ ponzu:master -- cgit v1.2.3