Skip to content

Instantly share code, notes, and snippets.

@mcdonc
Created March 7, 2019 01:20
Show Gist options
  • Save mcdonc/13f6021767296e5842e71cb9bc5c5ad2 to your computer and use it in GitHub Desktop.
Save mcdonc/13f6021767296e5842e71cb9bc5c5ad2 to your computer and use it in GitHub Desktop.
# 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