Created
March 7, 2019 01:20
-
-
Save mcdonc/13f6021767296e5842e71cb9bc5c5ad2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# validationtest.py single-file app | |
from pyramid.config import Configurator | |
from pyramid.view import view_config | |
from waitress import serve | |
import colander | |
from pyramid_deform import SessionFileUploadTempStore, CSRFSchema | |
import deform | |
from deform import widget | |
@colander.deferred | |
def deferred_upload_widget(node, kw): | |
request = kw["request"] | |
tmpstore = SessionFileUploadTempStore(request) | |
return widget.FileUploadWidget(tmpstore) | |
class FileUploads(colander.SequenceSchema): | |
file = colander.SchemaNode( | |
deform.FileData(), | |
missing=colander.drop, | |
widget=deferred_upload_widget, | |
) | |
@colander.deferred | |
def deferred_initialized_sequence_widget(node, kw): | |
""" Set an initial widget for FileUploads """ | |
return widget.SequenceWidget(min_len=1) | |
class PropertyDamageSchema(CSRFSchema): | |
number = colander.SchemaNode( | |
colander.Int(), | |
title="Invalid", | |
validator=colander.Range( | |
min=0, max=1), | |
) | |
files = FileUploads( | |
missing=None, | |
widget=deferred_initialized_sequence_widget | |
) | |
class IncidentTypeEdit(object): | |
def __init__(self, request): | |
self.request = request | |
@view_config( | |
renderer="type_create.jinja2", | |
request_method=['GET', 'HEAD'] | |
) | |
def __call__(self): | |
schema = PropertyDamageSchema().bind( | |
request=self.request, | |
) | |
b = deform.Button(name="create", title="Create Incident Type") | |
form = deform.Form(schema, buttons=(b,)) | |
incident = { | |
'files':[ | |
{'filename':'abc','uid':'abc'}, | |
{'filename':'def','uid':'def'}, | |
], | |
'number':50 | |
} | |
form = form.render(incident) | |
return {'form':form, 'created':False} | |
@view_config( | |
renderer="type_create.jinja2", | |
request_method=['POST'] | |
) | |
def validate(self): | |
schema = PropertyDamageSchema().bind( | |
request=self.request, | |
) | |
b = deform.Button(name="create", title="Create Incident Type") | |
form = deform.Form(schema, buttons=(b,)) | |
try: | |
captured = form.validate(self.request.POST.items()) | |
except deform.exception.ValidationFailure as e: | |
form = e.render() | |
else: | |
form = form.render(captured) | |
return {'form':form, 'created':True} | |
if __name__ == '__main__': | |
from pyramid.session import SignedCookieSessionFactory | |
my_session_factory = SignedCookieSessionFactory('itsaseekreet') | |
config = Configurator( | |
settings={ | |
'pyramid_deform.tempdir':'/tmp', | |
'jinja2.directories':'.', | |
}) | |
config.include('pyramid_jinja2') | |
config.set_session_factory(my_session_factory) | |
config.scan() | |
app = config.make_wsgi_app() | |
serve(app) | |
# type_create.jinja2 | |
<html> | |
<head> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> | |
<!-- Latest compiled and minified CSS --> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> | |
<!-- Optional theme --> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> | |
<!-- Latest compiled and minified JavaScript --> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> | |
<script type="text/javascript"> | |
(function ($) { | |
var Upload = function (element, options) { | |
this.$element = $(element); | |
this.options = $.extend({}, Upload.DEFAULTS, | |
this.$element.data(), | |
options); | |
this.orig_style = this.$element.attr('style'); | |
this.$input_group = $(this.options.template) | |
.replaceAll(this.$element) | |
.attr('style', this.orig_style) | |
.css({position: 'relative', overflow: 'hidden'}); | |
this.$input_group.find(':text').before(this.$element); | |
this.$element | |
.on('change.deform.upload', $.proxy(this, 'update')) | |
.css(this.options.element_style); | |
this.update(); | |
}; | |
Upload.DEFAULTS = { | |
filename: null, | |
selectfile: 'Select file…', | |
changefile: 'Change file…', | |
template: '<div>' | |
+ '<div class="input-group">' | |
+ '<span class="input-group-btn">' | |
+ '<span class="btn btn-default btn-file"></span>' | |
+ '</span>' | |
+ '<input type="text" readonly=""' | |
+ ' class="form-control upload-filename"/>' | |
+ '</div>' | |
+ '</div>', | |
element_style: { | |
position: 'absolute', | |
/* Older FF (3.5) seems to put a margin on the bottom of | |
* the file input (the margin is proportional to | |
* font-size, so in this case it's significant.) Shift | |
* bottom a bit to allow for some slop. | |
*/ | |
//bottom: '0', | |
bottom: '-40px', | |
right: '0', | |
minWidth: '100%', | |
minHeight: '100%', | |
fontSize: '999px', | |
textAlign: 'right', | |
filter: 'alpha(opacity=0)', | |
opacity: '0', | |
background: 'red', | |
cursor: 'inherit', | |
display: 'block' | |
} | |
}; | |
Upload.prototype.update = function () { | |
var selected_filename = this.$element.val().replace(/.*[\\\/]/, ''), | |
options = this.options, | |
filename = selected_filename || options.filename; | |
this.$input_group.find(':text') | |
.val(filename); | |
this.$input_group.find('.btn-file') | |
.text(filename ? options.changefile : options.selectfile); | |
}; | |
Upload.prototype.destroy = function () { | |
this.$element | |
.off('.deform.upload') | |
.attr('style', this.orig_style || null) | |
.replaceAll(this.$input_group) | |
.removeData('deform.upload'); | |
}; | |
//////////////////////////////////////////////////////////////// | |
// plugin definition | |
var old = $.fn.upload; | |
$.fn.upload = function (option) { | |
return this.each(function () { | |
var $this = $(this), | |
data = $this.data('deform.upload'); | |
if (!data) { | |
var options = typeof option == 'object' && option; | |
data = new Upload(this, options); | |
$this.data('deform.upload', data); | |
} | |
if (typeof option == 'string') { | |
data[option](); | |
} | |
}); | |
}; | |
$.fn.upload.Constructor = Upload; | |
$.fn.upload.noConflict = function () { | |
$.fn.upload = old; | |
return this; | |
}; | |
})(window.jQuery); | |
</script> | |
<script> | |
$(document).ready(function(){ | |
deform.load(); | |
}); | |
var deform_loaded = false; | |
var deform = { | |
callbacks: [], | |
addCallback: function (oid, callback) { | |
deform.callbacks.push([oid, callback]); | |
}, | |
clearCallbacks: function () { | |
deform.callbacks = []; | |
}, | |
load: function() { | |
$(function() { | |
if (!deform_loaded) { | |
deform.processCallbacks(); | |
deform.focusFirstInput(); | |
deform_loaded = true; | |
}}); | |
}, | |
processCallbacks: function () { | |
$(deform.callbacks).each(function(num, item) { | |
var oid = item[0]; | |
var callback = item[1]; | |
callback(oid); | |
} | |
); | |
deform.clearCallbacks(); | |
}, | |
addSequenceItem: function (protonode, before) { | |
// - Clone the prototype node and add it before the "before" node. | |
// Also ensure any callbacks are run for the widget. | |
// In order to avoid breaking accessibility: | |
// | |
// - Find each tag within the prototype node with an id | |
// that has the string ``deformField(\d+)`` within it, and modify | |
// its id to have a random component. | |
// - For each label referencing an change id, change the label's | |
// for attribute to the new id. | |
var fieldmatch = /deformField(\d+)/; | |
var namematch = /(.+)?-[#]{3}/; | |
var code = protonode.attr('prototype'); | |
var html = decodeURIComponent(code); | |
var $htmlnode = $(html); | |
var $idnodes = $htmlnode.find('[id]'); | |
var $namednodes = $htmlnode.find('[name]'); | |
var genid = deform.randomString(6); | |
var idmap = {}; | |
// replace ids containing ``deformField`` and associated label for= | |
// items which point at them | |
$idnodes.each(function(idx, node) { | |
var $node = $(node); | |
var oldid = $node.attr('id'); | |
var newid = oldid.replace(fieldmatch, "deformField$1-" + genid); | |
$node.attr('id', newid); | |
idmap[oldid] = newid; | |
var labelselector = 'label[for=' + oldid + ']'; | |
var $fornodes = $htmlnode.find(labelselector); | |
$fornodes.attr('for', newid); | |
}); | |
// replace names a containing ```deformField`` like we do for ids | |
$namednodes.each(function(idx, node) { | |
var $node = $(node); | |
var oldname = $node.attr('name'); | |
var newname = oldname.replace(fieldmatch, "deformField$1-" + genid); | |
$node.attr('name', newname); | |
}); | |
$htmlnode.insertBefore(before); | |
$(deform.callbacks).each(function(num, item) { | |
var oid = item[0]; | |
var callback = item[1]; | |
var newid = idmap[oid]; | |
if (newid) { | |
callback(newid); | |
} | |
}); | |
deform.clearCallbacks(); | |
var old_len = parseInt(before.attr('now_len')||'0', 10); | |
before.attr('now_len', old_len + 1); | |
// we added something to the dom, trigger a change event | |
var e = jQuery.Event("change"); | |
$('#deform').trigger(e); | |
}, | |
appendSequenceItem: function(node) { | |
var $oid_node = $(node).closest('.deform-seq'); | |
var $proto_node = $oid_node.find('.deform-proto').first(); | |
var $before_node = $oid_node.find('.deform-insert-before').last(); | |
var min_len = parseInt($before_node.attr('min_len')||'0', 10); | |
var max_len = parseInt($before_node.attr('max_len')||'9999', 10); | |
var now_len = parseInt($before_node.attr('now_len')||'0', 10); | |
var orderable = parseInt($before_node.attr('orderable')||'0', 10); | |
if (now_len < max_len) { | |
deform.addSequenceItem($proto_node, $before_node); | |
deform.processSequenceButtons($oid_node, min_len, max_len, | |
now_len + 1, orderable); | |
} | |
return false; | |
}, | |
removeSequenceItem: function(clicked) { | |
var $item_node = $(clicked).closest('.deform-seq-item'); | |
var $oid_node = $item_node.closest('.deform-seq'); | |
var $before_node = $oid_node.find('.deform-insert-before').last(); | |
var min_len = parseInt($before_node.attr('min_len')||'0', 10); | |
var max_len = parseInt($before_node.attr('max_len')||'9999', 10); | |
var now_len = parseInt($before_node.attr('now_len')||'0', 10); | |
var orderable = parseInt($before_node.attr('orderable')||'0', 10); | |
if (now_len > min_len) { | |
$before_node.attr('now_len', now_len - 1); | |
$item_node.remove(); | |
deform.processSequenceButtons($oid_node, min_len, max_len, | |
now_len-1, orderable); | |
} | |
// we removed something from the dom, trigger a change event | |
var e = jQuery.Event("change"); | |
$('#deform').trigger(e); | |
return false; | |
}, | |
processSequenceButtons: function(oid_node, min_len, max_len, now_len, | |
orderable) { | |
orderable = !!orderable; // convert to bool | |
var has_multiple = now_len > 1; | |
var $ul = oid_node.find('.deform-seq-container').not(oid_node.find('.deform-seq-container .deform-seq-container')); | |
var $lis = $ul.find('.deform-seq-item').not($ul.find('.deform-seq-container .deform-seq-item')); | |
var show_closebutton = now_len > min_len; | |
var show_addbutton = now_len < max_len; | |
$lis.find('.deform-close-button').not($lis.find('.deform-seq-container .deform-close-button')).toggle(show_closebutton); | |
oid_node.find('.deform-seq-add').not(oid_node.find('.deform-seq-container .deform-seq-add')).toggle(show_addbutton); | |
$lis.find('.deform-order-button').not($lis.find('.deform-seq-container .deform-order-button')).toggle(orderable && has_multiple); | |
}, | |
focusFirstInput: function (el) { | |
el = el || document.body; | |
var input = $(el).find(':input') | |
.filter('[id ^= deformField]') | |
.filter('[type != hidden]') | |
.first(); | |
if (input) { | |
var raw = input.get(0); | |
if (raw) { | |
if (raw.type === 'text' || raw.type === 'file' || | |
raw.type == 'password' || raw.type == 'text' || | |
raw.type == 'textarea') { | |
if (!input.hasClass("hasDatepicker")) { | |
input.focus(); | |
} | |
} | |
} | |
} | |
}, | |
randomString: function (length) { | |
var chr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'; | |
chr = chr.split(''); | |
if (! length) { | |
length = Math.floor(Math.random() * chr.length); | |
} | |
var str = ''; | |
for (var i = 0; i < length; i++) { | |
str += chr[Math.floor(Math.random() * chr.length)]; | |
} | |
return str; | |
} | |
}; | |
</script> | |
</head> | |
<body> | |
<div class="row"> | |
<div class="col-md-12"> | |
<h2>Form</h2> | |
{{ form|safe }} | |
</div> | |
</div> | |
<script type="text/javascript"> | |
$(document).ready(function(){ | |
function showHide() { | |
var s = $("#item-sequential_id"); | |
var e = $("#item-employee"); | |
var a = $("input[name='anonymous']:radio:checked").val(); | |
if (a == "TRUE") { | |
s.show(); | |
e.hide(); | |
} else if (a == "FALSE") { | |
s.hide(); | |
e.show(); | |
$("div#item-employee>span.select2-container").width("100%"); // required to prevent select2 input from inheriting a width of 0 when initially hidden | |
} else { | |
s.hide(); | |
e.hide(); | |
} | |
} | |
showHide(); | |
$("input[name='anonymous']:radio").click(showHide); | |
// show/hide location_other for NearMiss | |
function showHideNearMissLocationOther(event) { | |
var locationOther = $("#item-nearMissLocationOther"); | |
if (event.currentTarget.value == "OTHER_PLEASE_EXPLAIN") { | |
locationOther.show(); | |
} else { | |
locationOther.hide(); | |
} | |
} | |
var location = $("#nearMissLocation"); | |
location.change(showHideNearMissLocationOther); | |
location.trigger("change"); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment