From a88cf11a5de41b5ceaeba7533657931e9bea47f6 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Wed, 5 Mar 2014 17:28:27 -0500 Subject: [PATCH] CRM-13966 HR-301 - More robust cliet-side entityRef, toward refactoring out jquery.crmContactField.js --- CRM/Core/Form.php | 22 ++++----- CRM/Core/Form/Renderer.php | 9 ---- CRM/Core/Resources.php | 19 +++++--- js/Common.js | 93 ++++++++++++++++++++++++++------------ 4 files changed, 85 insertions(+), 58 deletions(-) diff --git a/CRM/Core/Form.php b/CRM/Core/Form.php index 7c069c95f3..fd47c2de40 100644 --- a/CRM/Core/Form.php +++ b/CRM/Core/Form.php @@ -1260,13 +1260,12 @@ class CRM_Core_Form extends HTML_QuickForm_Page { * @return HTML_QuickForm_Element */ function addEntityRef($name, $label = '', $props = array(), $required = FALSE) { + require_once "api/api.php"; $config = CRM_Core_Config::singleton(); // Default properties $props['api'] = CRM_Utils_Array::value('api', $props, array()); - $props['entity'] = CRM_Utils_Array::value('entity', $props, 'contact'); - - $props['class'] = isset($props['class']) ? $props['class'] . ' ' : ''; - $props['class'] .= "crm-select2 crm-form-entityref"; + $props['entity'] = _civicrm_api_get_entity_name_from_camel(CRM_Utils_Array::value('entity', $props, 'contact')); + $props['class'] = ltrim(CRM_Utils_Array::value('class', $props, '') . ' crm-form-entityref'); if ($props['entity'] == 'contact' && isset($props['create']) && !(CRM_Core_Permission::check('edit all contacts') || CRM_Core_Permission::check('add contacts'))) { unset($props['create']); @@ -1281,14 +1280,11 @@ class CRM_Core_Form extends HTML_QuickForm_Page { } } - $defaults = array( - 'minimumInputLength' => 1, - 'multiple' => !empty($props['multiple']), - 'placeholder' => CRM_Utils_Array::value('placeholder', $props, $required ? ts('- select %1 -', array(1 => ts($props['entity']))) : ts('- none -')), - 'allowClear' => !$required, - ); - if ($props['entity'] == 'contact') { - $defaults['formatInputTooShort'] = $config->includeEmailInName ? ts('Start typing a name or email...') : ts('Start typing a name...'); + $props['placeholder'] = CRM_Utils_Array::value('placeholder', $props, $required ? ts('- select %1 -', array(1 => ts(str_replace('_', ' ', $props['entity'])))) : ts('- none -')); + + $defaults = array(); + if (!empty($props['multiple'])) { + $defaults['multiple'] = TRUE; } $props['select'] = CRM_Utils_Array::value('select', $props, array()) + $defaults; @@ -1306,7 +1302,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page { if (!empty($props['create'])) { $props['data-create-links'] = json_encode($props['create']); } - CRM_Utils_Array::remove($props, 'multiple', 'select', 'api', 'entity', 'placeholder', 'create'); + CRM_Utils_Array::remove($props, 'multiple', 'select', 'api', 'entity', 'create'); } /** diff --git a/CRM/Core/Form/Renderer.php b/CRM/Core/Form/Renderer.php index 9bb1046e30..21fecf497e 100644 --- a/CRM/Core/Form/Renderer.php +++ b/CRM/Core/Form/Renderer.php @@ -211,7 +211,6 @@ class CRM_Core_Form_Renderer extends HTML_QuickForm_Renderer_ArraySmarty { } if ($val) { $entity = $field->getAttribute('data-api-entity'); - $select = json_decode($field->getAttribute('data-select-params'), TRUE); $api = json_decode($field->getAttribute('data-api-params'), TRUE); $params = CRM_Utils_Array::value('params', $api, array()); // Support serialized values @@ -224,10 +223,6 @@ class CRM_Core_Form_Renderer extends HTML_QuickForm_Renderer_ArraySmarty { $field->removeAttribute('class'); } if (!empty($result['values'])) { - // Simplify array for single selects - makes client-side code simpler (but feels somehow wrong) - if (empty($select['multiple'])) { - $result['values'] = $result['values'][0]; - } $field->setAttribute('data-entity-value', json_encode($result['values'])); } } @@ -242,10 +237,6 @@ class CRM_Core_Form_Renderer extends HTML_QuickForm_Renderer_ArraySmarty { function renderFrozenEntityRef(&$el, $field) { $entity = $field->getAttribute('data-api-entity'); $vals = json_decode($field->getAttribute('data-entity-value'), TRUE); - // Hack for single-entity @see self::preProcessEntityRef - if (isset($vals['id'])) { - $vals = array($vals); - } $display = array(); foreach ($vals as $val) { // Format contact as link diff --git a/CRM/Core/Resources.php b/CRM/Core/Resources.php index 9d4e4cb2ef..edc0a95118 100644 --- a/CRM/Core/Resources.php +++ b/CRM/Core/Resources.php @@ -529,12 +529,17 @@ class CRM_Core_Resources { * @param string $js */ function addLocalization(&$js) { - $js .= ' - $.fn.select2.defaults.formatNoMatches = ' . json_encode(ts('None found.')) . '; - $.fn.select2.defaults.formatLoadMore = ' . json_encode(ts('Loading...')) . '; - $.fn.select2.defaults.formatSearching = ' . json_encode(ts('Searching...')) . '; - $.fn.select2.defaults.formatInputTooShort = ' . json_encode(ts('Enter search term...')) . '; - '; + $config = CRM_Core_Config::singleton(); + + // Localize select2 strings + $contactSearch = json_encode($config->includeEmailInName ? ts('Start typing a name or email...') : ts('Start typing a name...')); + $otherSearch = json_encode(ts('Enter search term...')); + $js .= " + $.fn.select2.defaults.formatNoMatches = " . json_encode(ts("None found.")) . "; + $.fn.select2.defaults.formatLoadMore = " . json_encode(ts("Loading...")) . "; + $.fn.select2.defaults.formatSearching = " . json_encode(ts("Searching...")) . "; + $.fn.select2.defaults.formatInputTooShort = function(){return cj(this).data('api-entity') == 'contact' ? $contactSearch : $otherSearch}; + "; } /** @@ -553,7 +558,7 @@ class CRM_Core_Resources { "packages/jquery/jquery-ui/js/jquery-ui-1.10.3.custom$min.js", "packages/jquery/jquery-ui/css/black-tie/jquery-ui-1.10.3.custom$min.css", - "packages/backbone/lodash.underscore$min.js", + "packages/backbone/lodash.compat$min.js", "packages/jquery/plugins/select2/select2.js", // No mini until release of select2 3.4.6 "packages/jquery/plugins/select2/select2.css", diff --git a/js/Common.js b/js/Common.js index 72d49f9d88..b34348d89f 100644 --- a/js/Common.js +++ b/js/Common.js @@ -277,27 +277,45 @@ CRM.validate = CRM.validate || { /** * Select2 api leaves something to be desired. To alter options on-the-fly often requires re-rendering the whole thing. * So making this function public in case anyone needs it. + * @param options object */ - CRM.utils.buildSelect2Element = function() { - var $el = $(this); - var options = {}; - // quickform doesn't support optgroups so here's a hack :( - $('option[value^=crm_optgroup]', this).each(function() { - $(this).nextUntil('option[value^=crm_optgroup]').wrapAll(''); - $(this).remove(); - }); - // Defaults for single-selects - if ($el.is('select:not([multiple])')) { - options.minimumResultsForSearch = 10; - options.allowClear = !($el.hasClass('required')); - if ($('option:first', this).val() === '') { - options.placeholderOption = 'first'; + $.fn.crmSelect2 = function(options) { + return $(this).each(function () { + var + $el = $(this), + defaults = {allowClear: !$el.hasClass('required')}; + // quickform doesn't support optgroups so here's a hack :( + $('option[value^=crm_optgroup]', this).each(function () { + $(this).nextUntil('option[value^=crm_optgroup]').wrapAll(''); + $(this).remove(); + }); + // Defaults for single-selects + if ($el.is('select:not([multiple])')) { + defaults.minimumResultsForSearch = 10; + if ($('option:first', this).val() === '') { + defaults.placeholderOption = 'first'; + } } - } - $.extend(options, $el.data('select-params') || {}); - // Autocomplete using the getlist api - if ($el.data('api-entity') && $el.hasClass('crm-form-entityref')) { - $el.addClass('crm-ajax-select'); + $el.select2($.extend(defaults, $el.data('select-params') || {}, options || {})); + }); + }; + + /** + * Initialize a select2 autocomplete using the getlist api + * @param options object + */ + $.fn.crmEntityRef = function(options) { + options = options || {}; + options.select = options.select || {}; + return $(this).each(function() { + var + $el = $(this), + entity = options.entity || $el.data('api-entity') || 'contact', + selectParams = {}; + $el.data('api-entity', entity); + $el.data('select-params', $.extend({}, $el.data('select-params') || {}, options.select)); + $el.data('api-params', $.extend({}, $el.data('api-params') || {}, options.api)); + $el.addClass('crm-ajax-select crm-' + entity + '-ref'); var settings = { // Use select2 ajax helper instead of CRM.api because it provides more value ajax: { @@ -316,28 +334,45 @@ CRM.validate = CRM.validate || { return {more: data.more_results, results: data.values || []}; } }, + minimumInputLength: 1, formatResult: CRM.utils.formatSelect2Result, formatSelection: function(row) { return row.label; }, escapeMarkup: function (m) {return m;}, - initSelection: function(el, callback) { - callback(el.data('entity-value')); + initSelection: function($el, callback) { + var + multiple = !!$el.data('select-params').multiple, + val = $el.val(), + stored = $el.data('entity-value') || []; + if (val === '') { + return; + } + // If we already have this data, just return it + if (!_.xor(val.split(','), _.pluck(stored, 'id')).length) { + callback(multiple ? stored : stored[0]); + } else { + var params = $el.data('api-params') || {}; + params.id = val; + CRM.api3($el.data('api-entity'), 'getlist', params).done(function(result) { + callback(multiple ? result.values : result.values[0]) + }); + } } }; if ($el.data('create-links')) { - options.formatInputTooShort = function() { - var txt = $el.data('select-params').formatInputTooShort || $.fn.select2.defaults.formatInputTooShort; + selectParams.formatInputTooShort = function() { + var txt = $el.data('select-params').formatInputTooShort || $.fn.select2.defaults.formatInputTooShort.call(this); if ($el.data('create-links').length) { txt += ' ' + ts('or') + '
' + CRM.utils.formatSelect2CreateLinks($el); } return txt; }; - options.formatNoMatches = function() { + selectParams.formatNoMatches = function() { var txt = $el.data('select-params').formatNoMatches || $.fn.select2.defaults.formatNoMatches; return txt + '
' + CRM.utils.formatSelect2CreateLinks($el); }; - $el.on('select2-open', function() { + $el.off('.createLinks').on('select2-open.createLinks', function() { var $el = $(this); $('#select2-drop').off('.crmEntity').on('click.crmEntity', 'a.crm-add-entity', function(e) { $el.select2('close'); @@ -358,9 +393,8 @@ CRM.validate = CRM.validate || { }); }); } - options = $.extend(settings, options); - } - $(this).select2(options).removeClass('crm-select2'); + $el.crmSelect2($.extend(settings, $el.data('select-params'), selectParams)); + }); }; CRM.utils.formatSelect2Result = function(row) { @@ -409,7 +443,8 @@ CRM.validate = CRM.validate || { target.toggleClass('crm-row-selected', $(this).is(':checked')); }) .find('input.select-row:checked').parents('tr').addClass('crm-row-selected'); - $('.crm-select2', e.target).each(CRM.utils.buildSelect2Element); + $('.crm-select2:not(.select2-offscreen)', e.target).crmSelect2(); + $('.crm-form-entityref:not(.select2-offscreen)', e.target).crmEntityRef(); }); /** -- 2.25.1