commit
78ad473e9c
@ -0,0 +1,7 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
MYSQL_DEFAULTS_FILE="my.cnf" |
||||||
|
MYSQL="mysql --defaults-file=$MYSQL_DEFAULTS_FILE" |
||||||
|
|
||||||
|
echo "echo 'DROP DATABASE scriptform_acc' | $MYSQL" |
||||||
|
echo "$MYSQL < dbs/${sample_db}.sql" |
@ -0,0 +1,9 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
if [ "$passwd" != "123foobar" ]; then |
||||||
|
echo "Invalid password" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
echo "RESTARTING" |
||||||
|
ls -l /home/fboender |
@ -0,0 +1,90 @@ |
|||||||
|
{ |
||||||
|
"title": "MegaCorp acceptance jobs", |
||||||
|
"forms": { |
||||||
|
"enable_firewall": { |
||||||
|
"title": "Enable firewall", |
||||||
|
"description": "Enable access to the acceptance environment from the entered IP", |
||||||
|
"submit_title": "Enable access", |
||||||
|
"script": "job_clean_database.sh", |
||||||
|
"fields": [ |
||||||
|
{ |
||||||
|
"name": "ip_address", |
||||||
|
"title": "IP Address", |
||||||
|
"type": "string", |
||||||
|
"required": true |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "expire_days", |
||||||
|
"title": "Expire (days)", |
||||||
|
"type": "integer", |
||||||
|
"max": 31, |
||||||
|
"min": 2 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "expire_date", |
||||||
|
"title": "Expire (date)", |
||||||
|
"type": "date", |
||||||
|
"required": true |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "network", |
||||||
|
"title": "Which network", |
||||||
|
"type": "radio", |
||||||
|
"options": [ |
||||||
|
["intra", "Whole intranet"], |
||||||
|
["machine", "Acceptance machine"] |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "comment", |
||||||
|
"title": "Comment", |
||||||
|
"type": "text" |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
"clean_database": { |
||||||
|
"title": "Load clean database", |
||||||
|
"description": "Recreate the acceptance database from scratch. This deletes all the information in the database", |
||||||
|
"submit_title": "Run", |
||||||
|
"script": "job_clean_database.sh", |
||||||
|
"fields": [ |
||||||
|
{ |
||||||
|
"name": "sample_db", |
||||||
|
"title": "Sample database", |
||||||
|
"type": "select", |
||||||
|
"options": [ |
||||||
|
["empty", "Empty database"], |
||||||
|
["dev", "Development test database"], |
||||||
|
["ua", "Acceptance database"] |
||||||
|
] |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
"import_csv": { |
||||||
|
"title": "Import CSV data into database", |
||||||
|
"description": "Load a CSV with test data into the database", |
||||||
|
"submit_title": "Import CSV", |
||||||
|
"script": "job_restart_acc.sh", |
||||||
|
"fields": [ |
||||||
|
{ |
||||||
|
"name": "csv_file", |
||||||
|
"title": "CSV file", |
||||||
|
"type": "file" |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
"restart_services": { |
||||||
|
"title": "Restart Acceptance services", |
||||||
|
"description": "Restarts the acceptance services (web, db, cache). Consult with <a href='foo@example.com'>John Foo</a> first!", |
||||||
|
"submit_title": "Restart", |
||||||
|
"script": "job_restart_acc.sh", |
||||||
|
"fields": [ |
||||||
|
{ |
||||||
|
"name": "passwd", |
||||||
|
"title": "John Foo gave you a password. What is it?", |
||||||
|
"type": "password" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,500 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
|
||||||
|
import sys |
||||||
|
import optparse |
||||||
|
import os |
||||||
|
import stat |
||||||
|
import json |
||||||
|
import BaseHTTPServer |
||||||
|
from BaseHTTPServer import BaseHTTPRequestHandler |
||||||
|
import cgi |
||||||
|
import re |
||||||
|
import datetime |
||||||
|
import subprocess |
||||||
|
|
||||||
|
|
||||||
|
html_header = '''<html> |
||||||
|
<head> |
||||||
|
<style> |
||||||
|
.btn {{ color: #FFFFFF; font-weight: bold; font-size: 0.90em; |
||||||
|
background-color: #1D98E4; border-color: #1D98E4; padding: 9px; |
||||||
|
border-radius: 4px; border-width: 0px; text-decoration: none; |
||||||
|
}} |
||||||
|
.error {{ color: #FF0000; }} |
||||||
|
*,body {{ font-family: sans-serif; }} |
||||||
|
h1 {{ color: #555555; text-align: center; margin: 32px auto 32px auto; }} |
||||||
|
pre {{ font-family: monospace; }} |
||||||
|
|
||||||
|
/* List of available forms */ |
||||||
|
div.list {{ width: 50%; margin: 40px auto 0px auto; }} |
||||||
|
div.list li {{ font-size: 0.90em; list-style: none; |
||||||
|
margin-bottom: 65px; }} |
||||||
|
div.list h2 {{ background-color: #E0E5E5; |
||||||
|
border-radius: 3px; font-weight: bold; |
||||||
|
padding: 10px; font-size: 1.2em; }} |
||||||
|
div.list p.form-description {{ margin-left: 25px; }} |
||||||
|
div.list a.form-link {{ margin-left: 25px; }} |
||||||
|
|
||||||
|
/* Form display */ |
||||||
|
div.form {{ width: 50%; margin: 40px auto 0px auto; }} |
||||||
|
div.form h2 {{ font-weight: bold; background-color: #E0E5E5; padding: 25px; |
||||||
|
border-radius: 10px; }} |
||||||
|
div.form p.form-description {{ font-size: 0.90em; |
||||||
|
margin: 40px 25px 65px 25px; }} |
||||||
|
div.form li {{ font-size: 0.90em; list-style: none; }} |
||||||
|
div.form p.form-field-title {{ margin-bottom: 0px; }} |
||||||
|
div.form p.form-field-input {{ margin-top: 0px; }} |
||||||
|
select, |
||||||
|
textarea, |
||||||
|
input[type=text], |
||||||
|
input[type=number], |
||||||
|
input[type=date], |
||||||
|
input[type=password], |
||||||
|
input[type=submit] {{ color: #606060; padding: 9px; border-radius: 4px; |
||||||
|
border: 1px solid #D0D0D0; |
||||||
|
background-color: #F9F9F9;}} |
||||||
|
input[type=submit] {{ color: #FFFFFF; font-weight: bold; |
||||||
|
background-color: #1D98E4; border-color: #1D98E4}} |
||||||
|
textarea {{ width: 80%; height: 120px; }} |
||||||
|
/* Result display */ |
||||||
|
div.result {{ width: 50%; margin: 40px auto 0px auto; }} |
||||||
|
div.result h2 {{ background-color: #E0E5E5; border-radius: 3px; |
||||||
|
font-weight: bold; padding: 10px; }} |
||||||
|
div.result div.result-result {{ margin-left: 25px; }} |
||||||
|
div.result ul {{ margin-top: 64px; padding-left: 0px; }} |
||||||
|
div.result ul li {{ list-style: none; float: left; margin-right: 20px; |
||||||
|
font-size: 0.90em; }} |
||||||
|
div.result ul.nav {{ margin-bottom: 128px; }} |
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>{title}</h1> |
||||||
|
<div class="page"> |
||||||
|
''' |
||||||
|
|
||||||
|
html_footer = ''' |
||||||
|
</div> |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
''' |
||||||
|
|
||||||
|
|
||||||
|
def cmd(cmd, input=None, env=None): |
||||||
|
""" |
||||||
|
Run command `cmd` in a shell. `input` (string) is passed in the |
||||||
|
process' STDIN. |
||||||
|
|
||||||
|
Returns a dictionary: `{'stdout': <string>, 'stderr': <string>, 'exitcode': |
||||||
|
<int>}`. |
||||||
|
""" |
||||||
|
p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, |
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
||||||
|
env=env) |
||||||
|
stdout, stderr = p.communicate(input) |
||||||
|
return { |
||||||
|
'stdout': stdout, |
||||||
|
'stderr': stderr, |
||||||
|
'exitcode': p.returncode |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class FormDefinition: |
||||||
|
""" |
||||||
|
FormDefinition holds information about a single form and provides methods |
||||||
|
for validation of the form values. |
||||||
|
""" |
||||||
|
def __init__(self, name, title, description, fields, script=None, |
||||||
|
submit_title="Submit"): |
||||||
|
self.name = name |
||||||
|
self.title = title |
||||||
|
self.description = description |
||||||
|
self.fields = fields |
||||||
|
self.script = script |
||||||
|
self.submit_title = submit_title |
||||||
|
|
||||||
|
def get_field(self, field_name): |
||||||
|
for field in self.fields: |
||||||
|
if field['name'] == field_name: |
||||||
|
return field |
||||||
|
|
||||||
|
def validate(self, form_values): |
||||||
|
""" |
||||||
|
Validate all relevant fields for this form against form_values. |
||||||
|
""" |
||||||
|
values = {} |
||||||
|
for field_name in form_values: |
||||||
|
if field_name == 'form_name': |
||||||
|
continue |
||||||
|
v = self.validate_field(field_name, |
||||||
|
form_values.getfirst(field_name)) |
||||||
|
if v is not None: |
||||||
|
values[field_name] = v |
||||||
|
|
||||||
|
# Make sure all required fields are there |
||||||
|
for field in self.fields: |
||||||
|
if 'required' in field and \ |
||||||
|
field['required'] is True and \ |
||||||
|
field['name'] not in values: |
||||||
|
raise ValueError( |
||||||
|
"Required field {} not present".format(field['name'])) |
||||||
|
|
||||||
|
return values |
||||||
|
|
||||||
|
def validate_field(self, field_name, value): |
||||||
|
""" |
||||||
|
Validate a field in this form. |
||||||
|
""" |
||||||
|
# Find field definition by iterating through all the fields. |
||||||
|
field_def = self.get_field(field_name) |
||||||
|
if not field_def: |
||||||
|
raise KeyError("Unknown field: {}".format(field_name)) |
||||||
|
|
||||||
|
field_type = field_def['type'] |
||||||
|
validate_cb = getattr(self, 'validate_{}'.format(field_type), None) |
||||||
|
if not validate_cb: |
||||||
|
return value |
||||||
|
else: |
||||||
|
return validate_cb(field_def, value) |
||||||
|
|
||||||
|
def validate_integer(self, field_def, value): |
||||||
|
try: |
||||||
|
int(value) |
||||||
|
return value |
||||||
|
except ValueError: |
||||||
|
if field_def.get('required', False): |
||||||
|
raise |
||||||
|
return None |
||||||
|
|
||||||
|
def validate_float(self, field_def, value): |
||||||
|
try: |
||||||
|
return float(value) |
||||||
|
except ValueError: |
||||||
|
if field_def.get('required', False): |
||||||
|
raise |
||||||
|
return None |
||||||
|
|
||||||
|
def validate_date(self, field_def, value): |
||||||
|
m = re.match('([0-9]{4})-([0-9]{2})-([0-9]{2})', value) |
||||||
|
if m: |
||||||
|
return value |
||||||
|
#g = m.groups() |
||||||
|
#return datetime.date(int(g[0]), int(g[1]), int(g[2])) |
||||||
|
elif field_def.get('required', False): |
||||||
|
raise ValueError( |
||||||
|
"Invalid value for date field: {}".format(value)) |
||||||
|
return None |
||||||
|
|
||||||
|
def validate_radio(self, field_def, value): |
||||||
|
if not value in [o[0] for o in field_def['options']]: |
||||||
|
raise ValueError( |
||||||
|
"Invalid value for radio button: {}".format(value)) |
||||||
|
return value |
||||||
|
|
||||||
|
def validate_select(self, field_def, value): |
||||||
|
if not value in [o[0] for o in field_def['options']]: |
||||||
|
raise ValueError( |
||||||
|
"Invalid value for dropdown: {}".format(value)) |
||||||
|
return value |
||||||
|
|
||||||
|
|
||||||
|
class WebSrv: |
||||||
|
""" |
||||||
|
Very basic web server. |
||||||
|
""" |
||||||
|
def __init__(self, app_class, listen_addr='', listen_port=80): |
||||||
|
WebAppHandler.app_class = app_class |
||||||
|
httpd = BaseHTTPServer.HTTPServer((listen_addr, listen_port), |
||||||
|
WebAppHandler) |
||||||
|
httpd.serve_forever() |
||||||
|
|
||||||
|
|
||||||
|
class WebAppHandler(BaseHTTPRequestHandler): |
||||||
|
""" |
||||||
|
Basic web server request handler. Handles GET and POST requests. Uses |
||||||
|
self.app_class (set by WebSrv) to dispatch GET and POST requests to a |
||||||
|
custom class. It looks for a method with the name of the request path in |
||||||
|
self.app_class and then dispatches the request to that method. If no path |
||||||
|
is set, it dispatches to the 'index' or 'default' method. |
||||||
|
""" |
||||||
|
def do_GET(self): |
||||||
|
self.call(*self.parse(self.path)) |
||||||
|
|
||||||
|
def do_POST(self): |
||||||
|
form_values = cgi.FieldStorage( |
||||||
|
fp=self.rfile, |
||||||
|
headers=self.headers, |
||||||
|
environ={'REQUEST_METHOD': 'POST'}) |
||||||
|
self.call(self.path.strip('/'), params={'form_values': form_values}) |
||||||
|
|
||||||
|
def parse(self, reqinfo): |
||||||
|
if '?' in reqinfo: |
||||||
|
path, params = reqinfo.split('?', 1) |
||||||
|
params = dict( |
||||||
|
[p.split('=', 1) for p in params.split('&') if '=' in p] |
||||||
|
) |
||||||
|
return (path.strip('/'), params) |
||||||
|
else: |
||||||
|
return (self.path.strip('/'), {}) |
||||||
|
|
||||||
|
def call(self, path, params): |
||||||
|
""" |
||||||
|
Find a method to call on self.app_class based on `path` and call it. |
||||||
|
""" |
||||||
|
response_code = 200 |
||||||
|
try: |
||||||
|
if hasattr(self.app_class, path) and \ |
||||||
|
callable(getattr(self.app_class, path)): |
||||||
|
out = getattr(self.app_class, path)(**params) |
||||||
|
elif path == '' and hasattr(self.app_class, 'index'): |
||||||
|
out = self.app_class.index(**params) |
||||||
|
elif hasattr(self.app_class, 'default'): |
||||||
|
out = self.app_class.default(**params) |
||||||
|
else: |
||||||
|
response_code = 404 |
||||||
|
out = 'Not Found' |
||||||
|
except Exception, e: |
||||||
|
self.wfile.write(e) |
||||||
|
raise |
||||||
|
self.send_response(response_code) |
||||||
|
self.send_header('Content-type', 'text/html') |
||||||
|
self.end_headers() |
||||||
|
self.wfile.write(out) |
||||||
|
|
||||||
|
|
||||||
|
class ScriptFormWebApp: |
||||||
|
""" |
||||||
|
This class is a request handler for WebSrv. |
||||||
|
""" |
||||||
|
def __init__(self, vitaform, callbacks): |
||||||
|
self.vitaform = vitaform |
||||||
|
self.callbacks = callbacks |
||||||
|
|
||||||
|
def index(self): |
||||||
|
return self.list() |
||||||
|
|
||||||
|
def list(self): |
||||||
|
h_form_list = [] |
||||||
|
for form_name, form_def in self.vitaform.forms.items(): |
||||||
|
h_form_list.append(''' |
||||||
|
<li> |
||||||
|
<h2 class="form-title">{title}</h2> |
||||||
|
<p class="form-description">{description}</p> |
||||||
|
<a class="form-link btn" href="/form?form_name={name}"> |
||||||
|
{title} |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
'''.format(title=form_def.title, |
||||||
|
description=form_def.description, |
||||||
|
name=form_name) |
||||||
|
) |
||||||
|
|
||||||
|
return ''' |
||||||
|
{header} |
||||||
|
<div class="list"> |
||||||
|
{form_list} |
||||||
|
</div> |
||||||
|
{footer} |
||||||
|
'''.format(header=html_header.format(title=self.vitaform.title), |
||||||
|
footer=html_footer, |
||||||
|
form_list=''.join(h_form_list)) |
||||||
|
|
||||||
|
def form(self, form_name): |
||||||
|
field_tpl = { |
||||||
|
"string": '<input {} type="text" name="{}" />', |
||||||
|
"number": '<input {} type="number" min="{}" max="{}" name="{}" />', |
||||||
|
"integer": '<input {} type="number" min="{}" max="{}" name="{}" />', |
||||||
|
"float": '<input {} type="number" min="{}" max="{}" name="{}" />', |
||||||
|
"date": '<input {} type="date" name="{}" />', |
||||||
|
"file": '<input {} type="file" name="{}" />', |
||||||
|
"password": '<input {} type="password" name="{}" />', |
||||||
|
"text": '<textarea {} name="{}"></textarea>', |
||||||
|
"select": '<option value="{}">{}</option>', |
||||||
|
"radio": '<input checked type="radio" name="{}" value="{}">{}<br/>', |
||||||
|
} |
||||||
|
|
||||||
|
def render_field(field): |
||||||
|
tpl = field_tpl[field['type']] |
||||||
|
|
||||||
|
required = '' |
||||||
|
if field.get('required', None): |
||||||
|
required='required' |
||||||
|
|
||||||
|
if field['type'] == 'string': |
||||||
|
input = tpl.format(required, field['name']) |
||||||
|
elif field['type'] == 'number' or \ |
||||||
|
field['type'] == 'integer' or \ |
||||||
|
field['type'] == 'float': |
||||||
|
input = tpl.format(required, field.get('min', ''), |
||||||
|
field.get('max', ''), |
||||||
|
field['name']) |
||||||
|
elif field['type'] == 'date': |
||||||
|
input = tpl.format(required, field['name']) |
||||||
|
elif field['type'] == 'file': |
||||||
|
input = tpl.format(required, field['name']) |
||||||
|
elif field['type'] == 'password': |
||||||
|
input = tpl.format(required, field['name']) |
||||||
|
elif field['type'] == 'radio': |
||||||
|
input = ''.join( |
||||||
|
[ |
||||||
|
tpl.format(field['name'], o[0], o[1]) |
||||||
|
for o in field['options'] |
||||||
|
] |
||||||
|
) |
||||||
|
elif field['type'] == 'text': |
||||||
|
input = tpl.format(required, field['name']) |
||||||
|
elif field['type'] == 'select': |
||||||
|
options = ''.join([ |
||||||
|
tpl.format(o[0], o[1]) for o in field['options'] |
||||||
|
] |
||||||
|
) |
||||||
|
input = '<select {} name="{}">{}</select>'.format(required, field['name'], options) |
||||||
|
else: |
||||||
|
raise ValueError("Unsupported field type: {}".format( |
||||||
|
field['type']) |
||||||
|
) |
||||||
|
|
||||||
|
return (''' |
||||||
|
<li> |
||||||
|
<p class="form-field-title">{title}</p> |
||||||
|
<p class="form-field-input">{input}</p> |
||||||
|
</li> |
||||||
|
'''.format(title=field['title'], |
||||||
|
input=input)) |
||||||
|
|
||||||
|
form = self.vitaform.get_form(form_name) |
||||||
|
|
||||||
|
return ''' |
||||||
|
{header} |
||||||
|
<div class="form"> |
||||||
|
<h2 class="form-title">{title}</h2> |
||||||
|
<p class="form-description">{description}</p> |
||||||
|
<form action="submit" method="post" enctype="multipart/form-data"> |
||||||
|
<input type="hidden" name="form_name" value="{name}" /> |
||||||
|
<ul> |
||||||
|
{fields} |
||||||
|
<li><input type="submit" value="{submit_title}" /></li> |
||||||
|
</ul> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
{footer} |
||||||
|
'''.format( |
||||||
|
header=html_header.format(title=self.vitaform.title), |
||||||
|
footer=html_footer, |
||||||
|
title=form.title, |
||||||
|
description=form.description, |
||||||
|
name=form.name, |
||||||
|
fields=''.join([render_field(f) for f in form.fields]), |
||||||
|
submit_title=form.submit_title |
||||||
|
) |
||||||
|
|
||||||
|
def submit(self, form_values): |
||||||
|
form_name = form_values.getfirst('form_name', None) |
||||||
|
|
||||||
|
# Validate the form values |
||||||
|
form = self.vitaform.get_form(form_name) |
||||||
|
values = form.validate(form_values) |
||||||
|
|
||||||
|
# Call user's callback |
||||||
|
if form.script: |
||||||
|
# Run an external script |
||||||
|
env = os.environ.copy() |
||||||
|
env.update(values) |
||||||
|
res = cmd(form.script, env=env) |
||||||
|
if res['exitcode'] != 0: |
||||||
|
result = '<span class="error">{}</span>'.format(res['stderr']) |
||||||
|
else: |
||||||
|
result = '<pre>{}</pre>'.format(res['stdout']) |
||||||
|
else: |
||||||
|
# Run a python callback |
||||||
|
callback = self.callbacks[form_name] |
||||||
|
result = callback(values) |
||||||
|
|
||||||
|
return ''' |
||||||
|
{header} |
||||||
|
<div class="result"> |
||||||
|
<h2 class="result-title">{title}</h2> |
||||||
|
<h3 class="result-subtitle">Result</h3> |
||||||
|
<div class="result-result">{result}</div> |
||||||
|
<ul class="nav"> |
||||||
|
<li> |
||||||
|
<a class="back-list btn" href="/">Back to the list</a> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<a class="back-form btn" href="/form?form_name={form_name}"> |
||||||
|
Back to the form |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
{footer} |
||||||
|
'''.format( |
||||||
|
header=html_header.format(title=self.vitaform.title), |
||||||
|
footer=html_footer, |
||||||
|
title=form.title, |
||||||
|
form_name=form.name, |
||||||
|
result=result, |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
class ScriptForm: |
||||||
|
""" |
||||||
|
'Main' class that orchestrates parsing the Form definition file |
||||||
|
`config_file`, hooking up callbacks and running the webserver. |
||||||
|
""" |
||||||
|
def __init__(self, config_file, callbacks={}): |
||||||
|
self.forms = {} |
||||||
|
self.callbacks = {} |
||||||
|
self.title = 'ScriptForm Actions' |
||||||
|
self.basepath = os.path.realpath(os.path.dirname(config_file)) |
||||||
|
|
||||||
|
self._load_config(config_file) |
||||||
|
for form_name, cb in callbacks.items(): |
||||||
|
self.callbacks[form_name] = cb |
||||||
|
|
||||||
|
# Validate scripts |
||||||
|
for form_name, form_def in self.forms.items(): |
||||||
|
if form_def.script: |
||||||
|
if not stat.S_IXUSR & os.stat(form_def.script)[stat.ST_MODE]: |
||||||
|
raise Exception("{} is not executable".format(form_def.script)) |
||||||
|
else: |
||||||
|
if not form_name in self.callbacks: |
||||||
|
raise Exception("No script or callback registered for '{}'".format(form_name)) |
||||||
|
|
||||||
|
def _load_config(self, path): |
||||||
|
config = json.load(file(path, 'r')) |
||||||
|
if 'title' in config: |
||||||
|
self.title = config['title'] |
||||||
|
for form_name, form in config['forms'].items(): |
||||||
|
if 'script' in form: |
||||||
|
script = os.path.join(self.basepath, form['script']) |
||||||
|
else: |
||||||
|
script = None |
||||||
|
self.forms[form_name] = \ |
||||||
|
FormDefinition(form_name, |
||||||
|
form['title'], |
||||||
|
form['description'], |
||||||
|
form['fields'], |
||||||
|
script, |
||||||
|
submit_title=form.get('submit_title', None)) |
||||||
|
|
||||||
|
def get_form(self, form_name): |
||||||
|
return self.forms[form_name] |
||||||
|
|
||||||
|
def run(self, listen_addr='0.0.0.0', listen_port=80): |
||||||
|
vitaform = self |
||||||
|
webapp = ScriptFormWebApp(vitaform, self.callbacks) |
||||||
|
WebSrv(webapp, listen_addr=listen_addr, listen_port=listen_port) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
parser = optparse.OptionParser() |
||||||
|
parser.set_usage(sys.argv[0] + " [option] <form_definition.json>") |
||||||
|
|
||||||
|
parser.add_option("-p", "--port", dest="port", action="store", type="int", default=80, help="Port to listen on.") |
||||||
|
|
||||||
|
(options, args) = parser.parse_args() |
||||||
|
if len(args) < 1: |
||||||
|
parser.error("Insufficient number of arguments") |
||||||
|
|
||||||
|
sf = ScriptForm(args[0]) |
||||||
|
sf.run(listen_port=options.port) |
Loading…
Reference in new issue