diff --git a/doc/MANUAL.md b/doc/MANUAL.md index 49a29c5..a10a89c 100644 --- a/doc/MANUAL.md +++ b/doc/MANUAL.md @@ -23,6 +23,9 @@ This is the manual for version %%VERSION%%. 1. [Tutorial](#tutorial) - [Your first form](#tutorial_firstform) - [Output types](#tutorial_output) + - [Fields](#tutorial_fields) + - [Uploads](#tutorial_uploads) + - [Validation](#tutorial_validation) 1. [Form config (JSON) files](#form_config) 1. [Field types](#field_types) - [String](#field_types_string) @@ -382,6 +385,8 @@ should not just output a result, but also the required HTTP headers to properly display it. This lets your send binary files (images, downloads, etc) to the browser. +Read more about output types in the '[Output types](#output_types)' section. + ### Fields As you've seen, we've kept the `fields` option empty in the previous examples. @@ -389,9 +394,190 @@ The `fields` option lets us specify input fields that will appear in the form. Every field has at least a `name`, `title` and `type`. Many fields support additional options for validation, etc. +There are fields available for many types: strings, numbers, dates, dropdown +boxes, file uploads, etc. For a full list see the [Field types](#field_types) +section of the user manual. + The simplest is the `string` field. This field type simply lets the user enter a value. Put the following in the `sf_tutorial.json` file, replacing the -original content: +original content (or create a new json file): + + { + "title": "Tutorial step 3", + "forms": [ + { + "name": "hello_world", + "title": "Hello, world!", + "description": "Greetings", + "script": "job_helloworld.sh", + "fields": [ + { + "name": "name", + "title": "Name", + "type": "string" + } + ] + } + ] + } + +Create the `job_helloworld.sh` script: + + #!/bin/sh + + if [ -z "$name" ]; then + name="world" + fi + + echo "Hello, $name!" + +Make it executable: + + $ chmod 755 job_helloworld.sh + +And start Scriptform (not required if it's still running and you're using the +same .json file): + + $ scriptform -p8000 -f -r ./sf_tutorial.json + +Point your browser to http://127.0.0.01:8000. Try submitting the form with and +without entering a name. + +As you can see, Scriptform makes form values available to scripts through the +environment. This makes it easy to implement scripts in any language you'd +like. For example, this is what a script implemented in Python would look like: + + #!/usr/bin/env python + + import os + + name = os.environ['name'] + if not name: + name = "world" + + print "Hello, {0}!".format(name) + +### Upload + +Let's extend our form with a file upload. Modify `sf_tutorial.json` and add an upload field: + + ... + { + "name": "upload", + "title": "Upload a file", + "type": "file" + } + ... + +We'll also change it to run a different script: + + ... + "script": "job_upload.py", + ... + +The entire file now looks like this: + + { + "title": "Tutorial step 4: Uploads", + "forms": [ + { + "name": "hello_world", + "title": "Hello, world!", + "description": "Greetings", + "script": "job_upload.sh", + "fields": [ + { + "name": "name", + "title": "Name", + "type": "string" + }, + { + "name": "upload", + "title": "Upload a file", + "type": "file" + } + ] + } + ] + } + +We'll make the script output the size of the file in bytes. `job_upload.sh`: + + #!/bin/sh + + if [ -z "$name" ]; then + name="stranger" + fi + echo "Hello, $name!" + + if [ -z "$upload" ]; then + echo "Looks like you didn't upload a file!" + else + FILE_SIZE=$(wc -c $upload | cut -d " " -f1) + echo "The size in bytes of $upload__name is $FILE_SIZE" + fi + +When we submit the form with a file uploaded, the results look like this: + + Hello, stranger! + The size in bytes of README.md is 146 + +Scriptform will stream the uploaded file to a temporary file (usually something +like `/tmp/scriptform_e4CAXk`) and put the received file name in the +`XXXX__name` variable. Temporary files are automatically removed when the +script is done running, so if you want to keep it around, you should move it do +a different directory. + +### Validation + +Scriptform offers a simple way to validate form values before executing +scripts. This saves you the trouble of having to do all the validation in your +script. Validation is achieved by speciying additional field definition +parameters. + +Let's modify the previous upload example and add some validation to it. We'll +make the `name` field have a minimum and maximum length: + + { + "name": "name", + "title": "Name", + "type": "string", + "minlen": 2, + "maxlen": 10 + }, + +We'll change the `upload` field so it's required and you're only allowed to +upload '.txt' files: + + + { + "name": "upload", + "title": "Upload a file", + "type": "file", + "required": true, + "extensions": ["txt"] + } + +Try the validation out by submitting the form with some right and wrong values +and by uploading no file or a file with a wrong extension. You'll see that +Scriptform validates the submitted form before the script is executed. If any +validations fail, the form is shown again. + +More details on validation and other additional options that can be supplied to +field definitions can be found in the '[Field types](#field_types)' chapter. + +### Further reading + +This concludes the tutorial for Scriptform, although it has a lot more to +offer. Some suggestions on further reading materials: + +* [Script execution](#script_execution): Details on how scripts are executed. +* [Users](#users): Scriptform can do user management. +* [Form customization](#cust): Learn how to customize your forms. + +And finally, **please read** the [Security](#security) section for important +information regarding Scriptform's security. + @@ -523,7 +709,7 @@ For example, here's a form config file that contains two forms: { "name": "username", "title": "Username", - "type": "string" + "type": "string", "required": true }, { diff --git a/examples/tutorial/job_helloworld.py b/examples/tutorial/job_helloworld.py new file mode 100755 index 0000000..699554a --- /dev/null +++ b/examples/tutorial/job_helloworld.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +import os + +name = os.environ['name'] +if not name: + name = "world" + +print "Hello, {0}!".format(name) diff --git a/examples/tutorial/job_helloworld.sh b/examples/tutorial/job_helloworld.sh new file mode 100755 index 0000000..96ef4f0 --- /dev/null +++ b/examples/tutorial/job_helloworld.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ -z "$name" ]; then + name="world" +fi + +echo "Hello, $name!" diff --git a/examples/tutorial/job_sysinfo.sh b/examples/tutorial/job_sysinfo.sh new file mode 100755 index 0000000..169facc --- /dev/null +++ b/examples/tutorial/job_sysinfo.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +HOSTNAME=$(hostname -f) +MEM=$(free -h) +DISK=$(df -h) + +cat << END_OF_TEXT +Hostname +======== + +$HOSTNAME + + +Memory +====== + +$MEM + + +Disk +==== + +$DISK +END_OF_TEXT diff --git a/examples/tutorial/job_sysinfo_output.sh b/examples/tutorial/job_sysinfo_output.sh new file mode 100755 index 0000000..3a433c9 --- /dev/null +++ b/examples/tutorial/job_sysinfo_output.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +HOSTNAME=$(hostname -f) +MEM=$(free -h) +DISK=$(df -h) + +cat << END_OF_TEXT +

Hostname

+
$HOSTNAME
+ +

Memory

+
$MEM
+ +

Disk

+
$DISK
+END_OF_TEXT diff --git a/examples/tutorial/job_upload.sh b/examples/tutorial/job_upload.sh new file mode 100755 index 0000000..bed156a --- /dev/null +++ b/examples/tutorial/job_upload.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +if [ -z "$name" ]; then + name="stranger" +fi +echo "Hello, $name!" + +if [ -z "$upload" ]; then + echo "Looks like you didn't upload a file!" +else + FILE_SIZE=$(wc -c $upload | cut -d " " -f1) + echo "The size in bytes of $upload__name is $FILE_SIZE" +fi + diff --git a/examples/tutorial/tutorial.json b/examples/tutorial/tutorial.json new file mode 100644 index 0000000..0cd25f9 --- /dev/null +++ b/examples/tutorial/tutorial.json @@ -0,0 +1,12 @@ +{ + "title": "Tutorial", + "forms": [ + { + "name": "System information", + "title": "System information", + "description": "Show information about the operating system", + "script": "job_sysinfo.sh", + "fields": [] + } + ] +} diff --git a/examples/tutorial/tutorial_fields.json b/examples/tutorial/tutorial_fields.json new file mode 100644 index 0000000..a342304 --- /dev/null +++ b/examples/tutorial/tutorial_fields.json @@ -0,0 +1,18 @@ +{ + "title": "Tutorial step 3", + "forms": [ + { + "name": "hello_world", + "title": "Hello, world!", + "description": "Greetings", + "script": "job_helloworld.py", + "fields": [ + { + "name": "name", + "title": "Name", + "type": "string" + } + ] + } + ] +} diff --git a/examples/tutorial/tutorial_output.json b/examples/tutorial/tutorial_output.json new file mode 100644 index 0000000..753ea92 --- /dev/null +++ b/examples/tutorial/tutorial_output.json @@ -0,0 +1,13 @@ +{ + "title": "Tutorial", + "forms": [ + { + "name": "System information", + "title": "System information", + "description": "Show information about the operating system", + "script": "job_sysinfo_output.sh", + "output": "html", + "fields": [] + } + ] +} diff --git a/examples/tutorial/tutorial_upload.json b/examples/tutorial/tutorial_upload.json new file mode 100644 index 0000000..09e944e --- /dev/null +++ b/examples/tutorial/tutorial_upload.json @@ -0,0 +1,23 @@ +{ + "title": "Tutorial step 4: Uploads", + "forms": [ + { + "name": "hello_world", + "title": "Hello, world!", + "description": "Greetings", + "script": "job_upload.sh", + "fields": [ + { + "name": "name", + "title": "Name", + "type": "string" + }, + { + "name": "upload", + "title": "Upload a file", + "type": "file" + } + ] + } + ] +} diff --git a/examples/tutorial/tutorial_validate.json b/examples/tutorial/tutorial_validate.json new file mode 100644 index 0000000..dcea842 --- /dev/null +++ b/examples/tutorial/tutorial_validate.json @@ -0,0 +1,27 @@ +{ + "title": "Tutorial step 4: Uploads", + "forms": [ + { + "name": "hello_world", + "title": "Hello, world!", + "description": "Greetings", + "script": "job_upload.sh", + "fields": [ + { + "name": "name", + "title": "Name", + "type": "string", + "minlen": 2, + "maxlen": 10 + }, + { + "name": "upload", + "title": "Upload a file", + "type": "file", + "required": true, + "extensions": ["txt"] + } + ] + } + ] +}