form handling of f.options, f.regex, f.listable, f.rearchable

This commit is contained in:
mdipierro
2017-11-10 18:41:41 -06:00
parent eb7222aa92
commit 14e58276cf
5 changed files with 103 additions and 46 deletions
+42 -16
View File
@@ -12,6 +12,10 @@
$.error('web2py.js has already been loaded!'); $.error('web2py.js has already been loaded!');
} }
var FORMDATA_IS_SUPPORTED = typeof(FormData) !== 'undefined';
var animateIn = 'fadeIn';
// var animateIn = 'slideDown';
String.prototype.reverse = function () { String.prototype.reverse = function () {
return this.split('').reverse().join(''); return this.split('').reverse().join('');
}; };
@@ -178,7 +182,7 @@
}, },
/* manage errors in forms */ /* manage errors in forms */
manage_errors: function (target) { manage_errors: function (target) {
$('div.error', target).hide().slideDown('slow'); $('div.error', target).hide()[animateIn]('slow');
}, },
after_ajax: function (xhr) { after_ajax: function (xhr) {
/* called whenever an ajax request completes */ /* called whenever an ajax request completes */
@@ -263,13 +267,17 @@
} }
}); });
/* help preventing double form submission for normal form (not LOADed) */ /* help preventing double form submission for normal form (not LOADed) */
$(doc).on('submit', 'form', function () { $(doc).on('submit', 'form', function (e) {
var submit_button = $(this).find(web2py.formInputClickSelector); var submit_buttons = $(this).find(web2py.formInputClickSelector);
web2py.disableElement(submit_button); submit_buttons.each(function() {
web2py.disableElement($(this));
})
/* safeguard in case the form doesn't trigger a refresh, /* safeguard in case the form doesn't trigger a refresh,
see https://github.com/web2py/web2py/issues/1100 */ see https://github.com/web2py/web2py/issues/1100 */
setTimeout(function () { setTimeout(function () {
web2py.enableElement(submit_button); submit_buttons.each(function() {
web2py.enableElement($(this));
});
}, 5000); }, 5000);
}); });
doc.ajaxSuccess(function (e, xhr) { doc.ajaxSuccess(function (e, xhr) {
@@ -320,7 +328,15 @@
form.submit(function (e) { form.submit(function (e) {
web2py.disableElement(form.find(web2py.formInputClickSelector)); web2py.disableElement(form.find(web2py.formInputClickSelector));
web2py.hide_flash(); web2py.hide_flash();
web2py.ajax_page('post', url, form.serialize(), target, form);
var formData;
if (FORMDATA_IS_SUPPORTED) {
formData = new FormData(form[0]); // Allows file uploads.
} else {
formData = form.serialize(); // Fallback for older browsers.
}
web2py.ajax_page('post', url, formData, target, form);
e.preventDefault(); e.preventDefault();
}); });
form.on('click', web2py.formInputClickSelector, function (e) { form.on('click', web2py.formInputClickSelector, function (e) {
@@ -339,11 +355,18 @@
if (web2py.isUndefined(element)) element = $(document); if (web2py.isUndefined(element)) element = $(document);
/* if target is not there, fill it with something that there isn't in the page*/ /* if target is not there, fill it with something that there isn't in the page*/
if (web2py.isUndefined(target) || target === '') target = 'w2p_none'; if (web2py.isUndefined(target) || target === '') target = 'w2p_none';
/* processData and contentType must be set to false when passing a FormData
object to jQuery.ajax. */
var isFormData = Object.prototype.toString.call(data) === '[object FormData]';
var contentType = isFormData ? false : 'application/x-www-form-urlencoded; charset=UTF-8';
if (web2py.fire(element, 'ajax:before', null, target)) { /*test a usecase, should stop here if returns false */ if (web2py.fire(element, 'ajax:before', null, target)) { /*test a usecase, should stop here if returns false */
$.ajax({ $.ajax({
'type': method, 'type': method,
'url': action, 'url': action,
'data': data, 'data': data,
'processData': !isFormData,
'contentType': contentType,
'beforeSend': function (xhr, settings) { 'beforeSend': function (xhr, settings) {
xhr.setRequestHeader('web2py-component-location', document.location); xhr.setRequestHeader('web2py-component-location', document.location);
xhr.setRequestHeader('web2py-component-element', target); xhr.setRequestHeader('web2py-component-element', target);
@@ -595,7 +618,7 @@
var flash = $('.w2p_flash'); var flash = $('.w2p_flash');
web2py.hide_flash(); web2py.hide_flash();
flash.html(message).addClass(status); flash.html(message).addClass(status);
if (flash.html()) flash.append('<span id="closeflash"> &times; </span>').slideDown(); if (flash.html()) flash.append('<span id="closeflash"> &times; </span>')[animateIn]();
}, },
hide_flash: function () { hide_flash: function () {
$('.w2p_flash').fadeOut(0).html(''); $('.w2p_flash').fadeOut(0).html('');
@@ -609,7 +632,7 @@
for (var k = 0; k < triggers[id].length; k++) { for (var k = 0; k < triggers[id].length; k++) {
var dep = $('#' + triggers[id][k], target); var dep = $('#' + triggers[id][k], target);
var tr = $('#' + triggers[id][k] + '__row', target); var tr = $('#' + triggers[id][k] + '__row', target);
if (t.is(dep.attr('data-show-if'))) tr.slideDown(); if (t.is(dep.attr('data-show-if'))) tr[animateIn]();
else tr.hide(); else tr.hide();
} }
}; };
@@ -699,8 +722,9 @@
}); });
}, },
/* Disables form elements: /* Disables form elements:
- Does not disable elements with 'data-w2p_disable' attribute
- Caches element value in 'w2p_enable_with' data store - Caches element value in 'w2p_enable_with' data store
- Replaces element text with value of 'data-disable-with' attribute - Replaces element text with value of 'data-w2p_disable_with' attribute
- Sets disabled property to true - Sets disabled property to true
*/ */
disableFormElements: function (form) { disableFormElements: function (form) {
@@ -712,13 +736,15 @@
if (!web2py.isUndefined(disable)) { if (!web2py.isUndefined(disable)) {
return false; return false;
} }
if (web2py.isUndefined(disable_with)) { if (!element.is(':file')) { // Altering file input values is not allowed.
element.data('w2p_disable_with', element[method]()); if (web2py.isUndefined(disable_with)) {
element.data('w2p_disable_with', element[method]());
}
if (web2py.isUndefined(element.data('w2p_enable_with'))) {
element.data('w2p_enable_with', element[method]());
}
element[method](element.data('w2p_disable_with'));
} }
if (web2py.isUndefined(element.data('w2p_enable_with'))) {
element.data('w2p_enable_with', element[method]());
}
element[method](element.data('w2p_disable_with'));
element.prop('disabled', true); element.prop('disabled', true);
}); });
}, },
@@ -799,4 +825,4 @@ web2py_event_handlers = jQuery.web2py.event_handlers;
web2py_trap_link = jQuery.web2py.trap_link; web2py_trap_link = jQuery.web2py.trap_link;
web2py_calc_entropy = jQuery.web2py.calc_entropy; web2py_calc_entropy = jQuery.web2py.calc_entropy;
*/ */
/* compatibility code - end*/ /* compatibility code - end*/
+42 -16
View File
@@ -12,6 +12,10 @@
$.error('web2py.js has already been loaded!'); $.error('web2py.js has already been loaded!');
} }
var FORMDATA_IS_SUPPORTED = typeof(FormData) !== 'undefined';
var animateIn = 'fadeIn';
// var animateIn = 'slideDown';
String.prototype.reverse = function () { String.prototype.reverse = function () {
return this.split('').reverse().join(''); return this.split('').reverse().join('');
}; };
@@ -178,7 +182,7 @@
}, },
/* manage errors in forms */ /* manage errors in forms */
manage_errors: function (target) { manage_errors: function (target) {
$('div.error', target).hide().slideDown('slow'); $('div.error', target).hide()[animateIn]('slow');
}, },
after_ajax: function (xhr) { after_ajax: function (xhr) {
/* called whenever an ajax request completes */ /* called whenever an ajax request completes */
@@ -263,13 +267,17 @@
} }
}); });
/* help preventing double form submission for normal form (not LOADed) */ /* help preventing double form submission for normal form (not LOADed) */
$(doc).on('submit', 'form', function () { $(doc).on('submit', 'form', function (e) {
var submit_button = $(this).find(web2py.formInputClickSelector); var submit_buttons = $(this).find(web2py.formInputClickSelector);
web2py.disableElement(submit_button); submit_buttons.each(function() {
web2py.disableElement($(this));
})
/* safeguard in case the form doesn't trigger a refresh, /* safeguard in case the form doesn't trigger a refresh,
see https://github.com/web2py/web2py/issues/1100 */ see https://github.com/web2py/web2py/issues/1100 */
setTimeout(function () { setTimeout(function () {
web2py.enableElement(submit_button); submit_buttons.each(function() {
web2py.enableElement($(this));
});
}, 5000); }, 5000);
}); });
doc.ajaxSuccess(function (e, xhr) { doc.ajaxSuccess(function (e, xhr) {
@@ -320,7 +328,15 @@
form.submit(function (e) { form.submit(function (e) {
web2py.disableElement(form.find(web2py.formInputClickSelector)); web2py.disableElement(form.find(web2py.formInputClickSelector));
web2py.hide_flash(); web2py.hide_flash();
web2py.ajax_page('post', url, form.serialize(), target, form);
var formData;
if (FORMDATA_IS_SUPPORTED) {
formData = new FormData(form[0]); // Allows file uploads.
} else {
formData = form.serialize(); // Fallback for older browsers.
}
web2py.ajax_page('post', url, formData, target, form);
e.preventDefault(); e.preventDefault();
}); });
form.on('click', web2py.formInputClickSelector, function (e) { form.on('click', web2py.formInputClickSelector, function (e) {
@@ -339,11 +355,18 @@
if (web2py.isUndefined(element)) element = $(document); if (web2py.isUndefined(element)) element = $(document);
/* if target is not there, fill it with something that there isn't in the page*/ /* if target is not there, fill it with something that there isn't in the page*/
if (web2py.isUndefined(target) || target === '') target = 'w2p_none'; if (web2py.isUndefined(target) || target === '') target = 'w2p_none';
/* processData and contentType must be set to false when passing a FormData
object to jQuery.ajax. */
var isFormData = Object.prototype.toString.call(data) === '[object FormData]';
var contentType = isFormData ? false : 'application/x-www-form-urlencoded; charset=UTF-8';
if (web2py.fire(element, 'ajax:before', null, target)) { /*test a usecase, should stop here if returns false */ if (web2py.fire(element, 'ajax:before', null, target)) { /*test a usecase, should stop here if returns false */
$.ajax({ $.ajax({
'type': method, 'type': method,
'url': action, 'url': action,
'data': data, 'data': data,
'processData': !isFormData,
'contentType': contentType,
'beforeSend': function (xhr, settings) { 'beforeSend': function (xhr, settings) {
xhr.setRequestHeader('web2py-component-location', document.location); xhr.setRequestHeader('web2py-component-location', document.location);
xhr.setRequestHeader('web2py-component-element', target); xhr.setRequestHeader('web2py-component-element', target);
@@ -595,7 +618,7 @@
var flash = $('.w2p_flash'); var flash = $('.w2p_flash');
web2py.hide_flash(); web2py.hide_flash();
flash.html(message).addClass(status); flash.html(message).addClass(status);
if (flash.html()) flash.append('<span id="closeflash"> &times; </span>').slideDown(); if (flash.html()) flash.append('<span id="closeflash"> &times; </span>')[animateIn]();
}, },
hide_flash: function () { hide_flash: function () {
$('.w2p_flash').fadeOut(0).html(''); $('.w2p_flash').fadeOut(0).html('');
@@ -609,7 +632,7 @@
for (var k = 0; k < triggers[id].length; k++) { for (var k = 0; k < triggers[id].length; k++) {
var dep = $('#' + triggers[id][k], target); var dep = $('#' + triggers[id][k], target);
var tr = $('#' + triggers[id][k] + '__row', target); var tr = $('#' + triggers[id][k] + '__row', target);
if (t.is(dep.attr('data-show-if'))) tr.slideDown(); if (t.is(dep.attr('data-show-if'))) tr[animateIn]();
else tr.hide(); else tr.hide();
} }
}; };
@@ -699,8 +722,9 @@
}); });
}, },
/* Disables form elements: /* Disables form elements:
- Does not disable elements with 'data-w2p_disable' attribute
- Caches element value in 'w2p_enable_with' data store - Caches element value in 'w2p_enable_with' data store
- Replaces element text with value of 'data-disable-with' attribute - Replaces element text with value of 'data-w2p_disable_with' attribute
- Sets disabled property to true - Sets disabled property to true
*/ */
disableFormElements: function (form) { disableFormElements: function (form) {
@@ -712,13 +736,15 @@
if (!web2py.isUndefined(disable)) { if (!web2py.isUndefined(disable)) {
return false; return false;
} }
if (web2py.isUndefined(disable_with)) { if (!element.is(':file')) { // Altering file input values is not allowed.
element.data('w2p_disable_with', element[method]()); if (web2py.isUndefined(disable_with)) {
element.data('w2p_disable_with', element[method]());
}
if (web2py.isUndefined(element.data('w2p_enable_with'))) {
element.data('w2p_enable_with', element[method]());
}
element[method](element.data('w2p_disable_with'));
} }
if (web2py.isUndefined(element.data('w2p_enable_with'))) {
element.data('w2p_enable_with', element[method]());
}
element[method](element.data('w2p_disable_with'));
element.prop('disabled', true); element.prop('disabled', true);
}); });
}, },
@@ -799,4 +825,4 @@ web2py_event_handlers = jQuery.web2py.event_handlers;
web2py_trap_link = jQuery.web2py.trap_link; web2py_trap_link = jQuery.web2py.trap_link;
web2py_calc_entropy = jQuery.web2py.calc_entropy; web2py_calc_entropy = jQuery.web2py.calc_entropy;
*/ */
/* compatibility code - end*/ /* compatibility code - end*/
+14 -9
View File
@@ -50,6 +50,10 @@ def _default_validators(db, field):
requires.append(validators.IS_TIME()) requires.append(validators.IS_TIME())
elif field_type == 'datetime': elif field_type == 'datetime':
requires.append(validators.IS_DATETIME()) requires.append(validators.IS_DATETIME())
elif field.options is not None:
requires = IS_IN_SET(field.options, multiple=field_type.startswith('list:'))
elif field.regex and not requires:
requires = IS_REGEX(regex)
elif db and field_type.startswith('reference') and \ elif db and field_type.startswith('reference') and \
field_type.find('.') < 0 and \ field_type.find('.') < 0 and \
field_type[10:] in db.tables: field_type[10:] in db.tables:
@@ -76,16 +80,17 @@ def _default_validators(db, field):
requires._and = validators.IS_NOT_IN_DB(db, field) requires._and = validators.IS_NOT_IN_DB(db, field)
if not field.notnull: if not field.notnull:
requires = validators.IS_EMPTY_OR(requires) requires = validators.IS_EMPTY_OR(requires)
return requires return requires
# does not get here for reference and list:reference # does not get here for reference and list:reference
if field.unique: if isinstance(requires, list):
requires.insert(0, validators.IS_NOT_IN_DB(db, field)) if field.unique:
excluded_fields = ['string', 'upload', 'text', 'password', 'boolean'] requires.insert(0, validators.IS_NOT_IN_DB(db, field))
if (field.notnull or field.unique) and field_type not in excluded_fields: excluded_fields = ['string', 'upload', 'text', 'password', 'boolean']
requires.insert(0, validators.IS_NOT_EMPTY()) if (field.notnull or field.unique) and field_type not in excluded_fields:
elif not field.notnull and not field.unique and requires: requires.insert(0, validators.IS_NOT_EMPTY())
requires[0] = \ elif not field.notnull and not field.unique and requires:
validators.IS_EMPTY_OR(requires[0], null='' if field.type in ('string', 'text', 'password') else None) null = null='' if field.type in ('string', 'text', 'password') else None
requires[0] = validators.IS_EMPTY_OR(requires[0], null=null)
return requires return requires
DAL.serializers = {'json': custom_json, 'xml': xml} DAL.serializers = {'json': custom_json, 'xml': xml}
+4 -4
View File
@@ -2348,12 +2348,12 @@ class SQLFORM(FORM):
for k, f in iteritems(table): for k, f in iteritems(table):
if isinstance(f, Field.Virtual): if isinstance(f, Field.Virtual):
f.tablename = table._tablename f.tablename = table._tablename
columns = [f for f in fields if f.tablename in tablenames] columns = [f for f in fields if f.tablename in tablenames and f.listable]
else: else:
fields = [] fields = []
columns = [] columns = []
filter1 = lambda f: isinstance(f, Field) and (f.type!='blob' or showblobs) filter1 = lambda f: isinstance(f, Field) and (f.type!='blob' or showblobs)
filter2 = lambda f: isinstance(f, Field) and f.readable filter2 = lambda f: isinstance(f, Field) and f.readable and f.listable
for table in tables: for table in tables:
fields += filter(filter1, table) fields += filter(filter1, table)
columns += filter(filter2, table) columns += filter(filter2, table)
@@ -2571,8 +2571,8 @@ class SQLFORM(FORM):
try: try:
# the query should be constructed using searchable # the query should be constructed using searchable
# fields but not virtual fields # fields but not virtual fields
sfields = reduce(lambda a, b: a + b, is_searchable = lambda f: f.readable and not isinstance(f, Field.Virtual) and f.searchable
[[f for f in t if f.readable and not isinstance(f, Field.Virtual)] for t in tables]) sfields = reduce(lambda a, b: a + b, [filter(is_searchable, t) for t in tables])
# use custom_query using searchable # use custom_query using searchable
if callable(searchable): if callable(searchable):
dbset = dbset(searchable(sfields, keywords)) dbset = dbset(searchable(sfields, keywords))