diff --git a/autonomie/__init__.py b/autonomie/__init__.py index 419ce7be..12c576c4 100644 --- a/autonomie/__init__.py +++ b/autonomie/__init__.py @@ -42,9 +42,6 @@ from autonomie.models.config import get_config from autonomie.utils.avatar import get_groups from autonomie.utils.avatar import get_avatar -from autonomie.utils.renderer import ( - customize_renderers, -) from autonomie.utils.session import get_session_factory @@ -180,6 +177,9 @@ def base_configure(config, dbsession, **settings): for module in AUTONOMIE_ADMIN_MODULES: config.include(module) + from autonomie.utils.renderer import ( + customize_renderers, + ) customize_renderers(config) return config diff --git a/autonomie/deform_templates/tasklines_sequence.pt b/autonomie/deform_templates/tasklines_sequence.pt index 396c2955..5c5edae0 100644 --- a/autonomie/deform_templates/tasklines_sequence.pt +++ b/autonomie/deform_templates/tasklines_sequence.pt @@ -57,7 +57,7 @@

diff --git a/autonomie/resources.py b/autonomie/resources.py index e7ae2bd1..031ebddc 100644 --- a/autonomie/resources.py +++ b/autonomie/resources.py @@ -44,8 +44,6 @@ lib_autonomie = Library("fanstatic", "static") -# ui_dialog.depends.add(bootstrap_js) - def get_resource(filepath, minified=None, depends=None): """ @@ -59,6 +57,14 @@ def get_resource(filepath, minified=None, depends=None): ) +# ui_dialog.depends.add(bootstrap_js) +# To fix https://github.com/Pylons/deform/issues/276 +# https://github.com/Pylons/deform/issues/275 (only in the case of task forms +# because we use custom templates) +# We use a custom deform js +custom_deform_js = get_resource("js/custom_deform.js") + + def get_main_group(): """ Return the main resource Group that will be used on all pages diff --git a/autonomie/static/js/custom_deform.js b/autonomie/static/js/custom_deform.js new file mode 100644 index 00000000..4f00051c --- /dev/null +++ b/autonomie/static/js/custom_deform.js @@ -0,0 +1,196 @@ +/* + * Register a top-level callback to the deform.load() function + * this will be called when the DOM has finished loading. No need + * to include the call at the end of the page. + */ + +$(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 jsvarmatch = /var field_oid = "deformField(\d+)";/; + var code = protonode.attr('prototype'); + var html = decodeURIComponent(code); + var genid = deform.randomString(6); + html = html.replace(jsvarmatch, "var field_oid = \"deformField$1-" + genid + "\";"); + var $htmlnode = $(html); + var $idnodes = $htmlnode.find('[id]'); + var $namednodes = $htmlnode.find('[name]'); + 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('.deformSeq'); + var $proto_node = $oid_node.find('.deformProto').first(); + var $before_node = $oid_node.find('.deformInsertBefore').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('.deformSeqItem'); + var $oid_node = $item_node.closest('.deformSeq'); + var $before_node = $oid_node.find('.deformInsertBefore').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.children('.deformSeqContainer:not(.deformSeq .deformSeqContainer)'); + var $lis = $ul.find('.deformSeqItem:not(.deformSeqContainer .deformSeqItem)'); + var show_closebutton = now_len > min_len; + var show_addbutton = now_len < max_len; + $lis.find('.deformClosebutton').toggle(show_closebutton); + oid_node.find('.deformSeqAdd').toggle(show_addbutton); + $lis.find('.deformOrderbutton').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; + } + +}; diff --git a/autonomie/utils/renderer.py b/autonomie/utils/renderer.py index 2f99c15e..daf48a1f 100644 --- a/autonomie/utils/renderer.py +++ b/autonomie/utils/renderer.py @@ -39,7 +39,7 @@ from pyramid.threadlocal import get_current_request -log = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class CustomRenderer(ZPTRendererFactory): @@ -128,6 +128,14 @@ def set_export_blacklist(): BLACKLISTED_KEYS = ('_acl', 'password', 'parent_id', 'parent', 'type_') +def set_custom_deform_js(): + from js import deform + from autonomie.resources import custom_deform_js + logger.debug(u"Overriding the default deform_js resource") + deform.deform_js = custom_deform_js + deform.resource_mapping['deform'] = [custom_deform_js] + + def customize_renderers(config): """ Customize the different renderers @@ -139,3 +147,4 @@ def customize_renderers(config): # Exporters set_export_formatters() set_export_blacklist() + set_custom_deform_js()