From 78de7ed98abff93fe5fef94907bcfa4f76dcef07 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Sat, 27 May 2017 10:27:51 -0700 Subject: [PATCH 1/4] adding docs to repo --- docs/.gitignore | 0 docs/CNAME | 1 + docs/LICENSE | 29 + docs/README.md | 26 + docs/build/404.html | 772 ++++++ docs/build/CLI/General-Usage/index.html | 1254 ++++++++++ .../CLI/Generating-References/index.html | 1055 +++++++++ docs/build/CNAME | 1 + docs/build/Content/An-Overview/index.html | 969 ++++++++ .../Content/Extending-Content/index.html | 959 ++++++++ docs/build/Form-Fields/HTML-Inputs/index.html | 2083 +++++++++++++++++ docs/build/HTTP-APIs/Content/index.html | 1405 +++++++++++ docs/build/HTTP-APIs/File-Metadata/index.html | 954 ++++++++ docs/build/HTTP-APIs/Search/index.html | 983 ++++++++ docs/build/Interfaces/API/index.html | 1186 ++++++++++ docs/build/Interfaces/Editor/index.html | 1059 +++++++++ docs/build/Interfaces/Format/index.html | 986 ++++++++ docs/build/Interfaces/Item/index.html | 2077 ++++++++++++++++ docs/build/Interfaces/Search/index.html | 983 ++++++++ .../Ponzu-Addons/Creating-Addons/index.html | 846 +++++++ .../Ponzu-Addons/Using-Addons/index.html | 846 +++++++ docs/build/Quickstart/Overview/index.html | 932 ++++++++ docs/build/References/Overview/index.html | 1163 +++++++++ docs/build/Running-Backups/Backups/index.html | 912 ++++++++ .../System-Configuration/Settings/index.html | 1131 +++++++++ .../build/System-Deployment/Docker/index.html | 977 ++++++++ .../System-Deployment/SysV-Style/index.html | 899 +++++++ docs/build/assets/images/favicon.ico | Bin 0 -> 1150 bytes .../images/icons/bitbucket-670608a71a.svg | 1 + .../assets/images/icons/github-1da075986e.svg | 1 + .../assets/images/icons/gitlab-5ad3f9f9e5.svg | 1 + .../javascripts/application-6b599127bc.js | 3 + .../javascripts/modernizr-56ade86843.js | 1 + .../stylesheets/application-4d0d3f2fbf.css | 1 + .../application-f78e5cb881.palette.css | 1 + docs/build/images/editor-checkbox.png | Bin 0 -> 4759 bytes docs/build/images/editor-file-repeater.png | Bin 0 -> 4976 bytes docs/build/images/editor-file.png | Bin 0 -> 4485 bytes docs/build/images/editor-input-repeater.png | Bin 0 -> 2682 bytes docs/build/images/editor-input.png | Bin 0 -> 1847 bytes docs/build/images/editor-richtext.png | Bin 0 -> 9381 bytes docs/build/images/editor-select-repeater.png | Bin 0 -> 4695 bytes docs/build/images/editor-select.png | Bin 0 -> 3997 bytes docs/build/images/editor-tags.png | Bin 0 -> 5977 bytes docs/build/images/editor-textarea.png | Bin 0 -> 2747 bytes docs/build/images/logo.png | Bin 0 -> 19734 bytes docs/build/images/ponzu-banner.png | Bin 0 -> 909819 bytes docs/build/index.html | 941 ++++++++ docs/build/mkdocs/js/lunr.min.js | 7 + docs/build/mkdocs/js/mustache.min.js | 1 + docs/build/mkdocs/js/require.js | 36 + .../js/search-results-template.mustache | 4 + docs/build/mkdocs/js/search.js | 88 + docs/build/mkdocs/js/text.js | 390 +++ docs/build/mkdocs/search_index.json | 1104 +++++++++ docs/build/sitemap.xml | 182 ++ docs/mkdocs.yml | 27 + docs/release.sh | 16 + 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 + 92 files changed, 29816 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/CNAME create mode 100644 docs/LICENSE create mode 100644 docs/README.md create mode 100644 docs/build/404.html create mode 100644 docs/build/CLI/General-Usage/index.html create mode 100644 docs/build/CLI/Generating-References/index.html create mode 100644 docs/build/CNAME create mode 100644 docs/build/Content/An-Overview/index.html create mode 100644 docs/build/Content/Extending-Content/index.html create mode 100644 docs/build/Form-Fields/HTML-Inputs/index.html create mode 100644 docs/build/HTTP-APIs/Content/index.html create mode 100644 docs/build/HTTP-APIs/File-Metadata/index.html create mode 100644 docs/build/HTTP-APIs/Search/index.html create mode 100644 docs/build/Interfaces/API/index.html create mode 100644 docs/build/Interfaces/Editor/index.html create mode 100644 docs/build/Interfaces/Format/index.html create mode 100644 docs/build/Interfaces/Item/index.html create mode 100644 docs/build/Interfaces/Search/index.html create mode 100644 docs/build/Ponzu-Addons/Creating-Addons/index.html create mode 100644 docs/build/Ponzu-Addons/Using-Addons/index.html create mode 100644 docs/build/Quickstart/Overview/index.html create mode 100644 docs/build/References/Overview/index.html create mode 100644 docs/build/Running-Backups/Backups/index.html create mode 100644 docs/build/System-Configuration/Settings/index.html create mode 100644 docs/build/System-Deployment/Docker/index.html create mode 100644 docs/build/System-Deployment/SysV-Style/index.html create mode 100644 docs/build/assets/images/favicon.ico create mode 100644 docs/build/assets/images/icons/bitbucket-670608a71a.svg create mode 100644 docs/build/assets/images/icons/github-1da075986e.svg create mode 100644 docs/build/assets/images/icons/gitlab-5ad3f9f9e5.svg create mode 100644 docs/build/assets/javascripts/application-6b599127bc.js create mode 100644 docs/build/assets/javascripts/modernizr-56ade86843.js create mode 100644 docs/build/assets/stylesheets/application-4d0d3f2fbf.css create mode 100644 docs/build/assets/stylesheets/application-f78e5cb881.palette.css create mode 100644 docs/build/images/editor-checkbox.png create mode 100644 docs/build/images/editor-file-repeater.png create mode 100644 docs/build/images/editor-file.png create mode 100644 docs/build/images/editor-input-repeater.png create mode 100644 docs/build/images/editor-input.png create mode 100644 docs/build/images/editor-richtext.png create mode 100644 docs/build/images/editor-select-repeater.png create mode 100644 docs/build/images/editor-select.png create mode 100644 docs/build/images/editor-tags.png create mode 100644 docs/build/images/editor-textarea.png create mode 100644 docs/build/images/logo.png create mode 100644 docs/build/images/ponzu-banner.png create mode 100644 docs/build/index.html create mode 100644 docs/build/mkdocs/js/lunr.min.js create mode 100644 docs/build/mkdocs/js/mustache.min.js create mode 100644 docs/build/mkdocs/js/require.js create mode 100644 docs/build/mkdocs/js/search-results-template.mustache create mode 100644 docs/build/mkdocs/js/search.js create mode 100644 docs/build/mkdocs/js/text.js create mode 100644 docs/build/mkdocs/search_index.json create mode 100644 docs/build/sitemap.xml create mode 100644 docs/mkdocs.yml create mode 100644 docs/release.sh 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 diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 00000000..119323fe --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +docs.ponzu-cms.org diff --git a/docs/LICENSE b/docs/LICENSE new file mode 100644 index 00000000..720d6cd1 --- /dev/null +++ b/docs/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2016 Boss Sauce Creative, LLC. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..990cd923 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,26 @@ +# Ponzu CMS + Server Framerwork Docs + +## Contributing + +Documentation contributions are welcome and appreciated. If you find something +lacking in documentation or have submitted a PR that is being merged into master, +please help everyone out and write some docs! + +**Note:** Docker is required to follow these instructions, but you can also use +MkDocs natively, [see details here](http://www.mkdocs.org/#installation). Ponzu +docs use the "Material" [theme](http://squidfunk.github.io/mkdocs-material/). + + +Here is how to run a local docs server and build them for release: + +1. Clone this repository +```bash +$ git clone https://github.com/ponzu-cms/docs.git +``` +2. Start the development server which will watch and auto-build the docs +```bash +$ docker run --rm -it -p 8000:8000 -v `pwd`:/docs squidfunk/mkdocs-material +``` +3. Submit a PR with your changes. If you run the build step, please do not add it to the PR. + +**Thank you!** diff --git a/docs/build/404.html b/docs/build/404.html new file mode 100644 index 00000000..f23455ab --- /dev/null +++ b/docs/build/404.html @@ -0,0 +1,772 @@ + + + + + + + + + + + + + + + + + + Ponzu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + + +
+
+ +

404 - Not found

+ + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/CLI/General-Usage/index.html b/docs/build/CLI/General-Usage/index.html new file mode 100644 index 00000000..884336ae --- /dev/null +++ b/docs/build/CLI/General-Usage/index.html @@ -0,0 +1,1254 @@ + + + + + + + + + + + + + + + + + + Using the Ponzu Command Line Interface (CLI) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

General Usage

+ +
$ ponzu [flags] command <params>
+
+ + +

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:

+
$ 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:

+
            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:

+
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 parameterGenerates
checkboxeditor.Checkbox()
customgenerates a pre-styled empty div to fill with HTML
fileeditor.File()
hiddeneditor.Input() + uses type=hidden
input, texteditor.Input()
richtexteditor.Richtext()
selecteditor.Select()
textareaeditor.Textarea()
tagseditor.Tags()
+

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 +for more details:

+
$ 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 +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:

+
$ 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:

+
$ 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:

+
$ ponzu upgrade
+
+ + +
+

add, a

+

Downloads an addon to GOPATH/src and copies it to the current Ponzu project's +/addons directory.

+

Example:

+
$ 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:

+
$ ponzu version
+Ponzu v0.8.2
+# (or)
+$ ponzu version --cli
+Ponzu v0.9.2
+
+ + +
+

Contributing

+
    +
  1. Checkout branch ponzu-dev
  2. +
  3. Make code changes
  4. +
  5. 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
    • +
    +
  6. +
  7. To add back to master:
      +
    • first push to origin ponzu-dev
    • +
    • create a pull request
    • +
    • will then be merged into master
    • +
    +
  8. +
+

A typical contribution workflow might look like:

+
# 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:

+
# ($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
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/CLI/Generating-References/index.html b/docs/build/CLI/Generating-References/index.html new file mode 100644 index 00000000..38c50a6a --- /dev/null +++ b/docs/build/CLI/Generating-References/index.html @@ -0,0 +1,1055 @@ + + + + + + + + + + + + + + + + + + How to Generate References using Ponzu CLI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Generating References

+ +

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 +<option></option> 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
+
$ 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.

+
// content/product.go
+package content
+...
+
+type Product struct {
+    item.Item
+
+    Title       string `json:"title"`
+    Price       int    `json:"price"`
+    Description string `json:"description"`
+}
+
+...
+
+ + +
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:

+
$ ponzu gen content catalog year:int product:@product,title,price
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/CNAME b/docs/build/CNAME new file mode 100644 index 00000000..119323fe --- /dev/null +++ b/docs/build/CNAME @@ -0,0 +1 @@ +docs.ponzu-cms.org diff --git a/docs/build/Content/An-Overview/index.html b/docs/build/Content/An-Overview/index.html new file mode 100644 index 00000000..19c81172 --- /dev/null +++ b/docs/build/Content/An-Overview/index.html @@ -0,0 +1,969 @@ + + + + + + + + + + + + + + + + + + Content Overview + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

An 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:

+
$ 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:

+
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 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.

+ +

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).

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Content/Extending-Content/index.html b/docs/build/Content/Extending-Content/index.html new file mode 100644 index 00000000..fc63245c --- /dev/null +++ b/docs/build/Content/Extending-Content/index.html @@ -0,0 +1,959 @@ + + + + + + + + + + + + + + + + + + Extending Content through built-in Interfaces and optional Addons + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Extending Content

+ +

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.

+

It is also common to add more advanced functionality to Content types using Addons. Refer to the Addon documentation for more information about how to use and create Ponzu Addons.

+

Item Interfaces

+

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.

+ +

API Interfaces

+

To enable 3rd-party clients to interact with your Content types, you can extend your types with the API interfaces:

+ +

Editor Interfaces

+

To manage how content is edited and handled in the CMS, use the following Editor interfaces:

+ +

Search Interfaces

+

To enable and customize full-text search on your content types, use the following interfaces:

+ + + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Form-Fields/HTML-Inputs/index.html b/docs/build/Form-Fields/HTML-Inputs/index.html new file mode 100644 index 00000000..ed74b064 --- /dev/null +++ b/docs/build/Form-Fields/HTML-Inputs/index.html @@ -0,0 +1,2083 @@ + + + + + + + + + + + + + + + + + + HTML Input Elements for Ponzu Editor Forms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

HTML Inputs

+ +

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:

+
// 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

+
Function Signature
+
Input(fieldName string, p interface{}, attrs, options map[string]string) []byte
+
+ + +
Example
+
...
+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.

+
+

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

+
Function Signature
+
InputRepeater(fieldName string, p interface{}, attrs, options map[string]string) []byte
+
+ + +
Example
+
...
+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

+
Function Signature
+
Checkbox(fieldName string, p interface{}, attrs, options map[string]string) []byte
+
+ + +
Example
+
...
+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 +using a theme called MaterialNote

+
Screenshot
+

HTML Richtext Input

+
Function Signature
+
Richtext(fieldName string, p interface{}, attrs map[string]string) []byte
+
+ + +
Example
+
...
+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

+
Function Signature
+
Tags(fieldName string, p interface{}, attrs map[string]string) []byte
+
+ + +
Example
+
...
+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.

+
+

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

+
Function Signature
+
File(fieldName string, p interface{}, attrs map[string]string) []byte
+
+ + +
Example
+
...
+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.

+
+

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

+
Function Signature
+
FileRepeater(fieldName string, p interface{}, attrs map[string]string) []byte
+
+ + +
Example
+
...
+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

+
Function Signature
+
func Select(fieldName string, p interface{}, attrs, options map[string]string) []byte
+
+ + +
Example
+
...
+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

+
Function Signature
+
func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]string) []byte
+
+ + +
Example
+
...
+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

+
Function Signature
+
Textarea(fieldName string, p interface{}, attrs map[string]string) []byte
+
+ + +
Example
+
...
+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 +package. It comes pre-installed with Ponzu as an "Addon".

+

reference.Select

+
Screenshot
+

HTML Select Input

+
Function Signature
+
func Select(fieldName string, p interface{}, attrs map[string]string, contentType, tmplString string) []byte
+
+ + +
Example
+
...
+editor.Field{
+    View: reference.Select("DirectedBy", s, map[string]string{
+        "label": "Select Dropdown",
+    }, "Director", `{{.last-name}}, {{.first_name}}`),
+},
+...
+
+ + +
+

reference.SelectRepeater

+
Screenshot
+

HTML Select Input

+
Function Signature
+
func SelectRepeater(fieldName string, p interface{}, attrs map[string]string, contentType, tmplString string) []byte
+
+ + +
Example
+
...
+editor.Field{
+    View: reference.SelectRepeater("PlacesFilmed", s, map[string]string{
+        "label": "Select Dropdown Repeater",
+    }, "Location", `{{.name}}, {{.region}}`),
+},
+...
+
+ + +
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/HTTP-APIs/Content/index.html b/docs/build/HTTP-APIs/Content/index.html new file mode 100644 index 00000000..4eb39c5a --- /dev/null +++ b/docs/build/HTTP-APIs/Content/index.html @@ -0,0 +1,1405 @@ + + + + + + + + + + + + + + + + + + Content HTTP API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Content

+ +

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 to learn +more about how to enable these endpoints.

+
+

Endpoints

+

Get Content by Type

+

GET /api/content?type=<Type>&id=<ID>

+
Sample Response
+
{
+  "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=<Type>

+
    +
  • optional params:
      +
    1. order (string: ASC / DESC, default: DESC)
    2. +
    3. count (int: -1 - N, default: 10, -1 returns all)
    4. +
    5. offset (int: 0 - N, default: 0)
    6. +
    +
  • +
+
Sample Response
+
{
+  "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=<Slug>

+
Sample Response
+
{
+  "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>

+
+

Request Data Encoding

+

Request must be multipart/form-data encoded. If not, a 400 Bad Request +Response will be returned.

+
+
Sample Response
+
{
+  "data": [
+    {
+        "id": 6, // will be omitted if status is pending
+        "type": "Review",
+        "status": "public"
+    }
+  ]
+}
+
+ + +
+

Update Content

+

POST /api/content/update?type=<Type>&id=<id>

+
+

Request Data Encoding

+

Request must be multipart/form-data encoded. If not, a 400 Bad Request +Response will be returned.

+
+
Sample Response
+
{
+  "data": [
+    {
+        "id": 6,
+        "type": "Review",
+        "status": "public"
+    }
+  ]
+}
+
+ + +
+

Delete Content

+

POST /api/content/delete?type=<Type>&id=<id>

+
+

Request Data Encoding

+

Request must be multipart/form-data encoded. If not, a 400 Bad Request +Response will be returned.

+
+
Sample Response
+
{
+  "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 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
+
+ + + +

Typewriter +Generate & sync front-end data structures from Ponzu content types. (Ponzu example)

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/HTTP-APIs/File-Metadata/index.html b/docs/build/HTTP-APIs/File-Metadata/index.html new file mode 100644 index 00000000..ba62cbc2 --- /dev/null +++ b/docs/build/HTTP-APIs/File-Metadata/index.html @@ -0,0 +1,954 @@ + + + + + + + + + + + + + + + + + + File Metadata HTTP API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

File Metadata

+ +

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=<Slug>

+
Sample Response
+
{
+  "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/build/HTTP-APIs/Search/index.html b/docs/build/HTTP-APIs/Search/index.html new file mode 100644 index 00000000..45c41f22 --- /dev/null +++ b/docs/build/HTTP-APIs/Search/index.html @@ -0,0 +1,983 @@ + + + + + + + + + + + + + + + + + + Full-text Search HTTP API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Search

+ +

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, +which handles the indexing and querying.

+
+

Endpoints

+

Search Content

+

GET /api/search?type=<Type>&q=<Query String>

+
+

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).
  • +
+
+
    +
  • +

    <Type> must implement db.Searchable

    +
  • +
  • +

    Search is currently limited to single <Type> per request

    +
  • +
  • +

    <Query String> documentation here: Bleve Docs - Query String

    +
  • +
  • +

    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:

    + +
  • +
+
Sample Response
+
{
+  "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...,
+    }
+  ]
+}
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Interfaces/API/index.html b/docs/build/Interfaces/API/index.html new file mode 100644 index 00000000..448562d8 --- /dev/null +++ b/docs/build/Interfaces/API/index.html @@ -0,0 +1,1186 @@ + + + + + + + + + + + + + + + + + + API Package Interfaces + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

API

+ +

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

+

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=<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 +interface, which will add "Approve" and "Reject" buttons to your Content types' +editor -- or implement api.Trustable to bypass +the "Pending" section altogether and become "Public" immediately.

+
Method Set
+
type Createable interface {
+    Create(http.ResponseWriter, *http.Request) error
+}
+
+ + +
Implementation
+
func (p *Post) Create(res http.ResponseWriter, req *http.Request) error {
+    return nil
+}
+
+ + +
+

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=<Type>&id=<id>. Request validation should be employed +otherwise any client could change data in your database.

+
Method Set
+
type Updateable interface {
+    Update(http.ResponseWriter, *http.Request) error
+}
+
+ + +
Implementation
+
func (p *Post) Update(res http.ResponseWriter, req *http.Request) error {
+    return nil
+}
+
+ + +
+

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=<Type>&id=<id>. Request validation should be employed +otherwise any client could delete data from your database.

+
Method Set
+
type Deleteable interface {
+    Delete(http.ResponseWriter, *http.Request) error
+}
+
+ + +
Implementation
+
func (p *Post) Delete(res http.ResponseWriter, req *http.Request) error {
+    return nil
+}
+
+ + +
+

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.

+
type Trustable interface {
+    AutoApprove(http.ResponseWriter, *http.Request) error
+}
+
+ + +
Implementation
+
func (p *Post) AutoApprove(res http.ResponseWriter, req *http.Request) error {
+    return nil
+}
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Interfaces/Editor/index.html b/docs/build/Interfaces/Editor/index.html new file mode 100644 index 00000000..c6b25f71 --- /dev/null +++ b/docs/build/Interfaces/Editor/index.html @@ -0,0 +1,1059 @@ + + + + + + + + + + + + + + + + + + Editor Package Interfaces + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Editor

+ +

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

+

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.

+
+

Implementing editor.Editable

+

Most of the time, Ponzu developers generate the majority of this code using +the Ponzu CLI generate command.

+
+
Method Set
+
type Editable interface {
+    MarshalEditor() ([]byte, error)
+}
+
+ + +
Implementation
+
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",
+            }),
+        },
+    )
+}
+
+ + +
+

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

+

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
+
type Mergeable interface {
+    Approve(http.ResponseWriter, *http.Request) error
+}
+
+ + +
Example
+
func (p *Post) Approve(res http.ResponseWriter, req *http.Request) error {
+    return nil
+}
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Interfaces/Format/index.html b/docs/build/Interfaces/Format/index.html new file mode 100644 index 00000000..d85bd14f --- /dev/null +++ b/docs/build/Interfaces/Format/index.html @@ -0,0 +1,986 @@ + + + + + + + + + + + + + + + + + + Format Package Interfaces + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Format

+ +

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

+

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
+
type CSVFormattable interface {
+    FormatCSV() []string
+}
+
+ + +
Implementation
+
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",
+    }
+}
+
+ + +
+

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.

+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Interfaces/Item/index.html b/docs/build/Interfaces/Item/index.html new file mode 100644 index 00000000..aafe0179 --- /dev/null +++ b/docs/build/Interfaces/Item/index.html @@ -0,0 +1,2077 @@ + + + + + + + + + + + + + + + + + + Item Package Interfaces + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + + + + +
+
+ + + +

Item

+ +

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

+

Pushable, if HTTP/2 Server Push +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
+
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.

+
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

+

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
+
type Hideable interface {
+    Hide(http.ResponseWriter, *http.Request) error
+}
+
+ + +
Implementation
+
func (p *Post) Hide(res http.ResponseWriter, req *http.Request) error {
+    return nil
+}
+
+ + +
+

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
+
type Omittable interface {
+    Omit() []string
+}
+
+ + +
Implementation
+
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

+

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
+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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.

+
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".

+
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.

+
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".

+
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

+

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

+

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
+
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.

+
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

+

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
+
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().

+
func (i *Item) SetSlug(slug string) {
+    i.Slug = slug
+}
+
+func (i *Item) ItemSlug() string {
+    return i.Slug
+}
+
+ + +
+

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
+
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.

+
func (i Item) Time() int64 {
+    return i.Timestamp
+}
+
+func (i Item) Touch() int64 {
+    return i.Updated
+}
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Interfaces/Search/index.html b/docs/build/Interfaces/Search/index.html new file mode 100644 index 00000000..1c328c7a --- /dev/null +++ b/docs/build/Interfaces/Search/index.html @@ -0,0 +1,983 @@ + + + + + + + + + + + + + + + + + + Search Package Interfaces + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Search

+ +

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

+

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.

+
+

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
+
type Searchable interface {
+    SearchMapping() (*mapping.IndexMappingImpl, error)
+    IndexContent() bool
+}
+
+ + +

By default, Ponzu sets up the Bleve's "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
+
// ...
+
+type Song struct {
+    item.Item
+
+    Name string `json:"name"`
+    // ...
+}
+
+func (s *Song) IndexContent() bool {
+    return true
+}
+
+ + +
+

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.

+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Ponzu-Addons/Creating-Addons/index.html b/docs/build/Ponzu-Addons/Creating-Addons/index.html new file mode 100644 index 00000000..690b103a --- /dev/null +++ b/docs/build/Ponzu-Addons/Creating-Addons/index.html @@ -0,0 +1,846 @@ + + + + + + + + + + + + + + + + + + How to create Ponzu Addons + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+ +
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Ponzu-Addons/Using-Addons/index.html b/docs/build/Ponzu-Addons/Using-Addons/index.html new file mode 100644 index 00000000..cfe1d765 --- /dev/null +++ b/docs/build/Ponzu-Addons/Using-Addons/index.html @@ -0,0 +1,846 @@ + + + + + + + + + + + + + + + + + + How to use Ponzu Addons + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+ +
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Quickstart/Overview/index.html b/docs/build/Quickstart/Overview/index.html new file mode 100644 index 00000000..fe023d62 --- /dev/null +++ b/docs/build/Quickstart/Overview/index.html @@ -0,0 +1,932 @@ + + + + + + + + + + + + + + + + + + Overview - Ponzu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Overview

+ +

Quickstart Steps

+

1) Install Go 1.8+

+

2) Install Ponzu CLI:

+
$ go get github.com/ponzu-cms/ponzu/…
+
+ + +

3) Create a new project (path is created in your GOPATH):

+
$ ponzu new github.com/nilslice/reviews
+
+ + +

4) Enter your new project directory:

+
$ cd $GOPATH/src/github.com/nilslice/reviews
+
+ + +

5) Generate content type file and boilerplate code (creates content/review.go):

+
$ 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:

+
$ ponzu build
+
+ + +

7) Run your project with defaults:

+
$ ponzu run
+
+ + +

8) Open browser to http://localhost:8080/admin

+

Notes

+
    +
  • One-time initialization to set configuration
  • +
  • All fields can be changed in Configuration afterward
  • +
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/References/Overview/index.html b/docs/build/References/Overview/index.html new file mode 100644 index 00000000..6766130f --- /dev/null +++ b/docs/build/References/Overview/index.html @@ -0,0 +1,1163 @@ + + + + + + + + + + + + + + + + + + References in Ponzu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Overview

+ +

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, but first we'll walk through +a quick tutorial on Ponzu's references.

+

To generate references from the CLI, please read through the documentation. +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.

+
$ 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

+
type Author struct {
+    item.Item
+
+    Name  string `json:"name"`
+    Photo string `json:"photo"`
+    Bio   string `json:"bio"`
+}
+
+ + +

content/book.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)

+
{
+    "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)

+
{
+    "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:

+
# 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:

+
# 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. +See the example below which demonstrates a complete implementation on the Book +struct, which has a reference to an Author.

+
Example
+

content/book.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.

+
+

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!

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/Running-Backups/Backups/index.html b/docs/build/Running-Backups/Backups/index.html new file mode 100644 index 00000000..aabe0294 --- /dev/null +++ b/docs/build/Running-Backups/Backups/index.html @@ -0,0 +1,912 @@ + + + + + + + + + + + + + + + + + + Running Backups on Ponzu systems + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Backups

+ +

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

+

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:

+
$ 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:

+
$ 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/build/System-Configuration/Settings/index.html b/docs/build/System-Configuration/Settings/index.html new file mode 100644 index 00000000..cafc145e --- /dev/null +++ b/docs/build/System-Configuration/Settings/index.html @@ -0,0 +1,1131 @@ + + + + + + + + + + + + + + + + + + Configuring Your Ponzu System Settings + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

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 +- 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. +
  3. Set the Administrator Email to register with Let's Encrypt
  4. +
  5. Stop your Ponzu server
  6. +
  7. Run your Ponzu server with the --https flag e.g. $ ponzu run --https
  8. +
  9. Visit your CMS admin with https:// prepended to your URL
  10. +
+
+

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.

+
+

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, the user:password pair tells your server +that the backup request is made from a trusted party.

+
+

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.

+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/System-Deployment/Docker/index.html b/docs/build/System-Deployment/Docker/index.html new file mode 100644 index 00000000..557b538c --- /dev/null +++ b/docs/build/System-Deployment/Docker/index.html @@ -0,0 +1,977 @@ + + + + + + + + + + + + + + + + + + Docker - Ponzu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + + + + +
+
+ + + +

Docker

+ +

Ponzu Docker build

+

Ponzu is distributed as a docker image, +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:

+
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.

+
# from the root of ponzu:
+docker build -t ponzu-dev
+
+ + +

Start the image, share the local directory and pseudo terminal (tty) into for debugging:

+
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 for contributing this!

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/System-Deployment/SysV-Style/index.html b/docs/build/System-Deployment/SysV-Style/index.html new file mode 100644 index 00000000..25df784b --- /dev/null +++ b/docs/build/System-Deployment/SysV-Style/index.html @@ -0,0 +1,899 @@ + + + + + + + + + + + + + + + + + + Deploying Ponzu on Linux with System-V style init + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

SysV Style

+ +

For reference, here is an example init script to run Ponzu servers. You must +define the PROJECT_DIR & RUNAS variables by replacing <PROJECT DIRECTORY> +& <USER> in the script below:

+
#!/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=<PROJECT DIRECTORY>
+SCRIPT='cd $PROJECT_DIR && ponzu run --port=80' # add --https here to get TLS/HTTPS
+RUNAS=<USER>
+
+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 <NAME> 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/build/assets/images/favicon.ico b/docs/build/assets/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e85006a3ce1c6fd81faa6d5a13095519c4a6fc96 GIT binary patch literal 1150 zcmd6lF-yZh9L1kl>(HSEK`2y^4yB6->f+$wD)=oNY!UheIt03Q=;qj=;8*Bap_4*& za8yAl;wmmx5Yyi^7dXN-WYdJ-{qNqpcez|5t#Fr0qTSYcPTG`I2PBk8r$~4kg^0zN zCJe(rhix3do!L$bZ+IuZ{i08x=JR3=e+M4pv0KsKA??{u_*EFfo|`p&t`Vf=jn{)F z1fKk9hWsmYwqWAP^JO*5u*R;*L&dX3H$%S7oB$f0{ISh{QVXuncnzN67WQH2`lip7 zhX+VI$6x$1+$8gMjh4+1l0N#8_0Fh=N#EwpKk{SeE!)SHFB@xQFX3y+8sF#_@!bDW eIdI-IC`$c%>bk?KbPeN9RHtL<1^)v~#xMt8oB^@` literal 0 HcmV?d00001 diff --git a/docs/build/assets/images/icons/bitbucket-670608a71a.svg b/docs/build/assets/images/icons/bitbucket-670608a71a.svg new file mode 100644 index 00000000..7d95cb22 --- /dev/null +++ b/docs/build/assets/images/icons/bitbucket-670608a71a.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/build/assets/images/icons/github-1da075986e.svg b/docs/build/assets/images/icons/github-1da075986e.svg new file mode 100644 index 00000000..3cacb2e0 --- /dev/null +++ b/docs/build/assets/images/icons/github-1da075986e.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/build/assets/images/icons/gitlab-5ad3f9f9e5.svg b/docs/build/assets/images/icons/gitlab-5ad3f9f9e5.svg new file mode 100644 index 00000000..b036a9b5 --- /dev/null +++ b/docs/build/assets/images/icons/gitlab-5ad3f9f9e5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/build/assets/javascripts/application-6b599127bc.js b/docs/build/assets/javascripts/application-6b599127bc.js new file mode 100644 index 00000000..8228c7e4 --- /dev/null +++ b/docs/build/assets/javascripts/application-6b599127bc.js @@ -0,0 +1,3 @@ +window.app=function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=95)}([function(e,t,n){"use strict";var r=n(30)("wks"),i=n(22),o=n(1).Symbol,s="function"==typeof o,a=e.exports=function(e){return r[e]||(r[e]=s&&o[e]||(s?o:i)("Symbol."+e))};a.store=r},function(e,t,n){"use strict";var r=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(e,t,n){"use strict";var r=n(11);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t,n){"use strict";var r=n(12),i=n(29);e.exports=n(5)?function(e,t,n){return r.f(e,t,i(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){"use strict";var r=e.exports={version:"2.4.0"};"number"==typeof __e&&(__e=r)},function(e,t,n){"use strict";e.exports=!n(25)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t,n){"use strict";var r={}.hasOwnProperty;e.exports=function(e,t){return r.call(e,t)}},function(e,t,n){"use strict";e.exports={}},function(e,t,n){"use strict";var r=n(1),i=n(3),o=n(6),s=n(22)("src"),a="toString",c=Function[a],u=(""+c).split(a);n(4).inspectSource=function(e){return c.call(e)},(e.exports=function(e,t,n,a){var c="function"==typeof n;c&&(o(n,"name")||i(n,"name",t)),e[t]!==n&&(c&&(o(n,s)||i(n,s,e[t]?""+e[t]:u.join(String(t)))),e===r?e[t]=n:a?e[t]?e[t]=n:i(e,t,n):(delete e[t],i(e,t,n)))})(Function.prototype,a,function(){return"function"==typeof this&&this[s]||c.call(this)})},function(e,t,n){"use strict";var r={}.toString;e.exports=function(e){return r.call(e).slice(8,-1)}},function(e,t,n){"use strict";var r=n(14);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,i){return e.call(t,n,r,i)}}return function(){return e.apply(t,arguments)}}},function(e,t,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};e.exports=function(e){return"object"===("undefined"==typeof e?"undefined":r(e))?null!==e:"function"==typeof e}},function(e,t,n){"use strict";var r=n(2),i=n(43),o=n(63),s=Object.defineProperty;t.f=n(5)?Object.defineProperty:function(e,t,n){if(r(e),t=o(t,!0),r(n),i)try{return s(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={createElement:function(e,t){var n=document.createElement(e);t&&Array.prototype.forEach.call(Object.keys(t),function(e){n.setAttribute(e,t[e])});for(var r=function e(t){Array.prototype.forEach.call(t,function(t){"string"==typeof t||"number"==typeof t?n.textContent+=t:Array.isArray(t)?e(t):"undefined"!=typeof t.__html?n.innerHTML+=t.__html:t instanceof Node&&n.appendChild(t)})},i=arguments.length,o=Array(i>2?i-2:0),s=2;s0?i:r)(e)}},function(e,t,n){"use strict";var r=n(45),i=n(16);e.exports=function(e){return r(i(e))}},function(e,t,n){"use strict";var r=0,i=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++r+i).toString(36))}},function(e,t,n){"use strict";e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t,n){"use strict";var r=n(1),i=n(4),o=n(3),s=n(8),a=n(10),c="prototype",u=function e(t,n,u){var l,f,h,d,p=t&e.F,m=t&e.G,v=t&e.S,y=t&e.P,g=t&e.B,w=m?r:v?r[n]||(r[n]={}):(r[n]||{})[c],b=m?i:i[n]||(i[n]={}),_=b[c]||(b[c]={});m&&(u=n);for(l in u)f=!p&&w&&void 0!==w[l],h=(f?w:u)[l],d=g&&f?a(h,r):y&&"function"==typeof h?a(Function.call,h):h,w&&s(w,l,h,t&e.U),b[l]!=h&&o(b,l,d),y&&_[l]!=h&&(_[l]=h)};r.core=i,u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,e.exports=u},function(e,t,n){"use strict";e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t,n){"use strict";e.exports=n(1).document&&document.documentElement},function(e,t,n){"use strict";var r=n(28),i=n(24),o=n(8),s=n(3),a=n(6),c=n(7),u=n(48),l=n(18),f=n(54),h=n(0)("iterator"),d=!([].keys&&"next"in[].keys()),p="@@iterator",m="keys",v="values",y=function(){return this};e.exports=function(e,t,n,g,w,b,_){u(n,t,g);var x,E,k,S=function(e){if(!d&&e in O)return O[e];switch(e){case m:return function(){return new n(this,e)};case v:return function(){return new n(this,e)}}return function(){return new n(this,e)}},T=t+" Iterator",L=w==v,C=!1,O=e.prototype,P=O[h]||O[p]||w&&O[w],A=P||S(w),M=w?L?S("entries"):A:void 0,R="Array"==t?O.entries||P:P;if(R&&(k=f(R.call(new e)),k!==Object.prototype&&(l(k,T,!0),r||a(k,h)||s(k,h,y))),L&&P&&P.name!==v&&(C=!0,A=function(){return P.call(this)}),r&&!_||!d&&!C&&O[h]||s(O,h,A),c[t]=A,c[T]=y,w)if(x={values:L?A:S(v),keys:b?A:S(m),entries:M},_)for(E in x)E in O||o(O,E,x[E]);else i(i.P+i.F*(d||C),t,x);return x}},function(e,t,n){"use strict";e.exports=!1},function(e,t,n){"use strict";e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,n){"use strict";var r=n(1),i="__core-js_shared__",o=r[i]||(r[i]={});e.exports=function(e){return o[e]||(o[e]={})}},function(e,t,n){"use strict";var r,i,o,s=n(10),a=n(44),c=n(26),u=n(17),l=n(1),f=l.process,h=l.setImmediate,d=l.clearImmediate,p=l.MessageChannel,m=0,v={},y="onreadystatechange",g=function(){var e=+this;if(v.hasOwnProperty(e)){var t=v[e];delete v[e],t()}},w=function(e){g.call(e.data)};h&&d||(h=function(e){for(var t=[],n=1;arguments.length>n;)t.push(arguments[n++]);return v[++m]=function(){a("function"==typeof e?e:Function(e),t)},r(m),m},d=function(e){delete v[e]},"process"==n(9)(f)?r=function(e){f.nextTick(s(g,e,1))}:p?(i=new p,o=i.port2,i.port1.onmessage=w,r=s(o.postMessage,o,1)):l.addEventListener&&"function"==typeof postMessage&&!l.importScripts?(r=function(e){l.postMessage(e+"","*")},l.addEventListener("message",w,!1)):r=y in u("script")?function(e){c.appendChild(u("script"))[y]=function(){c.removeChild(this),g.call(e)}}:function(e){setTimeout(s(g,e,1),0)}),e.exports={set:h,clear:d}},function(e,t,n){"use strict";var r=n(20),i=Math.min;e.exports=function(e){return e>0?i(r(e),9007199254740991):0}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=function(){function e(e,t){for(var n=0;n-1?t:e}function d(e,t){t=t||{};var n=t.body;if(e instanceof d){if(e.bodyUsed)throw new TypeError("Already read");this.url=e.url,this.credentials=e.credentials,t.headers||(this.headers=new i(e.headers)),this.method=e.method,this.mode=e.mode,n||null==e._bodyInit||(n=e._bodyInit,e.bodyUsed=!0)}else this.url=String(e);if(this.credentials=t.credentials||this.credentials||"omit",!t.headers&&this.headers||(this.headers=new i(t.headers)),this.method=h(t.method||this.method||"GET"),this.mode=t.mode||this.mode||null,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&n)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(n)}function p(e){var t=new FormData;return e.trim().split("&").forEach(function(e){if(e){var n=e.split("="),r=n.shift().replace(/\+/g," "),i=n.join("=").replace(/\+/g," ");t.append(decodeURIComponent(r),decodeURIComponent(i))}}),t}function m(e){var t=new i;return e.split(/\r?\n/).forEach(function(e){var n=e.split(":"),r=n.shift().trim();if(r){var i=n.join(":").trim();t.append(r,i)}}),t}function v(e,t){t||(t={}),this.type="default",this.status="status"in t?t.status:200,this.ok=this.status>=200&&this.status<300,this.statusText="statusText"in t?t.statusText:"OK",this.headers=new i(t.headers),this.url=t.url||"",this._initBody(e)}if(!e.fetch){var y={searchParams:"URLSearchParams"in e,iterable:"Symbol"in e&&"iterator"in Symbol,blob:"FileReader"in e&&"Blob"in e&&function(){try{return new Blob,!0}catch(e){return!1}}(),formData:"FormData"in e,arrayBuffer:"ArrayBuffer"in e};if(y.arrayBuffer)var g=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],w=function(e){return e&&DataView.prototype.isPrototypeOf(e)},b=ArrayBuffer.isView||function(e){return e&&g.indexOf(Object.prototype.toString.call(e))>-1};i.prototype.append=function(e,r){e=t(e),r=n(r);var i=this.map[e];this.map[e]=i?i+","+r:r},i.prototype.delete=function(e){delete this.map[t(e)]},i.prototype.get=function(e){return e=t(e),this.has(e)?this.map[e]:null},i.prototype.has=function(e){return this.map.hasOwnProperty(t(e))},i.prototype.set=function(e,r){this.map[t(e)]=n(r)},i.prototype.forEach=function(e,t){for(var n in this.map)this.map.hasOwnProperty(n)&&e.call(t,this.map[n],n,this)},i.prototype.keys=function(){var e=[];return this.forEach(function(t,n){e.push(n)}),r(e)},i.prototype.values=function(){var e=[];return this.forEach(function(t){e.push(t)}),r(e)},i.prototype.entries=function(){var e=[];return this.forEach(function(t,n){e.push([n,t])}),r(e)},y.iterable&&(i.prototype[Symbol.iterator]=i.prototype.entries);var _=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];d.prototype.clone=function(){return new d(this,{body:this._bodyInit})},f.call(d.prototype),f.call(v.prototype),v.prototype.clone=function(){return new v(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new i(this.headers),url:this.url})},v.error=function(){var e=new v(null,{status:0,statusText:""});return e.type="error",e};var x=[301,302,303,307,308];v.redirect=function(e,t){if(x.indexOf(t)===-1)throw new RangeError("Invalid status code");return new v(null,{status:t,headers:{location:e}})},e.Headers=i,e.Request=d,e.Response=v,e.fetch=function(e,t){return new Promise(function(n,r){var i=new d(e,t),o=new XMLHttpRequest;o.onload=function(){var e={status:o.status,statusText:o.statusText,headers:m(o.getAllResponseHeaders()||"")};e.url="responseURL"in o?o.responseURL:e.headers.get("X-Request-URL");var t="response"in o?o.response:o.responseText;n(new v(t,e))},o.onerror=function(){r(new TypeError("Network request failed"))},o.ontimeout=function(){r(new TypeError("Network request failed"))},o.open(i.method,i.url,!0),"include"===i.credentials&&(o.withCredentials=!0),"responseType"in o&&y.blob&&(o.responseType="blob"),i.headers.forEach(function(e,t){o.setRequestHeader(t,e)}),o.send("undefined"==typeof i._bodyInit?null:i._bodyInit)})},e.fetch.polyfill=!0}}("undefined"!=typeof self?self:void 0)},function(e,t,n){"use strict";(function(e){function r(t){new s.a.Event.Listener(document,"DOMContentLoaded",function(){if(!(document.body instanceof HTMLElement))throw new ReferenceError;o.a.attach(document.body),Modernizr.addTest("ios",function(){return!!navigator.userAgent.match(/(iPad|iPhone|iPod)/g)});var t=document.querySelectorAll("table:not([class])");if(Array.prototype.forEach.call(t,function(t){var n=e.createElement("div",{class:"md-typeset__scrollwrap"},e.createElement("div",{class:"md-typeset__table"}));t.nextSibling?t.parentNode.insertBefore(n,t.nextSibling):t.parentNode.appendChild(n),n.children[0].appendChild(t)}),Modernizr.ios){var n=document.querySelectorAll("[data-md-scrollfix]");Array.prototype.forEach.call(n,function(e){e.addEventListener("touchstart",function(){var t=e.scrollTop;0===t?e.scrollTop=1:t+e.offsetHeight===e.scrollHeight&&(e.scrollTop=t-1)})})}}).listen(),new s.a.Event.MatchMedia("(min-width: 1220px)",new s.a.Event.Listener(window,["scroll","resize","orientationchange"],new s.a.Header.Shadow("[data-md-component=container]","[data-md-component=header]"))),document.querySelector("[data-md-component=tabs]")&&new s.a.Event.Listener(window,["scroll","resize","orientationchange"],new s.a.Tabs.Toggle("[data-md-component=tabs]")).listen(),new s.a.Event.MatchMedia("(min-width: 1220px)",new s.a.Event.Listener(window,["scroll","resize","orientationchange"],new s.a.Sidebar.Position("[data-md-component=navigation]","[data-md-component=header]"))),document.querySelector("[data-md-component=toc]")&&new s.a.Event.MatchMedia("(min-width: 960px)",new s.a.Event.Listener(window,["scroll","resize","orientationchange"],new s.a.Sidebar.Position("[data-md-component=toc]","[data-md-component=header]"))),new s.a.Event.MatchMedia("(min-width: 960px)",new s.a.Event.Listener(window,"scroll",new s.a.Nav.Blur("[data-md-component=toc] [href]")));var n=document.querySelectorAll("[data-md-component=collapsible]");Array.prototype.forEach.call(n,function(e){new s.a.Event.MatchMedia("(min-width: 1220px)",new s.a.Event.Listener(e.previousElementSibling,"click",new s.a.Nav.Collapse(e)))}),new s.a.Event.MatchMedia("(max-width: 1219px)",new s.a.Event.Listener("[data-md-component=navigation] [data-md-toggle]","change",new s.a.Nav.Scrolling("[data-md-component=navigation] nav"))),new s.a.Event.MatchMedia("(max-width: 959px)",new s.a.Event.Listener("[data-md-toggle=search]","change",new s.a.Search.Lock("[data-md-toggle=search]"))),new s.a.Event.Listener("[data-md-component=query]",["focus","keyup","change"],new s.a.Search.Result("[data-md-component=result]",function(){return fetch(t.url.base+"/mkdocs/search_index.json",{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return e.docs.map(function(e){return e.location=t.url.base+e.location,e})})})).listen(),new s.a.Event.MatchMedia("(max-width: 959px)",new s.a.Event.Listener("[data-md-component=navigation] [href^='#']","click",function(){var e=document.querySelector("[data-md-toggle=drawer]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked&&(e.checked=!1,e.dispatchEvent(new CustomEvent("change")))})),new s.a.Event.Listener("[data-md-component=reset]","click",function(){setTimeout(function(){var e=document.querySelector("[data-md-component=query]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.focus()},10)}).listen(),new s.a.Event.Listener("[data-md-toggle=search]","change",function(e){setTimeout(function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t.focus()}},400,e.target)}).listen(),new s.a.Event.MatchMedia("(min-width: 960px)",new s.a.Event.Listener("[data-md-component=query]","focus",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked||(e.checked=!0,e.dispatchEvent(new CustomEvent("change")))})),new s.a.Event.Listener(window,"keydown",function(e){var t=document.querySelector("[data-md-toggle=search]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;var n=document.querySelector("[data-md-component=query]");if(!(n instanceof HTMLInputElement))throw new ReferenceError;if(!e.metaKey&&!e.ctrlKey)if(t.checked){if(13===e.keyCode){if(n===document.activeElement){e.preventDefault();var r=document.querySelector("[data-md-component=search] [href][data-md-state=active]");r instanceof HTMLLinkElement&&(window.location=r.getAttribute("href")),t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur()}}else if(27===e.keyCode)t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur();else if([8,37,39].indexOf(e.keyCode)!==-1)n!==document.activeElement&&n.focus();else if([9,38,40].indexOf(e.keyCode)!==-1){var i=e.shiftKey?38:40,o=9===e.keyCode?i:e.keyCode,s=Array.prototype.slice.call(document.querySelectorAll("[data-md-component=search] [href]"));if(!s.length)return;var a=s.find(function(e){if(!(e instanceof HTMLElement))throw new ReferenceError;return"active"===e.dataset.mdState});a&&(a.dataset.mdState="");var c=Math.max(0,(s.indexOf(a)+s.length+(38===o?-1:1))%s.length);if(!(s[c]instanceof HTMLElement))throw new ReferenceError;return s[c].dataset.mdState="active",s[c].focus(),e.preventDefault(),e.stopPropagation(),!1}}else 70!==e.keyCode&&83!==e.keyCode||(n.focus(),e.preventDefault())}).listen(),new s.a.Event.Listener(window,"keypress",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t!==document.activeElement&&t.focus()}}).listen(),function(){var e=document.querySelector("[data-md-source]");if(!e)return Promise.resolve([]);if(!(e instanceof HTMLAnchorElement))throw new ReferenceError;switch(e.dataset.mdSource){case"github":return new s.a.Source.Adapter.GitHub(e).fetch();default:return Promise.resolve([])}}().then(function(e){var t=document.querySelectorAll("[data-md-source]");Array.prototype.forEach.call(t,function(t){new s.a.Source.Repository(t).initialize(e)})})}Object.defineProperty(t,"__esModule",{value:!0});var i=n(71),o=n.n(i),s=n(74);n.d(t,"initialize",function(){return r})}).call(t,n(13))},function(e,t,n){"use strict";var r=n(0)("unscopables"),i=Array.prototype;void 0==i[r]&&n(3)(i,r,{}),e.exports=function(e){i[r][e]=!0}},function(e,t,n){"use strict";e.exports=function(e,t,n,r){if(!(e instanceof t)||void 0!==r&&r in e)throw TypeError(n+": incorrect invocation!");return e}},function(e,t,n){"use strict";var r=n(21),i=n(32),o=n(61);e.exports=function(e){return function(t,n,s){var a,c=r(t),u=i(c.length),l=o(s,u);if(e&&n!=n){for(;u>l;)if(a=c[l++],a!=a)return!0}else for(;u>l;l++)if((e||l in c)&&c[l]===n)return e||l||0;return!e&&-1}}},function(e,t,n){"use strict";var r=n(10),i=n(47),o=n(46),s=n(2),a=n(32),c=n(64),u={},l={},f=e.exports=function(e,t,n,f,h){var d,p,m,v,y=h?function(){return e}:c(e),g=r(n,f,t?2:1),w=0;if("function"!=typeof y)throw TypeError(e+" is not iterable!");if(o(y)){for(d=a(e.length);d>w;w++)if(v=t?g(s(p=e[w])[0],p[1]):g(e[w]),v===u||v===l)return v}else for(m=y.call(e);!(p=m.next()).done;)if(v=i(m,g,p.value,t),v===u||v===l)return v};f.BREAK=u,f.RETURN=l},function(e,t,n){"use strict";e.exports=!n(5)&&!n(25)(function(){return 7!=Object.defineProperty(n(17)("div"),"a",{get:function(){return 7}}).a})},function(e,t,n){"use strict";e.exports=function(e,t,n){var r=void 0===n;switch(t.length){case 0:return r?e():e.call(n);case 1:return r?e(t[0]):e.call(n,t[0]);case 2:return r?e(t[0],t[1]):e.call(n,t[0],t[1]);case 3:return r?e(t[0],t[1],t[2]):e.call(n,t[0],t[1],t[2]);case 4:return r?e(t[0],t[1],t[2],t[3]):e.call(n,t[0],t[1],t[2],t[3])}return e.apply(n,t)}},function(e,t,n){"use strict";var r=n(9);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==r(e)?e.split(""):Object(e)}},function(e,t,n){"use strict";var r=n(7),i=n(0)("iterator"),o=Array.prototype;e.exports=function(e){return void 0!==e&&(r.Array===e||o[i]===e)}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n,i){try{return i?t(r(n)[0],n[1]):t(n)}catch(t){var o=e.return;throw void 0!==o&&r(o.call(e)),t}}},function(e,t,n){"use strict";var r=n(52),i=n(29),o=n(18),s={};n(3)(s,n(0)("iterator"),function(){return this}),e.exports=function(e,t,n){e.prototype=r(s,{next:i(1,n)}),o(e,t+" Iterator")}},function(e,t,n){"use strict";var r=n(0)("iterator"),i=!1;try{var o=[7][r]();o.return=function(){i=!0},Array.from(o,function(){throw 2})}catch(e){}e.exports=function(e,t){if(!t&&!i)return!1;var n=!1;try{var o=[7],s=o[r]();s.next=function(){return{done:n=!0}},o[r]=function(){return s},e(o)}catch(e){}return n}},function(e,t,n){"use strict";e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,n){"use strict";var r=n(1),i=n(31).set,o=r.MutationObserver||r.WebKitMutationObserver,s=r.process,a=r.Promise,c="process"==n(9)(s);e.exports=function(){var e,t,n,u=function(){var r,i;for(c&&(r=s.domain)&&r.exit();e;){i=e.fn,e=e.next;try{i()}catch(r){throw e?n():t=void 0,r}}t=void 0,r&&r.enter()};if(c)n=function(){s.nextTick(u)};else if(o){var l=!0,f=document.createTextNode("");new o(u).observe(f,{characterData:!0}),n=function(){f.data=l=!l}}else if(a&&a.resolve){var h=a.resolve();n=function(){h.then(u)}}else n=function(){i.call(r,u)};return function(r){var i={fn:r,next:void 0};t&&(t.next=i),e||(e=i,n()),t=i}}},function(e,t,n){"use strict";var r=n(2),i=n(53),o=n(23),s=n(19)("IE_PROTO"),a=function(){},c="prototype",u=function(){var e,t=n(17)("iframe"),r=o.length,i="<",s=">";for(t.style.display="none",n(26).appendChild(t),t.src="javascript:",e=t.contentWindow.document,e.open(),e.write(i+"script"+s+"document.F=Object"+i+"/script"+s),e.close(),u=e.F;r--;)delete u[c][o[r]];return u()};e.exports=Object.create||function(e,t){var n;return null!==e?(a[c]=r(e),n=new a,a[c]=null,n[s]=e):n=u(),void 0===t?n:i(n,t)}},function(e,t,n){"use strict";var r=n(12),i=n(2),o=n(56);e.exports=n(5)?Object.defineProperties:function(e,t){i(e);for(var n,s=o(t),a=s.length,c=0;a>c;)r.f(e,n=s[c++],t[n]);return e}},function(e,t,n){"use strict";var r=n(6),i=n(62),o=n(19)("IE_PROTO"),s=Object.prototype;e.exports=Object.getPrototypeOf||function(e){return e=i(e),r(e,o)?e[o]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?s:null}},function(e,t,n){"use strict";var r=n(6),i=n(21),o=n(41)(!1),s=n(19)("IE_PROTO");e.exports=function(e,t){var n,a=i(e),c=0,u=[];for(n in a)n!=s&&r(a,n)&&u.push(n);for(;t.length>c;)r(a,n=t[c++])&&(~o(u,n)||u.push(n));return u}},function(e,t,n){"use strict";var r=n(55),i=n(23);e.exports=Object.keys||function(e){return r(e,i)}},function(e,t,n){"use strict";var r=n(8);e.exports=function(e,t,n){for(var i in t)r(e,i,t[i],n);return e}},function(e,t,n){"use strict";var r=n(1),i=n(12),o=n(5),s=n(0)("species");e.exports=function(e){var t=r[e];o&&t&&!t[s]&&i.f(t,s,{configurable:!0,get:function(){return this}})}},function(e,t,n){"use strict";var r=n(2),i=n(14),o=n(0)("species");e.exports=function(e,t){var n,s=r(e).constructor;return void 0===s||void 0==(n=r(s)[o])?t:i(n)}},function(e,t,n){"use strict";var r=n(20),i=n(16);e.exports=function(e){return function(t,n){var o,s,a=String(i(t)),c=r(n),u=a.length;return c<0||c>=u?e?"":void 0:(o=a.charCodeAt(c),o<55296||o>56319||c+1===u||(s=a.charCodeAt(c+1))<56320||s>57343?e?a.charAt(c):o:e?a.slice(c,c+2):(o-55296<<10)+(s-56320)+65536)}}},function(e,t,n){"use strict";var r=n(20),i=Math.max,o=Math.min;e.exports=function(e,t){return e=r(e),e<0?i(e+t,0):o(e,t)}},function(e,t,n){"use strict";var r=n(16);e.exports=function(e){return Object(r(e))}},function(e,t,n){"use strict";var r=n(11);e.exports=function(e,t){if(!r(e))return e;var n,i;if(t&&"function"==typeof(n=e.toString)&&!r(i=n.call(e)))return i;if("function"==typeof(n=e.valueOf)&&!r(i=n.call(e)))return i;if(!t&&"function"==typeof(n=e.toString)&&!r(i=n.call(e)))return i;throw TypeError("Can't convert object to primitive value")}},function(e,t,n){"use strict";var r=n(15),i=n(0)("iterator"),o=n(7);e.exports=n(4).getIteratorMethod=function(e){if(void 0!=e)return e[i]||e["@@iterator"]||o[r(e)]}},function(e,t,n){"use strict";var r=n(39),i=n(50),o=n(7),s=n(21);e.exports=n(27)(Array,"Array",function(e,t){this._t=s(e),this._i=0,this._k=t},function(){var e=this._t,t=this._k,n=this._i++;return!e||n>=e.length?(this._t=void 0,i(1)):"keys"==t?i(0,n):"values"==t?i(0,e[n]):i(0,[n,e[n]])},"values"),o.Arguments=o.Array,r("keys"),r("values"),r("entries")},function(e,t,n){"use strict";var r=n(15),i={};i[n(0)("toStringTag")]="z",i+""!="[object z]"&&n(8)(Object.prototype,"toString",function(){return"[object "+r(this)+"]"},!0)},function(e,t,n){"use strict";var r,i,o,s=n(28),a=n(1),c=n(10),u=n(15),l=n(24),f=n(11),h=n(14),d=n(40),p=n(42),m=n(59),v=n(31).set,y=n(51)(),g="Promise",w=a.TypeError,b=a.process,_=a[g],b=a.process,x="process"==u(b),E=function(){},k=!!function(){try{var e=_.resolve(1),t=(e.constructor={})[n(0)("species")]=function(e){e(E,E)};return(x||"function"==typeof PromiseRejectionEvent)&&e.then(E)instanceof t}catch(e){}}(),S=function(e,t){return e===t||e===_&&t===o},T=function(e){var t;return!(!f(e)||"function"!=typeof(t=e.then))&&t},L=function(e){return S(_,e)?new C(e):new i(e)},C=i=function(e){var t,n;this.promise=new e(function(e,r){if(void 0!==t||void 0!==n)throw w("Bad Promise constructor");t=e,n=r}),this.resolve=h(t),this.reject=h(n)},O=function(e){try{e()}catch(e){return{error:e}}},P=function(e,t){if(!e._n){e._n=!0;var n=e._c;y(function(){for(var r=e._v,i=1==e._s,o=0,s=function(t){var n,o,s=i?t.ok:t.fail,a=t.resolve,c=t.reject,u=t.domain;try{s?(i||(2==e._h&&R(e),e._h=1),s===!0?n=r:(u&&u.enter(),n=s(r),u&&u.exit()),n===t.promise?c(w("Promise-chain cycle")):(o=T(n))?o.call(n,a,c):a(n)):c(r)}catch(e){c(e)}};n.length>o;)s(n[o++]);e._c=[],e._n=!1,t&&!e._h&&A(e)})}},A=function(e){v.call(a,function(){var t,n,r,i=e._v;if(M(e)&&(t=O(function(){x?b.emit("unhandledRejection",i,e):(n=a.onunhandledrejection)?n({promise:e,reason:i}):(r=a.console)&&r.error&&r.error("Unhandled promise rejection",i)}),e._h=x||M(e)?2:1),e._a=void 0,t)throw t.error})},M=function e(t){if(1==t._h)return!1;for(var n,r=t._a||t._c,i=0;r.length>i;)if(n=r[i++],n.fail||!e(n.promise))return!1;return!0},R=function(e){v.call(a,function(){var t;x?b.emit("rejectionHandled",e):(t=a.onrejectionhandled)&&t({promise:e,reason:e._v})})},I=function(e){var t=this;t._d||(t._d=!0,t=t._w||t,t._v=e,t._s=2,t._a||(t._a=t._c.slice()),P(t,!0))},j=function e(t){var n,r=this;if(!r._d){r._d=!0,r=r._w||r;try{if(r===t)throw w("Promise can't be resolved itself");(n=T(t))?y(function(){var i={_w:r,_d:!1};try{n.call(t,c(e,i,1),c(I,i,1))}catch(e){I.call(i,e)}}):(r._v=t,r._s=1,P(r,!1))}catch(e){I.call({_w:r,_d:!1},e)}}};k||(_=function(e){d(this,_,g,"_h"),h(e),r.call(this);try{e(c(j,this,1),c(I,this,1))}catch(e){I.call(this,e)}},r=function(e){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0, +this._h=0,this._n=!1},r.prototype=n(57)(_.prototype,{then:function(e,t){var n=L(m(this,_));return n.ok="function"!=typeof e||e,n.fail="function"==typeof t&&t,n.domain=x?b.domain:void 0,this._c.push(n),this._a&&this._a.push(n),this._s&&P(this,!1),n.promise},catch:function(e){return this.then(void 0,e)}}),C=function(){var e=new r;this.promise=e,this.resolve=c(j,e,1),this.reject=c(I,e,1)}),l(l.G+l.W+l.F*!k,{Promise:_}),n(18)(_,g),n(58)(g),o=n(4)[g],l(l.S+l.F*!k,g,{reject:function(e){var t=L(this),n=t.reject;return n(e),t.promise}}),l(l.S+l.F*(s||!k),g,{resolve:function(e){if(e instanceof _&&S(e.constructor,this))return e;var t=L(this),n=t.resolve;return n(e),t.promise}}),l(l.S+l.F*!(k&&n(49)(function(e){_.all(e).catch(E)})),g,{all:function(e){var t=this,n=L(t),r=n.resolve,i=n.reject,o=O(function(){var n=[],o=0,s=1;p(e,!1,function(e){var a=o++,c=!1;n.push(void 0),s++,t.resolve(e).then(function(e){c||(c=!0,n[a]=e,--s||r(n))},i)}),--s||r(n)});return o&&i(o.error),n.promise},race:function(e){var t=this,n=L(t),r=n.reject,i=O(function(){p(e,!1,function(e){t.resolve(e).then(n.resolve,r)})});return i&&r(i.error),n.promise}})},function(e,t,n){"use strict";var r=n(60)(!0);n(27)(String,"String",function(e){this._t=String(e),this._i=0},function(){var e,t=this._t,n=this._i;return n>=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){"use strict";for(var r=n(65),i=n(8),o=n(1),s=n(3),a=n(7),c=n(0),u=c("iterator"),l=c("toStringTag"),f=a.Array,h=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],d=0;d<5;d++){var p,m=h[d],v=o[m],y=v&&v.prototype;if(y){y[u]||s(y,u,f),y[l]||s(y,l,m),a[m]=f;for(p in r)y[p]||i(y,p,r[p],!0)}}},function(e,t,n){"use strict";var r=/[|\\{}()[\]^$+*?.]/g;e.exports=function(e){if("string"!=typeof e)throw new TypeError("Expected a string");return e.replace(r,"\\$&")}},function(e,t,n){"use strict";var r,i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};!function(){function o(e,t){function n(e,t){return function(){return e.apply(t,arguments)}}var r;if(t=t||{},this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.touchBoundary=t.touchBoundary||10,this.layer=e,this.tapDelay=t.tapDelay||200,this.tapTimeout=t.tapTimeout||700,!o.notNeeded(e)){for(var i=["onMouse","onClick","onTouchStart","onTouchMove","onTouchEnd","onTouchCancel"],s=this,c=0,u=i.length;c=0,a=navigator.userAgent.indexOf("Android")>0&&!s,c=/iP(ad|hone|od)/.test(navigator.userAgent)&&!s,u=c&&/OS 4_\d(_\d)?/.test(navigator.userAgent),l=c&&/OS [6-7]_\d/.test(navigator.userAgent),f=navigator.userAgent.indexOf("BB10")>0;o.prototype.needsClick=function(e){switch(e.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(e.disabled)return!0;break;case"input":if(c&&"file"===e.type||e.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(e.className)},o.prototype.needsFocus=function(e){switch(e.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!a;case"input":switch(e.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!e.disabled&&!e.readOnly;default:return/\bneedsfocus\b/.test(e.className)}},o.prototype.sendClick=function(e,t){var n,r;document.activeElement&&document.activeElement!==e&&document.activeElement.blur(),r=t.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(e),!0,!0,window,1,r.screenX,r.screenY,r.clientX,r.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,e.dispatchEvent(n)},o.prototype.determineEventType=function(e){return a&&"select"===e.tagName.toLowerCase()?"mousedown":"click"},o.prototype.focus=function(e){var t;c&&e.setSelectionRange&&0!==e.type.indexOf("date")&&"time"!==e.type&&"month"!==e.type?(t=e.value.length,e.setSelectionRange(t,t)):e.focus()},o.prototype.updateScrollParent=function(e){var t,n;if(t=e.fastClickScrollParent,!t||!t.contains(e)){n=e;do{if(n.scrollHeight>n.offsetHeight){t=n,e.fastClickScrollParent=n;break}n=n.parentElement}while(n)}t&&(t.fastClickLastScrollTop=t.scrollTop)},o.prototype.getTargetElementFromEventTarget=function(e){return e.nodeType===Node.TEXT_NODE?e.parentNode:e},o.prototype.onTouchStart=function(e){var t,n,r;if(e.targetTouches.length>1)return!0;if(t=this.getTargetElementFromEventTarget(e.target),n=e.targetTouches[0],c){if(r=window.getSelection(),r.rangeCount&&!r.isCollapsed)return!0;if(!u){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return e.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(t)}}return this.trackingClick=!0,this.trackingClickStart=e.timeStamp,this.targetElement=t,this.touchStartX=n.pageX,this.touchStartY=n.pageY,e.timeStamp-this.lastClickTimen||Math.abs(t.pageY-this.touchStartY)>n},o.prototype.onTouchMove=function(e){return!this.trackingClick||((this.targetElement!==this.getTargetElementFromEventTarget(e.target)||this.touchHasMoved(e))&&(this.trackingClick=!1,this.targetElement=null),!0)},o.prototype.findControl=function(e){return void 0!==e.control?e.control:e.htmlFor?document.getElementById(e.htmlFor):e.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},o.prototype.onTouchEnd=function(e){var t,n,r,i,o,s=this.targetElement;if(!this.trackingClick)return!0;if(e.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=e.timeStamp,n=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,l&&(o=e.changedTouches[0],s=document.elementFromPoint(o.pageX-window.pageXOffset,o.pageY-window.pageYOffset)||s,s.fastClickScrollParent=this.targetElement.fastClickScrollParent),r=s.tagName.toLowerCase(),"label"===r){if(t=this.findControl(s)){if(this.focus(s),a)return!1;s=t}}else if(this.needsFocus(s))return e.timeStamp-n>100||c&&window.top!==window&&"input"===r?(this.targetElement=null,!1):(this.focus(s),this.sendClick(s,e),c&&"select"===r||(this.targetElement=null,e.preventDefault()),!1);return!(!c||u||(i=s.fastClickScrollParent,!i||i.fastClickLastScrollTop===i.scrollTop))||(this.needsClick(s)||(e.preventDefault(),this.sendClick(s,e)),!1)},o.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},o.prototype.onMouse=function(e){return!this.targetElement||(!!e.forwardedTouchEvent||(!e.cancelable||(!(!this.needsClick(this.targetElement)||this.cancelNextClick)||(e.stopImmediatePropagation?e.stopImmediatePropagation():e.propagationStopped=!0,e.stopPropagation(),e.preventDefault(),!1))))},o.prototype.onClick=function(e){var t;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===e.target.type&&0===e.detail||(t=this.onMouse(e),t||(this.targetElement=null),t)},o.prototype.destroy=function(){var e=this.layer;a&&(e.removeEventListener("mouseover",this.onMouse,!0),e.removeEventListener("mousedown",this.onMouse,!0),e.removeEventListener("mouseup",this.onMouse,!0)),e.removeEventListener("click",this.onClick,!0),e.removeEventListener("touchstart",this.onTouchStart,!1),e.removeEventListener("touchmove",this.onTouchMove,!1),e.removeEventListener("touchend",this.onTouchEnd,!1),e.removeEventListener("touchcancel",this.onTouchCancel,!1)},o.notNeeded=function(e){var t,n,r,i;if("undefined"==typeof window.ontouchstart)return!0;if(n=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!a)return!0;if(t=document.querySelector("meta[name=viewport]")){if(t.content.indexOf("user-scalable=no")!==-1)return!0;if(n>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(f&&(r=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),r[1]>=10&&r[2]>=3&&(t=document.querySelector("meta[name=viewport]")))){if(t.content.indexOf("user-scalable=no")!==-1)return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===e.style.msTouchAction||"manipulation"===e.style.touchAction||(i=+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1],!!(i>=27&&(t=document.querySelector("meta[name=viewport]"),t&&(t.content.indexOf("user-scalable=no")!==-1||document.documentElement.scrollWidth<=window.outerWidth)))||("none"===e.style.touchAction||"manipulation"===e.style.touchAction))},o.attach=function(e,t){return new o(e,t)},"object"===i(n(34))&&n(34)?(r=function(){return o}.call(t,n,t,e),!(void 0!==r&&(e.exports=r))):"undefined"!=typeof e&&e.exports?(e.exports=o.attach,e.exports.FastClick=o):window.FastClick=o}()},function(e,t,n){"use strict";var r,i,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};!function(s){var a=!1;if(r=s,i="function"==typeof r?r.call(t,n,t,e):r,!(void 0!==i&&(e.exports=i)),a=!0,"object"===o(t)&&(e.exports=s(),a=!0),!a){var c=window.Cookies,u=window.Cookies=s();u.noConflict=function(){return window.Cookies=c,u}}}(function(){function e(){for(var e=0,t={};e1){if(o=e({path:"/"},r.defaults,o),"number"==typeof o.expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*o.expires),o.expires=a}try{s=JSON.stringify(i),/^[\{\[]/.test(s)&&(i=s)}catch(e){}return i=n.write?n.write(i,t):encodeURIComponent(String(i)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[\(\)]/g,escape),document.cookie=[t,"=",i,o.expires?"; expires="+o.expires.toUTCString():"",o.path?"; path="+o.path:"",o.domain?"; domain="+o.domain:"",o.secure?"; secure":""].join("")}t||(s={});for(var c=document.cookie?document.cookie.split("; "):[],u=/(%[0-9A-Z]{2})+/g,l=0;l0&&r.push(new o.Token(t.slice(s,i),{position:[s,c],index:r.length})),s=i+1)}return r},o.tokenizer.separator=/[\s\-]+/,o.Pipeline=function(){this._stack=[]},o.Pipeline.registeredFunctions=Object.create(null),o.Pipeline.registerFunction=function(e,t){t in this.registeredFunctions&&o.utils.warn("Overwriting existing registered function: "+t),e.label=t,o.Pipeline.registeredFunctions[e.label]=e},o.Pipeline.warnIfFunctionNotRegistered=function(e){var t=e.label&&e.label in this.registeredFunctions;t||o.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},o.Pipeline.load=function(e){var t=new o.Pipeline;return e.forEach(function(e){var n=o.Pipeline.registeredFunctions[e];if(!n)throw new Error("Cannot load unregistered function: "+e);t.add(n)}),t},o.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){o.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},o.Pipeline.prototype.after=function(e,t){o.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");n+=1,this._stack.splice(n,0,t)},o.Pipeline.prototype.before=function(e,t){o.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");this._stack.splice(n,0,t)},o.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},o.Pipeline.prototype.run=function(e){for(var t=this._stack.length,n=0;n2;){if(s==e)throw"duplicate index";e>s&&(n=o),ee&&this.elements.splice(o,0,e,t),sa?u+=2:s==a&&(t+=n[c+1]*r[u+1],c+=2,u+=2);return t},o.Vector.prototype.similarity=function(e){return this.dot(e)/(this.magnitude()*e.magnitude())},o.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,n=0;t0){var s,a=i.str.charAt(0);a in i.node.edges?s=i.node.edges[a]:(s=new o.TokenSet,i.node.edges[a]=s),1==i.str.length?s.final=!0:r.push({node:s,editsRemaining:i.editsRemaining,str:i.str.slice(1)})}if(i.editsRemaining>0&&i.str.length>1){var c,a=i.str.charAt(1);a in i.node.edges?c=i.node.edges[a]:(c=new o.TokenSet,i.node.edges[a]=c),i.str.length<=2?c.final=!0:r.push({node:c,editsRemaining:i.editsRemaining-1,str:i.str.slice(2)})}if(i.editsRemaining>0&&1==i.str.length&&(i.node.final=!0),i.editsRemaining>0&&i.str.length>=1){if("*"in i.node.edges)var u=i.node.edges["*"];else{var u=new o.TokenSet;i.node.edges["*"]=u}1==i.str.length?u.final=!0:r.push({node:u,editsRemaining:i.editsRemaining-1,str:i.str.slice(1)})}if(i.editsRemaining>0){if("*"in i.node.edges)var l=i.node.edges["*"];else{var l=new o.TokenSet;i.node.edges["*"]=l}0==i.str.length?l.final=!0:r.push({node:l,editsRemaining:i.editsRemaining-1,str:i.str})}if(i.editsRemaining>0&&i.str.length>1){var f,h=i.str.charAt(0),d=i.str.charAt(1);d in i.node.edges?f=i.node.edges[d]:(f=new o.TokenSet,i.node.edges[d]=f),1==i.str.length?f.final=!0:r.push({node:f,editsRemaining:i.editsRemaining-1,str:h+i.str.slice(2)})}}return n},o.TokenSet.fromString=function(e){for(var t=new o.TokenSet,n=t,r=!1,i=0,s=e.length;i=e;t--){var n=this.uncheckedNodes[t],r=n.child.toString();r in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[r]:(n.child._str=r,this.minimizedNodes[r]=n.child),this.uncheckedNodes.pop()}},o.Index=function(e){this.invertedIndex=e.invertedIndex,this.documentVectors=e.documentVectors,this.tokenSet=e.tokenSet,this.documentCount=e.documentCount,this.averageDocumentLength=e.averageDocumentLength,this.b=e.b,this.k1=e.k1,this.fields=e.fields,this.pipeline=e.pipeline},o.Index.prototype.search=function(e){return this.query(function(t){var n=new o.QueryParser(e,t);n.parse()})},o.Index.prototype.query=function(e){var t=new o.Query(this.fields),n=Object.create(null),r=new o.Vector;e.call(t,t);for(var i=0;i1?this._b=1:this._b=e},o.Builder.prototype.k1=function(e){this._k1=e},o.Builder.prototype.add=function(e){var t=e[this._ref],n={};this.documentCount+=1,this.documentTermFrequencies[t]=n,this.documentLengths[t]=0;for(var r=0;r47&&t<58);e!=o.QueryLexer.EOS&&this.backup()},o.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(o.QueryLexer.TERM)),e.ignore(),e.more())return o.QueryLexer.lexText},o.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(o.QueryLexer.EDIT_DISTANCE),o.QueryLexer.lexText},o.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(o.QueryLexer.BOOST),o.QueryLexer.lexText},o.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(o.QueryLexer.TERM)},o.QueryLexer.termSeparator=o.tokenizer.separator,o.QueryLexer.lexText=function(e){for(;;){var t=e.next();if(t==o.QueryLexer.EOS)return o.QueryLexer.lexEOS;if(":"==t)return o.QueryLexer.lexField;if("~"==t)return e.backup(),e.width()>0&&e.emit(o.QueryLexer.TERM),o.QueryLexer.lexEditDistance;if("^"==t)return e.backup(),e.width()>0&&e.emit(o.QueryLexer.TERM),o.QueryLexer.lexBoost;if(t.match(o.QueryLexer.termSeparator))return o.QueryLexer.lexTerm}},o.QueryParser=function(e,t){this.lexer=new o.QueryLexer(e),this.query=t,this.currentClause={},this.lexemeIdx=0},o.QueryParser.prototype.parse=function(){ +this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=o.QueryParser.parseFieldOrTerm;e;)e=e(this);return this.query},o.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},o.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},o.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},o.QueryParser.parseFieldOrTerm=function(e){var t=e.peekLexeme();if(void 0!=t)switch(t.type){case o.QueryLexer.FIELD:return o.QueryParser.parseField;case o.QueryLexer.TERM:return o.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+t.type+" with value '"+t.str+"'";throw new o.QueryParseError(n,t.start,t.end)}},o.QueryParser.parseField=function(e){var t=e.consumeLexeme();if(void 0!=t){if(e.query.allFields.indexOf(t.str)==-1){var n=e.query.allFields.map(function(e){return"'"+e+"'"}).join(),r="unrecognised field '"+t.str+"', possible fields: "+n;throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.fields=[t.str];var i=e.peekLexeme();if(void 0==i){var r="expecting term, found nothing";throw new o.QueryParseError(r,t.start,t.end)}switch(i.type){case o.QueryLexer.TERM:return o.QueryParser.parseTerm;default:var r="expecting a field, found '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},o.QueryParser.parseTerm=function(e){var t=e.consumeLexeme();if(void 0!=t){e.currentClause.term=t.str.toLowerCase(),t.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(void 0==n)return void e.nextClause();switch(n.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+n.type+"'";throw new o.QueryParseError(r,n.start,n.end)}}},o.QueryParser.parseEditDistance=function(e){var t=e.consumeLexeme();if(void 0!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="edit distance must be numeric";throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.editDistance=n;var i=e.peekLexeme();if(void 0==i)return void e.nextClause();switch(i.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},o.QueryParser.parseBoost=function(e){var t=e.consumeLexeme();if(void 0!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="boost must be numeric";throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.boost=n;var i=e.peekLexeme();if(void 0==i)return void e.nextClause();switch(i.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},function(o,s){r=s,i="function"==typeof r?r.call(t,n,t,e):r,!(void 0!==i&&(e.exports=i))}(this,function(){return o})}()},function(e,t,n){"use strict";var r=n(75),i=n(77),o=n(79),s=n(83),a=n(86),c=n(88),u=n(93);t.a={Event:r.a,Header:i.a,Nav:o.a,Search:s.a,Sidebar:a.a,Source:c.a,Tabs:u.a}},function(e,t,n){"use strict";var r=n(33),i=n(76);t.a={Listener:r.a,MatchMedia:i.a}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=(n(33),function e(t,n){r(this,e),this.handler_=function(e){e.matches?n.listen():n.unlisten()};var i=window.matchMedia(t);i.addListener(this.handler_),this.handler_(i)});t.a=i},function(e,t,n){"use strict";var r=n(78);t.a={Shadow:r.a}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=function(){function e(e,t){for(var n=0;n=this.height_;e!==this.active_&&(this.header_.dataset.mdState=(this.active_=e)?"shadow":"")}},{key:"reset",value:function(){this.header_.dataset.mdState="",this.height_=0,this.active_=!1}}]),e}();t.a=o},function(e,t,n){"use strict";var r=n(80),i=n(81),o=n(82);t.a={Blur:r.a,Collapse:i.a,Scrolling:o.a}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=function(){function e(e,t){for(var n=0;n0&&(this.els_[n-1].dataset.mdState="blur"),this.index_=n;else for(var r=this.index_;r>=0;r--){if(!(this.anchors_[r].offsetTop-80>e)){this.index_=r;break}r>0&&(this.els_[r-1].dataset.mdState="")}this.offset_=e,this.dir_=t}}},{key:"reset",value:function(){Array.prototype.forEach.call(this.els_,function(e){e.dataset.mdState=""}),this.index_=0,this.offset_=window.pageYOffset}}]),e}();t.a=o},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=function(){function e(e,t){for(var n=0;nn){for(;" "!==e[n]&&--n>0;);return e.substring(0,n)+"..."}return e}},{key:"update",value:function(t){var n=this;if("focus"!==t.type||this.index_){if("focus"===t.type||"keyup"===t.type){var r=t.target;if(!(r instanceof HTMLInputElement))throw new ReferenceError;if(!this.index_||r.value===this.value_)return;for(;this.list_.firstChild;)this.list_.removeChild(this.list_.firstChild);if(this.value_=r.value,0===this.value_.length)return void(this.meta_.textContent=this.message_.placeholder);var i=this.index_.search(this.value_).reduce(function(e,t){var r=n.docs_.get(t.ref);if(r.parent){var i=r.parent.location;e.set(i,(e.get(i)||[]).concat(t))}else{var o=r.location;e.set(o,e.get(o)||[])}return e},new Map),s=new RegExp("\\b("+o()(this.value_.trim()).replace(" ","|")+")","img"),u=function(e){return""+e+""};i.forEach(function(t,r){var i=n.docs_.get(r);n.list_.appendChild(e.createElement("li",{class:"md-search-result__item"},e.createElement("a",{href:i.location,title:i.title,class:"md-search-result__link"},e.createElement("article",{class:"md-search-result__article md-search-result__article--document"},e.createElement("h1",{class:"md-search-result__title"},{__html:i.title.replace(s,u)}),i.text.length?e.createElement("p",{class:"md-search-result__teaser"},{__html:i.text.replace(s,u)}):{})),t.map(function(t){var r=n.docs_.get(t.ref);return e.createElement("a",{href:r.location,title:r.title,class:"md-search-result__link","data-md-rel":"anchor"},e.createElement("article",{class:"md-search-result__article"},e.createElement("h1",{class:"md-search-result__title"},{__html:r.title.replace(s,u)}),r.text.length?e.createElement("p",{class:"md-search-result__teaser"},{__html:n.truncate_(r.text.replace(s,u),400)}):{}))})))});var l=this.list_.querySelectorAll("[data-md-rel=anchor]");switch(Array.prototype.forEach.call(l,function(e){e.addEventListener("click",function(t){var n=document.querySelector("[data-md-toggle=search]");if(!(n instanceof HTMLInputElement))throw new ReferenceError;n.checked&&(n.checked=!1,n.dispatchEvent(new CustomEvent("change"))),t.preventDefault(),setTimeout(function(){document.location.href=e.href},100)})}),i.size){case 0:this.meta_.textContent=this.message_.none;break;case 1:this.meta_.textContent=this.message_.one;break;default:this.meta_.textContent=this.message_.other.replace("#",i.size)}}}else{var f=function(e){n.docs_=e.reduce(function(e,t){var n=t.location.split("#"),r=c(n,2),i=r[0],o=r[1];return o&&(t.parent=e.get(i),t.parent&&!t.parent.done&&(t.parent.title=t.title,t.parent.text=t.text,t.parent.done=!0)),t.text=t.text.replace(/\n/g," ").replace(/\s+/g," ").replace(/\s+([,.:;!?])/g,function(e,t){return t}),t.parent&&t.parent.title===t.title||e.set(t.location,t),e},new Map);var t=n.docs_;n.index_=a()(function(){var e=this;this.field("title",{boost:10}),this.field("text"),this.ref("location"),t.forEach(function(t){return e.add(t)})})};setTimeout(function(){return"function"==typeof n.data_?n.data_().then(f):f(n.data_)},250)}}}]),t}();t.a=l}).call(t,n(13))},function(e,t,n){"use strict";var r=n(87);t.a={Position:r.a}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=function(){function e(e,t){for(var n=0;n=this.offset_?"lock"!==this.el_.dataset.mdState&&(this.el_.dataset.mdState="lock"):"lock"===this.el_.dataset.mdState&&(this.el_.dataset.mdState="")}},{key:"reset",value:function(){this.el_.dataset.mdState="",this.el_.style.height="",this.height_=0}}]),e}();t.a=o},function(e,t,n){"use strict";var r=n(89),i=n(92);t.a={Adapter:r.a,Repository:i.a}},function(e,t,n){"use strict";var r=n(91);t.a={GitHub:r.a}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=n(72),o=n.n(i),s=function(){function e(e,t){for(var n=0;n1e4?(e/1e3).toFixed(0)+"k":e>1e3?(e/1e3).toFixed(1)+"k":""+e}},{key:"hash_",value:function(e){var t=0;if(0===e.length)return t;for(var n=0,r=e.length;n=this.offset_;e!==this.active_&&(this.el_.dataset.mdState=(this.active_=e)?"hidden":"")}},{key:"reset",value:function(){this.el_.dataset.mdState="",this.active_=!1}}]),e}();t.a=o},function(e,t,n){n(35),n(36),n(37),e.exports=n(38)}]); \ No newline at end of file diff --git a/docs/build/assets/javascripts/modernizr-56ade86843.js b/docs/build/assets/javascripts/modernizr-56ade86843.js new file mode 100644 index 00000000..cffa5835 --- /dev/null +++ b/docs/build/assets/javascripts/modernizr-56ade86843.js @@ -0,0 +1 @@ +!function(e,n,t){function r(e,n){return typeof e===n}function o(){var e,n,t,o,i,s,f;for(var a in w)if(w.hasOwnProperty(a)){if(e=[],n=w[a],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;tcode{margin:inherit;padding:inherit;border-radius:none;background-color:inherit;color:inherit;box-shadow:none}.md-typeset pre{margin:1em 0;padding:1rem 1.2rem;border-radius:.2rem;line-height:1.4;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset pre::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset pre::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset pre>code{margin:0;background-color:transparent;font-size:inherit;box-shadow:none;-webkit-box-decoration-break:none;box-decoration-break:none}.md-typeset kbd{padding:0 .29412em;border:.1rem solid #c9c9c9;border-radius:.2rem;border-bottom-color:#bcbcbc;background-color:#fcfcfc;color:#555;font-size:85%;box-shadow:0 .1rem 0 #b0b0b0;word-break:break-word}.md-typeset mark{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;background-color:rgba(255,235,59,.5);box-shadow:.25em 0 0 rgba(255,235,59,.5),-.25em 0 0 rgba(255,235,59,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset abbr{border-bottom:.1rem dotted rgba(0,0,0,.54);cursor:help}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.07812em}.md-typeset blockquote{padding-left:1.2rem;border-left:.4rem solid rgba(0,0,0,.26);color:rgba(0,0,0,.54)}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{margin-left:.625em;padding:0}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}.md-typeset dd{margin:1em 0 1em 1.875em}.md-typeset iframe,.md-typeset img,.md-typeset svg{max-width:100%}.md-typeset table:not([class]){box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);display:inline-block;max-width:100%;border-radius:.2rem;font-size:1.28rem;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}.md-typeset table:not([class]) th{min-width:10rem;padding:1.2rem 1.6rem;background-color:rgba(0,0,0,.54);color:#fff;vertical-align:top}.md-typeset table:not([class]) td{padding:1.2rem 1.6rem;border-top:.1rem solid rgba(0,0,0,.07);vertical-align:top}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset__scrollwrap{margin:1em -1.6rem;overflow-x:auto;-webkit-overflow-scrolling:touch}.md-typeset .md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 1.6rem}.md-typeset .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{font-size:62.5%}body,html{height:100%}body{position:relative}hr{display:block;height:.1rem;padding:0;border:0}.md-svg{display:none}.md-grid{max-width:122rem;margin-right:auto;margin-left:auto}.md-container,.md-main{overflow:auto}.md-container{display:table;width:100%;height:100%;padding-top:5.6rem;table-layout:fixed}.md-main{display:table-row;height:100%}.md-main__inner{min-height:100%;padding-top:3rem}.md-toggle{display:none}.md-overlay{position:fixed;top:0;width:0;height:0;-webkit-transition:width 0s .25s,height 0s .25s,opacity .25s;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);opacity:0;z-index:2}.md-flex{display:table}.md-flex__cell{display:table-cell;position:relative;vertical-align:top}.md-flex__cell--shrink{width:0}.md-flex__cell--stretch{display:table;width:100%;table-layout:fixed}.md-flex__ellipsis{display:table-cell;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@page{margin:25mm}.md-content__inner{margin:0 1.6rem 2.4rem;padding-top:2.4rem}.md-content__inner:before{display:block;height:.8rem;content:""}.md-content__inner>:last-child{margin-bottom:0}.md-content__icon{position:relative;margin:.8rem 0;padding-right:0;padding-left:0;float:right}html body .md-typeset .md-content__icon{color:rgba(0,0,0,.26)}.md-header{position:fixed;top:0;right:0;left:0;height:5.6rem;-webkit-transition:background-color .25s;transition:background-color .25s;background-color:#3f51b5;color:#fff;z-index:1;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-header,.no-js .md-header{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2)}.md-header-nav{padding:.4rem}.md-header-nav__button{position:relative;-webkit-transition:opacity .25s;transition:opacity .25s;z-index:1}.md-header-nav__button:hover{opacity:.7}.md-header-nav__button.md-logo img{display:block}.no-js .md-header-nav__button.md-icon--search{display:none}.md-header-nav__title{padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-header-nav__parent{color:hsla(0,0%,100%,.7)}.md-header-nav__parent:after{display:inline;color:hsla(0,0%,100%,.3);content:"/"}.md-header-nav__source{display:none}.md-footer-nav{background-color:rgba(0,0,0,.87);color:#fff}.md-footer-nav__inner{padding:.4rem;overflow:auto}.md-footer-nav__link{padding-top:2.8rem;padding-bottom:.8rem;-webkit-transition:opacity .25s;transition:opacity .25s}.md-footer-nav__link:hover{opacity:.7}.md-footer-nav__link--prev{width:25%;float:left}.md-footer-nav__link--next{width:75%;float:right;text-align:right}.md-footer-nav__button{-webkit-transition:background .25s;transition:background .25s}.md-footer-nav__title{position:relative;padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-footer-nav__direction{position:absolute;right:0;left:0;margin-top:-2rem;padding:0 2rem;color:hsla(0,0%,100%,.7);font-size:1.5rem}.md-footer-meta{background:rgba(0,0,0,.895)}.md-footer-meta__inner{margin-bottom:-.1rem;padding:.4rem;overflow:auto}html .md-footer-meta.md-typeset a{color:hsla(0,0%,100%,.7)}.md-footer-copyright{margin:0 1.2rem;padding:.8rem 0;color:hsla(0,0%,100%,.3);font-size:1.28rem}.md-footer-copyright__highlight{color:hsla(0,0%,100%,.7)}.md-footer-social{margin:0 .8rem;padding:.4rem 0 1.2rem}.md-footer-social__link{display:inline-block;width:3.2rem;height:3.2rem;border:.1rem solid hsla(0,0%,100%,.12);border-radius:100%;color:hsla(0,0%,100%,.7);font-size:1.6rem;text-align:center}.md-footer-social__link:before{line-height:1.9}.md-nav{font-size:1.4rem;line-height:1.3}.md-nav--secondary{-webkit-transition:border-left .25s;transition:border-left .25s;border-left:.4rem solid #3f51b5}.md-nav--secondary .md-nav__link--active{color:#3f51b5}.md-nav__title{display:block;padding:1.2rem 1.2rem 0;font-weight:700;text-overflow:ellipsis;overflow:hidden}.md-nav__title:before{display:none;content:"arrow_back"}.md-nav__title .md-nav__button{display:none}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 1.2rem}.md-nav__item:last-child{padding-bottom:1.2rem}.md-nav__item .md-nav__item{padding-right:0}.md-nav__item .md-nav__item:last-child{padding-bottom:0}.md-nav__button img{width:100%;height:auto}.md-nav__link{display:block;margin-top:.625em;-webkit-transition:color .125s;transition:color .125s;text-overflow:ellipsis;cursor:pointer;overflow:hidden}.md-nav__item--nested>.md-nav__link:after{content:"keyboard_arrow_down"}html .md-nav__link[for=toc],html .md-nav__link[for=toc]+.md-nav__link:after,html .md-nav__link[for=toc]~.md-nav{display:none}.md-nav__link[data-md-state=blur]{color:rgba(0,0,0,.54)}.md-nav__item--active>.md-nav__link,.md-nav__link:active{color:#3f51b5}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:#536dfe}.md-nav__source,.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}.md-search__inner{width:100%}.md-search__form{position:relative}.md-search__input{position:relative;padding:0 4.8rem 0 7.2rem;text-overflow:ellipsis;z-index:2}.md-search__input::-webkit-input-placeholder{-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::-moz-placeholder{-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input:-ms-input-placeholder{-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::placeholder{-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::-webkit-input-placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::-moz-placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input:-ms-input-placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::-ms-clear{display:none}.md-search__icon{position:absolute;-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:color .25s cubic-bezier(.1,.7,.1,1),opacity .25s;font-size:2.4rem;cursor:pointer;z-index:2}.md-search__icon:hover{opacity:.7}.md-search__icon[for=search]{top:.8rem;left:1.2rem}.md-search__icon[for=search]:before{content:"search"}.md-search__icon[type=reset]{top:.8rem;right:1.2rem;-webkit-transform:scale(.125);transform:scale(.125);-webkit-transition:opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);transition:opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);opacity:0}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]{-webkit-transform:scale(1);transform:scale(1);opacity:1}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]:hover{opacity:.7}.md-search__output{position:absolute;width:100%;border-radius:0 0 .2rem .2rem;overflow:hidden;z-index:1}.md-search__scrollwrap{height:100%;background:-webkit-linear-gradient(top,#fff 10%,hsla(0,0%,100%,0)),-webkit-linear-gradient(top,rgba(0,0,0,.26),rgba(0,0,0,.07) 35%,transparent 60%);background:linear-gradient(180deg,#fff 10%,hsla(0,0%,100%,0)),linear-gradient(180deg,rgba(0,0,0,.26),rgba(0,0,0,.07) 35%,transparent 60%);background-attachment:local,scroll;background-color:#fff;background-repeat:no-repeat;background-size:100% 2rem,100% 1rem;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07);overflow-y:auto;-webkit-overflow-scrolling:touch}.md-search-result{color:rgba(0,0,0,.87);word-break:break-word}.md-search-result__meta{padding:0 1.6rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-size:1.28rem;line-height:4rem}.md-search-result__list{margin:0;padding:0;border-top:.1rem solid rgba(0,0,0,.07);list-style:none}.md-search-result__item{box-shadow:0 -.1rem 0 rgba(0,0,0,.07)}.md-search-result__link{display:block;-webkit-transition:background .25s;transition:background .25s;outline:0;overflow:hidden}.md-search-result__link:hover,.md-search-result__link[data-md-state=active]{background-color:rgba(83,109,254,.1)}.md-search-result__link:hover .md-search-result__article:before,.md-search-result__link[data-md-state=active] .md-search-result__article:before{opacity:.7}.md-search-result__link:last-child .md-search-result__teaser{margin-bottom:1.2rem}.md-search-result__article{position:relative;padding:0 1.6rem;overflow:auto}.md-search-result__article--document:before{position:absolute;left:0;-webkit-transition:opacity .25s;transition:opacity .25s;color:rgba(0,0,0,.54);content:"find_in_page"}.md-search-result__article--document .md-search-result__title{margin:1.3rem 0;font-size:1.6rem;font-weight:400;line-height:1.4}.md-search-result__title{margin:.5em 0;font-size:1.28rem;font-weight:700;line-height:1.4}.md-search-result__teaser{display:-webkit-box;max-height:3.3rem;margin:.5em 0;color:rgba(0,0,0,.54);font-size:1.28rem;line-height:1.4;text-overflow:ellipsis;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2}.md-search-result em{font-style:normal;font-weight:700;text-decoration:underline}.md-sidebar{position:absolute;width:24.2rem;padding:2.4rem 0;overflow:hidden}.md-sidebar[data-md-state=lock]{position:fixed;top:5.6rem}.md-sidebar--secondary{display:none}.md-sidebar__scrollwrap{max-height:100%;margin:0 .4rem;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}@-webkit-keyframes a{0%{height:0}to{height:1.3rem}}@keyframes a{0%{height:0}to{height:1.3rem}}@-webkit-keyframes b{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes b{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}.md-source{display:block;padding-right:1.2rem;-webkit-transition:opacity .25s;transition:opacity .25s;font-size:1.3rem;line-height:1.2;white-space:nowrap}.md-source:hover{opacity:.7}.md-source:after,.md-source__icon{display:inline-block;height:4.8rem;content:"";vertical-align:middle}.md-source__icon{width:4.8rem}.md-source__icon svg{width:2.4rem;height:2.4rem;margin-top:1.2rem;margin-left:1.2rem}.md-source__icon+.md-source__repository{margin-left:-4.4rem;padding-left:4rem}.md-source__repository{display:inline-block;max-width:100%;margin-left:1.2rem;font-weight:700;text-overflow:ellipsis;overflow:hidden;vertical-align:middle}.md-source__facts{margin:0;padding:0;font-size:1.1rem;font-weight:700;list-style-type:none;opacity:.75;overflow:hidden}[data-md-state=done] .md-source__facts{-webkit-animation:a .25s ease-in;animation:a .25s ease-in}.md-source__fact{float:left}[data-md-state=done] .md-source__fact{-webkit-animation:b .4s ease-out;animation:b .4s ease-out}.md-source__fact:before{margin:0 .2rem;content:"\00B7"}.md-source__fact:first-child:before{display:none}.md-source-file{display:inline-block;margin:1em .5em 1em 0;padding-right:.5rem;border-radius:.2rem;background:rgba(0,0,0,.07);font-size:1.28rem;list-style-type:none;cursor:pointer;overflow:hidden}.md-source-file:before{display:inline-block;margin-right:.5rem;padding:.5rem;background:rgba(0,0,0,.26);color:#fff;font-size:1.6rem;content:"clear_all";vertical-align:middle}html .md-source-file{-webkit-transition:background .4s,color .4s,box-shadow .4s cubic-bezier(.4,0,.2,1);transition:background .4s,color .4s,box-shadow .4s cubic-bezier(.4,0,.2,1)}html .md-source-file:before{-webkit-transition:inherit;transition:inherit}html body .md-typeset .md-source-file{color:rgba(0,0,0,.54)}.md-source-file:hover{box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36)}.md-source-file:hover:before{background:#536dfe}.md-tabs{width:100%;-webkit-transition:background .25s;transition:background .25s;background:rgba(50,64,144,.9675);overflow:auto}.md-tabs__list{margin:0;margin-left:.4rem;padding:0;list-style:none;white-space:nowrap}.md-tabs__item{display:inline-block;height:4.8rem;padding-right:1.2rem;padding-left:1.2rem}.md-tabs__link{display:block;margin-top:1.6rem;-webkit-transition:color .25s,opacity .1s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:color .25s,opacity .1s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:color .25s,transform .4s cubic-bezier(.1,.7,.1,1),opacity .1s;transition:color .25s,transform .4s cubic-bezier(.1,.7,.1,1),opacity .1s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);color:hsla(0,0%,100%,.7);font-size:1.4rem}.md-tabs__link--active,.md-tabs__link:hover{color:#fff}.md-tabs__item:nth-child(2) .md-tabs__link{-webkit-transition-delay:.02s;transition-delay:.02s}.md-tabs__item:nth-child(3) .md-tabs__link{-webkit-transition-delay:.04s;transition-delay:.04s}.md-tabs__item:nth-child(4) .md-tabs__link{-webkit-transition-delay:.06s;transition-delay:.06s}.md-tabs__item:nth-child(5) .md-tabs__link{-webkit-transition-delay:.08s;transition-delay:.08s}.md-tabs__item:nth-child(6) .md-tabs__link{-webkit-transition-delay:.1s;transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{-webkit-transition-delay:.12s;transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{-webkit-transition-delay:.14s;transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{-webkit-transition-delay:.16s;transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{-webkit-transition-delay:.18s;transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{-webkit-transition-delay:.2s;transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{-webkit-transition-delay:.22s;transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{-webkit-transition-delay:.24s;transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{-webkit-transition-delay:.26s;transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{-webkit-transition-delay:.28s;transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{-webkit-transition-delay:.3s;transition-delay:.3s}.md-tabs[data-md-state=hidden]{background:#3f51b5;pointer-events:none}.md-tabs[data-md-state=hidden] .md-tabs__link{-webkit-transform:translateY(50%);transform:translateY(50%);-webkit-transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,transform 0s .4s,opacity .1s;transition:color .25s,transform 0s .4s,opacity .1s,-webkit-transform 0s .4s;opacity:0}.admonition{position:relative;margin:1.5625em 0;padding:.8rem 1.2rem;border-left:3.2rem solid rgba(68,138,255,.4);border-radius:.2rem;background-color:rgba(68,138,255,.15);font-size:1.28rem}.admonition:before{position:absolute;left:-2.6rem;color:#fff;font-size:2rem;content:"edit";vertical-align:-.25em}.admonition :first-child{margin-top:0}.admonition :last-child{margin-bottom:0}.admonition.summary,.admonition.tldr{border-color:rgba(0,176,255,.4);background-color:rgba(0,176,255,.15)}.admonition.summary:before,.admonition.tldr:before{content:"subject"}.admonition.hint,.admonition.important,.admonition.tip{border-color:rgba(0,191,165,.4);background-color:rgba(0,191,165,.15)}.admonition.hint:before,.admonition.important:before,.admonition.tip:before{content:"whatshot"}.admonition.check,.admonition.done,.admonition.success{border-color:rgba(0,230,118,.4);background-color:rgba(0,230,118,.15)}.admonition.check:before,.admonition.done:before,.admonition.success:before{content:"done"}.admonition.attention,.admonition.caution,.admonition.warning{border-color:rgba(255,145,0,.4);background-color:rgba(255,145,0,.15)}.admonition.attention:before,.admonition.caution:before,.admonition.warning:before{content:"warning"}.admonition.fail,.admonition.failure,.admonition.missing{border-color:rgba(255,82,82,.4);background-color:rgba(255,82,82,.15)}.admonition.fail:before,.admonition.failure:before,.admonition.missing:before{content:"clear"}.admonition.danger,.admonition.error{border-color:rgba(255,23,68,.4);background-color:rgba(255,23,68,.15)}.admonition.danger:before,.admonition.error:before{content:"flash_on"}.admonition.bug{border-color:rgba(245,0,87,.4);background-color:rgba(245,0,87,.15)}.admonition.bug:before{content:"bug_report"}.admonition.cite,.admonition.quote{border-color:hsla(0,0%,62%,.4);background-color:hsla(0,0%,62%,.15)}.admonition.cite:before,.admonition.quote:before{content:"format_quote"}.admonition-title{font-weight:700}html .admonition-title{margin-bottom:0}html .admonition-title+*{margin-top:0}.codehilite .o,.codehilite .ow{color:inherit}.codehilite .ge{color:#000}.codehilite .gr{color:#a00}.codehilite .gh{color:#999}.codehilite .go{color:#888}.codehilite .gp{color:#555}.codehilite .gs{color:inherit}.codehilite .gu{color:#aaa}.codehilite .gt{color:#a00}.codehilite .gd{background-color:#fdd}.codehilite .gi{background-color:#dfd}.codehilite .k{color:#3b78e7}.codehilite .kc{color:#a71d5d}.codehilite .kd,.codehilite .kn{color:#3b78e7}.codehilite .kp{color:#a71d5d}.codehilite .kr,.codehilite .kt{color:#3e61a2}.codehilite .c,.codehilite .cm{color:#999}.codehilite .cp{color:#666}.codehilite .c1,.codehilite .ch,.codehilite .cs{color:#999}.codehilite .na,.codehilite .nb{color:#c2185b}.codehilite .bp{color:#3e61a2}.codehilite .nc{color:#c2185b}.codehilite .no{color:#3e61a2}.codehilite .nd,.codehilite .ni{color:#666}.codehilite .ne,.codehilite .nf{color:#c2185b}.codehilite .nl{color:#3b5179}.codehilite .nn{color:#ec407a}.codehilite .nt{color:#3b78e7}.codehilite .nv,.codehilite .vc,.codehilite .vg,.codehilite .vi{color:#3e61a2}.codehilite .nx{color:#ec407a}.codehilite .il,.codehilite .m,.codehilite .mf,.codehilite .mh,.codehilite .mi,.codehilite .mo{color:#e74c3c}.codehilite .s,.codehilite .sb,.codehilite .sc{color:#0d904f}.codehilite .sd{color:#999}.codehilite .s2{color:#0d904f}.codehilite .se,.codehilite .sh,.codehilite .si,.codehilite .sx{color:#183691}.codehilite .sr{color:#009926}.codehilite .s1,.codehilite .ss{color:#0d904f}.codehilite .err{color:#a61717}.codehilite .w{color:transparent}.codehilite .hll{display:block;margin:0 -1.2rem;padding:0 1.2rem;background-color:rgba(255,235,59,.5)}.md-typeset .codehilite{margin:1em 0;padding:1rem 1.2rem .8rem;border-radius:.2rem;background-color:hsla(0,0%,93%,.5);color:#37474f;line-height:1.4;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset .codehilite::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset .codehilite::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset .codehilite::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset .codehilite pre{display:inline-block;min-width:100%;margin:0;padding:0;background-color:transparent;overflow:visible;vertical-align:top}.md-typeset .codehilitetable{display:block;margin:1em 0;border-radius:.2em;font-size:1.6rem;overflow:hidden}.md-typeset .codehilitetable tbody,.md-typeset .codehilitetable td{display:block;padding:0}.md-typeset .codehilitetable tr{display:-webkit-box;display:-ms-flexbox;display:flex}.md-typeset .codehilitetable .codehilite,.md-typeset .codehilitetable .linenodiv{margin:0;border-radius:0}.md-typeset .codehilitetable .linenodiv{padding:1rem 1.2rem .8rem}.md-typeset .codehilitetable .linenodiv,.md-typeset .codehilitetable .linenodiv>pre{height:100%}.md-typeset .codehilitetable .linenos{background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.26);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.md-typeset .codehilitetable .linenos pre{margin:0;padding:0;background-color:transparent;color:inherit;text-align:right}.md-typeset .codehilitetable .code{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:hidden}.md-typeset>.codehilitetable{box-shadow:none}.md-typeset .footnote{color:rgba(0,0,0,.54);font-size:1.28rem}.md-typeset .footnote ol{margin-left:0}.md-typeset .footnote li{-webkit-transition:color .25s;transition:color .25s}.md-typeset .footnote li:before{display:block;height:0}.md-typeset .footnote li:target{color:rgba(0,0,0,.87)}.md-typeset .footnote li:target:before{margin-top:-9rem;padding-top:9rem;pointer-events:none}.md-typeset .footnote li :first-child{margin-top:0}.md-typeset .footnote li:hover .footnote-backref,.md-typeset .footnote li:target .footnote-backref{-webkit-transform:translateX(0);transform:translateX(0);opacity:1}.md-typeset .footnote li:hover .footnote-backref:hover,.md-typeset .footnote li:target .footnote-backref{color:#536dfe}.md-typeset .footnote-ref:before{display:inline;margin:0 .2em;border-left:.1rem solid rgba(0,0,0,.26);font-size:1.25em;content:"";vertical-align:-.5rem}.md-typeset .footnote-backref{display:inline-block;-webkit-transform:translateX(.5rem);transform:translateX(.5rem);-webkit-transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s,-webkit-transform .25s .125s;color:rgba(0,0,0,.26);font-size:0;opacity:0;vertical-align:text-bottom}.md-typeset .footnote-backref:before{font-size:1.6rem;content:"keyboard_return"}.md-typeset .headerlink{display:inline-block;margin-left:1rem;-webkit-transform:translateY(.5rem);transform:translateY(.5rem);-webkit-transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s,-webkit-transform .25s .25s;opacity:0}html body .md-typeset .headerlink{color:rgba(0,0,0,.26)}.md-typeset [id]:before{display:inline-block;content:""}.md-typeset [id]:target:before{margin-top:-9.8rem;padding-top:9.8rem}.md-typeset [id] .headerlink:focus,.md-typeset [id]:hover .headerlink,.md-typeset [id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset [id] .headerlink:focus,.md-typeset [id]:hover .headerlink:hover,.md-typeset [id]:target .headerlink{color:#536dfe}.md-typeset h1 .headerlink{display:none}.md-typeset h2[id]:before{display:block;margin-top:-.4rem;padding-top:.4rem}.md-typeset h2[id]:target:before{margin-top:-8.4rem;padding-top:8.4rem}.md-typeset h3[id]:before{display:block;margin-top:-.7rem;padding-top:.7rem}.md-typeset h3[id]:target:before{margin-top:-8.7rem;padding-top:8.7rem}.md-typeset h4[id]:before{display:block;margin-top:-.8rem;padding-top:.8rem}.md-typeset h4[id]:target:before{margin-top:-8.8rem;padding-top:8.8rem}.md-typeset h5[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem}.md-typeset h5[id]:target:before{margin-top:-9.1rem;padding-top:9.1rem}.md-typeset h6[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem}.md-typeset h6[id]:target:before{margin-top:-9.1rem;padding-top:9.1rem}.md-typeset .MJXc-display{margin:.75em 0;padding:.25em 0;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset .MathJax_CHTML{outline:0}.md-typeset .comment.critic,.md-typeset del.critic,.md-typeset ins.critic{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:#fdd;box-shadow:.25em 0 0 #fdd,-.25em 0 0 #fdd}.md-typeset ins.critic{background-color:#dfd;box-shadow:.25em 0 0 #dfd,-.25em 0 0 #dfd}.md-typeset .critic.comment{background-color:hsla(0,0%,93%,.5);color:#37474f;box-shadow:.25em 0 0 hsla(0,0%,93%,.5),-.25em 0 0 hsla(0,0%,93%,.5)}.md-typeset .critic.comment:before{padding-right:.125em;color:rgba(0,0,0,.26);content:"chat";vertical-align:-.125em}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:1.6rem;padding-left:1.6rem;box-shadow:none}.md-typeset .critic.block :first-child{margin-top:.5em}.md-typeset .critic.block :last-child{margin-bottom:.5em}.md-typeset .emojione{width:2rem;vertical-align:text-top}.md-typeset code.codehilite{margin:0 .29412em;padding:.07353em 0}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}.md-typeset .task-list-control .task-list-indicator:before{position:absolute;top:.05em;left:-1.25em;color:rgba(0,0,0,.26);font-size:1.5em;content:"check_box_outline_blank";vertical-align:-.25em}.md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator:before{content:"check_box"}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}@media print{.md-typeset a:after{color:rgba(0,0,0,.54);content:" [" attr(href) "]"}.md-typeset code{box-shadow:none;-webkit-box-decoration-break:initial;box-decoration-break:slice}.md-content__icon,.md-footer,.md-header,.md-sidebar,.md-tabs,.md-typeset .headerlink{display:none}}@media only screen and (max-width:44.9375em){.md-typeset pre{margin:1em -1.6rem;padding:1rem 1.6rem;border-radius:0}.md-footer-nav__link--prev .md-footer-nav__title{display:none}.md-search-result__teaser{max-height:5rem;-webkit-line-clamp:3}.codehilite .hll{margin:0 -1.6rem;padding:0 1.6rem}.md-typeset>.codehilite{padding:1rem 1.6rem .8rem}.md-typeset>.codehilite,.md-typeset>.codehilitetable{margin:1em -1.6rem;border-radius:0}.md-typeset>.codehilitetable .codehilite,.md-typeset>.codehilitetable .linenodiv{padding:1rem 1.6rem}.md-typeset>p>.MJXc-display{margin:.75em -1.6rem;padding:.25em 1.6rem}}@media only screen and (min-width:100em){html{font-size:68.75%}}@media only screen and (min-width:125em){html{font-size:75%}}@media only screen and (max-width:59.9375em){body[data-md-state=lock]{overflow:hidden}.ios body[data-md-state=lock] .md-container{display:none}.md-nav--secondary{border-left:0}html .md-nav__link[for=toc]{display:block;padding-right:4.8rem}html .md-nav__link[for=toc]:after{color:inherit;content:"toc"}html .md-nav__link[for=toc]+.md-nav__link{display:none}html .md-nav__link[for=toc]~.md-nav{display:-webkit-box;display:-ms-flexbox;display:flex}.md-nav__source{display:block;padding:.4rem;background-color:rgba(50,64,144,.9675);color:#fff}.md-search__overlay{position:absolute;top:.4rem;left:.4rem;width:4rem;height:4rem;-webkit-transform-origin:center;transform-origin:center;-webkit-transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:transform .3s .1s,opacity .2s .2s;transition:transform .3s .1s,opacity .2s .2s,-webkit-transform .3s .1s;border-radius:2rem;background-color:#fff;overflow:hidden;pointer-events:none}[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transition:opacity .1s,-webkit-transform .4s;transition:opacity .1s,-webkit-transform .4s;transition:transform .4s,opacity .1s;transition:transform .4s,opacity .1s,-webkit-transform .4s;opacity:1}.md-search__inner{position:fixed;top:0;left:100%;height:100%;-webkit-transform:translateX(5%);transform:translateX(5%);-webkit-transition:left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;transition:left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;opacity:0;z-index:2}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;transition:left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;opacity:1}.md-search__input{width:100%;height:5.6rem;font-size:1.8rem}.md-search__icon[for=search]{top:1.6rem;left:1.6rem}.md-search__icon[for=search][for=search]:before{content:"arrow_back"}.md-search__icon[type=reset]{top:1.6rem;right:1.6rem}.md-search__output{top:5.6rem;bottom:0}.md-search-result__article--document:before{display:none}}@media only screen and (max-width:76.1875em){[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;-webkit-transition:width 0s,height 0s,opacity .25s;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-header-nav__button.md-icon--home,.md-header-nav__button.md-logo{display:none}.md-nav{background-color:#fff}.md-nav--primary,.md-nav--primary .md-nav{display:-webkit-box;display:-ms-flexbox;display:flex;position:absolute;top:0;right:0;left:0;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:1.6rem;line-height:1.5}html .md-nav--primary .md-nav__title{position:relative;height:11.2rem;padding:6rem 1.6rem .4rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-weight:400;line-height:4.8rem;white-space:nowrap;cursor:pointer}html .md-nav--primary .md-nav__title:before{display:block;position:absolute;top:.4rem;left:.4rem;width:4rem;height:4rem;color:rgba(0,0,0,.54)}html .md-nav--primary .md-nav__title~.md-nav__list{background:-webkit-linear-gradient(top,#fff 10%,hsla(0,0%,100%,0)),-webkit-linear-gradient(top,rgba(0,0,0,.26),rgba(0,0,0,.07) 35%,transparent 60%);background:linear-gradient(180deg,#fff 10%,hsla(0,0%,100%,0)),linear-gradient(180deg,rgba(0,0,0,.26),rgba(0,0,0,.07) 35%,transparent 60%);background-attachment:local,scroll;background-color:#fff;background-repeat:no-repeat;background-size:100% 2rem,100% 1rem;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07)}html .md-nav--primary .md-nav__title~.md-nav__list>.md-nav__item:first-child{border-top:0}html .md-nav--primary .md-nav__title--site{position:relative;background-color:#3f51b5;color:#fff}html .md-nav--primary .md-nav__title--site .md-nav__button{display:block;position:absolute;top:.4rem;left:.4rem;width:6.4rem;height:6.4rem;font-size:4.8rem}html .md-nav--primary .md-nav__title--site:before{display:none}.md-nav--primary .md-nav__list{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow-y:auto}.md-nav--primary .md-nav__item{padding:0;border-top:.1rem solid rgba(0,0,0,.07)}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:4.8rem}.md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"keyboard_arrow_right"}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:1.6rem}.md-nav--primary .md-nav__link:after{position:absolute;top:50%;right:1.2rem;margin-top:-1.2rem;color:rgba(0,0,0,.54);font-size:2.4rem}.md-nav--primary .md-nav__link:focus:after,.md-nav--primary .md-nav__link:hover:after{color:inherit}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{position:static;background-color:transparent}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:2.8rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:4rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:5.2rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:6.4rem}.md-nav__toggle~.md-nav{display:none}.csstransforms3d .md-nav__toggle~.md-nav{-webkit-transform:translateX(100%);transform:translateX(100%);-webkit-transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s;transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);opacity:0}.csstransforms3d .md-nav__toggle~.md-nav,.md-nav__toggle:checked~.md-nav{display:-webkit-box;display:-ms-flexbox;display:flex}.csstransforms3d .md-nav__toggle:checked~.md-nav{-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1}.md-sidebar--primary{position:fixed;top:0;left:-24.2rem;width:24.2rem;height:100%;-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);background-color:#fff;z-index:2}.no-csstransforms3d .md-sidebar--primary{display:none}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);-webkit-transform:translateX(24.2rem);transform:translateX(24.2rem)}.no-csstransforms3d [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{display:block}.md-sidebar--primary .md-sidebar__scrollwrap{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;margin:0}.md-tabs{display:none}}@media only screen and (min-width:60em){.md-content{margin-right:24.2rem}.md-header-nav__button.md-icon--search{display:none}.md-header-nav__source{display:block;width:23rem;max-width:23rem;padding-right:1.2rem}.md-search{margin-right:2.8rem;padding:.4rem}.md-search__overlay{position:fixed;top:0;left:0;width:0;height:0;-webkit-transition:width 0s .25s,height 0s .25s,opacity .25s;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);cursor:pointer}[data-md-toggle=search]:checked~.md-header .md-search__overlay{width:100%;height:100%;-webkit-transition:width 0s,height 0s,opacity .25s;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-search__inner{position:relative}.md-search__form{width:23rem;float:right;-webkit-transition:width .25s cubic-bezier(.1,.7,.1,1);transition:width .25s cubic-bezier(.1,.7,.1,1);border-radius:.2rem}.md-search__input{width:100%;height:4rem;padding-left:4.8rem;-webkit-transition:background-color .25s cubic-bezier(.1,.7,.1,1),color .25s cubic-bezier(.1,.7,.1,1);transition:background-color .25s cubic-bezier(.1,.7,.1,1),color .25s cubic-bezier(.1,.7,.1,1);border-radius:.2rem;background-color:rgba(0,0,0,.26);color:#fff;font-size:1.6rem}.md-search__input+.md-search__icon,.md-search__input::-webkit-input-placeholder{color:#fff}.md-search__input+.md-search__icon,.md-search__input::-moz-placeholder{color:#fff}.md-search__input+.md-search__icon,.md-search__input:-ms-input-placeholder{color:#fff}.md-search__input+.md-search__icon,.md-search__input::placeholder{color:#fff}.md-search__input:hover{background-color:hsla(0,0%,100%,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{border-radius:.2rem .2rem 0 0;background-color:#fff;color:rgba(0,0,0,.87);text-overflow:none}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::-moz-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input:-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:rgba(0,0,0,.54)}.md-search__output{top:4rem;-webkit-transition:opacity .4s;transition:opacity .4s;opacity:0}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}.md-search__scrollwrap{max-height:0}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-search-result__article,.md-search-result__meta{padding-left:4.8rem}.md-sidebar--secondary{display:block;margin-left:100%;-webkit-transform:translate(-100%);transform:translate(-100%)}}@media only screen and (min-width:76.25em){.md-content{margin-left:24.2rem}.md-content__inner{margin-right:2.4rem;margin-left:2.4rem}.md-header{box-shadow:none}.md-header[data-md-state=shadow]{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2)}.md-header-nav__button.md-icon--menu{display:none}.md-nav[data-md-state=animate]{-webkit-transition:max-height .25s cubic-bezier(.86,0,.07,1);transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav__toggle~.md-nav{max-height:0;overflow:hidden}.md-nav[data-md-state=expand],.md-nav__toggle:checked~.md-nav{max-height:100%}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--nested>.md-nav__link:after{display:inline-block;-webkit-transform-origin:.45em .45em;transform-origin:.45em .45em;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;vertical-align:-.125em}.js .md-nav__item--nested>.md-nav__link:after{-webkit-transition:-webkit-transform .4s;transition:-webkit-transform .4s;transition:transform .4s;transition:transform .4s,-webkit-transform .4s}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link:after{-webkit-transform:rotateX(180deg);transform:rotateX(180deg)}.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__form{width:68.8rem}.md-sidebar--secondary{margin-left:122rem}.md-sidebar__inner{border-right:.1rem solid rgba(0,0,0,.07)}.md-tabs~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{font-size:0}.md-tabs--active~.md-main .md-nav--primary .md-nav__title--site{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item{font-size:0}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{display:none;font-size:1.4rem;overflow:auto}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested>.md-nav__link{margin-top:1.2rem;font-weight:700;pointer-events:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested>.md-nav__link:after{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active{display:block}.md-tabs--active~.md-main .md-nav[data-md-level="1"]{max-height:none}.md-tabs--active~.md-main .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-left:0}}@media only screen and (max-width:29.9375em){.md-header-nav__parent{display:none}[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(45);transform:scale(45)}}@media only screen and (min-width:45em){.md-footer-nav__link{width:50%}.md-footer-copyright{max-width:75%;float:left}.md-footer-social{padding:1.2rem 0;float:right}}@media only screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(60);transform:scale(60)}}@media only screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(75);transform:scale(75)}}@media only screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__form{width:46.8rem}.md-search-result__teaser{max-height:5rem;-webkit-line-clamp:3}} \ No newline at end of file diff --git a/docs/build/assets/stylesheets/application-f78e5cb881.palette.css b/docs/build/assets/stylesheets/application-f78e5cb881.palette.css new file mode 100644 index 00000000..69bbee08 --- /dev/null +++ b/docs/build/assets/stylesheets/application-f78e5cb881.palette.css @@ -0,0 +1 @@ +button[data-md-color-accent],button[data-md-color-primary]{width:13rem;margin-bottom:.4rem;padding:2.4rem .8rem .4rem;-webkit-transition:background-color .25s,opacity .25s;transition:background-color .25s,opacity .25s;border-radius:.2rem;color:#fff;font-size:1.28rem;text-align:left;cursor:pointer}button[data-md-color-accent]:hover,button[data-md-color-primary]:hover{opacity:.75}button[data-md-color-primary=red]{background-color:#ef5350}[data-md-color-primary=red] .md-typeset a{color:#ef5350}[data-md-color-primary=red] .md-header{background-color:#ef5350}[data-md-color-primary=red] .md-nav__item--active>.md-nav__link,[data-md-color-primary=red] .md-nav__link:active{color:#ef5350}[data-md-color-primary=red] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=pink]{background-color:#e91e63}[data-md-color-primary=pink] .md-typeset a{color:#e91e63}[data-md-color-primary=pink] .md-header{background-color:#e91e63}[data-md-color-primary=pink] .md-nav__item--active>.md-nav__link,[data-md-color-primary=pink] .md-nav__link:active{color:#e91e63}[data-md-color-primary=pink] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=purple]{background-color:#ab47bc}[data-md-color-primary=purple] .md-typeset a{color:#ab47bc}[data-md-color-primary=purple] .md-header{background-color:#ab47bc}[data-md-color-primary=purple] .md-nav__item--active>.md-nav__link,[data-md-color-primary=purple] .md-nav__link:active{color:#ab47bc}[data-md-color-primary=purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-purple]{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-typeset a{color:#7e57c2}[data-md-color-primary=deep-purple] .md-header{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__item--active>.md-nav__link,[data-md-color-primary=deep-purple] .md-nav__link:active{color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=indigo]{background-color:#3f51b5}[data-md-color-primary=indigo] .md-typeset a{color:#3f51b5}[data-md-color-primary=indigo] .md-header{background-color:#3f51b5}[data-md-color-primary=indigo] .md-nav__item--active>.md-nav__link,[data-md-color-primary=indigo] .md-nav__link:active{color:#3f51b5}[data-md-color-primary=indigo] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue]{background-color:#2196f3}[data-md-color-primary=blue] .md-typeset a{color:#2196f3}[data-md-color-primary=blue] .md-header{background-color:#2196f3}[data-md-color-primary=blue] .md-nav__item--active>.md-nav__link,[data-md-color-primary=blue] .md-nav__link:active{color:#2196f3}[data-md-color-primary=blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-blue]{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-typeset a{color:#03a9f4}[data-md-color-primary=light-blue] .md-header{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__item--active>.md-nav__link,[data-md-color-primary=light-blue] .md-nav__link:active{color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=cyan]{background-color:#00bcd4}[data-md-color-primary=cyan] .md-typeset a{color:#00bcd4}[data-md-color-primary=cyan] .md-header{background-color:#00bcd4}[data-md-color-primary=cyan] .md-nav__item--active>.md-nav__link,[data-md-color-primary=cyan] .md-nav__link:active{color:#00bcd4}[data-md-color-primary=cyan] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=teal]{background-color:#009688}[data-md-color-primary=teal] .md-typeset a{color:#009688}[data-md-color-primary=teal] .md-header{background-color:#009688}[data-md-color-primary=teal] .md-nav__item--active>.md-nav__link,[data-md-color-primary=teal] .md-nav__link:active{color:#009688}[data-md-color-primary=teal] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=green]{background-color:#4caf50}[data-md-color-primary=green] .md-typeset a{color:#4caf50}[data-md-color-primary=green] .md-header{background-color:#4caf50}[data-md-color-primary=green] .md-nav__item--active>.md-nav__link,[data-md-color-primary=green] .md-nav__link:active{color:#4caf50}[data-md-color-primary=green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-green]{background-color:#7cb342}[data-md-color-primary=light-green] .md-typeset a{color:#7cb342}[data-md-color-primary=light-green] .md-header{background-color:#7cb342}[data-md-color-primary=light-green] .md-nav__item--active>.md-nav__link,[data-md-color-primary=light-green] .md-nav__link:active{color:#7cb342}[data-md-color-primary=light-green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=lime]{background-color:#c0ca33}[data-md-color-primary=lime] .md-typeset a{color:#c0ca33}[data-md-color-primary=lime] .md-header{background-color:#c0ca33}[data-md-color-primary=lime] .md-nav__item--active>.md-nav__link,[data-md-color-primary=lime] .md-nav__link:active{color:#c0ca33}[data-md-color-primary=lime] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=yellow]{background-color:#f9a825}[data-md-color-primary=yellow] .md-typeset a{color:#f9a825}[data-md-color-primary=yellow] .md-header{background-color:#f9a825}[data-md-color-primary=yellow] .md-nav__item--active>.md-nav__link,[data-md-color-primary=yellow] .md-nav__link:active{color:#f9a825}[data-md-color-primary=yellow] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=amber]{background-color:#ffb300}[data-md-color-primary=amber] .md-typeset a{color:#ffb300}[data-md-color-primary=amber] .md-header{background-color:#ffb300}[data-md-color-primary=amber] .md-nav__item--active>.md-nav__link,[data-md-color-primary=amber] .md-nav__link:active{color:#ffb300}[data-md-color-primary=amber] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=orange]{background-color:#fb8c00}[data-md-color-primary=orange] .md-typeset a{color:#fb8c00}[data-md-color-primary=orange] .md-header{background-color:#fb8c00}[data-md-color-primary=orange] .md-nav__item--active>.md-nav__link,[data-md-color-primary=orange] .md-nav__link:active{color:#fb8c00}[data-md-color-primary=orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-orange]{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-typeset a{color:#ff7043}[data-md-color-primary=deep-orange] .md-header{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__item--active>.md-nav__link,[data-md-color-primary=deep-orange] .md-nav__link:active{color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=brown]{background-color:#795548}[data-md-color-primary=brown] .md-typeset a{color:#795548}[data-md-color-primary=brown] .md-header{background-color:#795548}[data-md-color-primary=brown] .md-nav__item--active>.md-nav__link,[data-md-color-primary=brown] .md-nav__link:active{color:#795548}[data-md-color-primary=brown] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=grey]{background-color:#757575}[data-md-color-primary=grey] .md-typeset a{color:#757575}[data-md-color-primary=grey] .md-header{background-color:#757575}[data-md-color-primary=grey] .md-nav__item--active>.md-nav__link,[data-md-color-primary=grey] .md-nav__link:active{color:#757575}[data-md-color-primary=grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue-grey]{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-typeset a{color:#546e7a}[data-md-color-primary=blue-grey] .md-header{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__item--active>.md-nav__link,[data-md-color-primary=blue-grey] .md-nav__link:active{color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-accent=red]{background-color:#ff1744}[data-md-color-accent=red] .md-typeset a:active,[data-md-color-accent=red] .md-typeset a:hover{color:#ff1744}[data-md-color-accent=red] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=red] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-nav__link:hover,[data-md-color-accent=red] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=red] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=red] .md-typeset [id] .headerlink:focus,[data-md-color-accent=red] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=red] .md-typeset [id]:target .headerlink{color:#ff1744}[data-md-color-accent=red] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-search-result__link:hover,[data-md-color-accent=red] .md-search-result__link[data-md-state=active]{background-color:rgba(255,23,68,.1)}[data-md-color-accent=red] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-source-file:hover:before{background-color:#ff1744}button[data-md-color-accent=pink]{background-color:#f50057}[data-md-color-accent=pink] .md-typeset a:active,[data-md-color-accent=pink] .md-typeset a:hover{color:#f50057}[data-md-color-accent=pink] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=pink] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-nav__link:hover,[data-md-color-accent=pink] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=pink] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=pink] .md-typeset [id] .headerlink:focus,[data-md-color-accent=pink] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=pink] .md-typeset [id]:target .headerlink{color:#f50057}[data-md-color-accent=pink] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-search-result__link:hover,[data-md-color-accent=pink] .md-search-result__link[data-md-state=active]{background-color:rgba(245,0,87,.1)}[data-md-color-accent=pink] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-source-file:hover:before{background-color:#f50057}button[data-md-color-accent=purple]{background-color:#e040fb}[data-md-color-accent=purple] .md-typeset a:active,[data-md-color-accent=purple] .md-typeset a:hover{color:#e040fb}[data-md-color-accent=purple] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=purple] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-nav__link:hover,[data-md-color-accent=purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=purple] .md-typeset [id]:target .headerlink{color:#e040fb}[data-md-color-accent=purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-search-result__link:hover,[data-md-color-accent=purple] .md-search-result__link[data-md-state=active]{background-color:rgba(224,64,251,.1)}[data-md-color-accent=purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-source-file:hover:before{background-color:#e040fb}button[data-md-color-accent=deep-purple]{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset a:active,[data-md-color-accent=deep-purple] .md-typeset a:hover{color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-purple] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-nav__link:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-purple] .md-typeset [id]:target .headerlink{color:#7c4dff}[data-md-color-accent=deep-purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-search-result__link:hover,[data-md-color-accent=deep-purple] .md-search-result__link[data-md-state=active]{background-color:rgba(124,77,255,.1)}[data-md-color-accent=deep-purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-source-file:hover:before{background-color:#7c4dff}button[data-md-color-accent=indigo]{background-color:#536dfe}[data-md-color-accent=indigo] .md-typeset a:active,[data-md-color-accent=indigo] .md-typeset a:hover{color:#536dfe}[data-md-color-accent=indigo] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=indigo] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-nav__link:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=indigo] .md-typeset [id] .headerlink:focus,[data-md-color-accent=indigo] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=indigo] .md-typeset [id]:target .headerlink{color:#536dfe}[data-md-color-accent=indigo] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-search-result__link:hover,[data-md-color-accent=indigo] .md-search-result__link[data-md-state=active]{background-color:rgba(83,109,254,.1)}[data-md-color-accent=indigo] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-source-file:hover:before{background-color:#536dfe}button[data-md-color-accent=blue]{background-color:#448aff}[data-md-color-accent=blue] .md-typeset a:active,[data-md-color-accent=blue] .md-typeset a:hover{color:#448aff}[data-md-color-accent=blue] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=blue] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-nav__link:hover,[data-md-color-accent=blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=blue] .md-typeset [id]:target .headerlink{color:#448aff}[data-md-color-accent=blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-search-result__link:hover,[data-md-color-accent=blue] .md-search-result__link[data-md-state=active]{background-color:rgba(68,138,255,.1)}[data-md-color-accent=blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-source-file:hover:before{background-color:#448aff}button[data-md-color-accent=light-blue]{background-color:#0091ea}[data-md-color-accent=light-blue] .md-typeset a:active,[data-md-color-accent=light-blue] .md-typeset a:hover{color:#0091ea}[data-md-color-accent=light-blue] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-blue] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-nav__link:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-blue] .md-typeset [id]:target .headerlink{color:#0091ea}[data-md-color-accent=light-blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-search-result__link:hover,[data-md-color-accent=light-blue] .md-search-result__link[data-md-state=active]{background-color:rgba(0,145,234,.1)}[data-md-color-accent=light-blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-source-file:hover:before{background-color:#0091ea}button[data-md-color-accent=cyan]{background-color:#00b8d4}[data-md-color-accent=cyan] .md-typeset a:active,[data-md-color-accent=cyan] .md-typeset a:hover{color:#00b8d4}[data-md-color-accent=cyan] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=cyan] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-nav__link:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=cyan] .md-typeset [id] .headerlink:focus,[data-md-color-accent=cyan] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=cyan] .md-typeset [id]:target .headerlink{color:#00b8d4}[data-md-color-accent=cyan] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-search-result__link:hover,[data-md-color-accent=cyan] .md-search-result__link[data-md-state=active]{background-color:rgba(0,184,212,.1)}[data-md-color-accent=cyan] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-source-file:hover:before{background-color:#00b8d4}button[data-md-color-accent=teal]{background-color:#00bfa5}[data-md-color-accent=teal] .md-typeset a:active,[data-md-color-accent=teal] .md-typeset a:hover{color:#00bfa5}[data-md-color-accent=teal] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=teal] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-nav__link:hover,[data-md-color-accent=teal] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=teal] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=teal] .md-typeset [id] .headerlink:focus,[data-md-color-accent=teal] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=teal] .md-typeset [id]:target .headerlink{color:#00bfa5}[data-md-color-accent=teal] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-search-result__link:hover,[data-md-color-accent=teal] .md-search-result__link[data-md-state=active]{background-color:rgba(0,191,165,.1)}[data-md-color-accent=teal] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-source-file:hover:before{background-color:#00bfa5}button[data-md-color-accent=green]{background-color:#00c853}[data-md-color-accent=green] .md-typeset a:active,[data-md-color-accent=green] .md-typeset a:hover{color:#00c853}[data-md-color-accent=green] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=green] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-nav__link:hover,[data-md-color-accent=green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=green] .md-typeset [id]:target .headerlink{color:#00c853}[data-md-color-accent=green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-search-result__link:hover,[data-md-color-accent=green] .md-search-result__link[data-md-state=active]{background-color:rgba(0,200,83,.1)}[data-md-color-accent=green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-source-file:hover:before{background-color:#00c853}button[data-md-color-accent=light-green]{background-color:#64dd17}[data-md-color-accent=light-green] .md-typeset a:active,[data-md-color-accent=light-green] .md-typeset a:hover{color:#64dd17}[data-md-color-accent=light-green] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-green] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-nav__link:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-green] .md-typeset [id]:target .headerlink{color:#64dd17}[data-md-color-accent=light-green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-search-result__link:hover,[data-md-color-accent=light-green] .md-search-result__link[data-md-state=active]{background-color:rgba(100,221,23,.1)}[data-md-color-accent=light-green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-source-file:hover:before{background-color:#64dd17}button[data-md-color-accent=lime]{background-color:#aeea00}[data-md-color-accent=lime] .md-typeset a:active,[data-md-color-accent=lime] .md-typeset a:hover{color:#aeea00}[data-md-color-accent=lime] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=lime] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-nav__link:hover,[data-md-color-accent=lime] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=lime] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=lime] .md-typeset [id] .headerlink:focus,[data-md-color-accent=lime] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=lime] .md-typeset [id]:target .headerlink{color:#aeea00}[data-md-color-accent=lime] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-search-result__link:hover,[data-md-color-accent=lime] .md-search-result__link[data-md-state=active]{background-color:rgba(174,234,0,.1)}[data-md-color-accent=lime] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-source-file:hover:before{background-color:#aeea00}button[data-md-color-accent=yellow]{background-color:#ffd600}[data-md-color-accent=yellow] .md-typeset a:active,[data-md-color-accent=yellow] .md-typeset a:hover{color:#ffd600}[data-md-color-accent=yellow] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=yellow] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-nav__link:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=yellow] .md-typeset [id] .headerlink:focus,[data-md-color-accent=yellow] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=yellow] .md-typeset [id]:target .headerlink{color:#ffd600}[data-md-color-accent=yellow] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-search-result__link:hover,[data-md-color-accent=yellow] .md-search-result__link[data-md-state=active]{background-color:rgba(255,214,0,.1)}[data-md-color-accent=yellow] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-source-file:hover:before{background-color:#ffd600}button[data-md-color-accent=amber]{background-color:#ffab00}[data-md-color-accent=amber] .md-typeset a:active,[data-md-color-accent=amber] .md-typeset a:hover{color:#ffab00}[data-md-color-accent=amber] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=amber] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-nav__link:hover,[data-md-color-accent=amber] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=amber] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=amber] .md-typeset [id] .headerlink:focus,[data-md-color-accent=amber] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=amber] .md-typeset [id]:target .headerlink{color:#ffab00}[data-md-color-accent=amber] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-search-result__link:hover,[data-md-color-accent=amber] .md-search-result__link[data-md-state=active]{background-color:rgba(255,171,0,.1)}[data-md-color-accent=amber] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-source-file:hover:before{background-color:#ffab00}button[data-md-color-accent=orange]{background-color:#ff9100}[data-md-color-accent=orange] .md-typeset a:active,[data-md-color-accent=orange] .md-typeset a:hover{color:#ff9100}[data-md-color-accent=orange] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=orange] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-nav__link:hover,[data-md-color-accent=orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=orange] .md-typeset [id]:target .headerlink{color:#ff9100}[data-md-color-accent=orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-search-result__link:hover,[data-md-color-accent=orange] .md-search-result__link[data-md-state=active]{background-color:rgba(255,145,0,.1)}[data-md-color-accent=orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-source-file:hover:before{background-color:#ff9100}button[data-md-color-accent=deep-orange]{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset a:active,[data-md-color-accent=deep-orange] .md-typeset a:hover{color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-orange] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-nav__link:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-orange] .md-typeset [id]:target .headerlink{color:#ff6e40}[data-md-color-accent=deep-orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-search-result__link:hover,[data-md-color-accent=deep-orange] .md-search-result__link[data-md-state=active]{background-color:rgba(255,110,64,.1)}[data-md-color-accent=deep-orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-source-file:hover:before{background-color:#ff6e40}@media only screen and (max-width:59.9375em){[data-md-color-primary=red] .md-nav__source{background-color:rgba(190,66,64,.9675)}[data-md-color-primary=pink] .md-nav__source{background-color:rgba(185,24,79,.9675)}[data-md-color-primary=purple] .md-nav__source{background-color:rgba(136,57,150,.9675)}[data-md-color-primary=deep-purple] .md-nav__source{background-color:rgba(100,69,154,.9675)}[data-md-color-primary=indigo] .md-nav__source{background-color:rgba(50,64,144,.9675)}[data-md-color-primary=blue] .md-nav__source{background-color:rgba(26,119,193,.9675)}[data-md-color-primary=light-blue] .md-nav__source{background-color:rgba(2,134,194,.9675)}[data-md-color-primary=cyan] .md-nav__source{background-color:rgba(0,150,169,.9675)}[data-md-color-primary=teal] .md-nav__source{background-color:rgba(0,119,108,.9675)}[data-md-color-primary=green] .md-nav__source{background-color:rgba(60,139,64,.9675)}[data-md-color-primary=light-green] .md-nav__source{background-color:rgba(99,142,53,.9675)}[data-md-color-primary=lime] .md-nav__source{background-color:rgba(153,161,41,.9675)}[data-md-color-primary=yellow] .md-nav__source{background-color:rgba(198,134,29,.9675)}[data-md-color-primary=amber] .md-nav__source{background-color:rgba(203,142,0,.9675)}[data-md-color-primary=orange] .md-nav__source{background-color:rgba(200,111,0,.9675)}[data-md-color-primary=deep-orange] .md-nav__source{background-color:rgba(203,89,53,.9675)}[data-md-color-primary=brown] .md-nav__source{background-color:rgba(96,68,57,.9675)}[data-md-color-primary=grey] .md-nav__source{background-color:rgba(93,93,93,.9675)}[data-md-color-primary=blue-grey] .md-nav__source{background-color:rgba(67,88,97,.9675)}}@media only screen and (max-width:76.1875em){html [data-md-color-primary=red] .md-nav--primary .md-nav__title--site{background-color:#ef5350}html [data-md-color-primary=pink] .md-nav--primary .md-nav__title--site{background-color:#e91e63}html [data-md-color-primary=purple] .md-nav--primary .md-nav__title--site{background-color:#ab47bc}html [data-md-color-primary=deep-purple] .md-nav--primary .md-nav__title--site{background-color:#7e57c2}html [data-md-color-primary=indigo] .md-nav--primary .md-nav__title--site{background-color:#3f51b5}html [data-md-color-primary=blue] .md-nav--primary .md-nav__title--site{background-color:#2196f3}html [data-md-color-primary=light-blue] .md-nav--primary .md-nav__title--site{background-color:#03a9f4}html [data-md-color-primary=cyan] .md-nav--primary .md-nav__title--site{background-color:#00bcd4}html [data-md-color-primary=teal] .md-nav--primary .md-nav__title--site{background-color:#009688}html [data-md-color-primary=green] .md-nav--primary .md-nav__title--site{background-color:#4caf50}html [data-md-color-primary=light-green] .md-nav--primary .md-nav__title--site{background-color:#7cb342}html [data-md-color-primary=lime] .md-nav--primary .md-nav__title--site{background-color:#c0ca33}html [data-md-color-primary=yellow] .md-nav--primary .md-nav__title--site{background-color:#f9a825}html [data-md-color-primary=amber] .md-nav--primary .md-nav__title--site{background-color:#ffb300}html [data-md-color-primary=orange] .md-nav--primary .md-nav__title--site{background-color:#fb8c00}html [data-md-color-primary=deep-orange] .md-nav--primary .md-nav__title--site{background-color:#ff7043}html [data-md-color-primary=brown] .md-nav--primary .md-nav__title--site{background-color:#795548}html [data-md-color-primary=grey] .md-nav--primary .md-nav__title--site{background-color:#757575}html [data-md-color-primary=blue-grey] .md-nav--primary .md-nav__title--site{background-color:#546e7a}}@media only screen and (min-width:60em){[data-md-color-primary=red] .md-nav--secondary{border-left:.4rem solid #ef5350}[data-md-color-primary=pink] .md-nav--secondary{border-left:.4rem solid #e91e63}[data-md-color-primary=purple] .md-nav--secondary{border-left:.4rem solid #ab47bc}[data-md-color-primary=deep-purple] .md-nav--secondary{border-left:.4rem solid #7e57c2}[data-md-color-primary=indigo] .md-nav--secondary{border-left:.4rem solid #3f51b5}[data-md-color-primary=blue] .md-nav--secondary{border-left:.4rem solid #2196f3}[data-md-color-primary=light-blue] .md-nav--secondary{border-left:.4rem solid #03a9f4}[data-md-color-primary=cyan] .md-nav--secondary{border-left:.4rem solid #00bcd4}[data-md-color-primary=teal] .md-nav--secondary{border-left:.4rem solid #009688}[data-md-color-primary=green] .md-nav--secondary{border-left:.4rem solid #4caf50}[data-md-color-primary=light-green] .md-nav--secondary{border-left:.4rem solid #7cb342}[data-md-color-primary=lime] .md-nav--secondary{border-left:.4rem solid #c0ca33}[data-md-color-primary=yellow] .md-nav--secondary{border-left:.4rem solid #f9a825}[data-md-color-primary=amber] .md-nav--secondary{border-left:.4rem solid #ffb300}[data-md-color-primary=orange] .md-nav--secondary{border-left:.4rem solid #fb8c00}[data-md-color-primary=deep-orange] .md-nav--secondary{border-left:.4rem solid #ff7043}[data-md-color-primary=brown] .md-nav--secondary{border-left:.4rem solid #795548}[data-md-color-primary=grey] .md-nav--secondary{border-left:.4rem solid #757575}[data-md-color-primary=blue-grey] .md-nav--secondary{border-left:.4rem solid #546e7a}}@media only screen and (min-width:76.25em){[data-md-color-primary=red] .md-tabs{background:rgba(190,66,64,.9675)}[data-md-color-primary=red] .md-tabs[data-md-state=hidden]{background:#ef5350}[data-md-color-primary=pink] .md-tabs{background:rgba(185,24,79,.9675)}[data-md-color-primary=pink] .md-tabs[data-md-state=hidden]{background:#e91e63}[data-md-color-primary=purple] .md-tabs{background:rgba(136,57,150,.9675)}[data-md-color-primary=purple] .md-tabs[data-md-state=hidden]{background:#ab47bc}[data-md-color-primary=deep-purple] .md-tabs{background:rgba(100,69,154,.9675)}[data-md-color-primary=deep-purple] .md-tabs[data-md-state=hidden]{background:#7e57c2}[data-md-color-primary=indigo] .md-tabs{background:rgba(50,64,144,.9675)}[data-md-color-primary=indigo] .md-tabs[data-md-state=hidden]{background:#3f51b5}[data-md-color-primary=blue] .md-tabs{background:rgba(26,119,193,.9675)}[data-md-color-primary=blue] .md-tabs[data-md-state=hidden]{background:#2196f3}[data-md-color-primary=light-blue] .md-tabs{background:rgba(2,134,194,.9675)}[data-md-color-primary=light-blue] .md-tabs[data-md-state=hidden]{background:#03a9f4}[data-md-color-primary=cyan] .md-tabs{background:rgba(0,150,169,.9675)}[data-md-color-primary=cyan] .md-tabs[data-md-state=hidden]{background:#00bcd4}[data-md-color-primary=teal] .md-tabs{background:rgba(0,119,108,.9675)}[data-md-color-primary=teal] .md-tabs[data-md-state=hidden]{background:#009688}[data-md-color-primary=green] .md-tabs{background:rgba(60,139,64,.9675)}[data-md-color-primary=green] .md-tabs[data-md-state=hidden]{background:#4caf50}[data-md-color-primary=light-green] .md-tabs{background:rgba(99,142,53,.9675)}[data-md-color-primary=light-green] .md-tabs[data-md-state=hidden]{background:#7cb342}[data-md-color-primary=lime] .md-tabs{background:rgba(153,161,41,.9675)}[data-md-color-primary=lime] .md-tabs[data-md-state=hidden]{background:#c0ca33}[data-md-color-primary=yellow] .md-tabs{background:rgba(198,134,29,.9675)}[data-md-color-primary=yellow] .md-tabs[data-md-state=hidden]{background:#f9a825}[data-md-color-primary=amber] .md-tabs{background:rgba(203,142,0,.9675)}[data-md-color-primary=amber] .md-tabs[data-md-state=hidden]{background:#ffb300}[data-md-color-primary=orange] .md-tabs{background:rgba(200,111,0,.9675)}[data-md-color-primary=orange] .md-tabs[data-md-state=hidden]{background:#fb8c00}[data-md-color-primary=deep-orange] .md-tabs{background:rgba(203,89,53,.9675)}[data-md-color-primary=deep-orange] .md-tabs[data-md-state=hidden]{background:#ff7043}[data-md-color-primary=brown] .md-tabs{background:rgba(96,68,57,.9675)}[data-md-color-primary=brown] .md-tabs[data-md-state=hidden]{background:#795548}[data-md-color-primary=grey] .md-tabs{background:rgba(93,93,93,.9675)}[data-md-color-primary=grey] .md-tabs[data-md-state=hidden]{background:#757575}[data-md-color-primary=blue-grey] .md-tabs{background:rgba(67,88,97,.9675)}[data-md-color-primary=blue-grey] .md-tabs[data-md-state=hidden]{background:#546e7a}} \ No newline at end of file diff --git a/docs/build/images/editor-checkbox.png b/docs/build/images/editor-checkbox.png new file mode 100644 index 0000000000000000000000000000000000000000..dcea71c755d97dfc93e255c6e38ef8fa53166436 GIT binary patch literal 4759 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 literal 0 HcmV?d00001 diff --git a/docs/build/images/editor-file.png b/docs/build/images/editor-file.png new file mode 100644 index 0000000000000000000000000000000000000000..93ab56f851494f31749cfb5e9c55e12869a1fd5f GIT binary patch literal 4485 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 literal 0 HcmV?d00001 diff --git a/docs/build/images/editor-input-repeater.png b/docs/build/images/editor-input-repeater.png new file mode 100644 index 0000000000000000000000000000000000000000..5ab4256ab82026d3ae29d6a535730221a1fab5dd GIT binary patch literal 2682 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 literal 0 HcmV?d00001 diff --git a/docs/build/images/editor-richtext.png b/docs/build/images/editor-richtext.png new file mode 100644 index 0000000000000000000000000000000000000000..a953eda4f208bd9bdff331c71810537f74a1a1bb GIT binary patch literal 9381 zcmeHtX*iW{`|gUO5`LAC!mo(Tl@N=Cj4L8@hDDKio@bIwNkrx`^Q_E6NJ28tWzM`T zGs`^uul2tFee6$rAMd-5y+7^OhugB&v+n19uKT*q>pag}&=Un2^7C}(5eNi1T2@jS zfgmzLAWj9GC4qNNUn%q@{IY+DRy_+p9%o+!!rxaNq@FscSQ|MwKesbP7+YCe8gkhi z*clpH*_&89tPsKUKNJ9qSP5TXws2&lI#C8TMAQPuNdKOK120KfKQ`Il9aXCv$t=#oiI1?VW3b?4v=tde(Bff9ZpKAyb~ zqrtyBR_mmwsi|Q*1STBZXlc@Ob01mP;-mT+VJ>MYe|uy2aDS-&IOE5U{V{22X?8w7 zHG`ISYLb$#)$^Zz)V8XUvT|;5F!+1s(5rrNaq-nbWctBMO;N`P(tSIBdwC=)KR=%Z zN9S#Mir1+9$?AA*S5?Q?A&j>6$kg^J#Mca_d>@tp8O^`nnVFlHS&r~q{`w@Yk}0pG ztE(HZ!tp6NHL%3g`4IatM_gC6Ze~61? z&q2we4wiVAM=BJRl$7G`Jb!3|(cbkySx(epq@<)^Rytmzx4nIR_}OSZGgyBdpLvuA zDw&!`H~)r&ze!h;NN!$UU%Yi)4sxYZVtswRV0BOa2!zgyZzx}NZS8Bq;^R1cuJ_4tH8wNeVSDNKqyfb0-F3lK_mwJ{ix)30 z4izO!hcXll4-fB)%SfB;uKfvLY&1I9US6haYw#uI^Ez~au;)0hjz5x<>+4CAacsJx zVY2k=bHnWfL3^Y4dVVzBeUmV&%0DD~9w^U)<%+obrs1Pj6|hBR=0k;>R$Z$*dD+>r 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 D^y(Y} literal 0 HcmV?d00001 diff --git a/docs/build/images/editor-select.png b/docs/build/images/editor-select.png new file mode 100644 index 0000000000000000000000000000000000000000..ffd0ca6864b0100797a2459d8a200bfcc565b722 GIT binary patch literal 3997 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 P&exEMA;O?s&l&S?%1@Zjxm}Gm1*~ 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 P0Xhh6he0uHuipI^p47Nf literal 0 HcmV?d00001 diff --git a/docs/build/images/logo.png b/docs/build/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..40b7b2121ee3ef872df47e8a664adf8bbc6b2a6f GIT binary patch literal 19734 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 literal 0 HcmV?d00001 diff --git a/docs/build/images/ponzu-banner.png b/docs/build/images/ponzu-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..855f97e384401a2c0605bfd09a31dea934290500 GIT binary patch literal 909819 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 + + + + + + + + + + + + + + + + Ponzu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + +

Ponzu CMS + Server Framework Docs

+

Ponzu

+

What is Ponzu?

+
+

Watch the video introduction

+
+

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), 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

+

Table of Contents

+
    +
  1. CLI
  2. +
  3. Content
  4. +
  5. Form Fields
  6. +
  7. HTTP API - Content
  8. +
  9. HTTP API - File Metadata
  10. +
  11. HTTP API - Search
  12. +
  13. Interfaces - API
  14. +
  15. Interfaces - Editor
  16. +
  17. Interfaces - Item
  18. +
  19. Interfaces - Search
  20. +
  21. Creating Ponzu Addons
  22. +
  23. Using Ponzu Addons
  24. +
  25. Quickstart
  26. +
  27. Backups
  28. +
  29. System Configuration
  30. +
+

Need help? Get in touch

+ +
+

current version: v0.9.2 @ ponzu:master

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/mkdocs/js/lunr.min.js b/docs/build/mkdocs/js/lunr.min.js new file mode 100644 index 00000000..b0198dff --- /dev/null +++ b/docs/build/mkdocs/js/lunr.min.js @@ -0,0 +1,7 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.7.0 + * Copyright (C) 2016 Oliver Nightingale + * MIT Licensed + * @license + */ +!function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.7.0",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.utils.asString=function(t){return void 0===t||null===t?"":t.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(e){return arguments.length&&null!=e&&void 0!=e?Array.isArray(e)?e.map(function(e){return t.utils.asString(e).toLowerCase()}):e.toString().trim().toLowerCase().split(t.tokenizer.seperator):[]},t.tokenizer.seperator=/[\s\-]+/,t.tokenizer.load=function(t){var e=this.registeredFunctions[t];if(!e)throw new Error("Cannot load un-registered function: "+t);return e},t.tokenizer.label="default",t.tokenizer.registeredFunctions={"default":t.tokenizer},t.tokenizer.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing tokenizer: "+n),e.label=n,this.registeredFunctions[n]=e},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,r=0;n>r;r++){for(var o=t[r],s=0;i>s&&(o=this._stack[s](o,r,t),void 0!==o&&""!==o);s++);void 0!==o&&""!==o&&e.push(o)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(o===t)return r;t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r]}return o===t?r:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,r=e+Math.floor(i/2),o=this.elements[r];i>1;)t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r];return o>t?r:t>o?r+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,r=0,o=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>o-1||r>s-1)break;a[i]!==h[r]?a[i]h[r]&&r++:(n.add(a[i]),i++,r++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone();for(var r=0,o=n.toArray();rp;p++)c[p]===a&&d++;h+=d/f*l.boost}}this.tokenStore.add(a,{ref:o,tf:h})}n&&this.eventEmitter.emit("add",e,this)},t.Index.prototype.remove=function(t,e){var n=t[this._ref],e=void 0===e?!0:e;if(this.documentStore.has(n)){var i=this.documentStore.get(n);this.documentStore.remove(n),i.forEach(function(t){this.tokenStore.remove(t,n)},this),e&&this.eventEmitter.emit("remove",t,this)}},t.Index.prototype.update=function(t,e){var e=void 0===e?!0:e;this.remove(t,!1),this.add(t,!1),e&&this.eventEmitter.emit("update",t,this)},t.Index.prototype.idf=function(t){var e="@"+t;if(Object.prototype.hasOwnProperty.call(this._idfCache,e))return this._idfCache[e];var n=this.tokenStore.count(t),i=1;return n>0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(this.tokenizerFn(e)),i=new t.Vector,r=[],o=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*o,h=this,u=this.tokenStore.expand(e).reduce(function(n,r){var o=h.corpusTokens.indexOf(r),s=h.idf(r),u=1,l=new t.SortedSet;if(r!==e){var c=Math.max(3,r.length-e.length);u=1/Math.log(c)}o>-1&&i.insert(o,a*s*u);for(var f=h.tokenStore.get(r),d=Object.keys(f),p=d.length,v=0;p>v;v++)l.add(f[d[v]].ref);return n.union(l)},new t.SortedSet);r.push(u)},this);var a=r.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,r=new t.Vector,o=0;i>o;o++){var s=n.elements[o],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);r.insert(this.corpusTokens.indexOf(s),a*h)}return r},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,tokenizer:this.tokenizerFn.label,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",r=n+"[^aeiouy]*",o=i+"[aeiou]*",s="^("+r+")?"+o+r,a="^("+r+")?"+o+r+"("+o+")?$",h="^("+r+")?"+o+r+o+r,u="^("+r+")?"+i,l=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(u),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),k=new RegExp("^"+r+i+"[^aeiouwxy]$"),x=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,F=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,_=/^(.+?)(s|t)(ion)$/,z=/^(.+?)e$/,O=/ll$/,P=new RegExp("^"+r+i+"[^aeiouwxy]$"),T=function(n){var i,r,o,s,a,h,u;if(n.length<3)return n;if(o=n.substr(0,1),"y"==o&&(n=o.toUpperCase()+n.substr(1)),s=p,a=v,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=g,a=m,s.test(n)){var T=s.exec(n);s=l,s.test(T[1])&&(s=y,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,u=k,a.test(n)?n+="e":h.test(n)?(s=y,n=n.replace(s,"")):u.test(n)&&(n+="e"))}if(s=x,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+t[r])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+e[r])}if(s=F,a=_,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=z,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=P,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=O,a=c,s.test(n)&&a.test(n)&&(s=y,n=n.replace(s,"")),"y"==o&&(n=o.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.generateStopWordFilter=function(t){var e=t.reduce(function(t,e){return t[e]=e,t},{});return function(t){return t&&e[t]!==t?t:void 0}},t.stopWordFilter=t.generateStopWordFilter(["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){return t.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t.charAt(0),r=t.slice(1);return i in n||(n[i]={docs:{}}),0===r.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(r,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n":">",'"':""","'":"'","/":"/"};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tags){if(typeof tags==="string")tags=tags.split(spaceRe,2);if(!isArray(tags)||tags.length!==2)throw new Error("Invalid tags: "+tags);openingTagRe=new RegExp(escapeRegExp(tags[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tags[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tags[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function(){return this.tail===""};Scanner.prototype.scan=function(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function(view){return new Context(view,this)};Context.prototype.lookup=function(name){var cache=this.cache;var value;if(name in cache){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this._renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this._unescapedValue(token,context);else if(symbol==="name")value=this._escapedValue(token,context);else if(symbol==="text")value=this._rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype._renderSection=function(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;jthis.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&& +(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= +this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f); +if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval", +"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b, +a);this.check()}));this.errback?q(a,"error",u(this,this.errback)):this.events.error&&q(a,"error",u(this,function(a){this.emit("error",a)}))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b, +registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a); +b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n, +q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d, +e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==e&&(!("."===k||".."===k)||1e.attachEvent.toString().indexOf("[native code"))&& +!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"), +s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"=== +b.readyState)return N=b}),e=N;e&&(b||(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this); diff --git a/docs/build/mkdocs/js/search-results-template.mustache b/docs/build/mkdocs/js/search-results-template.mustache new file mode 100644 index 00000000..a8b3862f --- /dev/null +++ b/docs/build/mkdocs/js/search-results-template.mustache @@ -0,0 +1,4 @@ + diff --git a/docs/build/mkdocs/js/search.js b/docs/build/mkdocs/js/search.js new file mode 100644 index 00000000..d5c86616 --- /dev/null +++ b/docs/build/mkdocs/js/search.js @@ -0,0 +1,88 @@ +require([ + base_url + '/mkdocs/js/mustache.min.js', + base_url + '/mkdocs/js/lunr.min.js', + 'text!search-results-template.mustache', + 'text!../search_index.json', +], function (Mustache, lunr, results_template, data) { + "use strict"; + + function getSearchTerm() + { + var sPageURL = window.location.search.substring(1); + var sURLVariables = sPageURL.split('&'); + for (var i = 0; i < sURLVariables.length; i++) + { + var sParameterName = sURLVariables[i].split('='); + if (sParameterName[0] == 'q') + { + return decodeURIComponent(sParameterName[1].replace(/\+/g, '%20')); + } + } + } + + var index = lunr(function () { + this.field('title', {boost: 10}); + this.field('text'); + this.ref('location'); + }); + + data = JSON.parse(data); + var documents = {}; + + for (var i=0; i < data.docs.length; i++){ + var doc = data.docs[i]; + doc.location = base_url + doc.location; + index.add(doc); + documents[doc.location] = doc; + } + + var search = function(){ + + var query = document.getElementById('mkdocs-search-query').value; + var search_results = document.getElementById("mkdocs-search-results"); + while (search_results.firstChild) { + search_results.removeChild(search_results.firstChild); + } + + if(query === ''){ + return; + } + + var results = index.search(query); + + if (results.length > 0){ + for (var i=0; i < results.length; i++){ + var result = results[i]; + doc = documents[result.ref]; + doc.base_url = base_url; + doc.summary = doc.text.substring(0, 200); + var html = Mustache.to_html(results_template, doc); + search_results.insertAdjacentHTML('beforeend', html); + } + } else { + search_results.insertAdjacentHTML('beforeend', "

No results found

"); + } + + if(jQuery){ + /* + * We currently only automatically hide bootstrap models. This + * requires jQuery to work. + */ + jQuery('#mkdocs_search_modal a').click(function(){ + jQuery('#mkdocs_search_modal').modal('hide'); + }); + } + + }; + + var search_input = document.getElementById('mkdocs-search-query'); + + var term = getSearchTerm(); + if (term){ + search_input.value = term; + search(); + } + + search_input.addEventListener("keyup", search); + +}); diff --git a/docs/build/mkdocs/js/text.js b/docs/build/mkdocs/js/text.js new file mode 100644 index 00000000..17921b6e --- /dev/null +++ b/docs/build/mkdocs/js/text.js @@ -0,0 +1,390 @@ +/** + * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/requirejs/text for details + */ +/*jslint regexp: true */ +/*global require, XMLHttpRequest, ActiveXObject, + define, window, process, Packages, + java, location, Components, FileUtils */ + +define(['module'], function (module) { + 'use strict'; + + var text, fs, Cc, Ci, xpcIsWindows, + progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], + xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, + bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, + hasLocation = typeof location !== 'undefined' && location.href, + defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), + defaultHostName = hasLocation && location.hostname, + defaultPort = hasLocation && (location.port || undefined), + buildMap = {}, + masterConfig = (module.config && module.config()) || {}; + + text = { + version: '2.0.12', + + strip: function (content) { + //Strips declarations so that external SVG and XML + //documents can be added to a document without worry. Also, if the string + //is an HTML document, only the part inside the body tag is returned. + if (content) { + content = content.replace(xmlRegExp, ""); + var matches = content.match(bodyRegExp); + if (matches) { + content = matches[1]; + } + } else { + content = ""; + } + return content; + }, + + jsEscape: function (content) { + return content.replace(/(['\\])/g, '\\$1') + .replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r") + .replace(/[\u2028]/g, "\\u2028") + .replace(/[\u2029]/g, "\\u2029"); + }, + + createXhr: masterConfig.createXhr || function () { + //Would love to dump the ActiveX crap in here. Need IE 6 to die first. + var xhr, i, progId; + if (typeof XMLHttpRequest !== "undefined") { + return new XMLHttpRequest(); + } else if (typeof ActiveXObject !== "undefined") { + for (i = 0; i < 3; i += 1) { + progId = progIds[i]; + try { + xhr = new ActiveXObject(progId); + } catch (e) {} + + if (xhr) { + progIds = [progId]; // so faster next time + break; + } + } + } + + return xhr; + }, + + /** + * Parses a resource name into its component parts. Resource names + * look like: module/name.ext!strip, where the !strip part is + * optional. + * @param {String} name the resource name + * @returns {Object} with properties "moduleName", "ext" and "strip" + * where strip is a boolean. + */ + parseName: function (name) { + var modName, ext, temp, + strip = false, + index = name.indexOf("."), + isRelative = name.indexOf('./') === 0 || + name.indexOf('../') === 0; + + if (index !== -1 && (!isRelative || index > 1)) { + modName = name.substring(0, index); + ext = name.substring(index + 1, name.length); + } else { + modName = name; + } + + temp = ext || modName; + index = temp.indexOf("!"); + if (index !== -1) { + //Pull off the strip arg. + strip = temp.substring(index + 1) === "strip"; + temp = temp.substring(0, index); + if (ext) { + ext = temp; + } else { + modName = temp; + } + } + + return { + moduleName: modName, + ext: ext, + strip: strip + }; + }, + + xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, + + /** + * Is an URL on another domain. Only works for browser use, returns + * false in non-browser environments. Only used to know if an + * optimized .js version of a text resource should be loaded + * instead. + * @param {String} url + * @returns Boolean + */ + useXhr: function (url, protocol, hostname, port) { + var uProtocol, uHostName, uPort, + match = text.xdRegExp.exec(url); + if (!match) { + return true; + } + uProtocol = match[2]; + uHostName = match[3]; + + uHostName = uHostName.split(':'); + uPort = uHostName[1]; + uHostName = uHostName[0]; + + return (!uProtocol || uProtocol === protocol) && + (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && + ((!uPort && !uHostName) || uPort === port); + }, + + finishLoad: function (name, strip, content, onLoad) { + content = strip ? text.strip(content) : content; + if (masterConfig.isBuild) { + buildMap[name] = content; + } + onLoad(content); + }, + + load: function (name, req, onLoad, config) { + //Name has format: some.module.filext!strip + //The strip part is optional. + //if strip is present, then that means only get the string contents + //inside a body tag in an HTML string. For XML/SVG content it means + //removing the declarations so the content can be inserted + //into the current doc without problems. + + // Do not bother with the work if a build and text will + // not be inlined. + if (config && config.isBuild && !config.inlineText) { + onLoad(); + return; + } + + masterConfig.isBuild = config && config.isBuild; + + var parsed = text.parseName(name), + nonStripName = parsed.moduleName + + (parsed.ext ? '.' + parsed.ext : ''), + url = req.toUrl(nonStripName), + useXhr = (masterConfig.useXhr) || + text.useXhr; + + // Do not load if it is an empty: url + if (url.indexOf('empty:') === 0) { + onLoad(); + return; + } + + //Load the text. Use XHR if possible and in a browser. + if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { + text.get(url, function (content) { + text.finishLoad(name, parsed.strip, content, onLoad); + }, function (err) { + if (onLoad.error) { + onLoad.error(err); + } + }); + } else { + //Need to fetch the resource across domains. Assume + //the resource has been optimized into a JS module. Fetch + //by the module name + extension, but do not include the + //!strip part to avoid file system issues. + req([nonStripName], function (content) { + text.finishLoad(parsed.moduleName + '.' + parsed.ext, + parsed.strip, content, onLoad); + }); + } + }, + + write: function (pluginName, moduleName, write, config) { + if (buildMap.hasOwnProperty(moduleName)) { + var content = text.jsEscape(buildMap[moduleName]); + write.asModule(pluginName + "!" + moduleName, + "define(function () { return '" + + content + + "';});\n"); + } + }, + + writeFile: function (pluginName, moduleName, req, write, config) { + var parsed = text.parseName(moduleName), + extPart = parsed.ext ? '.' + parsed.ext : '', + nonStripName = parsed.moduleName + extPart, + //Use a '.js' file name so that it indicates it is a + //script that can be loaded across domains. + fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; + + //Leverage own load() method to load plugin value, but only + //write out values that do not have the strip argument, + //to avoid any potential issues with ! in file names. + text.load(nonStripName, req, function (value) { + //Use own write() method to construct full module value. + //But need to create shell that translates writeFile's + //write() to the right interface. + var textWrite = function (contents) { + return write(fileName, contents); + }; + textWrite.asModule = function (moduleName, contents) { + return write.asModule(moduleName, fileName, contents); + }; + + text.write(pluginName, nonStripName, textWrite, config); + }, config); + } + }; + + if (masterConfig.env === 'node' || (!masterConfig.env && + typeof process !== "undefined" && + process.versions && + !!process.versions.node && + !process.versions['node-webkit'])) { + //Using special require.nodeRequire, something added by r.js. + fs = require.nodeRequire('fs'); + + text.get = function (url, callback, errback) { + try { + var file = fs.readFileSync(url, 'utf8'); + //Remove BOM (Byte Mark Order) from utf8 files if it is there. + if (file.indexOf('\uFEFF') === 0) { + file = file.substring(1); + } + callback(file); + } catch (e) { + if (errback) { + errback(e); + } + } + }; + } else if (masterConfig.env === 'xhr' || (!masterConfig.env && + text.createXhr())) { + text.get = function (url, callback, errback, headers) { + var xhr = text.createXhr(), header; + xhr.open('GET', url, true); + + //Allow plugins direct access to xhr headers + if (headers) { + for (header in headers) { + if (headers.hasOwnProperty(header)) { + xhr.setRequestHeader(header.toLowerCase(), headers[header]); + } + } + } + + //Allow overrides specified in config + if (masterConfig.onXhr) { + masterConfig.onXhr(xhr, url); + } + + xhr.onreadystatechange = function (evt) { + var status, err; + //Do not explicitly handle errors, those should be + //visible via console output in the browser. + if (xhr.readyState === 4) { + status = xhr.status || 0; + if (status > 399 && status < 600) { + //An http 4xx or 5xx error. Signal an error. + err = new Error(url + ' HTTP status: ' + status); + err.xhr = xhr; + if (errback) { + errback(err); + } + } else { + callback(xhr.responseText); + } + + if (masterConfig.onXhrComplete) { + masterConfig.onXhrComplete(xhr, url); + } + } + }; + xhr.send(null); + }; + } else if (masterConfig.env === 'rhino' || (!masterConfig.env && + typeof Packages !== 'undefined' && typeof java !== 'undefined')) { + //Why Java, why is this so awkward? + text.get = function (url, callback) { + var stringBuffer, line, + encoding = "utf-8", + file = new java.io.File(url), + lineSeparator = java.lang.System.getProperty("line.separator"), + input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), + content = ''; + try { + stringBuffer = new java.lang.StringBuffer(); + line = input.readLine(); + + // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 + // http://www.unicode.org/faq/utf_bom.html + + // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 + if (line && line.length() && line.charAt(0) === 0xfeff) { + // Eat the BOM, since we've already found the encoding on this file, + // and we plan to concatenating this buffer with others; the BOM should + // only appear at the top of a file. + line = line.substring(1); + } + + if (line !== null) { + stringBuffer.append(line); + } + + while ((line = input.readLine()) !== null) { + stringBuffer.append(lineSeparator); + stringBuffer.append(line); + } + //Make sure we return a JavaScript string and not a Java string. + content = String(stringBuffer.toString()); //String + } finally { + input.close(); + } + callback(content); + }; + } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && + typeof Components !== 'undefined' && Components.classes && + Components.interfaces)) { + //Avert your gaze! + Cc = Components.classes; + Ci = Components.interfaces; + Components.utils['import']('resource://gre/modules/FileUtils.jsm'); + xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); + + text.get = function (url, callback) { + var inStream, convertStream, fileObj, + readData = {}; + + if (xpcIsWindows) { + url = url.replace(/\//g, '\\'); + } + + fileObj = new FileUtils.File(url); + + //XPCOM, you so crazy + try { + inStream = Cc['@mozilla.org/network/file-input-stream;1'] + .createInstance(Ci.nsIFileInputStream); + inStream.init(fileObj, 1, 0, false); + + convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] + .createInstance(Ci.nsIConverterInputStream); + convertStream.init(inStream, "utf-8", inStream.available(), + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + + convertStream.readString(inStream.available(), readData); + convertStream.close(); + inStream.close(); + callback(readData.value); + } catch (e) { + throw new Error((fileObj && fileObj.path || '') + ': ' + e); + } + }; + } + return text; +}); diff --git a/docs/build/mkdocs/search_index.json b/docs/build/mkdocs/search_index.json new file mode 100644 index 00000000..b9dca9c9 --- /dev/null +++ b/docs/build/mkdocs/search_index.json @@ -0,0 +1,1104 @@ +{ + "docs": [ + { + "location": "/", + "text": "Ponzu CMS + Server Framework Docs\n\n\n\n\nWhat is Ponzu?\n\n\n\n\nWatch the \nvideo introduction\n\n\n\n\nPonzu is a powerful and efficient open-source HTTP server framework and CMS. It \nprovides automatic, free, and secure HTTP/2 over TLS (certificates obtained via \n\nLet's Encrypt\n), a useful CMS and scaffolding to generate \ncontent editors, and a fast HTTP API on which to build modern applications.\n\n\nWant to jump in right away? Try the \nQuickstart\n\n\nTable of Contents\n\n\n\n\nCLI\n\n\nContent\n\n\nForm Fields\n\n\nHTTP API - Content\n\n\nHTTP API - File Metadata\n\n\nHTTP API - Search\n\n\nInterfaces - API\n\n\nInterfaces - Editor\n\n\nInterfaces - Item\n\n\nInterfaces - Search\n\n\nCreating Ponzu Addons\n\n\nUsing Ponzu Addons\n\n\nQuickstart\n\n\nBackups\n\n\nSystem Configuration\n\n\n\n\nNeed help? Get in touch\n\n\n\n\nChat: \n#ponzu on Slack\n\n\nReach out on Twitter: \n@ponzu_cms\n\n\nFile an \nissue\n\n\n\n\n\n\ncurrent version: \nv0.9.2\n @ ponzu:master", + "title": "Home" + }, + { + "location": "/#ponzu-cms-server-framework-docs", + "text": "", + "title": "Ponzu CMS + Server Framework Docs" + }, + { + "location": "/#what-is-ponzu", + "text": "Watch the video introduction Ponzu is a powerful and efficient open-source HTTP server framework and CMS. It \nprovides automatic, free, and secure HTTP/2 over TLS (certificates obtained via Let's Encrypt ), a useful CMS and scaffolding to generate \ncontent editors, and a fast HTTP API on which to build modern applications. Want to jump in right away? Try the Quickstart", + "title": "What is Ponzu?" + }, + { + "location": "/#table-of-contents", + "text": "CLI Content Form Fields HTTP API - Content HTTP API - File Metadata HTTP API - Search Interfaces - API Interfaces - Editor Interfaces - Item Interfaces - Search Creating Ponzu Addons Using Ponzu Addons Quickstart Backups System Configuration", + "title": "Table of Contents" + }, + { + "location": "/#need-help-get-in-touch", + "text": "Chat: #ponzu on Slack Reach out on Twitter: @ponzu_cms File an issue current version: v0.9.2 @ ponzu:master", + "title": "Need help? Get in touch" + }, + { + "location": "/CLI/General-Usage/", + "text": "$ ponzu \n[\nflags\n]\n \ncommand\n \nparams\n\n\n\n\n\n\nCommands\n\n\nnew\n\n\nCreates a project directory of the name supplied as a parameter immediately\nfollowing the 'new' option in the $GOPATH/src directory. Note: 'new' depends on \nthe program 'git' and possibly a network connection. If there is no local \nrepository to clone from at the local machine's $GOPATH, 'new' will attempt to \nclone the 'github.com/ponzu-cms/ponzu' package from over the network.\n\n\nExample:\n\n\n$ ponzu new github.com/nilslice/proj\n\n New ponzu project created at \n$GOPATH\n/src/github.com/nilslice/proj\n\n\n\n\n\n\n\ngenerate, gen, g\n\n\nGenerate boilerplate code for various Ponzu components, such as \ncontent\n.\n\n\nExample:\n\n\n generator struct fields and built-in types...\n \n|\n \n|\n\n v v \n$ ponzu gen content review title:\nstring\n body:\nstring\n:richtext rating:\nint\n\n ^ ^\n \n|\n \n|\n\n struct \ntype\n \n(\noptional\n)\n input view specifier\n\n\n\n\n\nThe command above will generate the file \ncontent/review.go\n with boilerplate\nmethods, as well as struct definition, and corresponding field tags like:\n\n\ntype\n \nReview\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nTitle\n \nstring\n \n`json:\ntitle\n`\n\n \nBody\n \nstring\n \n`json:\nbody\n`\n\n \nRating\n \nint\n \n`json:\nrating\n`\n\n \nTags\n \n[]\nstring\n \n`json:\ntags\n`\n\n\n}\n\n\n\n\n\n\nThe generate command will intelligently parse more sophisticated field names\nsuch as 'field_name' and convert it to 'FieldName' and vice versa, only where \nappropriate as per common Go idioms. Errors will be reported, but successful \ngenerate commands return nothing.\n\n\nInput View Specifiers\n \n(optional)\n\n\nThe CLI can optionally parse a third parameter on the fields provided to generate \nthe type of HTML view an editor field is presented within. If no third parameter\nis added, a plain text HTML input will be generated. In the example above, the \nargument shown as \nbody:string:richtext\n would show the Richtext input instead\nof a plain text HTML input (as shown in the screenshot). The following input\nview specifiers are implemented:\n\n\n\n\n\n\n\n\nCLI parameter\n\n\nGenerates\n\n\n\n\n\n\n\n\n\n\ncheckbox\n\n\neditor.Checkbox()\n\n\n\n\n\n\ncustom\n\n\ngenerates a pre-styled empty div to fill with HTML\n\n\n\n\n\n\nfile\n\n\neditor.File()\n\n\n\n\n\n\nhidden\n\n\neditor.Input()\n + uses type=hidden\n\n\n\n\n\n\ninput, text\n\n\neditor.Input()\n\n\n\n\n\n\nrichtext\n\n\neditor.Richtext()\n\n\n\n\n\n\nselect\n\n\neditor.Select()\n\n\n\n\n\n\ntextarea\n\n\neditor.Textarea()\n\n\n\n\n\n\ntags\n\n\neditor.Tags()\n\n\n\n\n\n\n\n\nGenerate Content References\n\n\nIt's also possible to generate all of the code needed to create references between\nyour content types. The syntax to do so is below, but refer to the \ndocumentation\n\nfor more details:\n\n\n$ ponzu gen c author name:string genre:string:select\n$ ponzu gen c book title:string author:@author,name,genre \n\n\n\n\n\nThe commands above will generate a \nBook\n Content type with a reference to an\n\nAuthor\n item, by also generating a \nreference.Select\n\nas the view for the \nauthor\n field.\n\n\n\n\nbuild\n\n\nFrom within your Ponzu project directory, running build will copy and move \nthe necessary files from your workspace into the vendored directory, and \nwill build/compile the project to then be run. \n\n\nOptional flags:\n- \n--gocmd\n sets the binary used when executing \ngo build\n within \nponzu\n build step\n\n\nExample:\n\n\n$ ponzu build\n\n(\nor\n)\n\n$ ponzu build --gocmd\n=\ngo1.8rc1 \n# useful for testing\n\n\n\n\n\n\nErrors will be reported, but successful build commands return nothing.\n\n\n\n\nrun\n\n\nStarts the HTTP server for the JSON API, Admin System, or both.\nThe segments, separated by a comma, describe which services to start, either \n'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, \nif the server should utilize TLS encryption - served over HTTPS, which is\nautomatically managed using Let's Encrypt (https://letsencrypt.org) \n\n\nOptional flags:\n- \n--port\n sets the port on which the server listens for HTTP requests [defaults to 8080]\n- \n--https-port\n sets the port on which the server listens for HTTPS requests [defaults to 443]\n- \n--https\n enables auto HTTPS management via Let's Encrypt (port is always 443)\n- \n--dev-https\n generates self-signed SSL certificates for development-only (port is 10443)\n\n\nExample: \n\n\n$ ponzu run\n\n(\nor\n)\n\n$ ponzu run --port\n=\n8080\n --https admin,api\n\n(\nor\n)\n \n$ ponzu run admin\n\n(\nor\n)\n\n$ ponzu run --port\n=\n8888\n api\n\n(\nor\n)\n\n$ ponzu --dev-https run\n\n\n\n\n\nDefaults to \n$ ponzu run --port=8080 admin,api\n (running Admin \n API on port 8080, without TLS)\n\n\nNote:\n \nAdmin and API cannot run on separate processes unless you use a copy of the\ndatabase, since the first process to open it receives a lock. If you intend\nto run the Admin and API on separate processes, you must call them with the\n'ponzu' command independently.\n\n\n\n\nupgrade\n\n\nWill backup your own custom project code (like content, addons, uploads, etc) so\nwe can safely re-clone Ponzu from the latest version you have or from the network \nif necessary. Before running \n$ ponzu upgrade\n, you should update the \nponzu\n\npackage by running \n$ go get -u github.com/ponzu-cms/ponzu/...\n \n\n\nExample:\n\n\n$ ponzu upgrade\n\n\n\n\n\n\n\nadd, a\n\n\nDownloads an addon to GOPATH/src and copies it to the current Ponzu project's\n\n/addons\n directory.\n\n\nExample:\n\n\n$ ponzu add github.com/bosssauce/fbscheduler\n\n\n\n\n\nErrors will be reported, but successful add commands return nothing.\n\n\n\n\nversion, v\n\n\nPrints the version of Ponzu your project is using. Must be called from within a \nPonzu project directory. By passing the \n--cli\n flag, the \nversion\n command will \nprint the version of the Ponzu CLI you have installed.\n\n\nExample:\n\n\n$ ponzu version\nPonzu v0.8.2\n\n# (or)\n\n$ ponzu version --cli\nPonzu v0.9.2\n\n\n\n\n\n\n\nContributing\n\n\n\n\nCheckout branch ponzu-dev\n\n\nMake code changes\n\n\nTest changes to ponzu-dev branch\n\n\nmake a commit to ponzu-dev\n\n\nto manually test, you will need to use a new copy (ponzu new path/to/code), \nbut pass the \n--dev\n flag so that ponzu generates a new copy from the \nponzu-dev\n \nbranch, not master by default (i.e. \n$ponzu new --dev /path/to/code\n)\n\n\nbuild and run with \n$ ponzu build\n and \n$ ponzu run\n\n\n\n\n\n\nTo add back to master: \n\n\nfirst push to origin ponzu-dev\n\n\ncreate a pull request \n\n\nwill then be merged into master\n\n\n\n\n\n\n\n\nA typical contribution workflow might look like:\n\n\n# clone the repository and checkout ponzu-dev\n\n$ git clone https://github.com/ponzu-cms/ponzu path/to/local/ponzu \n# (or your fork)\n\n$ git checkout ponzu-dev\n\n\n# install ponzu with go get or from your own local path\n\n$ go get github.com/ponzu-cms/ponzu/...\n\n# or\n\n$ \ncd\n /path/to/local/ponzu \n$ go install ./...\n\n\n# edit files, add features, etc\n\n$ git add -A\n$ git commit -m \nedited files, added features, etc\n\n\n\n# now you need to test the feature.. make a new ponzu project, but pass --dev flag\n\n$ ponzu --dev new /path/to/new/project \n# will create $GOPATH/src/path/to/new/project\n\n\n\n# build \n run ponzu from the new project directory\n\n$ \ncd\n /path/to/new/project\n$ ponzu build \n ponzu run\n\n\n# push to your origin:ponzu-dev branch and create a PR at ponzu-cms/ponzu\n\n$ git push origin ponzu-dev\n\n# ... go to https://github.com/ponzu-cms/ponzu and create a PR\n\n\n\n\n\n\nNote:\n if you intend to work on your own fork and contribute from it, you will\nneed to also pass \n--fork=path/to/your/fork\n (using OS-standard filepath structure),\nwhere \npath/to/your/fork\n \nmust\n be within \n$GOPATH/src\n, and you are working from a branch\ncalled \nponzu-dev\n. \n\n\nFor example: \n\n\n# ($GOPATH/src is implied in the fork path, do not add it yourself)\n\n$ ponzu new --dev --fork\n=\ngithub.com/nilslice/ponzu /path/to/new/project", + "title": "General Usage" + }, + { + "location": "/CLI/General-Usage/#commands", + "text": "", + "title": "Commands" + }, + { + "location": "/CLI/General-Usage/#new", + "text": "Creates a project directory of the name supplied as a parameter immediately\nfollowing the 'new' option in the $GOPATH/src directory. Note: 'new' depends on \nthe program 'git' and possibly a network connection. If there is no local \nrepository to clone from at the local machine's $GOPATH, 'new' will attempt to \nclone the 'github.com/ponzu-cms/ponzu' package from over the network. Example: $ ponzu new github.com/nilslice/proj New ponzu project created at $GOPATH /src/github.com/nilslice/proj", + "title": "new" + }, + { + "location": "/CLI/General-Usage/#generate-gen-g", + "text": "Generate boilerplate code for various Ponzu components, such as content . Example: generator struct fields and built-in types...\n | | \n v v \n$ ponzu gen content review title: string body: string :richtext rating: int \n ^ ^\n | | \n struct type ( optional ) input view specifier The command above will generate the file content/review.go with boilerplate\nmethods, as well as struct definition, and corresponding field tags like: type Review struct { \n item . Item \n\n Title string `json: title ` \n Body string `json: body ` \n Rating int `json: rating ` \n Tags [] string `json: tags ` } The generate command will intelligently parse more sophisticated field names\nsuch as 'field_name' and convert it to 'FieldName' and vice versa, only where \nappropriate as per common Go idioms. Errors will be reported, but successful \ngenerate commands return nothing. Input View Specifiers (optional) The CLI can optionally parse a third parameter on the fields provided to generate \nthe type of HTML view an editor field is presented within. If no third parameter\nis added, a plain text HTML input will be generated. In the example above, the \nargument shown as body:string:richtext would show the Richtext input instead\nof a plain text HTML input (as shown in the screenshot). The following input\nview specifiers are implemented: CLI parameter Generates checkbox editor.Checkbox() custom generates a pre-styled empty div to fill with HTML file editor.File() hidden editor.Input() + uses type=hidden input, text editor.Input() richtext editor.Richtext() select editor.Select() textarea editor.Textarea() tags editor.Tags() Generate Content References It's also possible to generate all of the code needed to create references between\nyour content types. The syntax to do so is below, but refer to the documentation \nfor more details: $ ponzu gen c author name:string genre:string:select\n$ 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 \nas the view for the author field.", + "title": "generate, gen, g" + }, + { + "location": "/CLI/General-Usage/#build", + "text": "From within your Ponzu project directory, running build will copy and move \nthe necessary files from your workspace into the vendored directory, and \nwill build/compile the project to then be run. Optional flags:\n- --gocmd sets the binary used when executing go build within ponzu build step Example: $ ponzu build ( or ) \n$ ponzu build --gocmd = go1.8rc1 # useful for testing Errors will be reported, but successful build commands return nothing.", + "title": "build" + }, + { + "location": "/CLI/General-Usage/#run", + "text": "Starts the HTTP server for the JSON API, Admin System, or both.\nThe segments, separated by a comma, describe which services to start, either \n'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, \nif the server should utilize TLS encryption - served over HTTPS, which is\nautomatically managed using Let's Encrypt (https://letsencrypt.org) Optional flags:\n- --port sets the port on which the server listens for HTTP requests [defaults to 8080]\n- --https-port sets the port on which the server listens for HTTPS requests [defaults to 443]\n- --https enables auto HTTPS management via Let's Encrypt (port is always 443)\n- --dev-https generates self-signed SSL certificates for development-only (port is 10443) Example: $ ponzu run ( or ) \n$ ponzu run --port = 8080 --https admin,api ( or ) \n$ ponzu run admin ( or ) \n$ ponzu run --port = 8888 api ( or ) \n$ ponzu --dev-https run Defaults to $ ponzu run --port=8080 admin,api (running Admin API on port 8080, without TLS) Note: \nAdmin and API cannot run on separate processes unless you use a copy of the\ndatabase, since the first process to open it receives a lock. If you intend\nto run the Admin and API on separate processes, you must call them with the\n'ponzu' command independently.", + "title": "run" + }, + { + "location": "/CLI/General-Usage/#upgrade", + "text": "Will backup your own custom project code (like content, addons, uploads, etc) so\nwe can safely re-clone Ponzu from the latest version you have or from the network \nif necessary. Before running $ ponzu upgrade , you should update the ponzu \npackage by running $ go get -u github.com/ponzu-cms/ponzu/... Example: $ ponzu upgrade", + "title": "upgrade" + }, + { + "location": "/CLI/General-Usage/#add-a", + "text": "Downloads an addon to GOPATH/src and copies it to the current Ponzu project's /addons directory. Example: $ ponzu add github.com/bosssauce/fbscheduler Errors will be reported, but successful add commands return nothing.", + "title": "add, a" + }, + { + "location": "/CLI/General-Usage/#version-v", + "text": "Prints the version of Ponzu your project is using. Must be called from within a \nPonzu project directory. By passing the --cli flag, the version command will \nprint the version of the Ponzu CLI you have installed. Example: $ ponzu version\nPonzu v0.8.2 # (or) \n$ ponzu version --cli\nPonzu v0.9.2", + "title": "version, v" + }, + { + "location": "/CLI/General-Usage/#contributing", + "text": "Checkout branch ponzu-dev Make code changes 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), \nbut pass the --dev flag so that ponzu generates a new copy from the ponzu-dev \nbranch, not master by default (i.e. $ponzu new --dev /path/to/code ) build and run with $ ponzu build and $ ponzu run 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: # clone the repository and checkout ponzu-dev \n$ git clone https://github.com/ponzu-cms/ponzu path/to/local/ponzu # (or your fork) \n$ git checkout ponzu-dev # install ponzu with go get or from your own local path \n$ go get github.com/ponzu-cms/ponzu/... # or \n$ cd /path/to/local/ponzu \n$ go install ./... # edit files, add features, etc \n$ git add -A\n$ git commit -m edited files, added features, etc # now you need to test the feature.. make a new ponzu project, but pass --dev flag \n$ ponzu --dev new /path/to/new/project # will create $GOPATH/src/path/to/new/project # build run ponzu from the new project directory \n$ cd /path/to/new/project\n$ ponzu build ponzu run # push to your origin:ponzu-dev branch and create a PR at ponzu-cms/ponzu \n$ 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\nneed to also pass --fork=path/to/your/fork (using OS-standard filepath structure),\nwhere path/to/your/fork must be within $GOPATH/src , and you are working from a branch\ncalled ponzu-dev . For example: # ($GOPATH/src is implied in the fork path, do not add it yourself) \n$ ponzu new --dev --fork = github.com/nilslice/ponzu /path/to/new/project", + "title": "Contributing" + }, + { + "location": "/CLI/Generating-References/", + "text": "In Ponzu, users make connections between Content types using references. In order \nto use the CLI to generate these references, a slightly different syntax is required. \nIn all cases, the Content type you wish to reference does not need to exist prior\nto the \"parent\" type referencing it at generate-time, but in the following examples,\nthe referenced \"child\" type will be shown before the parent type for clarity.\n\n\nSyntax\n\n\n@\n\n\nThe \n@\n symbol is used to declare that the following name is a reference. The \nCLI will take care to parse the name and treat it as a Content type to which the \ncurrent type refers.\n\n\n[]\n\n\nThe \n[]\n, which if used, is always in front of the \n@\n symbol. It signifies \nthat the reference type is a slice or a collection of references. When \n[]\n\nis used, the CLI will automatically generate a \nreference.SelectRepeater()\n view \nfor you.\n\n\n,arg1,arg2,argN\n\n\nImmediately following the reference name (after the @ symbol), users may optionally\npass arguments to specify how the reference is displayed in the parent type's\neditor. References are included in the parent types editor as a dropdown menu, with\neach possible reference as an option. These arguments define what goes inside the\n\noption\n/option\n text node, as would be seen by an Admin.\n\n\nThe arguments must be valid JSON struct tag names from the reference type's fields. \nNotice in the example below, the \ntitle\n and \nprice\n are formatted exactly as they \nwere in the generate command for the \nproduct\n type.\n\n\n\n\n\n\nExample\n\n\n$ ponzu gen content product title:string price:int description:string:textarea\n$ ponzu gen content catalog year:int products:\n[]@product\n,title,price\n\n\n\n\n\nThe commands above output the following. For demonstration, we will omit the full\ncode generated for the \nProduct\n, since the reference is in the \nCatalog\n type.\n\n\n// content/product.go\n\n\npackage\n \ncontent\n\n\n...\n\n\n\ntype\n \nProduct\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nTitle\n \nstring\n \n`json:\ntitle\n`\n\n \nPrice\n \nint\n \n`json:\nprice\n`\n\n \nDescription\n \nstring\n \n`json:\ndescription\n`\n\n\n}\n\n\n\n...\n\n\n\n\n\n\npackage\n \ncontent\n\n\n\nimport\n \n(\n\n \nfmt\n\n\n \ngithub.com/bosssauce/reference\n\n\n \ngithub.com/ponzu-cms/ponzu/management/editor\n\n \ngithub.com/ponzu-cms/ponzu/system/item\n\n\n)\n\n\n\ntype\n \nCatalog\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nYear\n \nint\n \n`json:\nyear\n`\n\n \n// all references are stored as []string or string types\n\n \nProducts\n \n[]\nstring\n \n`json:\nproducts\n`\n \n\n}\n\n\n\nfunc\n \n(\nc\n \n*\nCatalog\n)\n \nMarshalEditor\n()\n \n([]\nbyte\n,\n \nerror\n)\n \n{\n\n \nview\n,\n \nerr\n \n:=\n \neditor\n.\nForm\n(\nc\n,\n\n \neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nInput\n(\nYear\n,\n \nc\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nYear\n,\n\n \ntype\n:\n \ntext\n,\n\n \nplaceholder\n:\n \nEnter the Year here\n,\n\n \n}),\n\n \n},\n\n \neditor\n.\nField\n{\n\n \n// reference.SelectRepeater since []@product was used\n\n \nView\n:\n \nreference\n.\nSelectRepeater\n(\nProducts\n,\n \nc\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nProducts\n,\n\n \n},\n\n \nProduct\n,\n \n// generated from @product\n\n \n`{{ .title }} {{ .price }} `\n,\n \n// generated from ,title,price args\n\n \n),\n\n \n},\n\n \n)\n\n\n \nif\n \nerr\n \n!=\n \nnil\n \n{\n\n \nreturn\n \nnil\n,\n \nfmt\n.\nErrorf\n(\nFailed to render Catalog editor view: %s\n,\n \nerr\n.\nError\n())\n\n \n}\n\n\n \nreturn\n \nview\n,\n \nnil\n\n\n}\n\n\n\nfunc\n \ninit\n()\n \n{\n\n \nitem\n.\nTypes\n[\nCatalog\n]\n \n=\n \nfunc\n()\n \ninterface\n{}\n \n{\n \nreturn\n \nnew\n(\nCatalog\n)\n \n}\n\n\n}\n\n\n\n\n\n\nNote:\n\nIf the reference should be only a single item, rather than a slice (or collection)\nof items, omit the \n[]\n, changing the command to:\n\n\n$ ponzu gen content catalog year:int product:@product,title,price", + "title": "Generating References" + }, + { + "location": "/CLI/Generating-References/#syntax", + "text": "", + "title": "Syntax" + }, + { + "location": "/CLI/Generating-References/#_1", + "text": "The @ symbol is used to declare that the following name is a reference. The \nCLI will take care to parse the name and treat it as a Content type to which the \ncurrent type refers.", + "title": "@" + }, + { + "location": "/CLI/Generating-References/#_2", + "text": "The [] , which if used, is always in front of the @ symbol. It signifies \nthat the reference type is a slice or a collection of references. When [] \nis used, the CLI will automatically generate a reference.SelectRepeater() view \nfor you.", + "title": "[]" + }, + { + "location": "/CLI/Generating-References/#arg1arg2argn", + "text": "Immediately following the reference name (after the @ symbol), users may optionally\npass arguments to specify how the reference is displayed in the parent type's\neditor. References are included in the parent types editor as a dropdown menu, with\neach possible reference as an option. These arguments define what goes inside the option /option text node, as would be seen by an Admin. The arguments must be valid JSON struct tag names from the reference type's fields. \nNotice in the example below, the title and price are formatted exactly as they \nwere in the generate command for the product type.", + "title": ",arg1,arg2,argN" + }, + { + "location": "/CLI/Generating-References/#example", + "text": "$ ponzu gen content product title:string price:int description:string:textarea\n$ ponzu gen content catalog year:int products: []@product ,title,price The commands above output the following. For demonstration, we will omit the full\ncode generated for the Product , since the reference is in the Catalog type. // content/product.go package content ... type Product struct { \n item . Item \n\n Title string `json: title ` \n Price int `json: price ` \n Description string `json: description ` } ... package content import ( \n fmt \n\n github.com/bosssauce/reference \n\n github.com/ponzu-cms/ponzu/management/editor \n github.com/ponzu-cms/ponzu/system/item ) type Catalog struct { \n item . Item \n\n Year int `json: year ` \n // all references are stored as []string or string types \n Products [] string `json: products ` } func ( c * Catalog ) MarshalEditor () ([] byte , error ) { \n view , err := editor . Form ( c , \n editor . Field { \n View : editor . Input ( Year , c , map [ string ] string { \n label : Year , \n type : text , \n placeholder : Enter the Year here , \n }), \n }, \n editor . Field { \n // reference.SelectRepeater since []@product was used \n View : reference . SelectRepeater ( Products , c , map [ string ] string { \n label : Products , \n }, \n Product , // generated from @product \n `{{ .title }} {{ .price }} ` , // generated from ,title,price args \n ), \n }, \n ) \n\n if err != nil { \n return nil , fmt . Errorf ( Failed to render Catalog editor view: %s , err . Error ()) \n } \n\n return view , nil } func init () { \n item . Types [ Catalog ] = func () interface {} { return new ( Catalog ) } } Note: \nIf the reference should be only a single item, rather than a slice (or collection)\nof items, omit the [] , changing the command to: $ ponzu gen content catalog year:int product:@product,title,price", + "title": "Example" + }, + { + "location": "/Content/An-Overview/", + "text": "Nearly everything you work on in Ponzu is inside content files on the content types you create. These types must all reside in the \ncontent\n 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 \neditor.Editable\n interface, and add their own \ninterface{}\n container to the global \nitem.Types\n map. \n\n\nSound 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.\n\n\nIt is rare to hand-write a new Content type, and should be generated instead!\n\n\nGenerating Content types\n\n\nTo generate content types and boilerplate code, use the Ponzu CLI \ngenerate\n command as such:\n\n\n$ ponzu generate content post title:string body:string:richtext author:string\n\n\n\n\n\nThe command above will create a file at \ncontent/post.go\n and will generate the following code:\n\n\npackage\n \ncontent\n\n\n\nimport\n \n(\n\n \nfmt\n\n\n \ngithub.com/ponzu-cms/ponzu/management/editor\n\n \ngithub.com/ponzu-cms/ponzu/system/item\n\n\n)\n\n\n\ntype\n \nPost\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nTitle\n \nstring\n \n`json:\ntitle\n`\n\n \nBody\n \nstring\n \n`json:\nbody\n`\n\n \nAuthor\n \nstring\n \n`json:\nauthor\n`\n\n\n}\n\n\n\n// MarshalEditor writes a buffer of html to edit a Post within the CMS\n\n\n// and implements editor.Editable\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nMarshalEditor\n()\n \n([]\nbyte\n,\n \nerror\n)\n \n{\n\n \nview\n,\n \nerr\n \n:=\n \neditor\n.\nForm\n(\np\n,\n\n \n// Take note that the first argument to these Input-like functions\n\n \n// is the string version of each Post field, and must follow\n\n \n// this pattern for auto-decoding and auto-encoding reasons:\n\n \neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nInput\n(\nTitle\n,\n \np\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nTitle\n,\n\n \ntype\n:\n \ntext\n,\n\n \nplaceholder\n:\n \nEnter the Title here\n,\n\n \n}),\n\n \n},\n\n \neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nRichtext\n(\nBody\n,\n \np\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nBody\n,\n\n \nplaceholder\n:\n \nEnter the Body here\n,\n\n \n}),\n\n \n},\n\n \neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nInput\n(\nAuthor\n,\n \np\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nAuthor\n,\n\n \ntype\n:\n \ntext\n,\n\n \nplaceholder\n:\n \nEnter the Author here\n,\n\n \n}),\n\n \n},\n\n \n)\n\n\n \nif\n \nerr\n \n!=\n \nnil\n \n{\n\n \nreturn\n \nnil\n,\n \nfmt\n.\nErrorf\n(\nFailed to render Post editor view: %s\n,\n \nerr\n.\nError\n())\n\n \n}\n\n\n \nreturn\n \nview\n,\n \nnil\n\n\n}\n\n\n\nfunc\n \ninit\n()\n \n{\n\n \nitem\n.\nTypes\n[\nPost\n]\n \n=\n \nfunc\n()\n \ninterface\n{}\n \n{\n \nreturn\n \nnew\n(\nPost\n)\n \n}\n\n\n}\n\n\n\n\n\n\nThe code above is the baseline amount required to manage content for the \nPost\n type from within the CMS. See \nExtending Content\n for information about how to add more functionality to your Content types. \n\n\nAll content managed by the CMS and exposed via the API is considered an \"item\", and thus should embed the \nitem.Item\n 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 \ngenerate\n command via Ponzu CLI will embed Item. \n\n\nRelated packages\n\n\nThe \nitem\n package has a number of useful interfaces, which make it simple to add functionality to all content types and other types that embed Item. \n\n\nThe \neditor\n package has the Editable interface, which allows types to create an editor for their fields within the CMS. Additionally, there is a helper function \neditor.Form\n which simplifies defining the editor's input layout and input types using \neditor.Input\n and various other functions to make HTML input elements like Select, Checkbox, Richtext, Textarea and more.\n\n\nThe \napi\n package has interfaces including \napi.Createable\n and \napi.Mergeable\n which make it trivial to accept and approve or reject content submitted from 3rd parties (POST from HTML forms, mobile clients, etc).", + "title": "An Overview" + }, + { + "location": "/Content/An-Overview/#generating-content-types", + "text": "To generate content types and boilerplate code, use the Ponzu CLI generate command as such: $ 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: package content import ( \n fmt \n\n github.com/ponzu-cms/ponzu/management/editor \n github.com/ponzu-cms/ponzu/system/item ) type Post struct { \n item . Item \n\n Title string `json: title ` \n Body string `json: body ` \n 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 ) { \n view , err := editor . Form ( p , \n // Take note that the first argument to these Input-like functions \n // is the string version of each Post field, and must follow \n // this pattern for auto-decoding and auto-encoding reasons: \n editor . Field { \n View : editor . Input ( Title , p , map [ string ] string { \n label : Title , \n type : text , \n placeholder : Enter the Title here , \n }), \n }, \n editor . Field { \n View : editor . Richtext ( Body , p , map [ string ] string { \n label : Body , \n placeholder : Enter the Body here , \n }), \n }, \n editor . Field { \n View : editor . Input ( Author , p , map [ string ] string { \n label : Author , \n type : text , \n placeholder : Enter the Author here , \n }), \n }, \n ) \n\n if err != nil { \n return nil , fmt . Errorf ( Failed to render Post editor view: %s , err . Error ()) \n } \n\n return view , nil } func init () { \n 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 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.", + "title": "Generating Content types" + }, + { + "location": "/Content/An-Overview/#related-packages", + "text": "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).", + "title": "Related packages" + }, + { + "location": "/Content/Extending-Content/", + "text": "Extending your Content types with more features and functionality within the system\nis done by implementing the various built-in interfaces provided by Ponzu. To learn \nmore about interfaces, see \nA Tour of Go - Interfaces\n.\n\n\nIt is also common to add more advanced functionality to Content types using Addons. Refer to the \nAddon documentation\n for more information about how to use and create Ponzu Addons.\n\n\nItem Interfaces\n\n\nAll Content types which embed an \nitem.Item\n will implicitly \nimplement\n its many\ninterfaces. In Ponzu, the following interfaces are exported from the \nsystem/item\n\npackage and have a default implementation which can be overridden to change your\ncontent types' functionality within the system.\n\n\n\n\nitem.Pushable\n\n\nitem.Hideable\n\n\nitem.Omittable\n\n\nitem.Hookable\n\n\nitem.Identifiable\n\n\nitem.Sortable\n\n\nitem.Sluggable\n\n\n\n\nAPI Interfaces\n\n\nTo enable 3rd-party clients to interact with your Content types, you can extend your types with the API interfaces:\n\n\n\n\napi.Createable\n\n\napi.Updateable\n\n\napi.Deleteable\n\n\napi.Trustable\n\n\n\n\nEditor Interfaces\n\n\nTo manage how content is edited and handled in the CMS, use the following Editor interfaces:\n\n\n\n\neditor.Editable\n\n\neditor.Mergeable\n\n\n\n\nSearch Interfaces\n\n\nTo enable and customize full-text search on your content types, use the following interfaces:\n\n\n\n\nsearch.Searchable", + "title": "Extending Content" + }, + { + "location": "/Content/Extending-Content/#item-interfaces", + "text": "All Content types which embed an item.Item will implicitly implement its many\ninterfaces. In Ponzu, the following interfaces are exported from the system/item \npackage and have a default implementation which can be overridden to change your\ncontent types' functionality within the system. item.Pushable item.Hideable item.Omittable item.Hookable item.Identifiable item.Sortable item.Sluggable", + "title": "Item Interfaces" + }, + { + "location": "/Content/Extending-Content/#api-interfaces", + "text": "To enable 3rd-party clients to interact with your Content types, you can extend your types with the API interfaces: api.Createable api.Updateable api.Deleteable api.Trustable", + "title": "API Interfaces" + }, + { + "location": "/Content/Extending-Content/#editor-interfaces", + "text": "To manage how content is edited and handled in the CMS, use the following Editor interfaces: editor.Editable editor.Mergeable", + "title": "Editor Interfaces" + }, + { + "location": "/Content/Extending-Content/#search-interfaces", + "text": "To enable and customize full-text search on your content types, use the following interfaces: search.Searchable", + "title": "Search Interfaces" + }, + { + "location": "/Form-Fields/HTML-Inputs/", + "text": "Ponzu provides a number of helpful HTML Inputs to create forms which CMS admins\nuse to manage content. The input functions are typically used inside a Content\ntype's \nMarshalEditor()\n func from within an \neditor.Form()\n - for example:\n\n\n// MarshalEditor writes a buffer of html to edit a Post within the CMS\n\n\n// and implements editor.Editable\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nMarshalEditor\n()\n \n([]\nbyte\n,\n \nerror\n)\n \n{\n\n \nview\n,\n \nerr\n \n:=\n \neditor\n.\nForm\n(\np\n,\n\n \neditor\n.\nField\n{\n \n// \n- editor.Fields contain input-like funcs\n\n \nView\n:\n \neditor\n.\nInput\n(\nTitle\n,\n \np\n,\n \nmap\n[\nstring\n]\nstring\n{\n \n// \n- makes a text input\n\n \nlabel\n:\n \nTitle\n,\n\n \ntype\n:\n \ntext\n,\n\n \nplaceholder\n:\n \nEnter the Title here\n,\n\n \n}),\n\n \n},\n\n \neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nRichtext\n(\nBody\n,\n \np\n,\n \nmap\n[\nstring\n]\nstring\n{\n \n// \n- makes a WYSIWIG editor\n\n \nlabel\n:\n \nBody\n,\n\n \nplaceholder\n:\n \nEnter the Body here\n,\n\n \n}),\n\n \n},\n\n \neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nInput\n(\nAuthor\n,\n \np\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nAuthor\n,\n\n \ntype\n:\n \ntext\n,\n\n \nplaceholder\n:\n \nEnter the Author here\n,\n\n \n}),\n\n \n},\n\n \n)\n\n\n \nif\n \nerr\n \n!=\n \nnil\n \n{\n\n \nreturn\n \nnil\n,\n \nfmt\n.\nErrorf\n(\nFailed to render Post editor view: %s\n,\n \nerr\n.\nError\n())\n\n \n}\n\n\n \nreturn\n \nview\n,\n \nnil\n\n\n}\n\n\n\n\n\n\n\n\nField Input Functions\n\n\nThere are many of these input-like HTML view funcs exported from Ponzu's\n\nmanagement/editor\n package. Below is a list of the built-in options:\n\n\neditor.Input\n\n\nThe \neditor.Input\n function produces a standard text input.\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nInput\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n,\n \noptions\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nInput\n(\nTitle\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nTitle\n,\n\n \ntype\n:\n \ntext\n,\n\n \nplaceholder\n:\n \nEnter the Title here\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\neditor.InputRepeater\n\n\nThe \neditor.InputRepeater\n function applies a controller UI to the \neditor.Input\n \nview so any arbitrary number of inputs can be added for your field.\n\n\n\n\nUsing Repeaters\n\n\nWhen using the \neditor.InputRepeater\n make sure it's corresponding field is a \nslice \n[]T\n\ntype. You will experience errors if it is not.\n\n\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nInputRepeater\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n,\n \noptions\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nInputRepeater\n(\nTitle\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nTitles\n,\n\n \ntype\n:\n \ntext\n,\n\n \nplaceholder\n:\n \nEnter the Title here\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\neditor.Checkbox\n\n\nThe \neditor.Checkbox\n function returns any number of checkboxes in a collection,\ndefined by the value:name map of options.\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nCheckbox\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n,\n \noptions\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nCheckbox\n(\nOptions\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nOptions\n,\n\n \n},\n \nmap\n[\nstring\n]\nstring\n{\n\n \n// \nvalue\n: \nDisplay Name\n,\n\n \n1\n:\n \nFirst\n,\n\n \n2\n:\n \nSecond\n,\n\n \n3\n:\n \nThird\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\neditor.Richtext\n\n\nThe \neditor.Richetext\n function displays an HTML5 rich text / WYSYWIG editor which\nsupports text formatting and styling, images, quotes, arbitrary HTML, and more. \n\n\nThe rich text editor is a modified version of \nSummernote\n \nusing a theme called \nMaterialNote\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nRichtext\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nRichtext\n(\nOpinion\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nRich Text Editor\n,\n\n \nplaceholder\n:\n \nEnter the Opinion here\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\neditor.Tags\n\n\nThe \neditor.Tags\n function returns a container input element for lists of arbitrary\nbits of information.\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nTags\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nTags\n(\nCategory\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nTags\n,\n\n \nplaceholder\n:\n \n+Category\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\neditor.File\n\n\nThe \neditor.File\n function returns an HTML file upload element, which saves files\ninto the \n/uploads\n directory, and can be viewed from the \"Uploads\" section in the\nAdmin dashboard. See also the \nFile Metadata API\n.\n\n\n\n\nField Type\n\n\nWhen using the \neditor.File\n function, its corresponding field type must be\na \nstring\n, as files will be stored as URL paths in the database. \n\n\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nFile\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nFile\n(\nPhoto\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nFile Upload\n,\n\n \nplaceholder\n:\n \nUpload the Photo here\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\neditor.FileRepeater\n\n\nThe \neditor.FileRepeater\n function applies a controller UI to the \neditor.File\n \nview so any arbitrary number of uploads can be added for your field.\n\n\n\n\nUsing Repeaters\n\n\nWhen using the \neditor.FileRepeater\n make sure it's corresponding field is a \nslice \n[]string\n\ntype. You will experience errors if it is not.\n\n\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nFileRepeater\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nFileRepeater\n(\nPhoto\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nFile Upload Repeater\n,\n\n \nplaceholder\n:\n \nUpload the Photo here\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\neditor.Select\n\n\nThe \neditor.Select\n function returns a single HTML select input with options\nas defined in the \noptions map[string]string\n parameter of the function call.\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nfunc\n \nSelect\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n,\n \noptions\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nSelect\n(\nRating\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nSelect Dropdown\n,\n\n \n},\n \nmap\n[\nstring\n]\nstring\n{\n\n \n// \nvalue\n: \nDisplay Name\n,\n\n \nG\n:\n \nG\n,\n\n \nPG\n:\n \nPG\n,\n\n \nPG-13\n:\n \nPG-13\n,\n\n \nR\n:\n \nR\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\neditor.SelectRepeater\n\n\nThe \neditor.SelectRepeater\n function applies a controller UI to the \neditor.Select\n \nview so any arbitrary number of dropdowns can be added for your field.\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nfunc\n \nSelectRepeater\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n,\n \noptions\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nSelectRepeater\n(\nRating\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nSelect Dropdown Repeater\n,\n\n \n},\n \nmap\n[\nstring\n]\nstring\n{\n\n \n// \nvalue\n: \nDisplay Name\n,\n\n \nG\n:\n \nG\n,\n\n \nPG\n:\n \nPG\n,\n\n \nPG-13\n:\n \nPG-13\n,\n\n \nR\n:\n \nR\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\neditor.Textarea\n\n\nThe \neditor.Textarea\n function returns an HTML textarea input to add unstyled text\nblocks. Newlines in the textarea are preserved.\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nTextarea\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n \nmap\n[\nstring\n]\nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nTextarea\n(\nReadme\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nTextarea\n,\n\n \nplaceholder\n:\n \nEnter the Readme here\n,\n\n \n}),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\nData References\n\n\nIt is common to want to keep a reference from one Content type to another. To do\nthis in Ponzu, use the \nbosssauce/reference\n \npackage. It comes pre-installed with Ponzu as an \n\"Addon\"\n.\n\n\nreference.Select\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nfunc\n \nSelect\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n \nmap\n[\nstring\n]\nstring\n,\n \ncontentType\n,\n \ntmplString\n \nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \nreference\n.\nSelect\n(\nDirectedBy\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nSelect Dropdown\n,\n\n \n},\n \nDirector\n,\n \n`{{.last-name}}, {{.first_name}}`\n),\n\n\n},\n\n\n...\n\n\n\n\n\n\n\n\nreference.SelectRepeater\n\n\nScreenshot\n\n\n\n\nFunction Signature\n\n\nfunc\n \nSelectRepeater\n(\nfieldName\n \nstring\n,\n \np\n \ninterface\n{},\n \nattrs\n \nmap\n[\nstring\n]\nstring\n,\n \ncontentType\n,\n \ntmplString\n \nstring\n)\n \n[]\nbyte\n\n\n\n\n\n\nExample\n\n\n...\n\n\neditor\n.\nField\n{\n\n \nView\n:\n \nreference\n.\nSelectRepeater\n(\nPlacesFilmed\n,\n \ns\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nSelect Dropdown Repeater\n,\n\n \n},\n \nLocation\n,\n \n`{{.name}}, {{.region}}`\n),\n\n\n},\n\n\n...", + "title": "HTML Inputs" + }, + { + "location": "/Form-Fields/HTML-Inputs/#field-input-functions", + "text": "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:", + "title": "Field Input Functions" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editorinput", + "text": "The editor.Input function produces a standard text input.", + "title": "editor.Input" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature", + "text": "Input ( fieldName string , p interface {}, attrs , options map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example", + "text": "... editor . Field { \n View : editor . Input ( Title , s , map [ string ] string { \n label : Title , \n type : text , \n placeholder : Enter the Title here , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editorinputrepeater", + "text": "The editor.InputRepeater function applies a controller UI to the editor.Input \nview so any arbitrary number of inputs can be added for your field. Using Repeaters When using the editor.InputRepeater make sure it's corresponding field is a slice []T \ntype. You will experience errors if it is not.", + "title": "editor.InputRepeater" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_1", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_1", + "text": "InputRepeater ( fieldName string , p interface {}, attrs , options map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_1", + "text": "... editor . Field { \n View : editor . InputRepeater ( Title , s , map [ string ] string { \n label : Titles , \n type : text , \n placeholder : Enter the Title here , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editorcheckbox", + "text": "The editor.Checkbox function returns any number of checkboxes in a collection,\ndefined by the value:name map of options.", + "title": "editor.Checkbox" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_2", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_2", + "text": "Checkbox ( fieldName string , p interface {}, attrs , options map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_2", + "text": "... editor . Field { \n View : editor . Checkbox ( Options , s , map [ string ] string { \n label : Options , \n }, map [ string ] string { \n // value : Display Name , \n 1 : First , \n 2 : Second , \n 3 : Third , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editorrichtext", + "text": "The editor.Richetext function displays an HTML5 rich text / WYSYWIG editor which\nsupports text formatting and styling, images, quotes, arbitrary HTML, and more. The rich text editor is a modified version of Summernote \nusing a theme called MaterialNote", + "title": "editor.Richtext" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_3", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_3", + "text": "Richtext ( fieldName string , p interface {}, attrs map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_3", + "text": "... editor . Field { \n View : editor . Richtext ( Opinion , s , map [ string ] string { \n label : Rich Text Editor , \n placeholder : Enter the Opinion here , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editortags", + "text": "The editor.Tags function returns a container input element for lists of arbitrary\nbits of information.", + "title": "editor.Tags" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_4", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_4", + "text": "Tags ( fieldName string , p interface {}, attrs map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_4", + "text": "... editor . Field { \n View : editor . Tags ( Category , s , map [ string ] string { \n label : Tags , \n placeholder : +Category , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editorfile", + "text": "The editor.File function returns an HTML file upload element, which saves files\ninto the /uploads directory, and can be viewed from the \"Uploads\" section in the\nAdmin dashboard. See also the File Metadata API . Field Type When using the editor.File function, its corresponding field type must be\na string , as files will be stored as URL paths in the database.", + "title": "editor.File" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_5", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_5", + "text": "File ( fieldName string , p interface {}, attrs map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_5", + "text": "... editor . Field { \n View : editor . File ( Photo , s , map [ string ] string { \n label : File Upload , \n placeholder : Upload the Photo here , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editorfilerepeater", + "text": "The editor.FileRepeater function applies a controller UI to the editor.File \nview so any arbitrary number of uploads can be added for your field. Using Repeaters When using the editor.FileRepeater make sure it's corresponding field is a slice []string \ntype. You will experience errors if it is not.", + "title": "editor.FileRepeater" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_6", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_6", + "text": "FileRepeater ( fieldName string , p interface {}, attrs map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_6", + "text": "... editor . Field { \n View : editor . FileRepeater ( Photo , s , map [ string ] string { \n label : File Upload Repeater , \n placeholder : Upload the Photo here , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editorselect", + "text": "The editor.Select function returns a single HTML select input with options\nas defined in the options map[string]string parameter of the function call.", + "title": "editor.Select" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_7", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_7", + "text": "func Select ( fieldName string , p interface {}, attrs , options map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_7", + "text": "... editor . Field { \n View : editor . Select ( Rating , s , map [ string ] string { \n label : Select Dropdown , \n }, map [ string ] string { \n // value : Display Name , \n G : G , \n PG : PG , \n PG-13 : PG-13 , \n R : R , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editorselectrepeater", + "text": "The editor.SelectRepeater function applies a controller UI to the editor.Select \nview so any arbitrary number of dropdowns can be added for your field.", + "title": "editor.SelectRepeater" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_8", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_8", + "text": "func SelectRepeater ( fieldName string , p interface {}, attrs , options map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_8", + "text": "... editor . Field { \n View : editor . SelectRepeater ( Rating , s , map [ string ] string { \n label : Select Dropdown Repeater , \n }, map [ string ] string { \n // value : Display Name , \n G : G , \n PG : PG , \n PG-13 : PG-13 , \n R : R , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#editortextarea", + "text": "The editor.Textarea function returns an HTML textarea input to add unstyled text\nblocks. Newlines in the textarea are preserved.", + "title": "editor.Textarea" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_9", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_9", + "text": "Textarea ( fieldName string , p interface {}, attrs map [ string ] string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_9", + "text": "... editor . Field { \n View : editor . Textarea ( Readme , s , map [ string ] string { \n label : Textarea , \n placeholder : Enter the Readme here , \n }), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#data-references", + "text": "It is common to want to keep a reference from one Content type to another. To do\nthis in Ponzu, use the bosssauce/reference \npackage. It comes pre-installed with Ponzu as an \"Addon\" .", + "title": "Data References" + }, + { + "location": "/Form-Fields/HTML-Inputs/#referenceselect", + "text": "", + "title": "reference.Select" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_10", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_10", + "text": "func Select ( fieldName string , p interface {}, attrs map [ string ] string , contentType , tmplString string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_10", + "text": "... editor . Field { \n View : reference . Select ( DirectedBy , s , map [ string ] string { \n label : Select Dropdown , \n }, Director , `{{.last-name}}, {{.first_name}}` ), }, ...", + "title": "Example" + }, + { + "location": "/Form-Fields/HTML-Inputs/#referenceselectrepeater", + "text": "", + "title": "reference.SelectRepeater" + }, + { + "location": "/Form-Fields/HTML-Inputs/#screenshot_11", + "text": "", + "title": "Screenshot" + }, + { + "location": "/Form-Fields/HTML-Inputs/#function-signature_11", + "text": "func SelectRepeater ( fieldName string , p interface {}, attrs map [ string ] string , contentType , tmplString string ) [] byte", + "title": "Function Signature" + }, + { + "location": "/Form-Fields/HTML-Inputs/#example_11", + "text": "... editor . Field { \n View : reference . SelectRepeater ( PlacesFilmed , s , map [ string ] string { \n label : Select Dropdown Repeater , \n }, Location , `{{.name}}, {{.region}}` ), }, ...", + "title": "Example" + }, + { + "location": "/HTTP-APIs/Content/", + "text": "Ponzu provides a read \n write HTTP API to access and interact with content on a\nsystem. By default, write access (including create, update and delete) and search \nare disabled. See the section on Ponzu's \nAPI Interfaces\n to learn\nmore about how to enable these endpoints.\n\n\n\n\nEndpoints\n\n\nGet Content by Type\n\n\nGET\n \n/api/content?type=\nType\nid=\nID\n\n\nSample Response\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nuuid\n:\n \n024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n\n \nid\n:\n \n6\n,\n\n \nslug\n:\n \nitem-id-024a5797-e064-4ee0-abe3-415cb6d3ed18\n \n// customizable\n\n \ntimestamp\n:\n \n1493926453826\n,\n \n// milliseconds since Unix epoch\n\n \nupdated\n:\n \n1493926453826\n,\n\n \n// your content data...,\n\n \n}\n\n \n]\n\n\n}\n\n\n\n\n\n\n\n\nGet Contents by Type\n\n\nGET\n \n/api/contents?type=\nType\n\n\n\n\noptional params:\n\n\norder\n (string: ASC / DESC, default: DESC)\n\n\ncount\n (int: -1 - N, default: 10, -1 returns all)\n\n\noffset\n (int: 0 - N, default: 0)\n\n\n\n\n\n\n\n\nSample Response\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nuuid\n:\n \n024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n\n \nid\n:\n \n6\n,\n\n \nslug\n:\n \nitem-id-024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n \n// customizable\n\n \ntimestamp\n:\n \n1493926453826\n,\n \n// milliseconds since Unix epoch\n\n \nupdated\n:\n \n1493926453826\n,\n\n \n// your content data...,\n\n \n},\n\n \n{\n\n \nuuid\n:\n \n5a9177c7-634d-4fb1-88a6-ef6c45de797c\n,\n\n \nid\n:\n \n7\n,\n\n \nslug\n:\n \nitem-id-5a9177c7-634d-4fb1-88a6-ef6c45de797c\n,\n \n// customizable\n\n \ntimestamp\n:\n \n1493926453826\n,\n \n// milliseconds since Unix epoch\n\n \nupdated\n:\n \n1493926453826\n,\n\n \n// your content data...,\n\n \n},\n\n \n// more objects...\n\n \n]\n\n\n}\n\n\n\n\n\n\n\n\nGet Content by Slug\n\n\nGET\n \n/api/content?slug=\nSlug\n\n\nSample Response\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nuuid\n:\n \n024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n\n \nid\n:\n \n6\n,\n\n \nslug\n:\n \nitem-id-024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n \n// customizable\n\n \ntimestamp\n:\n \n1493926453826\n,\n \n// milliseconds since Unix epoch\n\n \nupdated\n:\n \n1493926453826\n,\n\n \n// your content data...,\n\n \n}\n\n \n]\n\n\n}\n\n\n\n\n\n\n\n\nNew Content\n\n\nPOST\n \n/api/content/create?type=\nType\n\n\n\n\nRequest Data Encoding\n\n\nRequest must be \nmultipart/form-data\n encoded. If not, a \n400 Bad Request\n \nResponse will be returned.\n\n\n\n\nSample Response\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nid\n:\n \n6\n,\n \n// will be omitted if status is pending\n\n \ntype\n:\n \nReview\n,\n\n \nstatus\n:\n \npublic\n\n \n}\n\n \n]\n\n\n}\n\n\n\n\n\n\n\n\nUpdate Content\n\n\nPOST\n \n/api/content/update?type=\nType\nid=\nid\n\n\n\n\nRequest Data Encoding\n\n\nRequest must be \nmultipart/form-data\n encoded. If not, a \n400 Bad Request\n \nResponse will be returned.\n\n\n\n\nSample Response\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nid\n:\n \n6\n,\n\n \ntype\n:\n \nReview\n,\n\n \nstatus\n:\n \npublic\n\n \n}\n\n \n]\n\n\n}\n\n\n\n\n\n\n\n\nDelete Content\n\n\nPOST\n \n/api/content/delete?type=\nType\nid=\nid\n\n\n\n\nRequest Data Encoding\n\n\nRequest must be \nmultipart/form-data\n encoded. If not, a \n400 Bad Request\n \nResponse will be returned.\n\n\n\n\nSample Response\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nid\n:\n \n6\n,\n\n \ntype\n:\n \nReview\n,\n\n \nstatus\n:\n \ndeleted\n\n \n}\n\n \n]\n\n\n}\n\n\n\n\n\n\n\n\nAdditional Information\n\n\nAll 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.\n\n\nResponse Headers\n\n\nThe following headers are common across all Ponzu API responses. Some of them can be modified\nin the \nsystem configuration\n while your system is running.\n\n\nHTTP/1.1\n\n\nHTTP/1.1 200 OK\nAccess-Control-Allow-Headers: Accept, Authorization, Content-Type\nAccess-Control-Allow-Origin: *\nCache-Control: max-age=2592000, public\nContent-Encoding: gzip\nContent-Type: application/json\nEtag: MTQ5Mzk0NTYzNQ==\nVary: Accept-Encoding\nDate: Fri, 05 May 2017 01:15:49 GMT\nContent-Length: 199\n\n\n\n\n\nHTTP/2\n\n\naccess-control-allow-headers: Accept, Authorization, Content-Type\naccess-control-allow-origin: *\ncache-control: max-age=2592000, public\ncontent-encoding: gzip\ncontent-length: 199\ncontent-type: application/json\ndate: Fri, 05 May 2017 01:38:11 GMT\netag: MTQ5Mzk0ODI4MA==\nstatus: 200\nvary: Accept-Encoding\n\n\n\n\n\nHelpful links\n\n\nTypewriter\n\nGenerate \n sync front-end data structures from Ponzu content types. (\nPonzu example\n)", + "title": "Content" + }, + { + "location": "/HTTP-APIs/Content/#endpoints", + "text": "", + "title": "Endpoints" + }, + { + "location": "/HTTP-APIs/Content/#get-content-by-type", + "text": "GET /api/content?type= Type id= ID", + "title": "Get Content by Type" + }, + { + "location": "/HTTP-APIs/Content/#sample-response", + "text": "{ \n data : [ \n { \n uuid : 024a5797-e064-4ee0-abe3-415cb6d3ed18 , \n id : 6 , \n slug : item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18 // customizable \n timestamp : 1493926453826 , // milliseconds since Unix epoch \n updated : 1493926453826 , \n // your content data..., \n } \n ] }", + "title": "Sample Response" + }, + { + "location": "/HTTP-APIs/Content/#get-contents-by-type", + "text": "GET /api/contents?type= Type optional params: order (string: ASC / DESC, default: DESC) count (int: -1 - N, default: 10, -1 returns all) offset (int: 0 - N, default: 0)", + "title": "Get Contents by Type" + }, + { + "location": "/HTTP-APIs/Content/#sample-response_1", + "text": "{ \n data : [ \n { \n uuid : 024a5797-e064-4ee0-abe3-415cb6d3ed18 , \n id : 6 , \n slug : item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18 , // customizable \n timestamp : 1493926453826 , // milliseconds since Unix epoch \n updated : 1493926453826 , \n // your content data..., \n }, \n { \n uuid : 5a9177c7-634d-4fb1-88a6-ef6c45de797c , \n id : 7 , \n slug : item-id-5a9177c7-634d-4fb1-88a6-ef6c45de797c , // customizable \n timestamp : 1493926453826 , // milliseconds since Unix epoch \n updated : 1493926453826 , \n // your content data..., \n }, \n // more objects... \n ] }", + "title": "Sample Response" + }, + { + "location": "/HTTP-APIs/Content/#get-content-by-slug", + "text": "GET /api/content?slug= Slug", + "title": "Get Content by Slug" + }, + { + "location": "/HTTP-APIs/Content/#sample-response_2", + "text": "{ \n data : [ \n { \n uuid : 024a5797-e064-4ee0-abe3-415cb6d3ed18 , \n id : 6 , \n slug : item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18 , // customizable \n timestamp : 1493926453826 , // milliseconds since Unix epoch \n updated : 1493926453826 , \n // your content data..., \n } \n ] }", + "title": "Sample Response" + }, + { + "location": "/HTTP-APIs/Content/#new-content", + "text": "POST /api/content/create?type= Type Request Data Encoding Request must be multipart/form-data encoded. If not, a 400 Bad Request \nResponse will be returned.", + "title": "New Content" + }, + { + "location": "/HTTP-APIs/Content/#sample-response_3", + "text": "{ \n data : [ \n { \n id : 6 , // will be omitted if status is pending \n type : Review , \n status : public \n } \n ] }", + "title": "Sample Response" + }, + { + "location": "/HTTP-APIs/Content/#update-content", + "text": "POST /api/content/update?type= Type id= id Request Data Encoding Request must be multipart/form-data encoded. If not, a 400 Bad Request \nResponse will be returned.", + "title": "Update Content" + }, + { + "location": "/HTTP-APIs/Content/#sample-response_4", + "text": "{ \n data : [ \n { \n id : 6 , \n type : Review , \n status : public \n } \n ] }", + "title": "Sample Response" + }, + { + "location": "/HTTP-APIs/Content/#delete-content", + "text": "POST /api/content/delete?type= Type id= id Request Data Encoding Request must be multipart/form-data encoded. If not, a 400 Bad Request \nResponse will be returned.", + "title": "Delete Content" + }, + { + "location": "/HTTP-APIs/Content/#sample-response_5", + "text": "{ \n data : [ \n { \n id : 6 , \n type : Review , \n status : deleted \n } \n ] }", + "title": "Sample Response" + }, + { + "location": "/HTTP-APIs/Content/#additional-information", + "text": "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.", + "title": "Additional Information" + }, + { + "location": "/HTTP-APIs/Content/#response-headers", + "text": "The following headers are common across all Ponzu API responses. Some of them can be modified\nin the system configuration while your system is running.", + "title": "Response Headers" + }, + { + "location": "/HTTP-APIs/Content/#http11", + "text": "HTTP/1.1 200 OK\nAccess-Control-Allow-Headers: Accept, Authorization, Content-Type\nAccess-Control-Allow-Origin: *\nCache-Control: max-age=2592000, public\nContent-Encoding: gzip\nContent-Type: application/json\nEtag: MTQ5Mzk0NTYzNQ==\nVary: Accept-Encoding\nDate: Fri, 05 May 2017 01:15:49 GMT\nContent-Length: 199", + "title": "HTTP/1.1" + }, + { + "location": "/HTTP-APIs/Content/#http2", + "text": "access-control-allow-headers: Accept, Authorization, Content-Type\naccess-control-allow-origin: *\ncache-control: max-age=2592000, public\ncontent-encoding: gzip\ncontent-length: 199\ncontent-type: application/json\ndate: Fri, 05 May 2017 01:38:11 GMT\netag: MTQ5Mzk0ODI4MA==\nstatus: 200\nvary: Accept-Encoding", + "title": "HTTP/2" + }, + { + "location": "/HTTP-APIs/Content/#helpful-links", + "text": "Typewriter \nGenerate sync front-end data structures from Ponzu content types. ( Ponzu example )", + "title": "Helpful links" + }, + { + "location": "/HTTP-APIs/File-Metadata/", + "text": "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. \n\n\n\n\nEndpoints\n\n\nGet File by Slug (single item)\n\n\nGET\n \n/api/uploads?slug=\nSlug\n\n\nSample Response\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nuuid\n:\n \n024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n\n \nid\n:\n \n6\n,\n\n \nslug\n:\n \nfilename.jpg\n,\n\n \ntimestamp\n:\n \n1493926453826\n,\n \n// milliseconds since Unix epoch\n\n \nupdated\n:\n \n1493926453826\n,\n\n \nname\n:\n \nfilename.jpg\n,\n\n \npath\n:\n \n/api/uploads/2017/05/filename.jpg\n,\n\n \ncontent_length\n:\n \n357557\n,\n\n \ncontent_type\n:\n \nimage/jpeg\n,\n\n \n}\n\n \n]\n\n\n}", + "title": "File Metadata" + }, + { + "location": "/HTTP-APIs/File-Metadata/#endpoints", + "text": "", + "title": "Endpoints" + }, + { + "location": "/HTTP-APIs/File-Metadata/#get-file-by-slug-single-item", + "text": "GET /api/uploads?slug= Slug", + "title": "Get File by Slug (single item)" + }, + { + "location": "/HTTP-APIs/File-Metadata/#sample-response", + "text": "{ \n data : [ \n { \n uuid : 024a5797-e064-4ee0-abe3-415cb6d3ed18 , \n id : 6 , \n slug : filename.jpg , \n timestamp : 1493926453826 , // milliseconds since Unix epoch \n updated : 1493926453826 , \n name : filename.jpg , \n path : /api/uploads/2017/05/filename.jpg , \n content_length : 357557 , \n content_type : image/jpeg , \n } \n ] }", + "title": "Sample Response" + }, + { + "location": "/HTTP-APIs/Search/", + "text": "Ponzu provides a read-only HTTP API to search the contents of your system's database. \nFull-text search is made possible by the use of \nBleve\n, \nwhich handles the indexing and querying. \n\n\n\n\nEndpoints\n\n\nSearch Content\n\n\nGET\n \n/api/search?type=\nType\nq=\nQuery String\n\n\n\n\nSearch must be enabled individually for each Content type\n\n\n\n\nSearch is not on by default to protect your data in case it shouldn't be indexed and published via the API.\n\n\nSearchMapping()\n is implemented with default mapping (ideal for 99% of use cases). \n\n\nTo enable search, add a \nIndexContent() bool\n method to your content type and return \ntrue\n (default implementation returns false).\n\n\n\n\n\n\n\n\n\n\nType\n must implement \ndb.Searchable\n\n\n\n\n\n\nSearch is currently limited to single \nType\n per request\n\n\n\n\n\n\nQuery String\n documentation here: \nBleve Docs - Query String\n\n\n\n\n\n\nSearch results are formatted exactly the same as standard Content API calls, so you don't need to change your client data model \n\n\n\n\n\n\nSearch handler will respect other interface implementations on your content, including: \n\n\n\n\nitem.Hideable\n\n\nitem.Omittable\n \n\n\nitem.Pushable\n \n(Note: only the first search result will be pushed)\n\n\n\n\n\n\n\n\nSample Response\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nuuid\n:\n \n024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n\n \nid\n:\n \n6\n,\n\n \nslug\n:\n \nitem-id-024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n \n// customizable\n\n \ntimestamp\n:\n \n1493926453826\n,\n \n// milliseconds since Unix epoch\n\n \nupdated\n:\n \n1493926453826\n,\n\n \n// your content data...,\n\n \n}\n\n \n]\n\n\n}", + "title": "Search" + }, + { + "location": "/HTTP-APIs/Search/#endpoints", + "text": "", + "title": "Endpoints" + }, + { + "location": "/HTTP-APIs/Search/#search-content", + "text": "GET /api/search?type= Type q= Query String 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). Type must implement db.Searchable Search is currently limited to single Type per request Query String documentation here: Bleve Docs - Query String 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 item.Omittable item.Pushable (Note: only the first search result will be pushed)", + "title": "Search Content" + }, + { + "location": "/HTTP-APIs/Search/#sample-response", + "text": "{ \n data : [ \n { \n uuid : 024a5797-e064-4ee0-abe3-415cb6d3ed18 , \n id : 6 , \n slug : item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18 , // customizable \n timestamp : 1493926453826 , // milliseconds since Unix epoch \n updated : 1493926453826 , \n // your content data..., \n } \n ] }", + "title": "Sample Response" + }, + { + "location": "/Interfaces/API/", + "text": "Ponzu provides a set of interfaces from the \nsystem/api\n package which enable \nricher interaction with your system from external clients. If you need to allow \n3rd-party apps to manage content, use the following interfaces.\n\n\nThe API interfaces adhere to a common function signature, expecting an \n\nhttp.ResponseWriter\n and \n*http.Request\n as arguments and returning an \nerror\n.\nThis provides Ponzu developers with full control over the request/response \nlife-cycle.\n\n\n\n\nInterfaces\n\n\napi.Createable\n\n\nExternalable enables 3rd-party clients (outside the CMS) to send content via a \n\nmultipart/form-data\n encoded \nPOST\n request to a specific endpoint: \n\n/api/content/create?type=\nType\n. When \napi.Createable\n is implemented, content \nwill be saved from the request in a \"Pending\" section which will is visible only \nwithin the CMS.\n\n\nTo work with \"Pending\" data, implement the \neditor.Mergeable\n\ninterface, which will add \"Approve\" and \"Reject\" buttons to your Content types'\neditor -- or implement \napi.Trustable\n to bypass \nthe \"Pending\" section altogether and become \"Public\" immediately. \n\n\nMethod Set\n\n\ntype\n \nCreateable\n \ninterface\n \n{\n\n \nCreate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nCreate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\n\n\napi.Updateable\n\n\nUpdateable enables 3rd-party clients (outside the CMS) to update existing content \nvia a \nmultipart/form-data\n encoded \nPOST\n request to a specific endpoint: \n\n/api/content/update?type=\nType\nid=\nid\n. Request validation should be employed \notherwise any client could change data in your database.\n\n\nMethod Set\n\n\ntype\n \nUpdateable\n \ninterface\n \n{\n\n \nUpdate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nUpdate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\n\n\napi.Deleteable\n\n\nUpdateable enables 3rd-party clients (outside the CMS) to delete existing content \nvia a \nmultipart/form-data\n encoded \nPOST\n request to a specific endpoint: \n\n/api/content/delete?type=\nType\nid=\nid\n. Request validation should be employed \notherwise any client could delete data from your database.\n\n\nMethod Set\n\n\ntype\n \nDeleteable\n \ninterface\n \n{\n\n \nDelete\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nDelete\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\n\n\napi.Trustable\n\n\nTrustable provides a way for submitted content (via \napi.Createable\n) to bypass \nthe \neditor.Mergeable\n step in which CMS end-users must manually click the \n\"Approve\" button in order for content to be put in the \"Public\" section and access \nvia the content API endpoints. \napi.Trustable\n has a single method: \nAutoApprove\n \nwhich will automatically approve content, bypassing the \"Pending\" section \naltogether.\n\n\ntype\n \nTrustable\n \ninterface\n \n{\n\n \nAutoApprove\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAutoApprove\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}", + "title": "API" + }, + { + "location": "/Interfaces/API/#interfaces", + "text": "", + "title": "Interfaces" + }, + { + "location": "/Interfaces/API/#apicreateable", + "text": "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= Type . When api.Createable is implemented, content \nwill be saved from the request in a \"Pending\" section which will is visible only \nwithin the CMS. To work with \"Pending\" data, implement the editor.Mergeable \ninterface, which will add \"Approve\" and \"Reject\" buttons to your Content types'\neditor -- or implement api.Trustable to bypass \nthe \"Pending\" section altogether and become \"Public\" immediately.", + "title": "api.Createable" + }, + { + "location": "/Interfaces/API/#method-set", + "text": "type Createable interface { \n Create ( http . ResponseWriter , * http . Request ) error }", + "title": "Method Set" + }, + { + "location": "/Interfaces/API/#implementation", + "text": "func ( p * Post ) Create ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "Implementation" + }, + { + "location": "/Interfaces/API/#apiupdateable", + "text": "Updateable enables 3rd-party clients (outside the CMS) to update existing content \nvia a multipart/form-data encoded POST request to a specific endpoint: /api/content/update?type= Type id= id . Request validation should be employed \notherwise any client could change data in your database.", + "title": "api.Updateable" + }, + { + "location": "/Interfaces/API/#method-set_1", + "text": "type Updateable interface { \n Update ( http . ResponseWriter , * http . Request ) error }", + "title": "Method Set" + }, + { + "location": "/Interfaces/API/#implementation_1", + "text": "func ( p * Post ) Update ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "Implementation" + }, + { + "location": "/Interfaces/API/#apideleteable", + "text": "Updateable enables 3rd-party clients (outside the CMS) to delete existing content \nvia a multipart/form-data encoded POST request to a specific endpoint: /api/content/delete?type= Type id= id . Request validation should be employed \notherwise any client could delete data from your database.", + "title": "api.Deleteable" + }, + { + "location": "/Interfaces/API/#method-set_2", + "text": "type Deleteable interface { \n Delete ( http . ResponseWriter , * http . Request ) error }", + "title": "Method Set" + }, + { + "location": "/Interfaces/API/#implementation_2", + "text": "func ( p * Post ) Delete ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "Implementation" + }, + { + "location": "/Interfaces/API/#apitrustable", + "text": "Trustable provides a way for submitted content (via api.Createable ) to bypass \nthe editor.Mergeable step in which CMS end-users must manually click the \n\"Approve\" button in order for content to be put in the \"Public\" section and access \nvia the content API endpoints. api.Trustable has a single method: AutoApprove \nwhich will automatically approve content, bypassing the \"Pending\" section \naltogether. type Trustable interface { \n AutoApprove ( http . ResponseWriter , * http . Request ) error }", + "title": "api.Trustable" + }, + { + "location": "/Interfaces/API/#implementation_3", + "text": "func ( p * Post ) AutoApprove ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "Implementation" + }, + { + "location": "/Interfaces/Editor/", + "text": "Ponzu provides a set of interfaces from the \nmanagement/editor\n package which \nextend the system's functionality and determine how content editors are rendered \nwithin the CMS.\n\n\n\n\nInterfaces\n\n\neditor.Editable\n\n\nEditable determines what \n[]bytes\n are rendered inside the editor page. Use \nEdtiable on anything inside your CMS that you want to provide configuration, editable \nfields, or any HTML/markup to display to an end-user.\n\n\n\n\nImplementing \neditor.Editable\n\n\nMost of the time, Ponzu developers generate the majority of this code using \nthe Ponzu CLI \ngenerate\n command\n.\n\n\n\n\nMethod Set\n\n\ntype\n \nEditable\n \ninterface\n \n{\n\n \nMarshalEditor\n()\n \n([]\nbyte\n,\n \nerror\n)\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nMarshalEditor\n()\n \n([]\nbyte\n,\n \nerror\n)\n \n{\n\n \n// The editor.Form func sets up a structured UI with default styles and form\n\n \n// elements based on the fields provided. Most often, Ponzu developers will\n\n \n// have the `$ ponzu generate` command generate the MarshalEditor func and \n\n \n// its internal form fields\n\n \nview\n,\n \nerr\n \n:=\n \neditor\n.\nForm\n(\np\n,\n\n \neditor\n.\nField\n{\n\n \nView\n:\n \neditor\n.\nInput\n(\nName\n,\n \np\n,\n \nmap\n[\nstring\n]\nstring\n{\n\n \nlabel\n:\n \nName\n,\n\n \ntype\n:\n \ntext\n,\n\n \nplaceholder\n:\n \nEnter the Name here\n,\n\n \n}),\n\n \n},\n\n \n)\n\n\n}\n\n\n\n\n\n\n\n\nMarshalEditor() \n View Rendering\n\n\nAlthough it is common to use the \neditor.Form\n and \neditor.Fields\n to structure your content editor inside \nMarshalEditor()\n, the method signature defines that its return value needs only to be \n[]byte, error\n. Keep in mind that you can return a \n[]byte\n of any raw HTML or other markup to be rendered in the editor view.\n\n\n\n\n\n\neditor.Mergeable\n\n\nMergable 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.\n\n\nMethod Set\n\n\ntype\n \nMergeable\n \ninterface\n \n{\n\n \nApprove\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n}\n\n\n\n\n\n\nExample\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nApprove\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}", + "title": "Editor" + }, + { + "location": "/Interfaces/Editor/#interfaces", + "text": "", + "title": "Interfaces" + }, + { + "location": "/Interfaces/Editor/#editoreditable", + "text": "Editable determines what []bytes are rendered inside the editor page. Use \nEdtiable on anything inside your CMS that you want to provide configuration, editable \nfields, or any HTML/markup to display to an end-user. Implementing editor.Editable Most of the time, Ponzu developers generate the majority of this code using \nthe Ponzu CLI generate command .", + "title": "editor.Editable" + }, + { + "location": "/Interfaces/Editor/#method-set", + "text": "type Editable interface { \n MarshalEditor () ([] byte , error ) }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Editor/#implementation", + "text": "func ( p * Post ) MarshalEditor () ([] byte , error ) { \n // The editor.Form func sets up a structured UI with default styles and form \n // elements based on the fields provided. Most often, Ponzu developers will \n // have the `$ ponzu generate` command generate the MarshalEditor func and \n // its internal form fields \n view , err := editor . Form ( p , \n editor . Field { \n View : editor . Input ( Name , p , map [ string ] string { \n label : Name , \n type : text , \n placeholder : Enter the Name here , \n }), \n }, \n ) } 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.", + "title": "Implementation" + }, + { + "location": "/Interfaces/Editor/#editormergeable", + "text": "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.", + "title": "editor.Mergeable" + }, + { + "location": "/Interfaces/Editor/#method-set_1", + "text": "type Mergeable interface { \n Approve ( http . ResponseWriter , * http . Request ) error }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Editor/#example", + "text": "func ( p * Post ) Approve ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "Example" + }, + { + "location": "/Interfaces/Format/", + "text": "Ponzu provides a set of interfaces from the \nmanagement/format\n package which \ndetermine how content data should be converted and formatted for exporting via\nthe Admin interface.\n\n\n\n\nInterfaces\n\n\nformat.CSVFormattable\n\n\nCSVFormattable controls if an \"Export\" button is added to the contents view for \na Content type in the CMS to export the data to CSV. If it is implemented, a\nbutton will be present beneath the \"New\" button per Content type. \n\n\nMethod Set\n\n\ntype\n \nCSVFormattable\n \ninterface\n \n{\n\n \nFormatCSV\n()\n \n[]\nstring\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nFormatCSV\n()\n \n[]\nstring\n \n{\n\n \n// []string contains the JSON struct tags generated for your Content type \n\n \n// implementing the interface\n\n \nreturn\n \n[]\nstring\n{\n\n \nid\n,\n\n \ntimestamp\n,\n\n \nslug\n,\n\n \ntitle\n,\n\n \nphotos\n,\n\n \nbody\n,\n\n \nwritten_by\n,\n\n \n}\n\n\n}\n\n\n\n\n\n\n\n\nFormatCSV() []string\n\n\nJust like other Ponzu content extension interfaces, like \nPush()\n, you will \nreturn the JSON struct tags for the fields you want exported to the CSV file. \nThese will also be the \"header\" row in the CSV file to give titles to the file\ncolumns. Keep in mind that all of item.Item's fields are available here as well.", + "title": "Format" + }, + { + "location": "/Interfaces/Format/#interfaces", + "text": "", + "title": "Interfaces" + }, + { + "location": "/Interfaces/Format/#formatcsvformattable", + "text": "CSVFormattable controls if an \"Export\" button is added to the contents view for \na Content type in the CMS to export the data to CSV. If it is implemented, a\nbutton will be present beneath the \"New\" button per Content type.", + "title": "format.CSVFormattable" + }, + { + "location": "/Interfaces/Format/#method-set", + "text": "type CSVFormattable interface { \n FormatCSV () [] string }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Format/#implementation", + "text": "func ( p * Post ) FormatCSV () [] string { \n // []string contains the JSON struct tags generated for your Content type \n // implementing the interface \n return [] string { \n id , \n timestamp , \n slug , \n title , \n photos , \n body , \n written_by , \n } } FormatCSV() []string Just like other Ponzu content extension interfaces, like Push() , you will \nreturn the JSON struct tags for the fields you want exported to the CSV file. \nThese will also be the \"header\" row in the CSV file to give titles to the file\ncolumns. Keep in mind that all of item.Item's fields are available here as well.", + "title": "Implementation" + }, + { + "location": "/Interfaces/Item/", + "text": "Ponzu provides a set of interfaces from the \nsystem/item\n package which extend \nthe functionality of the content in your system and how it interacts with other \ncomponents inside and outside of Ponzu. \n\n\n\n\nInterfaces\n\n\nitem.Pushable\n\n\nPushable, if \nHTTP/2 Server Push\n \nis supported by the client, can tell a handler which resources it would like to \nhave \"pushed\" preemptively to the client. This saves follow-on roundtrip requests \nfor other items which are referenced by the Pushable item. The \nPush\n method, the \nonly method in Pushable, must return a \n[]string\n containing the \njson\n field tags \nof the referenced items within the type.\n\n\nMethod Set\n\n\ntype\n \nPushable\n \ninterface\n \n{\n\n \n// the values contained in fields returned by Push must be URL paths\n\n \nPush\n()\n \n[]\nstring\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nThe \nPush\n method returns a \n[]string\n containing the \njson\n tag field names for\nwhich you want to have pushed to a supported client. The values for the field\nnames \nmust\n be URL paths, and cannot be from another origin.\n\n\ntype\n \nPost\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nHeaderPhoto\n \nstring\n \n`json:\nheader_photo\n`\n\n \nAuthor\n \nstring\n \n`json:\nauthor\n`\n \n// reference `/api/content/?type=Author\nid=2`\n\n \n// ...\n\n\n}\n\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nPush\n()\n \n[]\nstring\n \n{\n\n \nreturn\n \n[]\nstring\n{\n\n \nheader_photo\n,\n\n \nauthor\n,\n\n \n}\n\n\n}\n\n\n\n\n\n\n\n\nitem.Hideable\n\n\nHideable tells an API handler that data of this type shouldn\u2019t be exposed outside \nthe system. Hideable types cannot be used as references (relations in Content types).\nThe \nHide\n method, the only method in Hideable, takes an \nhttp.ResponseWriter, *http.Request\n \nand returns an \nerror\n. A special error in the \nitems\n package, \nErrAllowHiddenItem\n \ncan be returned as the error from Hide to instruct handlers to show hidden \ncontent in specific cases.\n\n\nMethod Set\n\n\ntype\n \nHideable\n \ninterface\n \n{\n\n \nHide\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nHide\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\n\n\nitem.Omittable\n\n\nOmittable tells a content API handler to keep certain fields from being exposed \nthrough the JSON response. It's single method, \nOmit\n takes no arguments and \nreturns a \n[]string\n which must be made up of the JSON struct tags for the type \ncontaining fields to be omitted.\n\n\nMethod Set\n\n\ntype\n \nOmittable\n \ninterface\n \n{\n\n \nOmit\n()\n \n[]\nstring\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\ntype\n \nPost\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nHeaderPhoto\n \nstring\n \n`json:\nheader_photo\n`\n\n \nAuthor\n \nstring\n \n`json:\nauthor\n`\n\n \n// ...\n\n\n}\n\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nOmit\n()\n \n[]\nstring\n \n{\n\n \nreturn\n \n[]\nstring\n{\n\n \nheader_photo\n,\n\n \nauthor\n,\n\n \n}\n\n\n}\n\n\n\n\n\n\n\n\nitem.Hookable\n\n\nHookable provides lifecycle hooks into the http handlers which manage Save, Delete,\nApprove, and Reject routines. All methods in its set take an \n\nhttp.ResponseWriter, *http.Request\n and return an \nerror\n.\n\n\nMethod Set\n\n\ntype\n \nHookable\n \ninterface\n \n{\n\n \nBeforeAPICreate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterAPICreate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeAPIUpdate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterAPIUpdate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeAPIDelete\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterAPIDelete\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeAdminCreate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterAdminCreate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeAdminUpdate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterAdminUpdate\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeAdminDelete\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterAdminDelete\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeSave\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterSave\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeDelete\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterDelete\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeApprove\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterApprove\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeReject\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterReject\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \n// Enable/Disable used exclusively for addons\n\n \nBeforeEnable\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterEnable\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n \nBeforeDisable\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n \nAfterDisable\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n\n\n}\n\n\n\n\n\n\nImplementations\n\n\nBeforeAPICreate\n\n\nBeforeAPICreate is called before an item is created via a 3rd-party client. If a \nnon-nil \nerror\n value is returned, the item will not be created/saved.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeAPICreate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterAPICreate\n\n\nAfterAPICreate is called after an item has been created via a 3rd-party client.\nAt this point, the item has been saved to the database. If a non-nil \nerror\n is\nreturned, it will respond to the client with an empty response, so be sure to \nuse the \nhttp.ResponseWriter\n from within your hook appropriately.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterAPICreate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeApprove\n\n\nBeforeApprove is called before an item is merged as \"Public\" from its prior \nstatus as \"Pending\". If a non-nil \nerror\n value is returned, the item will not be\nappproved, and an error message is displayed to the Admin. \n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeApprove\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterApprove\n\n\nAfterApprove is called after an item has been merged as \"Public\" from its prior\nstatus as \"Pending\". If a non-nil \nerror\n is returned, an error message is \ndisplayed to the Admin, however the item will already be irreversibly merged.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterApprove\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeReject\n\n\nBeforeReject is called before an item is rejected and deleted by default. To reject\nan item, but not delete it, return a non-nil \nerror\n from this hook - doing so \nwill allow the hook to do what you want it to do prior to the return, but the item\nwill remain in the \"Pending\" section.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeReject\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterReject\n\n\nAfterReject is called after an item is rejected and has been deleted.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterReject\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeSave\n\n\nBeforeSave is called before any CMS Admin or 3rd-party client triggers a save to \nthe database. This could be done by clicking the 'Save' button on a Content editor, \nor by a API call to Create or Update the Content item. By returning a non-nil \n\nerror\n value, the item will not be saved.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeSave\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterSave\n\n\nAfterSave is called after any CMS Admin or 3rd-party client triggers a save to \nthe database. This could be done by clicking the 'Save' button on a Content editor, \nor by a API call to Create or Update the Content item.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterSave\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeDelete\n\n\nBeforeDelete is called before any CMS Admin or 3rd-party client triggers a delete to \nthe database. This could be done by clicking the 'Delete' button on a Content editor, \nor by a API call to Delete the Content item. By returning a non-nil \nerror\n value,\nthe item will not be deleted.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeDelete\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterDelete\n\n\nAfterSave is called after any CMS Admin or 3rd-party client triggers a delete to \nthe database. This could be done by clicking the 'Delete' button on a Content editor, \nor by a API call to Delete the Content item.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterDelete\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeAPIDelete\n\n\nBeforeDelete is only called before a 3rd-party client triggers a delete to the \ndatabase. By returning a non-nil \nerror\n value, the item will not be deleted.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeAPIDelete\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterAPIDelete\n\n\nAfterAPIDelete is only called after a 3rd-party client triggers a delete to the \ndatabase.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterAPIDelete\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeAPIUpdate\n\n\nBeforeAPIUpdate is only called before a 3rd-party client triggers an update to \nthe database. By returning a non-nil \nerror\n value, the item will not be updated.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeAPIUpdate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterAPIUpdate\n\n\nAfterAPIUpdate is only called after a 3rd-party client triggers an update to \nthe database.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterAPIUpdate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeAdminCreate\n\n\nBeforeAdminCreate is only called before a CMS Admin creates a new Content item.\nIt is not called for subsequent saves to the item once it has been created and \nassigned an ID. By returning a non-nil \nerror\n value, the item will not be created.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeAdminCreate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterAdminCreate\n\n\nAfterAdminCreate is only called after a CMS Admin creates a new Content item.\nIt is not called for subsequent saves to the item once it has been created and \nassigned an ID.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterAdminCreate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeAdminUpdate\n\n\nBeforeAdminUpdate is only called before a CMS Admin updates a Content item. By \nreturning a non-nil \nerror\n, the item will not be updated.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeAdminUpdate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterAdminUpdate\n\n\nAfterAdminUpdate is only called after a CMS Admin updates a Content item.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterAdminUpdate\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeAdminDelete\n\n\nBeforeAdminDelete is only called before a CMS Admin deletes a Content item. By\nreturning a non-nil \nerror\n value, the item will not be deleted.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeAdminDelete\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterAdminDelete\n\n\nAfterAdminDelete is only called after a CMS Admin deletes a Content item.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterAdminDelete\n(\nres\n \nhttp\n.\nResponseWriter\n,\n \nreq\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeEnable\n\n\nBeforeEnable is only applicable to Addon items, and is called before the addon\nchanges status to \"Enabled\". By returning a non-nil \nerror\n value, the addon\nwill not become enabled.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeEnable\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterEnable\n\n\nAfterEnable is only applicable to Addon items, and is called after the addon \nchanges status to \"Enabled\".\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterEnable\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nBeforeDisable\n\n\nBeforeDisable is only applicable to Addon items, and is called before the addon\nchanges status to \"Disabled\". By returning a non-nil \nerror\n value, the addon\nwill not become disabled.\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nBeforeDisable\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nAfterDisable\n\n\nAfterDisable is only applicable to Addon items, and is called after the addon \nchanges status to \"Disabled\".\n\n\nfunc\n \n(\np\n \n*\nPost\n)\n \nAfterDisable\n(\nhttp\n.\nResponseWriter\n,\n \n*\nhttp\n.\nRequest\n)\n \nerror\n \n{\n\n \nreturn\n \nnil\n\n\n}\n\n\n\n\n\n\nHookable is implemented by Item by default as no-ops which are expected to be overridden. \n\n\n\n\nNote\n\n\nreturning an error from any of these \nHookable\n methods will end the request, \ncausing it to halt immediately after the hook. For example, returning an \nerror\n \nfrom \nBeforeDelete\n will result in the content being kept in the database. \nThe same logic applies to all of these interface methods that return an error \n- \nthe error defines the behavior\n.\n\n\n\n\n\n\nitem.Identifiable\n\n\nIdentifiable 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.\nMost notable, Identifiable\u2019s \nString\n method is used to set a meaningful display name for an Item. \nString\n is called by default in the Admin dashboard to show the Items of certain types, and in the default creation of an Item\u2019s slug.\nIdentifiable is implemented by Item by default.\n\n\nMethod Set\n\n\ntype\n \nIdentifiable\n \ninterface\n \n{\n\n \nItemID\n()\n \nint\n\n \nSetItemID\n(\nint\n)\n\n \nUniqueID\n()\n \nuuid\n.\nUUID\n\n \nString\n()\n \nstring\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nitem.Identifiable\n has a default implementation in the \nsystem/item\n package. \nIt is not advised to override these methods, with the exception of \nString()\n, \nwhich is commonly used to set the display name of Content items when listed in \nthe CMS, and to customize slugs.\n\n\nfunc\n \n(\ni\n \nItem\n)\n \nItemID\n()\n \nint\n \n{\n\n \nreturn\n \ni\n.\nID\n\n\n}\n\n\n\nfunc\n \n(\ni\n \n*\nItem\n)\n \nSetItemID\n(\nid\n \nint\n)\n \n{\n\n \ni\n.\nID\n \n=\n \nid\n\n\n}\n\n\n\nfunc\n \n(\ni\n \nItem\n)\n \nUniqueID\n()\n \nuuid\n.\nUUID\n \n{\n\n \nreturn\n \ni\n.\nUUID\n\n\n}\n\n\n\nfunc\n \n(\ni\n \nItem\n)\n \nString\n()\n \nstring\n \n{\n\n \nreturn\n \nfmt\n.\nSprintf\n(\nItem ID: %s\n,\n \ni\n.\nUniqueID\n())\n\n\n}\n\n\n\n\n\n\n\n\nitem.Sluggable\n\n\nSluggable 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 \nSetSlug\n method with your own.\nIt is not recommended to override \nSetSlug\n, but rather the \nString\n method on your content struct, which will have a similar, more predictable effect.\nSluggable is implemented by Item by default.\n\n\nMethod Set\n\n\ntype\n \nSluggable\n \ninterface\n \n{\n\n \nSetSlug\n(\nstring\n)\n\n \nItemSlug\n()\n \nstring\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nitem.Sluggable\n has a default implementation in the \nsystem/item\n package. It is\npossible to override these methods on your own Content types, but beware, behavior\nis undefined. It is tempting to override the \nSetSlug()\n method to customize your\nContent item slug, but try first to override the \nString()\n method found in the\n\nitem.Identifiable\n interface instead. If you don't get the desired results, try\n\nSetSlug()\n.\n\n\nfunc\n \n(\ni\n \n*\nItem\n)\n \nSetSlug\n(\nslug\n \nstring\n)\n \n{\n\n \ni\n.\nSlug\n \n=\n \nslug\n\n\n}\n\n\n\nfunc\n \n(\ni\n \n*\nItem\n)\n \nItemSlug\n()\n \nstring\n \n{\n\n \nreturn\n \ni\n.\nSlug\n\n\n}\n\n\n\n\n\n\n\n\nitem.Sortable\n\n\nSortable enables items to be sorted by time, as per the sort.Interface interface. Sortable is implemented by Item by default.\n\n\nMethod Set\n\n\ntype\n \nSortable\n \ninterface\n \n{\n\n \nTime\n()\n \nint64\n\n \nTouch\n()\n \nint64\n\n\n}\n\n\n\n\n\n\nImplementation\n\n\nitem.Sortable\n has a default implementation in the \nsystem/item\n package. It is\npossible to override these methods on your own Content type, but beware, behavior \nis undefined.\n\n\nfunc\n \n(\ni\n \nItem\n)\n \nTime\n()\n \nint64\n \n{\n\n \nreturn\n \ni\n.\nTimestamp\n\n\n}\n\n\n\nfunc\n \n(\ni\n \nItem\n)\n \nTouch\n()\n \nint64\n \n{\n\n \nreturn\n \ni\n.\nUpdated\n\n\n}", + "title": "Item" + }, + { + "location": "/Interfaces/Item/#interfaces", + "text": "", + "title": "Interfaces" + }, + { + "location": "/Interfaces/Item/#itempushable", + "text": "Pushable, if HTTP/2 Server Push \nis supported by the client, can tell a handler which resources it would like to \nhave \"pushed\" preemptively to the client. This saves follow-on roundtrip requests \nfor other items which are referenced by the Pushable item. The Push method, the \nonly method in Pushable, must return a []string containing the json field tags \nof the referenced items within the type.", + "title": "item.Pushable" + }, + { + "location": "/Interfaces/Item/#method-set", + "text": "type Pushable interface { \n // the values contained in fields returned by Push must be URL paths \n Push () [] string }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Item/#implementation", + "text": "The Push method returns a []string containing the json tag field names for\nwhich you want to have pushed to a supported client. The values for the field\nnames must be URL paths, and cannot be from another origin. type Post struct { \n item . Item \n\n HeaderPhoto string `json: header_photo ` \n Author string `json: author ` // reference `/api/content/?type=Author id=2` \n // ... } func ( p * Post ) Push () [] string { \n return [] string { \n header_photo , \n author , \n } }", + "title": "Implementation" + }, + { + "location": "/Interfaces/Item/#itemhideable", + "text": "Hideable tells an API handler that data of this type shouldn\u2019t be exposed outside \nthe system. Hideable types cannot be used as references (relations in Content types).\nThe Hide method, the only method in Hideable, takes an http.ResponseWriter, *http.Request \nand returns an error . A special error in the items package, ErrAllowHiddenItem \ncan be returned as the error from Hide to instruct handlers to show hidden \ncontent in specific cases.", + "title": "item.Hideable" + }, + { + "location": "/Interfaces/Item/#method-set_1", + "text": "type Hideable interface { \n Hide ( http . ResponseWriter , * http . Request ) error }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Item/#implementation_1", + "text": "func ( p * Post ) Hide ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "Implementation" + }, + { + "location": "/Interfaces/Item/#itemomittable", + "text": "Omittable tells a content API handler to keep certain fields from being exposed \nthrough the JSON response. It's single method, Omit takes no arguments and \nreturns a []string which must be made up of the JSON struct tags for the type \ncontaining fields to be omitted.", + "title": "item.Omittable" + }, + { + "location": "/Interfaces/Item/#method-set_2", + "text": "type Omittable interface { \n Omit () [] string }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Item/#implementation_2", + "text": "type Post struct { \n item . Item \n\n HeaderPhoto string `json: header_photo ` \n Author string `json: author ` \n // ... } func ( p * Post ) Omit () [] string { \n return [] string { \n header_photo , \n author , \n } }", + "title": "Implementation" + }, + { + "location": "/Interfaces/Item/#itemhookable", + "text": "Hookable provides lifecycle hooks into the http handlers which manage Save, Delete,\nApprove, and Reject routines. All methods in its set take an http.ResponseWriter, *http.Request and return an error .", + "title": "item.Hookable" + }, + { + "location": "/Interfaces/Item/#method-set_3", + "text": "type Hookable interface { \n BeforeAPICreate ( http . ResponseWriter , * http . Request ) error \n AfterAPICreate ( http . ResponseWriter , * http . Request ) error \n\n BeforeAPIUpdate ( http . ResponseWriter , * http . Request ) error \n AfterAPIUpdate ( http . ResponseWriter , * http . Request ) error \n\n BeforeAPIDelete ( http . ResponseWriter , * http . Request ) error \n AfterAPIDelete ( http . ResponseWriter , * http . Request ) error \n\n BeforeAdminCreate ( http . ResponseWriter , * http . Request ) error \n AfterAdminCreate ( http . ResponseWriter , * http . Request ) error \n\n BeforeAdminUpdate ( http . ResponseWriter , * http . Request ) error \n AfterAdminUpdate ( http . ResponseWriter , * http . Request ) error \n\n BeforeAdminDelete ( http . ResponseWriter , * http . Request ) error \n AfterAdminDelete ( http . ResponseWriter , * http . Request ) error \n\n BeforeSave ( http . ResponseWriter , * http . Request ) error \n AfterSave ( http . ResponseWriter , * http . Request ) error \n\n BeforeDelete ( http . ResponseWriter , * http . Request ) error \n AfterDelete ( http . ResponseWriter , * http . Request ) error \n\n BeforeApprove ( http . ResponseWriter , * http . Request ) error \n AfterApprove ( http . ResponseWriter , * http . Request ) error \n\n BeforeReject ( http . ResponseWriter , * http . Request ) error \n AfterReject ( http . ResponseWriter , * http . Request ) error \n\n // Enable/Disable used exclusively for addons \n BeforeEnable ( http . ResponseWriter , * http . Request ) error \n AfterEnable ( http . ResponseWriter , * http . Request ) error \n\n BeforeDisable ( http . ResponseWriter , * http . Request ) error \n AfterDisable ( http . ResponseWriter , * http . Request ) error }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Item/#implementations", + "text": "", + "title": "Implementations" + }, + { + "location": "/Interfaces/Item/#beforeapicreate", + "text": "BeforeAPICreate is called before an item is created via a 3rd-party client. If a \nnon-nil error value is returned, the item will not be created/saved. func ( p * Post ) BeforeAPICreate ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeAPICreate" + }, + { + "location": "/Interfaces/Item/#afterapicreate", + "text": "AfterAPICreate is called after an item has been created via a 3rd-party client.\nAt this point, the item has been saved to the database. If a non-nil error is\nreturned, it will respond to the client with an empty response, so be sure to \nuse the http.ResponseWriter from within your hook appropriately. func ( p * Post ) AfterAPICreate ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterAPICreate" + }, + { + "location": "/Interfaces/Item/#beforeapprove", + "text": "BeforeApprove is called before an item is merged as \"Public\" from its prior \nstatus as \"Pending\". If a non-nil error value is returned, the item will not be\nappproved, and an error message is displayed to the Admin. func ( p * Post ) BeforeApprove ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeApprove" + }, + { + "location": "/Interfaces/Item/#afterapprove", + "text": "AfterApprove is called after an item has been merged as \"Public\" from its prior\nstatus as \"Pending\". If a non-nil error is returned, an error message is \ndisplayed to the Admin, however the item will already be irreversibly merged. func ( p * Post ) AfterApprove ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterApprove" + }, + { + "location": "/Interfaces/Item/#beforereject", + "text": "BeforeReject is called before an item is rejected and deleted by default. To reject\nan item, but not delete it, return a non-nil error from this hook - doing so \nwill allow the hook to do what you want it to do prior to the return, but the item\nwill remain in the \"Pending\" section. func ( p * Post ) BeforeReject ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeReject" + }, + { + "location": "/Interfaces/Item/#afterreject", + "text": "AfterReject is called after an item is rejected and has been deleted. func ( p * Post ) AfterReject ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterReject" + }, + { + "location": "/Interfaces/Item/#beforesave", + "text": "BeforeSave is called before any CMS Admin or 3rd-party client triggers a save to \nthe database. This could be done by clicking the 'Save' button on a Content editor, \nor by a API call to Create or Update the Content item. By returning a non-nil error value, the item will not be saved. func ( p * Post ) BeforeSave ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeSave" + }, + { + "location": "/Interfaces/Item/#aftersave", + "text": "AfterSave is called after any CMS Admin or 3rd-party client triggers a save to \nthe database. This could be done by clicking the 'Save' button on a Content editor, \nor by a API call to Create or Update the Content item. func ( p * Post ) AfterSave ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterSave" + }, + { + "location": "/Interfaces/Item/#beforedelete", + "text": "BeforeDelete is called before any CMS Admin or 3rd-party client triggers a delete to \nthe database. This could be done by clicking the 'Delete' button on a Content editor, \nor by a API call to Delete the Content item. By returning a non-nil error value,\nthe item will not be deleted. func ( p * Post ) BeforeDelete ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeDelete" + }, + { + "location": "/Interfaces/Item/#afterdelete", + "text": "AfterSave is called after any CMS Admin or 3rd-party client triggers a delete to \nthe database. This could be done by clicking the 'Delete' button on a Content editor, \nor by a API call to Delete the Content item. func ( p * Post ) AfterDelete ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterDelete" + }, + { + "location": "/Interfaces/Item/#beforeapidelete", + "text": "BeforeDelete is only called before a 3rd-party client triggers a delete to the \ndatabase. By returning a non-nil error value, the item will not be deleted. func ( p * Post ) BeforeAPIDelete ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeAPIDelete" + }, + { + "location": "/Interfaces/Item/#afterapidelete", + "text": "AfterAPIDelete is only called after a 3rd-party client triggers a delete to the \ndatabase. func ( p * Post ) AfterAPIDelete ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterAPIDelete" + }, + { + "location": "/Interfaces/Item/#beforeapiupdate", + "text": "BeforeAPIUpdate is only called before a 3rd-party client triggers an update to \nthe database. By returning a non-nil error value, the item will not be updated. func ( p * Post ) BeforeAPIUpdate ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeAPIUpdate" + }, + { + "location": "/Interfaces/Item/#afterapiupdate", + "text": "AfterAPIUpdate is only called after a 3rd-party client triggers an update to \nthe database. func ( p * Post ) AfterAPIUpdate ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterAPIUpdate" + }, + { + "location": "/Interfaces/Item/#beforeadmincreate", + "text": "BeforeAdminCreate is only called before a CMS Admin creates a new Content item.\nIt is not called for subsequent saves to the item once it has been created and \nassigned an ID. By returning a non-nil error value, the item will not be created. func ( p * Post ) BeforeAdminCreate ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeAdminCreate" + }, + { + "location": "/Interfaces/Item/#afteradmincreate", + "text": "AfterAdminCreate is only called after a CMS Admin creates a new Content item.\nIt is not called for subsequent saves to the item once it has been created and \nassigned an ID. func ( p * Post ) AfterAdminCreate ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterAdminCreate" + }, + { + "location": "/Interfaces/Item/#beforeadminupdate", + "text": "BeforeAdminUpdate is only called before a CMS Admin updates a Content item. By \nreturning a non-nil error , the item will not be updated. func ( p * Post ) BeforeAdminUpdate ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeAdminUpdate" + }, + { + "location": "/Interfaces/Item/#afteradminupdate", + "text": "AfterAdminUpdate is only called after a CMS Admin updates a Content item. func ( p * Post ) AfterAdminUpdate ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterAdminUpdate" + }, + { + "location": "/Interfaces/Item/#beforeadmindelete", + "text": "BeforeAdminDelete is only called before a CMS Admin deletes a Content item. By\nreturning a non-nil error value, the item will not be deleted. func ( p * Post ) BeforeAdminDelete ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "BeforeAdminDelete" + }, + { + "location": "/Interfaces/Item/#afteradmindelete", + "text": "AfterAdminDelete is only called after a CMS Admin deletes a Content item. func ( p * Post ) AfterAdminDelete ( res http . ResponseWriter , req * http . Request ) error { \n return nil }", + "title": "AfterAdminDelete" + }, + { + "location": "/Interfaces/Item/#beforeenable", + "text": "BeforeEnable is only applicable to Addon items, and is called before the addon\nchanges status to \"Enabled\". By returning a non-nil error value, the addon\nwill not become enabled. func ( p * Post ) BeforeEnable ( http . ResponseWriter , * http . Request ) error { \n return nil }", + "title": "BeforeEnable" + }, + { + "location": "/Interfaces/Item/#afterenable", + "text": "AfterEnable is only applicable to Addon items, and is called after the addon \nchanges status to \"Enabled\". func ( p * Post ) AfterEnable ( http . ResponseWriter , * http . Request ) error { \n return nil }", + "title": "AfterEnable" + }, + { + "location": "/Interfaces/Item/#beforedisable", + "text": "BeforeDisable is only applicable to Addon items, and is called before the addon\nchanges status to \"Disabled\". By returning a non-nil error value, the addon\nwill not become disabled. func ( p * Post ) BeforeDisable ( http . ResponseWriter , * http . Request ) error { \n return nil }", + "title": "BeforeDisable" + }, + { + "location": "/Interfaces/Item/#afterdisable", + "text": "AfterDisable is only applicable to Addon items, and is called after the addon \nchanges status to \"Disabled\". func ( p * Post ) AfterDisable ( http . ResponseWriter , * http . Request ) error { \n return nil } Hookable is implemented by Item by default as no-ops which are expected to be overridden. Note returning an error from any of these Hookable methods will end the request, \ncausing it to halt immediately after the hook. For example, returning an error \nfrom BeforeDelete will result in the content being kept in the database. \nThe same logic applies to all of these interface methods that return an error \n- the error defines the behavior .", + "title": "AfterDisable" + }, + { + "location": "/Interfaces/Item/#itemidentifiable", + "text": "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.\nMost notable, Identifiable\u2019s 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\u2019s slug.\nIdentifiable is implemented by Item by default.", + "title": "item.Identifiable" + }, + { + "location": "/Interfaces/Item/#method-set_4", + "text": "type Identifiable interface { \n ItemID () int \n SetItemID ( int ) \n UniqueID () uuid . UUID \n String () string }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Item/#implementation_3", + "text": "item.Identifiable has a default implementation in the system/item package. \nIt is not advised to override these methods, with the exception of String() , \nwhich is commonly used to set the display name of Content items when listed in \nthe CMS, and to customize slugs. func ( i Item ) ItemID () int { \n return i . ID } func ( i * Item ) SetItemID ( id int ) { \n i . ID = id } func ( i Item ) UniqueID () uuid . UUID { \n return i . UUID } func ( i Item ) String () string { \n return fmt . Sprintf ( Item ID: %s , i . UniqueID ()) }", + "title": "Implementation" + }, + { + "location": "/Interfaces/Item/#itemsluggable", + "text": "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.\nIt is not recommended to override SetSlug , but rather the String method on your content struct, which will have a similar, more predictable effect.\nSluggable is implemented by Item by default.", + "title": "item.Sluggable" + }, + { + "location": "/Interfaces/Item/#method-set_5", + "text": "type Sluggable interface { \n SetSlug ( string ) \n ItemSlug () string }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Item/#implementation_4", + "text": "item.Sluggable has a default implementation in the system/item package. It is\npossible to override these methods on your own Content types, but beware, behavior\nis undefined. It is tempting to override the SetSlug() method to customize your\nContent 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() . func ( i * Item ) SetSlug ( slug string ) { \n i . Slug = slug } func ( i * Item ) ItemSlug () string { \n return i . Slug }", + "title": "Implementation" + }, + { + "location": "/Interfaces/Item/#itemsortable", + "text": "Sortable enables items to be sorted by time, as per the sort.Interface interface. Sortable is implemented by Item by default.", + "title": "item.Sortable" + }, + { + "location": "/Interfaces/Item/#method-set_6", + "text": "type Sortable interface { \n Time () int64 \n Touch () int64 }", + "title": "Method Set" + }, + { + "location": "/Interfaces/Item/#implementation_5", + "text": "item.Sortable has a default implementation in the system/item package. It is\npossible to override these methods on your own Content type, but beware, behavior \nis undefined. func ( i Item ) Time () int64 { \n return i . Timestamp } func ( i Item ) Touch () int64 { \n return i . Updated }", + "title": "Implementation" + }, + { + "location": "/Interfaces/Search/", + "text": "Ponzu provides a set of interfaces from the \nsystem/search\n package to enable and customize full-text search access to content in your system. \nSearch is not enabled by default\n, and must be enabled per Content type individually.\n\n\nInterfaces\n\n\nsearch.Searchable\n\n\nSearchable 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.\n\n\n\n\nSearch is \ndisabled\n for all Content items by default. Each Content item that should be indexed and searchable must implement the \nsearch.Searchable\n interface.\n\n\n\n\nMethod Set\n\n\ntype\n \nSearchable\n \ninterface\n \n{\n\n \nSearchMapping\n()\n \n(\n*\nmapping\n.\nIndexMappingImpl\n,\n \nerror\n)\n\n \nIndexContent\n()\n \nbool\n\n\n}\n\n\n\n\n\n\nBy default, Ponzu sets up the \nBleve's\n \"default mapping\", which is typically what you want for most content-based systems. This can be overridden by implementing your own \nSearchMapping() (*mapping.IndexMappingImpl, error)\n method on your Content type. \n\n\nThis way, all you need to do to get full-text search is to add the \nIndexContent() bool\n method to each Content type you want search enabled. Return \ntrue\n from this method to enable search. \n\n\nExample\n\n\n// ...\n\n\n\ntype\n \nSong\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nName\n \nstring\n \n`json:\nname\n`\n\n \n// ...\n\n\n}\n\n\n\nfunc\n \n(\ns\n \n*\nSong\n)\n \nIndexContent\n()\n \nbool\n \n{\n\n \nreturn\n \ntrue\n\n\n}\n\n\n\n\n\n\n\n\nIndexing Existing Content\n\n\nIf 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 \nmight\n ship with a re-indexing function at some point in the fututre.", + "title": "Search" + }, + { + "location": "/Interfaces/Search/#interfaces", + "text": "", + "title": "Interfaces" + }, + { + "location": "/Interfaces/Search/#searchsearchable", + "text": "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. Search is disabled for all Content items by default. Each Content item that should be indexed and searchable must implement the search.Searchable interface.", + "title": "search.Searchable" + }, + { + "location": "/Interfaces/Search/#method-set", + "text": "type Searchable interface { \n SearchMapping () ( * mapping . IndexMappingImpl , error ) \n IndexContent () bool } By default, Ponzu sets up the Bleve's \"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.", + "title": "Method Set" + }, + { + "location": "/Interfaces/Search/#example", + "text": "// ... type Song struct { \n item . Item \n\n Name string `json: name ` \n // ... } func ( s * Song ) IndexContent () bool { \n return true } 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.", + "title": "Example" + }, + { + "location": "/Ponzu-Addons/Creating-Addons/", + "text": "Coming soon\n\n\nFor a reference to creating your own addons, see:\n\nhttps://github.com/bosssauce/fbscheduler", + "title": "Creating Addons" + }, + { + "location": "/Ponzu-Addons/Creating-Addons/#coming-soon", + "text": "For a reference to creating your own addons, see: https://github.com/bosssauce/fbscheduler", + "title": "Coming soon" + }, + { + "location": "/Ponzu-Addons/Using-Addons/", + "text": "Coming soon\n\n\nFor a reference to creating your own addons, see:\n\nhttps://github.com/bosssauce/fbscheduler", + "title": "Using Addons" + }, + { + "location": "/Ponzu-Addons/Using-Addons/#coming-soon", + "text": "For a reference to creating your own addons, see: https://github.com/bosssauce/fbscheduler", + "title": "Coming soon" + }, + { + "location": "/Quickstart/Overview/", + "text": "Quickstart Steps\n\n\n1) Install \nGo 1.8+\n\n\n2) Install Ponzu CLI:\n\n\n$ go get github.com/ponzu-cms/ponzu/\u2026\n\n\n\n\n\n3) Create a new project (path is created in your GOPATH):\n\n\n$ ponzu new github.com/nilslice/reviews\n\n\n\n\n\n4) Enter your new project directory:\n\n\n$ \ncd\n \n$GOPATH\n/src/github.com/nilslice/reviews\n\n\n\n\n\n5) Generate content type file and boilerplate code (creates \ncontent/review.go\n):\n\n\n$ ponzu generate content review title:\nstring\n author:\nstring\n rating:\nfloat64\n body:\nstring\n:richtext website_url:\nstring\n items:\n[]string\n photo:string:file\n`\n\n\n\n\n\n\n6) Build your project:\n\n\n$ ponzu build\n\n\n\n\n\n7) Run your project with defaults:\n\n\n$ ponzu run\n\n\n\n\n\n8) Open browser to \nhttp://localhost:8080/admin\n\n\nNotes\n\n\n\n\nOne-time initialization to set configuration\n\n\nAll fields can be changed in Configuration afterward", + "title": "Overview" + }, + { + "location": "/Quickstart/Overview/#quickstart-steps", + "text": "1) Install Go 1.8+ 2) Install Ponzu CLI: $ go get github.com/ponzu-cms/ponzu/\u2026 3) Create a new project (path is created in your GOPATH): $ ponzu new github.com/nilslice/reviews 4) Enter your new project directory: $ cd $GOPATH /src/github.com/nilslice/reviews 5) Generate content type file and boilerplate code (creates content/review.go ): $ 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: $ ponzu build 7) Run your project with defaults: $ ponzu run 8) Open browser to http://localhost:8080/admin", + "title": "Quickstart Steps" + }, + { + "location": "/Quickstart/Overview/#notes", + "text": "One-time initialization to set configuration All fields can be changed in Configuration afterward", + "title": "Notes" + }, + { + "location": "/References/Overview/", + "text": "References in Ponzu allow you to create relationships between your Content types.\nPonzu uses an embedded database, rather than a more traditional relational database \nwith SQL support. This may seem unnatural since there is no native concept of\n\"foreign keys\" or \"joins\" like you may be used to. Instead, Ponzu wires up your\ndata using references, which are simply URL paths, like \n/api/content?type=Post\nid=1\n\n\nA foreign key as a URL path?! Am I crazy? No! For the purpose Ponzu serves, \nthis structure works quite well, especially given its creation was specifically \ntuned for HTTP/2 features such as \"Request/Response Multiplexing\" and \"Server Push.\" \n\n\nThere is a deeper dive into the HTTP/2 concepts \nbelow\n, but first we'll walk through\na quick tutorial on Ponzu's references. \n\n\nTo generate references from the CLI, please \nread through the documentation\n. \nThe example below assumes you understand the syntax. \n\n\n\n\nCreate Your Content Types\n\n\nHere we are creating two Content types, \nAuthor\n and \nBook\n. A \nBook\n will keep\na reference to an \nAuthor\n in the sense that an author wrote the book.\n\n\n$ ponzu gen c author name:string photo:string:file bio:string:textarea\n$ ponzu gen c book title:string author:@author,name pages:int year:int\n\n\n\n\n\nThe structs generated for each look like:\n\n\ncontent/author.go\n\n\ntype\n \nAuthor\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nName\n \nstring\n \n`json:\nname\n`\n\n \nPhoto\n \nstring\n \n`json:\nphoto\n`\n\n \nBio\n \nstring\n \n`json:\nbio\n`\n\n\n}\n\n\n\n\n\n\ncontent/book.go\n\n\ntype\n \nBook\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nTitle\n \nstring\n \n`json:\ntitle\n`\n\n \nAuthor\n \nstring\n \n`json:\nauthor\n`\n\n \nPages\n \nint\n \n`json:\npages\n`\n\n \nYear\n \nint\n \n`json:\nyear\n`\n\n\n}\n\n\n\n\n\n\nNotice how the \nAuthor\n field within the \nBook\n struct is a \nstring\n type, not\nan \nAuthor\n type. This is because the \nAuthor\n is stored as a \nstring\n in our\ndatabase, as a reference to the \nAuthor\n, instead of embedding the \nAuthor\n data \ninside the \nBook\n. \n\n\nSome example JSON data for the two structs looks like:\n\n\nGET\n \n/api/content?type=Author\nid=1\n (\nAuthor\n)\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nuuid\n:\n \n024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n\n \nid\n:\n \n1\n,\n\n \nslug\n:\n \nitem-id-024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n\n \ntimestamp\n:\n \n1493926453826\n,\n\n \nupdated\n:\n \n1493926453826\n,\n\n \nname\n:\n \nShel Silverstein\n,\n\n \nphoto\n:\n \n/api/uploads/2017/05/shel-silverstein.jpg\n,\n\n \nbio\n:\n \nSheldon Allan Silverstein was an American poet...\n\n \n}\n\n \n]\n\n\n}\n\n\n\n\n\n\nGET\n \n/api/content?type=Book\nid=1\n (\nBook\n)\n\n\n{\n\n \ndata\n:\n \n[\n\n \n{\n\n \nuuid\n:\n \n024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n\n \nid\n:\n \n1\n,\n\n \nslug\n:\n \nitem-id-024a5797-e064-4ee0-abe3-415cb6d3ed18\n,\n\n \ntimestamp\n:\n \n1493926453826\n,\n\n \nupdated\n:\n \n1493926453826\n,\n\n \ntitle\n:\n \nThe Giving Tree\n,\n\n \nauthor\n:\n \n/api/content?type=Author\nid=1\n,\n\n \npages\n:\n \n57\n,\n\n \nyear\n:\n \n1964\n\n \n}\n\n \n]\n\n\n}\n\n\n\n\n\n\nAs you can see, the \nAuthor\n is a reference as the \nauthor\n field in the JSON\nresponse for a \nBook\n. When you're building your client, you need to make a second\nrequest for the \nAuthor\n, to the URL path found in the \nauthor\n field of the \nBook\n\nresponse. \n\n\nFor example, in pseudo-code: \n\n\n# Request 1: \n\n\n$book\n \n=\n GET /api/content?type\n=\nBook\nid\n=\n1\n\n\n\n# Request 2: \n\n\n$author\n \n=\n GET \n$book\n.author \n# where author = /api/content?type=Author\nid=1\n\n\n\n\n\n\nUntil recently, this would be considered bad practice and would be costly to do\nover HTTP. However, with the wide availability of HTTP/2 clients, including all\nmodern web browsers, mobile devices, and HTTP/2 libraries in practically every \nprogramming language, this pattern is fast and scalable. \n\n\n\n\nDesigned For HTTP/2\n\n\nAt this point, you've likely noticed that you're still making two independent \nHTTP requests to your Ponzu server. Further, if there are multiple references or more\nthan one item, you'll be making many requests -- \nhow can that be efficient?\n \n\n\nThere are two main concepts at play: Request/Response Multiplexing and Server Push.\n\n\nRequest/Response Multiplexing\n\n\nWith HTTP/2, a client and server (peers) transfer data over a single TCP connection, \nand can send data back and forth at the same time. No longer does a request need\nto wait to be sent until after an expected response is read. This means that HTTP \nrequests can be sent much faster and at the \nsame time\n on a single connection. \nWhere previously, a client would open up several TCP connections, the re-use of a \nsingle connection reduces CPU overhead and makes the server more efficient.\n\n\nThis feature is automatically provided to you when using HTTP/2 - the only \nrequirement is that you connect via HTTPS and have active TLS certificates, which \nyou can get for free by running Ponzu with the \n--https\n flag and configuring it \nwith a properly set, active domain name of your own. \n\n\nServer Push\n\n\nAnother impactful feature of HTTP/2 is \"Server Push\": the ability to preemptively\nsend a response from the server to a client without waiting for a request. This\nis where Ponzu's reference design really shows it's power. Let's revisit the\nexample from above:\n\n\n# Request 1: \n\n\n$book\n \n=\n GET /api/content?type\n=\nBook\nid\n=\n1\n\n\n\n# Request 2: \n\n\n$author\n \n=\n GET \n$book\n.author \n# where author = /api/content?type=Author\nid=1\n\n\n\n\n\n\nInstead of waiting for the server to respond with the data for \n$book.author\n, \nthe response data is already in the client's cache before we even make the request!\nNow there is no round-trip made to the server and back, and the client reads the \npushed response from cache in fractions of a millisecond. \n\n\nBut, how does the server know which response to push and when? You'll need to \nspecify which fields of the type you've requested should be pushed. This is done\nby implementing the \nitem.Pushable\n interface\n. \nSee the example below which demonstrates a complete implementation on the \nBook\n\nstruct, which has a reference to an \nAuthor\n.\n\n\nExample\n\n\ncontent/book.go\n\n\n...\n\n\ntype\n \nBook\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nTitle\n \nstring\n \n`json:\ntitle\n`\n\n \nAuthor\n \nstring\n \n`json:\nauthor\n`\n\n \nPages\n \nint\n \n`json:\npages\n`\n\n \nYear\n \nint\n \n`json:\nyear\n`\n\n\n}\n\n\n\n\nfunc\n \n(\nb\n \n*\nBook\n)\n \nPush\n()\n \n[]\nstring\n \n{\n\n \nreturn\n \n[]\nstring\n{\n\n \n// the json struct tag is used to tell the server which\n\n \n// field(s) it should push - only URL paths originating\n\n \n// from your server can be pushed!\n\n \nauthor\n,\n \n \n}\n\n\n}\n\n\n...\n\n\n\n\n\n\nNow, whenever a single \nBook\n is requested, the server will preemptively push the\n\nAuthor\n referenced by the book. The response for the \nAuthor\n will \nalready be\non the client\n and will remain there until a request for the referenced \nAuthor\n \nhas been made.\n\n\n\n\nWhat else can I Push?\n\n\nOnly fields that are URL paths originating from your server can be pushed. \nThis means that you could also implement \nitem.Pushable\n on the \nAuthor\n\ntype, and return \n[]string{\"photo\"}\n to push the Author's image!\n\n\n\n\n\n\nOther Considerations\n\n\nHTTP/2 Server Push is a powerful feature, but it can be abused just like anything\nelse. To try and help mitigate potential issues, Ponzu has put some \"stop-gaps\"\nin place. Server Push is only activated on \nsingle item\n API responses, so you\nshouldn't expect to see references or files pushed from the \n/api/contents\n endpoint.\nAn exception to this is the \n/api/search\n endpoint, which only the \nfirst\n \nresult is pushed (if applicable) no matter how many items are in the response. \n\n\nYou should take advantage of HTTP/2 in Ponzu and get the most out of the system. \nWith the automatic HTTPS feature, there is no reason not to and you gain the \nadditional benefit of encrypting your traffic - which your users will appreciate!", + "title": "Overview" + }, + { + "location": "/References/Overview/#create-your-content-types", + "text": "Here we are creating two Content types, Author and Book . A Book will keep\na reference to an Author in the sense that an author wrote the book. $ ponzu gen c author name:string photo:string:file bio:string:textarea\n$ ponzu gen c book title:string author:@author,name pages:int year:int The structs generated for each look like: content/author.go type Author struct { \n item . Item \n\n Name string `json: name ` \n Photo string `json: photo ` \n Bio string `json: bio ` } content/book.go type Book struct { \n item . Item \n\n Title string `json: title ` \n Author string `json: author ` \n Pages int `json: pages ` \n Year int `json: year ` } Notice how the Author field within the Book struct is a string type, not\nan Author type. This is because the Author is stored as a string in our\ndatabase, as a reference to the Author , instead of embedding the Author data \ninside the Book . Some example JSON data for the two structs looks like: GET /api/content?type=Author id=1 ( Author ) { \n data : [ \n { \n uuid : 024a5797-e064-4ee0-abe3-415cb6d3ed18 , \n id : 1 , \n slug : item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18 , \n timestamp : 1493926453826 , \n updated : 1493926453826 , \n name : Shel Silverstein , \n photo : /api/uploads/2017/05/shel-silverstein.jpg , \n bio : Sheldon Allan Silverstein was an American poet... \n } \n ] } GET /api/content?type=Book id=1 ( Book ) { \n data : [ \n { \n uuid : 024a5797-e064-4ee0-abe3-415cb6d3ed18 , \n id : 1 , \n slug : item-id-024a5797-e064-4ee0-abe3-415cb6d3ed18 , \n timestamp : 1493926453826 , \n updated : 1493926453826 , \n title : The Giving Tree , \n author : /api/content?type=Author id=1 , \n pages : 57 , \n year : 1964 \n } \n ] } As you can see, the Author is a reference as the author field in the JSON\nresponse for a Book . When you're building your client, you need to make a second\nrequest for the Author , to the URL path found in the author field of the Book \nresponse. For example, in pseudo-code: # 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\nover HTTP. However, with the wide availability of HTTP/2 clients, including all\nmodern web browsers, mobile devices, and HTTP/2 libraries in practically every \nprogramming language, this pattern is fast and scalable.", + "title": "Create Your Content Types" + }, + { + "location": "/References/Overview/#designed-for-http2", + "text": "At this point, you've likely noticed that you're still making two independent \nHTTP requests to your Ponzu server. Further, if there are multiple references or more\nthan 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.", + "title": "Designed For HTTP/2" + }, + { + "location": "/References/Overview/#requestresponse-multiplexing", + "text": "With HTTP/2, a client and server (peers) transfer data over a single TCP connection, \nand can send data back and forth at the same time. No longer does a request need\nto wait to be sent until after an expected response is read. This means that HTTP \nrequests can be sent much faster and at the same time on a single connection. \nWhere previously, a client would open up several TCP connections, the re-use of a \nsingle connection reduces CPU overhead and makes the server more efficient. This feature is automatically provided to you when using HTTP/2 - the only \nrequirement is that you connect via HTTPS and have active TLS certificates, which \nyou can get for free by running Ponzu with the --https flag and configuring it \nwith a properly set, active domain name of your own.", + "title": "Request/Response Multiplexing" + }, + { + "location": "/References/Overview/#server-push", + "text": "Another impactful feature of HTTP/2 is \"Server Push\": the ability to preemptively\nsend a response from the server to a client without waiting for a request. This\nis where Ponzu's reference design really shows it's power. Let's revisit the\nexample from above: # 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 , \nthe response data is already in the client's cache before we even make the request!\nNow there is no round-trip made to the server and back, and the client reads the \npushed response from cache in fractions of a millisecond. But, how does the server know which response to push and when? You'll need to \nspecify which fields of the type you've requested should be pushed. This is done\nby implementing the item.Pushable interface . \nSee the example below which demonstrates a complete implementation on the Book \nstruct, which has a reference to an Author .", + "title": "Server Push" + }, + { + "location": "/References/Overview/#example", + "text": "content/book.go ... type Book struct { \n item . Item \n\n Title string `json: title ` \n Author string `json: author ` \n Pages int `json: pages ` \n Year int `json: year ` } func ( b * Book ) Push () [] string { \n return [] string { \n // the json struct tag is used to tell the server which \n // field(s) it should push - only URL paths originating \n // from your server can be pushed! \n author , \n } } ... 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\non the client and will remain there until a request for the referenced Author \nhas been made. What else can I Push? Only fields that are URL paths originating from your server can be pushed. \nThis means that you could also implement item.Pushable on the Author \ntype, and return []string{\"photo\"} to push the Author's image!", + "title": "Example" + }, + { + "location": "/References/Overview/#other-considerations", + "text": "HTTP/2 Server Push is a powerful feature, but it can be abused just like anything\nelse. To try and help mitigate potential issues, Ponzu has put some \"stop-gaps\"\nin place. Server Push is only activated on single item API responses, so you\nshouldn't expect to see references or files pushed from the /api/contents endpoint.\nAn exception to this is the /api/search endpoint, which only the first \nresult 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. \nWith the automatic HTTPS feature, there is no reason not to and you gain the \nadditional benefit of encrypting your traffic - which your users will appreciate!", + "title": "Other Considerations" + }, + { + "location": "/Running-Backups/Backups/", + "text": "Both the databases \nsystem.db\n \n \nanalytics.db\n, and the \n/uploads\n directory can be backed up over HTTP using \nwget\n, \ncurl\n, etc. All of which are located at the \n/admin/backup\n route and require HTTP Basic Auth. In order to enable backups, you must add a user/password pair inside the CMS Configuration at \n/admin/configure\n near the bottom of the page.\n\n\nAll backups are made using a \nGET\n request to the \n/admin/backup\n path with a query parameter of \n?source={system,analytics,uploads}\n (only one source can be included in the URL).\n\n\nHere are some full backup scripts to use or modify to fit your needs:\n\nhttps://github.com/ponzu-cms/backup-scripts\n\n\nSystem \n Analytics\n\n\nThe \nsystem.db\n \n \nanalytics.db\n 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.\n\n\nAn example backup request for the \nsystem.db\n data file would look like:\n\n\n$ curl --user user:pass \nhttps://example.com/admin/backup?source=system\n \n system.db.bak\n\n\n\n\n\nUploads\n\n\nThe \n/uploads\n directory is gzip compressed and archived as a tar file, stored in the temporary directory (typically \n/tmp\n on Linux) on your origin server with a timestamp in the file name. \n\n\nAn example backup request for the \n/uploads\n directory would look like:\n\n\n$ curl --user user:pass \nhttps://example.com/admin/backup?source=uploads\n \n uploads.tar.gz\n\n# unarchive the tarball with gzip \n\n$ tar xzf uploads.tar.gz", + "title": "Backups" + }, + { + "location": "/Running-Backups/Backups/#system-analytics", + "text": "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: $ curl --user user:pass https://example.com/admin/backup?source=system system.db.bak", + "title": "System & Analytics" + }, + { + "location": "/Running-Backups/Backups/#uploads", + "text": "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: $ curl --user user:pass https://example.com/admin/backup?source=uploads uploads.tar.gz # unarchive the tarball with gzip \n$ tar xzf uploads.tar.gz", + "title": "Uploads" + }, + { + "location": "/System-Configuration/Settings/", + "text": "Ponzu has several options which can be configured at run-time. To view these\nconfiguration settings, visit the \n/admin/configure\n page of your Ponzu CMS.\n\n\n\n\nSite Name\n\n\nThe Site Name setting changes the displayed name on your admin dashboard. This is\nvisible publicly on the \n/admin/login\n page.\n\n\n\n\nDomain Name\n\n\nInternally, Ponzu needs to know where its canonical HTTP access origin is, and\nrequires you to add the qualified domain name you are using. In development, use \n\nlocalhost\n or some other name mapped to the loopback address (\n127.0.0.1\n).\n\n\nOnce you have deployed your Ponzu server to a remote host and pointed a public \ndomain at it, you need to change the Domain Name setting to match. This is \nespecially important when fetching TLS (SSL) certificates from \nLet's Encrypt\n\n- since the process requires an active, verifiable domain. To set up your server\nwith TLS over HTTPS connections, follow these steps:\n\n\n\n\nSet your Domain Name in the system configuration\n\n\nSet the Administrator Email to register with Let's Encrypt\n\n\nStop your Ponzu server\n\n\nRun your Ponzu server with the \n--https\n flag e.g. \n$ ponzu run --https\n\n\nVisit your CMS admin with \nhttps://\n prepended to your URL\n\n\n\n\n\n\nVerifying HTTPS / TLS Connections\n\n\nIf successful, your APIs and CMS will be accessible via HTTPS, and you will\nsee a green indicator near the URL bar of most browsers. This also enables \nyour server to use the HTTP/2 protocol.\n\n\n\n\nDevelopment Environment\n\n\nYou can test HTTPS \n HTTP/2 connections in your development environment on \nlocalhost\n,\nby running Ponzu with the \n--devhttps\n flag e.g. \n$ ponzu --devhttps run\n \n\n\nIf you're greeted with a warning from the browser saying the connection is not\nsecure, follow the steps outlined in the CLI message, or here:\n\n\nIf your browser rejects HTTPS requests, try allowing insecure connections on localhost.\non Chrome, visit chrome://flags/#allow-insecure-localhost\n\n\n\n\n\n\n\nAdministrator Email\n\n\nThe Administrator Email is the contact email for the person who is the main admin\nof your Ponzu CMS. This can be changed at any point, but once a Let's Encrypt\ncertificate has been fetched using an Administrator Email, it will remain the \ncontact until a new certificate is requested. \n\n\n\n\nClient Secret\n\n\nThe Client Secret is a secure value used by the server to sign tokens and authenticate requests.\n\nDo not share this\n value with any untrusted party.\n\n\n\n\nSecurity and the Client Secret\n\n\nHTTP requests with a valid token, signed with the Client Secret, can take any\naction an Admin can within the CMS. Be cautious of this when sharing account\nlogins or details with anyone.\n\n\n\n\n\n\nEtag Header\n\n\nThe Etag Header value is automatically created when content is changed and serves\nas a caching validation mechanism.\n\n\n\n\nCORS\n\n\nCORS, or \"Cross-Origin Resource Sharing\" is a security setting which defines how\nresources (or URLs) can be accessed from outside clients / domains. By default, \nPonzu HTTP APIs can be accessed from any origin, meaning a script from an unknown\nwebsite could fetch data. \n\n\nBy disabling CORS, you limit API requests to only the Domain Name you set.\n\n\n\n\nGZIP\n\n\nGZIP is a popular codec which when applied to most HTTP responses, decreases data\ntransmission size and response times. The GZIP setting on Ponzu has a minor \nside-effect of using more CPU, so you can disable it if you notice your system \nis CPU-constrained. However, traffic levels would need to be extremely demanding\nfor this to be noticeable.\n\n\n\n\nHTTP Cache\n\n\nThe HTTP Cache configuration allows a system to disable the default HTTP cache,\nwhich saves the server from repeating API queries and sending responses -- it's\ngenerally advised to keep this enabled unless you have \nfrequently\n changing data.\n\n\nThe \nMax-Age\n value setting overrides the default 2592000-second (30 day) cache\n\nmax-age\n duration set in API response headers. The \n0\n value is an alias to \n\n2592000\n, so check the \nDisable HTTP Cache\n box if you don't want any caching.\n\n\n\n\nInvalidate Cache\n\n\nIf this box is checked and then the configuration is saved, the server will \nre-generate an Etag to send in responses. By doing so, the cache becomes invalidated\nand reset so new content or assets will be included in previously cached responses.\n\n\nThe cache is invalidated when content changes, so this is typically not a widely \nused setting.\n\n\n\n\nDatabase Backup Credentials\n\n\nIn order to enable HTTP backups of the components that make up your system, you\nwill need to add an HTTP Basic Auth user and password pair. When used to \n\nrun backups\n, the \nuser:password\n pair tells your server\nthat the backup request is made from a trusted party. \n\n\n\n\nBackup Access with Credentials\n\n\nThis \nuser:password\n pair should not be shared outside of your organization as \nit allows full database downloads and archives of your system's uploads.", + "title": "Settings" + }, + { + "location": "/System-Configuration/Settings/#site-name", + "text": "The Site Name setting changes the displayed name on your admin dashboard. This is\nvisible publicly on the /admin/login page.", + "title": "Site Name" + }, + { + "location": "/System-Configuration/Settings/#domain-name", + "text": "Internally, Ponzu needs to know where its canonical HTTP access origin is, and\nrequires 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 \ndomain at it, you need to change the Domain Name setting to match. This is \nespecially important when fetching TLS (SSL) certificates from Let's Encrypt \n- since the process requires an active, verifiable domain. To set up your server\nwith TLS over HTTPS connections, follow these steps: Set your Domain Name in the system configuration Set the Administrator Email to register with Let's Encrypt Stop your Ponzu server Run your Ponzu server with the --https flag e.g. $ ponzu run --https Visit your CMS admin with https:// prepended to your URL Verifying HTTPS / TLS Connections If successful, your APIs and CMS will be accessible via HTTPS, and you will\nsee a green indicator near the URL bar of most browsers. This also enables \nyour server to use the HTTP/2 protocol.", + "title": "Domain Name" + }, + { + "location": "/System-Configuration/Settings/#development-environment", + "text": "You can test HTTPS HTTP/2 connections in your development environment on localhost ,\nby 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\nsecure, follow the steps outlined in the CLI message, or here: If your browser rejects HTTPS requests, try allowing insecure connections on localhost.\non Chrome, visit chrome://flags/#allow-insecure-localhost", + "title": "Development Environment" + }, + { + "location": "/System-Configuration/Settings/#administrator-email", + "text": "The Administrator Email is the contact email for the person who is the main admin\nof your Ponzu CMS. This can be changed at any point, but once a Let's Encrypt\ncertificate has been fetched using an Administrator Email, it will remain the \ncontact until a new certificate is requested.", + "title": "Administrator Email" + }, + { + "location": "/System-Configuration/Settings/#client-secret", + "text": "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. Security and the Client Secret HTTP requests with a valid token, signed with the Client Secret, can take any\naction an Admin can within the CMS. Be cautious of this when sharing account\nlogins or details with anyone.", + "title": "Client Secret" + }, + { + "location": "/System-Configuration/Settings/#etag-header", + "text": "The Etag Header value is automatically created when content is changed and serves\nas a caching validation mechanism.", + "title": "Etag Header" + }, + { + "location": "/System-Configuration/Settings/#cors", + "text": "CORS, or \"Cross-Origin Resource Sharing\" is a security setting which defines how\nresources (or URLs) can be accessed from outside clients / domains. By default, \nPonzu HTTP APIs can be accessed from any origin, meaning a script from an unknown\nwebsite could fetch data. By disabling CORS, you limit API requests to only the Domain Name you set.", + "title": "CORS" + }, + { + "location": "/System-Configuration/Settings/#gzip", + "text": "GZIP is a popular codec which when applied to most HTTP responses, decreases data\ntransmission size and response times. The GZIP setting on Ponzu has a minor \nside-effect of using more CPU, so you can disable it if you notice your system \nis CPU-constrained. However, traffic levels would need to be extremely demanding\nfor this to be noticeable.", + "title": "GZIP" + }, + { + "location": "/System-Configuration/Settings/#http-cache", + "text": "The HTTP Cache configuration allows a system to disable the default HTTP cache,\nwhich saves the server from repeating API queries and sending responses -- it's\ngenerally 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.", + "title": "HTTP Cache" + }, + { + "location": "/System-Configuration/Settings/#invalidate-cache", + "text": "If this box is checked and then the configuration is saved, the server will \nre-generate an Etag to send in responses. By doing so, the cache becomes invalidated\nand 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 \nused setting.", + "title": "Invalidate Cache" + }, + { + "location": "/System-Configuration/Settings/#database-backup-credentials", + "text": "In order to enable HTTP backups of the components that make up your system, you\nwill need to add an HTTP Basic Auth user and password pair. When used to run backups , the user:password pair tells your server\nthat the backup request is made from a trusted party. Backup Access with Credentials This user:password pair should not be shared outside of your organization as \nit allows full database downloads and archives of your system's uploads.", + "title": "Database Backup Credentials" + }, + { + "location": "/System-Deployment/Docker/", + "text": "Ponzu Docker build\n\n\nPonzu is distributed as a \ndocker image\n, \nwhich aids in ponzu deployment. The Dockerfile in this directory is used by Ponzu \nto generate the docker image which contains the ponzu executable.\n\n\nIf you are deploying your own Ponzu project, you can write a new Dockerfile that\nis based from the \nponzu/ponzu\n image of your choice. For example:\n\n\nFROM\n ponzu/ponzu:latest\n\n\n\n# your project set up ...\n\n\n# ...\n\n\n# ...\n\n\n\n\n\n\nThe following are convenient commands during development of Ponzu core:\n\n\nBuild the docker image. Run from the root of the project.\n\n\n# from the root of ponzu:\n\ndocker build -t ponzu-dev\n\n\n\n\n\nStart the image, share the local directory and pseudo terminal (tty) into for debugging:\n\n\ndocker run -v \n$(\npwd\n)\n:/go/src/github.com/ponzu-cms/ponzu -it ponzu-dev\n\npwd\n \n# will output the go src directory for ponzu\n\nponzu version \n# will output the ponzu version\n\n\n# make an edit on your local and rebuild\n\ngo install ./...\n\n\n\n\n\nSpecial thanks to \n@krismeister\n for contributing this!", + "title": "Docker" + }, + { + "location": "/System-Deployment/Docker/#ponzu-docker-build", + "text": "Ponzu is distributed as a docker image , \nwhich aids in ponzu deployment. The Dockerfile in this directory is used by Ponzu \nto generate the docker image which contains the ponzu executable. If you are deploying your own Ponzu project, you can write a new Dockerfile that\nis based from the ponzu/ponzu image of your choice. For example: FROM ponzu/ponzu:latest # your project set up ... # ... # ...", + "title": "Ponzu Docker build" + }, + { + "location": "/System-Deployment/Docker/#the-following-are-convenient-commands-during-development-of-ponzu-core", + "text": "", + "title": "The following are convenient commands during development of Ponzu core:" + }, + { + "location": "/System-Deployment/Docker/#build-the-docker-image-run-from-the-root-of-the-project", + "text": "# from the root of ponzu: \ndocker build -t ponzu-dev", + "title": "Build the docker image. Run from the root of the project." + }, + { + "location": "/System-Deployment/Docker/#start-the-image-share-the-local-directory-and-pseudo-terminal-tty-into-for-debugging", + "text": "docker run -v $( pwd ) :/go/src/github.com/ponzu-cms/ponzu -it ponzu-dev pwd # will output the go src directory for ponzu \nponzu version # will output the ponzu version # make an edit on your local and rebuild \ngo install ./... Special thanks to @krismeister for contributing this!", + "title": "Start the image, share the local directory and pseudo terminal (tty) into for debugging:" + }, + { + "location": "/System-Deployment/SysV-Style/", + "text": "For reference, here is an example init script to run Ponzu servers. You must \ndefine the \nPROJECT_DIR\n \n \nRUNAS\n variables by replacing \nPROJECT DIRECTORY\n\n\n \nUSER\n in the script below:\n\n\n#!/bin/sh\n\n\n### BEGIN INIT INFO\n\n\n# Provides: ponzu-server\n\n\n# Required-Start: $local_fs $network $named $time $syslog\n\n\n# Required-Stop: $local_fs $network $named $time $syslog\n\n\n# Default-Start: 2 3 4 5\n\n\n# Default-Stop: 0 1 6\n\n\n# Description: Ponzu API \n Admin server\n\n\n### END INIT INFO\n\n\n\nPROJECT_DIR\n=\nPROJECT DIRECTORY\n\n\nSCRIPT\n=\ncd $PROJECT_DIR \n ponzu run --port=80\n \n# add --https here to get TLS/HTTPS\n\n\nRUNAS\n=\nUSER\n\n\n\nPIDFILE\n=\n/var/run/ponzu-server.pid\n\nLOGFILE\n=\n/var/log/ponzu-server.log\n\nstart\n()\n \n{\n\n \nif\n \n[\n -f /var/run/\n$PIDNAME\n \n]\n \n \nkill\n -0 \n$(\ncat /var/run/\n$PIDNAME\n)\n;\n \nthen\n\n \necho\n \nService already running\n \n2\n\n \nreturn\n \n1\n\n \nfi\n\n \necho\n \nStarting service\u2026\n \n2\n\n \nlocal\n \nCMD\n=\n$SCRIPT\n \n \\\n$LOGFILE\n\\\n \n echo \\$!\n\n su -c \n$CMD\n \n$RUNAS\n \n \n$PIDFILE\n\n \necho\n \nService started\n \n2\n\n\n}\n\n\nstop\n()\n \n{\n\n \nif\n \n[\n ! -f \n$PIDFILE\n \n]\n \n||\n ! \nkill\n -0 \n$(\ncat \n$PIDFILE\n)\n;\n \nthen\n\n \necho\n \nService not running\n \n2\n\n \nreturn\n \n1\n\n \nfi\n\n \necho\n \nStopping service\u2026\n \n2\n\n \nkill\n -15 \n$(\ncat \n$PIDFILE\n)\n \n rm -f \n$PIDFILE\n\n \necho\n \nService stopped\n \n2\n\n\n}\n\n\nuninstall\n()\n \n{\n\n \necho\n -n \nAre you really sure you want to uninstall this service? That cannot be undone. [yes|No] \n\n \nlocal\n SURE\n \nread\n SURE\n \nif\n \n[\n \n$SURE\n \n=\n \nyes\n \n]\n;\n \nthen\n\n stop\n rm -f \n$PIDFILE\n\n \necho\n \nNotice: log file is not be removed: \n$LOGFILE\n \n2\n\n update-rc.d -f \nNAME\n remove\n rm -fv \n$0\n\n \nfi\n\n\n}\n\n\n\ncase\n \n$1\n in\n start\n)\n\n start\n \n;;\n\n stop\n)\n\n stop\n \n;;\n\n uninstall\n)\n\n uninstall\n \n;;\n\n restart\n)\n\n stop\n start\n \n;;\n\n *\n)\n\n \necho\n \nUsage: \n$0\n {start|stop|restart|uninstall}\n\n\nesac", + "title": "SysV Style" + } + ] +} \ No newline at end of file diff --git a/docs/build/sitemap.xml b/docs/build/sitemap.xml new file mode 100644 index 00000000..b44409df --- /dev/null +++ b/docs/build/sitemap.xml @@ -0,0 +1,182 @@ + + + + + + / + 2017-05-22 + daily + + + + + + + /CLI/General-Usage/ + 2017-05-22 + daily + + + + /CLI/Generating-References/ + 2017-05-22 + daily + + + + + + + + /Content/An-Overview/ + 2017-05-22 + daily + + + + /Content/Extending-Content/ + 2017-05-22 + daily + + + + + + + + /Form-Fields/HTML-Inputs/ + 2017-05-22 + daily + + + + + + + + /HTTP-APIs/Content/ + 2017-05-22 + daily + + + + /HTTP-APIs/File-Metadata/ + 2017-05-22 + daily + + + + /HTTP-APIs/Search/ + 2017-05-22 + daily + + + + + + + + /Interfaces/API/ + 2017-05-22 + daily + + + + /Interfaces/Editor/ + 2017-05-22 + daily + + + + /Interfaces/Format/ + 2017-05-22 + daily + + + + /Interfaces/Item/ + 2017-05-22 + daily + + + + /Interfaces/Search/ + 2017-05-22 + daily + + + + + + + + /Ponzu-Addons/Creating-Addons/ + 2017-05-22 + daily + + + + /Ponzu-Addons/Using-Addons/ + 2017-05-22 + daily + + + + + + + + /Quickstart/Overview/ + 2017-05-22 + daily + + + + + + + + /References/Overview/ + 2017-05-22 + daily + + + + + + + + /Running-Backups/Backups/ + 2017-05-22 + daily + + + + + + + + /System-Configuration/Settings/ + 2017-05-22 + daily + + + + + + + + /System-Deployment/Docker/ + 2017-05-22 + daily + + + + /System-Deployment/SysV-Style/ + 2017-05-22 + daily + + + + + \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 00000000..71f0cb55 --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,27 @@ +theme: material +site_name: Ponzu +docs_dir: src +site_dir: build +repo_name: ponzu-cms/ponzu +repo_url: https://github.com/ponzu-cms/ponzu + +markdown_extensions: + - codehilite(linenums=false) + - meta + - admonition + - toc(permalink=true) + +google_analytics: + - 'UA-98609560-1' + - auto + +extra: + palette: + primary: grey + accent: light-blue + social: + - type: github + link: https://github.com/ponzu-cms + - type: twitter + link: https://twitter.com/ponzu_cms + logo: images/logo.png diff --git a/docs/release.sh b/docs/release.sh new file mode 100644 index 00000000..b84da01b --- /dev/null +++ b/docs/release.sh @@ -0,0 +1,16 @@ +#! /bin/bash +set -e + +echo "---- [release] Building Docs ----" +docker run --rm -it -p 8000:8000 -v `pwd`:/docs squidfunk/mkdocs-material build + +cp CNAME ./build + +git add -A +git commit -m "$1" + +echo "---- [release] Push: Master ----" +git push origin master + +echo "---- [release] Push: Build ----" +git subtree push --prefix build origin gh-pages diff --git a/docs/src/CLI/General-Usage.md b/docs/src/CLI/General-Usage.md new file mode 100644 index 00000000..3a4544f2 --- /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 00000000..77cd16a3 --- /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 00000000..1e9d53e0 --- /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 00000000..91a32a00 --- /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 00000000..b79c5dd9 --- /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 00000000..6a3e387c --- /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 00000000..19d6ab6b --- /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 00000000..2939ccec --- /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 00000000..1d9ab825 --- /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 00000000..63d3cebc --- /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 00000000..8a259d3d --- /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 00000000..32f250bd --- /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 00000000..a7cd0d16 --- /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 00000000..c6aed404 --- /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 00000000..78200c56 --- /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 00000000..6c4bec59 --- /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 00000000..8f5e40ef --- /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 00000000..15632a06 --- /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 00000000..71fd2ec1 --- /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 00000000..a998a38b --- /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 00000000..565b3997 --- /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 0000000000000000000000000000000000000000..dcea71c755d97dfc93e255c6e38ef8fa53166436 GIT binary patch literal 4759 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 literal 0 HcmV?d00001 diff --git a/docs/src/images/editor-file.png b/docs/src/images/editor-file.png new file mode 100644 index 0000000000000000000000000000000000000000..93ab56f851494f31749cfb5e9c55e12869a1fd5f GIT binary patch literal 4485 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 literal 0 HcmV?d00001 diff --git a/docs/src/images/editor-input-repeater.png b/docs/src/images/editor-input-repeater.png new file mode 100644 index 0000000000000000000000000000000000000000..5ab4256ab82026d3ae29d6a535730221a1fab5dd GIT binary patch literal 2682 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 literal 0 HcmV?d00001 diff --git a/docs/src/images/editor-richtext.png b/docs/src/images/editor-richtext.png new file mode 100644 index 0000000000000000000000000000000000000000..a953eda4f208bd9bdff331c71810537f74a1a1bb GIT binary patch literal 9381 zcmeHtX*iW{`|gUO5`LAC!mo(Tl@N=Cj4L8@hDDKio@bIwNkrx`^Q_E6NJ28tWzM`T zGs`^uul2tFee6$rAMd-5y+7^OhugB&v+n19uKT*q>pag}&=Un2^7C}(5eNi1T2@jS zfgmzLAWj9GC4qNNUn%q@{IY+DRy_+p9%o+!!rxaNq@FscSQ|MwKesbP7+YCe8gkhi z*clpH*_&89tPsKUKNJ9qSP5TXws2&lI#C8TMAQPuNdKOK120KfKQ`Il9aXCv$t=#oiI1?VW3b?4v=tde(Bff9ZpKAyb~ zqrtyBR_mmwsi|Q*1STBZXlc@Ob01mP;-mT+VJ>MYe|uy2aDS-&IOE5U{V{22X?8w7 zHG`ISYLb$#)$^Zz)V8XUvT|;5F!+1s(5rrNaq-nbWctBMO;N`P(tSIBdwC=)KR=%Z zN9S#Mir1+9$?AA*S5?Q?A&j>6$kg^J#Mca_d>@tp8O^`nnVFlHS&r~q{`w@Yk}0pG ztE(HZ!tp6NHL%3g`4IatM_gC6Ze~61? z&q2we4wiVAM=BJRl$7G`Jb!3|(cbkySx(epq@<)^Rytmzx4nIR_}OSZGgyBdpLvuA zDw&!`H~)r&ze!h;NN!$UU%Yi)4sxYZVtswRV0BOa2!zgyZzx}NZS8Bq;^R1cuJ_4tH8wNeVSDNKqyfb0-F3lK_mwJ{ix)30 z4izO!hcXll4-fB)%SfB;uKfvLY&1I9US6haYw#uI^Ez~au;)0hjz5x<>+4CAacsJx zVY2k=bHnWfL3^Y4dVVzBeUmV&%0DD~9w^U)<%+obrs1Pj6|hBR=0k;>R$Z$*dD+>r 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 D^y(Y} literal 0 HcmV?d00001 diff --git a/docs/src/images/editor-select.png b/docs/src/images/editor-select.png new file mode 100644 index 0000000000000000000000000000000000000000..ffd0ca6864b0100797a2459d8a200bfcc565b722 GIT binary patch literal 3997 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 P&exEMA;O?s&l&S?%1@Zjxm}Gm1*~ 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 P0Xhh6he0uHuipI^p47Nf literal 0 HcmV?d00001 diff --git a/docs/src/images/logo.png b/docs/src/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..40b7b2121ee3ef872df47e8a664adf8bbc6b2a6f GIT binary patch literal 19734 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 literal 0 HcmV?d00001 diff --git a/docs/src/images/ponzu-banner.png b/docs/src/images/ponzu-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..855f97e384401a2c0605bfd09a31dea934290500 GIT binary patch literal 909819 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 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 From abf2c7e97b45ca9c90f0f3b3d1b6272d3ee4370b Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Mon, 29 May 2017 11:40:05 -0700 Subject: [PATCH 2/4] remove unnecessary files --- docs/.gitignore | 0 docs/CNAME | 1 - docs/release.sh | 16 ---------------- 3 files changed, 17 deletions(-) delete mode 100644 docs/.gitignore delete mode 100644 docs/CNAME delete mode 100644 docs/release.sh diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index 119323fe..00000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -docs.ponzu-cms.org diff --git a/docs/release.sh b/docs/release.sh deleted file mode 100644 index b84da01b..00000000 --- a/docs/release.sh +++ /dev/null @@ -1,16 +0,0 @@ -#! /bin/bash -set -e - -echo "---- [release] Building Docs ----" -docker run --rm -it -p 8000:8000 -v `pwd`:/docs squidfunk/mkdocs-material build - -cp CNAME ./build - -git add -A -git commit -m "$1" - -echo "---- [release] Push: Master ----" -git push origin master - -echo "---- [release] Push: Build ----" -git subtree push --prefix build origin gh-pages From 0c395e5df3f82037fbdc6076df87d81ea5921574 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Mon, 29 May 2017 11:43:40 -0700 Subject: [PATCH 3/4] adding --docs and --docs-port flags to config and run local docs server --- cmd/ponzu/main.go | 33 ++++++++++++++++++++++++++------- system/admin/server.go | 21 +++++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/cmd/ponzu/main.go b/cmd/ponzu/main.go index 7572dcc9..25e16942 100644 --- a/cmd/ponzu/main.go +++ b/cmd/ponzu/main.go @@ -26,17 +26,20 @@ import ( ) var ( - port int httpsport int + port int + docsport int https bool devhttps bool + docs bool cli bool // for ponzu internal / core development - dev bool - fork string gocmd string - year = fmt.Sprintf("%d", time.Now().Year()) + fork string + dev bool + + year = fmt.Sprintf("%d", time.Now().Year()) ) var rootCmd = &cobra.Command{ @@ -55,7 +58,7 @@ The segments, separated by a comma, describe which services to start, either if the server should utilize TLS encryption - served over HTTPS, which is automatically managed using Let's Encrypt (https://letsencrypt.org) -Defaults to 'run -port=8080 admin,api' (running Admin & API on port 8080, without TLS) +Defaults to '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 @@ -81,6 +84,13 @@ $ ponzu run --port=8888 api`, addTLS = "--dev-https" } + var addDocs string + if docs { + addDocs = "--docs" + } else { + addDocs = "--docs=false" + } + var services string if len(args) > 0 { services = args[0] @@ -95,6 +105,8 @@ $ ponzu run --port=8888 api`, services, fmt.Sprintf("--port=%d", port), fmt.Sprintf("--https-port=%d", httpsport), + fmt.Sprintf("--docs-port=%d", docsport), + addDocs, addTLS, ) serve.Stderr = os.Stderr @@ -137,6 +149,11 @@ var serveCmd = &cobra.Command{ } } + // run docs server if --docs is true + if docs { + admin.Docs(docsport) + } + // save the https port the system is listening on err := db.PutConfig("https_port", fmt.Sprintf("%d", httpsport)) if err != nil { @@ -176,14 +193,16 @@ var serveCmd = &cobra.Command{ func init() { for _, cmd := range []*cobra.Command{runCmd, serveCmd} { - cmd.Flags().IntVar(&port, "port", 8080, "port for ponzu to bind its HTTP listener") cmd.Flags().IntVar(&httpsport, "https-port", 443, "port for ponzu to bind its HTTPS listener") + cmd.Flags().IntVar(&port, "port", 8080, "port for ponzu to bind its HTTP listener") + cmd.Flags().IntVar(&docsport, "docs-port", 1234, "[dev environment] override the documentation server port") + cmd.Flags().BoolVar(&docs, "docs", false, "[dev environment] run HTTP server to view local HTML documentation") cmd.Flags().BoolVar(&https, "https", false, "enable automatic TLS/SSL certificate management") cmd.Flags().BoolVar(&devhttps, "dev-https", false, "[dev environment] enable automatic TLS/SSL certificate management") } - RegisterCmdlineCommand(runCmd) RegisterCmdlineCommand(serveCmd) + RegisterCmdlineCommand(runCmd) pflags := rootCmd.PersistentFlags() pflags.StringVar(&gocmd, "gocmd", "go", "custom go command if using beta or new release of Go") diff --git a/system/admin/server.go b/system/admin/server.go index 426dabd5..9f28a0d8 100644 --- a/system/admin/server.go +++ b/system/admin/server.go @@ -1,6 +1,7 @@ package admin import ( + "fmt" "log" "net/http" "os" @@ -62,3 +63,23 @@ func Run() { // Database & uploads backup via HTTP route registered with Basic Auth middleware. http.HandleFunc("/admin/backup", system.BasicAuth(backupHandler)) } + +// Docs adds the documentation file server to the server, accessible at +// http://localhost:1234 by default +func Docs(port int) { + pwd, err := os.Getwd() + if err != nil { + log.Fatalln("Couldn't find current directory for file server.") + } + + docsDir := filepath.Join(pwd, "docs", "build") + + addr := fmt.Sprintf(":%d", port) + url := fmt.Sprintf("http://localhost%s", addr) + + fmt.Println("") + fmt.Println("View documentation offline at:", url) + fmt.Println("") + + go http.ListenAndServe(addr, http.FileServer(http.Dir(docsDir))) +} From a5c4288a8c743cac1637184b252595f150fb6748 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Sat, 10 Jun 2017 16:05:43 -0700 Subject: [PATCH 4/4] update docs with docs flag documentation --- docs/build/CLI/General-Usage/index.html | 16 +++++---- docs/build/mkdocs/search_index.json | 4 +-- docs/build/sitemap.xml | 44 ++++++++++++------------- docs/src/CLI/General-Usage.md | 5 ++- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/docs/build/CLI/General-Usage/index.html b/docs/build/CLI/General-Usage/index.html index 884336ae..3e583c01 100644 --- a/docs/build/CLI/General-Usage/index.html +++ b/docs/build/CLI/General-Usage/index.html @@ -1047,11 +1047,15 @@

run¶< '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)

+

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)
  • +
  • --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:

diff --git a/docs/build/mkdocs/search_index.json b/docs/build/mkdocs/search_index.json index b9dca9c9..b115dd13 100644 --- a/docs/build/mkdocs/search_index.json +++ b/docs/build/mkdocs/search_index.json @@ -27,7 +27,7 @@ }, { "location": "/CLI/General-Usage/", - "text": "$ ponzu \n[\nflags\n]\n \ncommand\n \nparams\n\n\n\n\n\n\nCommands\n\n\nnew\n\n\nCreates a project directory of the name supplied as a parameter immediately\nfollowing the 'new' option in the $GOPATH/src directory. Note: 'new' depends on \nthe program 'git' and possibly a network connection. If there is no local \nrepository to clone from at the local machine's $GOPATH, 'new' will attempt to \nclone the 'github.com/ponzu-cms/ponzu' package from over the network.\n\n\nExample:\n\n\n$ ponzu new github.com/nilslice/proj\n\n New ponzu project created at \n$GOPATH\n/src/github.com/nilslice/proj\n\n\n\n\n\n\n\ngenerate, gen, g\n\n\nGenerate boilerplate code for various Ponzu components, such as \ncontent\n.\n\n\nExample:\n\n\n generator struct fields and built-in types...\n \n|\n \n|\n\n v v \n$ ponzu gen content review title:\nstring\n body:\nstring\n:richtext rating:\nint\n\n ^ ^\n \n|\n \n|\n\n struct \ntype\n \n(\noptional\n)\n input view specifier\n\n\n\n\n\nThe command above will generate the file \ncontent/review.go\n with boilerplate\nmethods, as well as struct definition, and corresponding field tags like:\n\n\ntype\n \nReview\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nTitle\n \nstring\n \n`json:\ntitle\n`\n\n \nBody\n \nstring\n \n`json:\nbody\n`\n\n \nRating\n \nint\n \n`json:\nrating\n`\n\n \nTags\n \n[]\nstring\n \n`json:\ntags\n`\n\n\n}\n\n\n\n\n\n\nThe generate command will intelligently parse more sophisticated field names\nsuch as 'field_name' and convert it to 'FieldName' and vice versa, only where \nappropriate as per common Go idioms. Errors will be reported, but successful \ngenerate commands return nothing.\n\n\nInput View Specifiers\n \n(optional)\n\n\nThe CLI can optionally parse a third parameter on the fields provided to generate \nthe type of HTML view an editor field is presented within. If no third parameter\nis added, a plain text HTML input will be generated. In the example above, the \nargument shown as \nbody:string:richtext\n would show the Richtext input instead\nof a plain text HTML input (as shown in the screenshot). The following input\nview specifiers are implemented:\n\n\n\n\n\n\n\n\nCLI parameter\n\n\nGenerates\n\n\n\n\n\n\n\n\n\n\ncheckbox\n\n\neditor.Checkbox()\n\n\n\n\n\n\ncustom\n\n\ngenerates a pre-styled empty div to fill with HTML\n\n\n\n\n\n\nfile\n\n\neditor.File()\n\n\n\n\n\n\nhidden\n\n\neditor.Input()\n + uses type=hidden\n\n\n\n\n\n\ninput, text\n\n\neditor.Input()\n\n\n\n\n\n\nrichtext\n\n\neditor.Richtext()\n\n\n\n\n\n\nselect\n\n\neditor.Select()\n\n\n\n\n\n\ntextarea\n\n\neditor.Textarea()\n\n\n\n\n\n\ntags\n\n\neditor.Tags()\n\n\n\n\n\n\n\n\nGenerate Content References\n\n\nIt's also possible to generate all of the code needed to create references between\nyour content types. The syntax to do so is below, but refer to the \ndocumentation\n\nfor more details:\n\n\n$ ponzu gen c author name:string genre:string:select\n$ ponzu gen c book title:string author:@author,name,genre \n\n\n\n\n\nThe commands above will generate a \nBook\n Content type with a reference to an\n\nAuthor\n item, by also generating a \nreference.Select\n\nas the view for the \nauthor\n field.\n\n\n\n\nbuild\n\n\nFrom within your Ponzu project directory, running build will copy and move \nthe necessary files from your workspace into the vendored directory, and \nwill build/compile the project to then be run. \n\n\nOptional flags:\n- \n--gocmd\n sets the binary used when executing \ngo build\n within \nponzu\n build step\n\n\nExample:\n\n\n$ ponzu build\n\n(\nor\n)\n\n$ ponzu build --gocmd\n=\ngo1.8rc1 \n# useful for testing\n\n\n\n\n\n\nErrors will be reported, but successful build commands return nothing.\n\n\n\n\nrun\n\n\nStarts the HTTP server for the JSON API, Admin System, or both.\nThe segments, separated by a comma, describe which services to start, either \n'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, \nif the server should utilize TLS encryption - served over HTTPS, which is\nautomatically managed using Let's Encrypt (https://letsencrypt.org) \n\n\nOptional flags:\n- \n--port\n sets the port on which the server listens for HTTP requests [defaults to 8080]\n- \n--https-port\n sets the port on which the server listens for HTTPS requests [defaults to 443]\n- \n--https\n enables auto HTTPS management via Let's Encrypt (port is always 443)\n- \n--dev-https\n generates self-signed SSL certificates for development-only (port is 10443)\n\n\nExample: \n\n\n$ ponzu run\n\n(\nor\n)\n\n$ ponzu run --port\n=\n8080\n --https admin,api\n\n(\nor\n)\n \n$ ponzu run admin\n\n(\nor\n)\n\n$ ponzu run --port\n=\n8888\n api\n\n(\nor\n)\n\n$ ponzu --dev-https run\n\n\n\n\n\nDefaults to \n$ ponzu run --port=8080 admin,api\n (running Admin \n API on port 8080, without TLS)\n\n\nNote:\n \nAdmin and API cannot run on separate processes unless you use a copy of the\ndatabase, since the first process to open it receives a lock. If you intend\nto run the Admin and API on separate processes, you must call them with the\n'ponzu' command independently.\n\n\n\n\nupgrade\n\n\nWill backup your own custom project code (like content, addons, uploads, etc) so\nwe can safely re-clone Ponzu from the latest version you have or from the network \nif necessary. Before running \n$ ponzu upgrade\n, you should update the \nponzu\n\npackage by running \n$ go get -u github.com/ponzu-cms/ponzu/...\n \n\n\nExample:\n\n\n$ ponzu upgrade\n\n\n\n\n\n\n\nadd, a\n\n\nDownloads an addon to GOPATH/src and copies it to the current Ponzu project's\n\n/addons\n directory.\n\n\nExample:\n\n\n$ ponzu add github.com/bosssauce/fbscheduler\n\n\n\n\n\nErrors will be reported, but successful add commands return nothing.\n\n\n\n\nversion, v\n\n\nPrints the version of Ponzu your project is using. Must be called from within a \nPonzu project directory. By passing the \n--cli\n flag, the \nversion\n command will \nprint the version of the Ponzu CLI you have installed.\n\n\nExample:\n\n\n$ ponzu version\nPonzu v0.8.2\n\n# (or)\n\n$ ponzu version --cli\nPonzu v0.9.2\n\n\n\n\n\n\n\nContributing\n\n\n\n\nCheckout branch ponzu-dev\n\n\nMake code changes\n\n\nTest changes to ponzu-dev branch\n\n\nmake a commit to ponzu-dev\n\n\nto manually test, you will need to use a new copy (ponzu new path/to/code), \nbut pass the \n--dev\n flag so that ponzu generates a new copy from the \nponzu-dev\n \nbranch, not master by default (i.e. \n$ponzu new --dev /path/to/code\n)\n\n\nbuild and run with \n$ ponzu build\n and \n$ ponzu run\n\n\n\n\n\n\nTo add back to master: \n\n\nfirst push to origin ponzu-dev\n\n\ncreate a pull request \n\n\nwill then be merged into master\n\n\n\n\n\n\n\n\nA typical contribution workflow might look like:\n\n\n# clone the repository and checkout ponzu-dev\n\n$ git clone https://github.com/ponzu-cms/ponzu path/to/local/ponzu \n# (or your fork)\n\n$ git checkout ponzu-dev\n\n\n# install ponzu with go get or from your own local path\n\n$ go get github.com/ponzu-cms/ponzu/...\n\n# or\n\n$ \ncd\n /path/to/local/ponzu \n$ go install ./...\n\n\n# edit files, add features, etc\n\n$ git add -A\n$ git commit -m \nedited files, added features, etc\n\n\n\n# now you need to test the feature.. make a new ponzu project, but pass --dev flag\n\n$ ponzu --dev new /path/to/new/project \n# will create $GOPATH/src/path/to/new/project\n\n\n\n# build \n run ponzu from the new project directory\n\n$ \ncd\n /path/to/new/project\n$ ponzu build \n ponzu run\n\n\n# push to your origin:ponzu-dev branch and create a PR at ponzu-cms/ponzu\n\n$ git push origin ponzu-dev\n\n# ... go to https://github.com/ponzu-cms/ponzu and create a PR\n\n\n\n\n\n\nNote:\n if you intend to work on your own fork and contribute from it, you will\nneed to also pass \n--fork=path/to/your/fork\n (using OS-standard filepath structure),\nwhere \npath/to/your/fork\n \nmust\n be within \n$GOPATH/src\n, and you are working from a branch\ncalled \nponzu-dev\n. \n\n\nFor example: \n\n\n# ($GOPATH/src is implied in the fork path, do not add it yourself)\n\n$ ponzu new --dev --fork\n=\ngithub.com/nilslice/ponzu /path/to/new/project", + "text": "$ ponzu \n[\nflags\n]\n \ncommand\n \nparams\n\n\n\n\n\n\nCommands\n\n\nnew\n\n\nCreates a project directory of the name supplied as a parameter immediately\nfollowing the 'new' option in the $GOPATH/src directory. Note: 'new' depends on \nthe program 'git' and possibly a network connection. If there is no local \nrepository to clone from at the local machine's $GOPATH, 'new' will attempt to \nclone the 'github.com/ponzu-cms/ponzu' package from over the network.\n\n\nExample:\n\n\n$ ponzu new github.com/nilslice/proj\n\n New ponzu project created at \n$GOPATH\n/src/github.com/nilslice/proj\n\n\n\n\n\n\n\ngenerate, gen, g\n\n\nGenerate boilerplate code for various Ponzu components, such as \ncontent\n.\n\n\nExample:\n\n\n generator struct fields and built-in types...\n \n|\n \n|\n\n v v \n$ ponzu gen content review title:\nstring\n body:\nstring\n:richtext rating:\nint\n\n ^ ^\n \n|\n \n|\n\n struct \ntype\n \n(\noptional\n)\n input view specifier\n\n\n\n\n\nThe command above will generate the file \ncontent/review.go\n with boilerplate\nmethods, as well as struct definition, and corresponding field tags like:\n\n\ntype\n \nReview\n \nstruct\n \n{\n\n \nitem\n.\nItem\n\n\n \nTitle\n \nstring\n \n`json:\ntitle\n`\n\n \nBody\n \nstring\n \n`json:\nbody\n`\n\n \nRating\n \nint\n \n`json:\nrating\n`\n\n \nTags\n \n[]\nstring\n \n`json:\ntags\n`\n\n\n}\n\n\n\n\n\n\nThe generate command will intelligently parse more sophisticated field names\nsuch as 'field_name' and convert it to 'FieldName' and vice versa, only where \nappropriate as per common Go idioms. Errors will be reported, but successful \ngenerate commands return nothing.\n\n\nInput View Specifiers\n \n(optional)\n\n\nThe CLI can optionally parse a third parameter on the fields provided to generate \nthe type of HTML view an editor field is presented within. If no third parameter\nis added, a plain text HTML input will be generated. In the example above, the \nargument shown as \nbody:string:richtext\n would show the Richtext input instead\nof a plain text HTML input (as shown in the screenshot). The following input\nview specifiers are implemented:\n\n\n\n\n\n\n\n\nCLI parameter\n\n\nGenerates\n\n\n\n\n\n\n\n\n\n\ncheckbox\n\n\neditor.Checkbox()\n\n\n\n\n\n\ncustom\n\n\ngenerates a pre-styled empty div to fill with HTML\n\n\n\n\n\n\nfile\n\n\neditor.File()\n\n\n\n\n\n\nhidden\n\n\neditor.Input()\n + uses type=hidden\n\n\n\n\n\n\ninput, text\n\n\neditor.Input()\n\n\n\n\n\n\nrichtext\n\n\neditor.Richtext()\n\n\n\n\n\n\nselect\n\n\neditor.Select()\n\n\n\n\n\n\ntextarea\n\n\neditor.Textarea()\n\n\n\n\n\n\ntags\n\n\neditor.Tags()\n\n\n\n\n\n\n\n\nGenerate Content References\n\n\nIt's also possible to generate all of the code needed to create references between\nyour content types. The syntax to do so is below, but refer to the \ndocumentation\n\nfor more details:\n\n\n$ ponzu gen c author name:string genre:string:select\n$ ponzu gen c book title:string author:@author,name,genre \n\n\n\n\n\nThe commands above will generate a \nBook\n Content type with a reference to an\n\nAuthor\n item, by also generating a \nreference.Select\n\nas the view for the \nauthor\n field.\n\n\n\n\nbuild\n\n\nFrom within your Ponzu project directory, running build will copy and move \nthe necessary files from your workspace into the vendored directory, and \nwill build/compile the project to then be run. \n\n\nOptional flags:\n- \n--gocmd\n sets the binary used when executing \ngo build\n within \nponzu\n build step\n\n\nExample:\n\n\n$ ponzu build\n\n(\nor\n)\n\n$ ponzu build --gocmd\n=\ngo1.8rc1 \n# useful for testing\n\n\n\n\n\n\nErrors will be reported, but successful build commands return nothing.\n\n\n\n\nrun\n\n\nStarts the HTTP server for the JSON API, Admin System, or both.\nThe segments, separated by a comma, describe which services to start, either \n'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, \nif the server should utilize TLS encryption - served over HTTPS, which is\nautomatically managed using Let's Encrypt (https://letsencrypt.org) \n\n\nOptional flags:\n\n\n\n\n--port\n sets the port on which the server listens for HTTP requests [defaults to 8080]\n\n\n--https-port\n sets the port on which the server listens for HTTPS requests [defaults to 443]\n\n\n--https\n enables auto HTTPS management via Let's Encrypt (port is always 443)\n\n\n--dev-https\n generates self-signed SSL certificates for development-only (port is 10443)\n\n\n--docs\n runs a local documentation server in case of no network connection\n\n\n--docs-port\n sets the port on which the docs server listens for HTTP requests [defaults to 1234]\n\n\n\n\nExample: \n\n\n$ ponzu run\n\n(\nor\n)\n\n$ ponzu run --port\n=\n8080\n --https admin,api\n\n(\nor\n)\n \n$ ponzu run admin\n\n(\nor\n)\n\n$ ponzu run --port\n=\n8888\n api\n\n(\nor\n)\n\n$ ponzu run --dev-https\n\n\n\n\n\nDefaults to \n$ ponzu run --port=8080 admin,api\n (running Admin \n API on port 8080, without TLS)\n\n\nNote:\n \nAdmin and API cannot run on separate processes unless you use a copy of the\ndatabase, since the first process to open it receives a lock. If you intend\nto run the Admin and API on separate processes, you must call them with the\n'ponzu' command independently.\n\n\n\n\nupgrade\n\n\nWill backup your own custom project code (like content, addons, uploads, etc) so\nwe can safely re-clone Ponzu from the latest version you have or from the network \nif necessary. Before running \n$ ponzu upgrade\n, you should update the \nponzu\n\npackage by running \n$ go get -u github.com/ponzu-cms/ponzu/...\n \n\n\nExample:\n\n\n$ ponzu upgrade\n\n\n\n\n\n\n\nadd, a\n\n\nDownloads an addon to GOPATH/src and copies it to the current Ponzu project's\n\n/addons\n directory.\n\n\nExample:\n\n\n$ ponzu add github.com/bosssauce/fbscheduler\n\n\n\n\n\nErrors will be reported, but successful add commands return nothing.\n\n\n\n\nversion, v\n\n\nPrints the version of Ponzu your project is using. Must be called from within a \nPonzu project directory. By passing the \n--cli\n flag, the \nversion\n command will \nprint the version of the Ponzu CLI you have installed.\n\n\nExample:\n\n\n$ ponzu version\nPonzu v0.8.2\n\n# (or)\n\n$ ponzu version --cli\nPonzu v0.9.2\n\n\n\n\n\n\n\nContributing\n\n\n\n\nCheckout branch ponzu-dev\n\n\nMake code changes\n\n\nTest changes to ponzu-dev branch\n\n\nmake a commit to ponzu-dev\n\n\nto manually test, you will need to use a new copy (ponzu new path/to/code), \nbut pass the \n--dev\n flag so that ponzu generates a new copy from the \nponzu-dev\n \nbranch, not master by default (i.e. \n$ponzu new --dev /path/to/code\n)\n\n\nbuild and run with \n$ ponzu build\n and \n$ ponzu run\n\n\n\n\n\n\nTo add back to master: \n\n\nfirst push to origin ponzu-dev\n\n\ncreate a pull request \n\n\nwill then be merged into master\n\n\n\n\n\n\n\n\nA typical contribution workflow might look like:\n\n\n# clone the repository and checkout ponzu-dev\n\n$ git clone https://github.com/ponzu-cms/ponzu path/to/local/ponzu \n# (or your fork)\n\n$ git checkout ponzu-dev\n\n\n# install ponzu with go get or from your own local path\n\n$ go get github.com/ponzu-cms/ponzu/...\n\n# or\n\n$ \ncd\n /path/to/local/ponzu \n$ go install ./...\n\n\n# edit files, add features, etc\n\n$ git add -A\n$ git commit -m \nedited files, added features, etc\n\n\n\n# now you need to test the feature.. make a new ponzu project, but pass --dev flag\n\n$ ponzu --dev new /path/to/new/project \n# will create $GOPATH/src/path/to/new/project\n\n\n\n# build \n run ponzu from the new project directory\n\n$ \ncd\n /path/to/new/project\n$ ponzu build \n ponzu run\n\n\n# push to your origin:ponzu-dev branch and create a PR at ponzu-cms/ponzu\n\n$ git push origin ponzu-dev\n\n# ... go to https://github.com/ponzu-cms/ponzu and create a PR\n\n\n\n\n\n\nNote:\n if you intend to work on your own fork and contribute from it, you will\nneed to also pass \n--fork=path/to/your/fork\n (using OS-standard filepath structure),\nwhere \npath/to/your/fork\n \nmust\n be within \n$GOPATH/src\n, and you are working from a branch\ncalled \nponzu-dev\n. \n\n\nFor example: \n\n\n# ($GOPATH/src is implied in the fork path, do not add it yourself)\n\n$ ponzu new --dev --fork\n=\ngithub.com/nilslice/ponzu /path/to/new/project", "title": "General Usage" }, { @@ -52,7 +52,7 @@ }, { "location": "/CLI/General-Usage/#run", - "text": "Starts the HTTP server for the JSON API, Admin System, or both.\nThe segments, separated by a comma, describe which services to start, either \n'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, \nif the server should utilize TLS encryption - served over HTTPS, which is\nautomatically managed using Let's Encrypt (https://letsencrypt.org) Optional flags:\n- --port sets the port on which the server listens for HTTP requests [defaults to 8080]\n- --https-port sets the port on which the server listens for HTTPS requests [defaults to 443]\n- --https enables auto HTTPS management via Let's Encrypt (port is always 443)\n- --dev-https generates self-signed SSL certificates for development-only (port is 10443) Example: $ ponzu run ( or ) \n$ ponzu run --port = 8080 --https admin,api ( or ) \n$ ponzu run admin ( or ) \n$ ponzu run --port = 8888 api ( or ) \n$ ponzu --dev-https run Defaults to $ ponzu run --port=8080 admin,api (running Admin API on port 8080, without TLS) Note: \nAdmin and API cannot run on separate processes unless you use a copy of the\ndatabase, since the first process to open it receives a lock. If you intend\nto run the Admin and API on separate processes, you must call them with the\n'ponzu' command independently.", + "text": "Starts the HTTP server for the JSON API, Admin System, or both.\nThe segments, separated by a comma, describe which services to start, either \n'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, \nif the server should utilize TLS encryption - served over HTTPS, which is\nautomatically 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) --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: $ ponzu run ( or ) \n$ ponzu run --port = 8080 --https admin,api ( or ) \n$ ponzu run admin ( or ) \n$ ponzu run --port = 8888 api ( or ) \n$ ponzu run --dev-https Defaults to $ ponzu run --port=8080 admin,api (running Admin API on port 8080, without TLS) Note: \nAdmin and API cannot run on separate processes unless you use a copy of the\ndatabase, since the first process to open it receives a lock. If you intend\nto run the Admin and API on separate processes, you must call them with the\n'ponzu' command independently.", "title": "run" }, { diff --git a/docs/build/sitemap.xml b/docs/build/sitemap.xml index b44409df..300b8043 100644 --- a/docs/build/sitemap.xml +++ b/docs/build/sitemap.xml @@ -4,7 +4,7 @@ / - 2017-05-22 + 2017-06-10 daily @@ -13,13 +13,13 @@ /CLI/General-Usage/ - 2017-05-22 + 2017-06-10 daily /CLI/Generating-References/ - 2017-05-22 + 2017-06-10 daily @@ -29,13 +29,13 @@ /Content/An-Overview/ - 2017-05-22 + 2017-06-10 daily /Content/Extending-Content/ - 2017-05-22 + 2017-06-10 daily @@ -45,7 +45,7 @@ /Form-Fields/HTML-Inputs/ - 2017-05-22 + 2017-06-10 daily @@ -55,19 +55,19 @@ /HTTP-APIs/Content/ - 2017-05-22 + 2017-06-10 daily /HTTP-APIs/File-Metadata/ - 2017-05-22 + 2017-06-10 daily /HTTP-APIs/Search/ - 2017-05-22 + 2017-06-10 daily @@ -77,31 +77,31 @@ /Interfaces/API/ - 2017-05-22 + 2017-06-10 daily /Interfaces/Editor/ - 2017-05-22 + 2017-06-10 daily /Interfaces/Format/ - 2017-05-22 + 2017-06-10 daily /Interfaces/Item/ - 2017-05-22 + 2017-06-10 daily /Interfaces/Search/ - 2017-05-22 + 2017-06-10 daily @@ -111,13 +111,13 @@ /Ponzu-Addons/Creating-Addons/ - 2017-05-22 + 2017-06-10 daily /Ponzu-Addons/Using-Addons/ - 2017-05-22 + 2017-06-10 daily @@ -127,7 +127,7 @@ /Quickstart/Overview/ - 2017-05-22 + 2017-06-10 daily @@ -137,7 +137,7 @@ /References/Overview/ - 2017-05-22 + 2017-06-10 daily @@ -147,7 +147,7 @@ /Running-Backups/Backups/ - 2017-05-22 + 2017-06-10 daily @@ -157,7 +157,7 @@ /System-Configuration/Settings/ - 2017-05-22 + 2017-06-10 daily @@ -167,13 +167,13 @@ /System-Deployment/Docker/ - 2017-05-22 + 2017-06-10 daily /System-Deployment/SysV-Style/ - 2017-05-22 + 2017-06-10 daily diff --git a/docs/src/CLI/General-Usage.md b/docs/src/CLI/General-Usage.md index 3a4544f2..dbb89700 100644 --- a/docs/src/CLI/General-Usage.md +++ b/docs/src/CLI/General-Usage.md @@ -121,10 +121,13 @@ 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) +- `--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: ```bash @@ -136,7 +139,7 @@ $ ponzu run admin (or) $ ponzu run --port=8888 api (or) -$ ponzu --dev-https run +$ ponzu run --dev-https ``` Defaults to `$ ponzu run --port=8080 admin,api` (running Admin & API on port 8080, without TLS)