X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=js%2FCommon.js;h=ce8546c5708edf2e4cf03397d9332f54a00665ee;hb=0a1c80afc6e291e9b44581dfc0e40e1b1df09adc;hp=8a5306763778e4b27059c4a0e07e57a9bd870545;hpb=b344fd6552d12c5ae345fd52bf4f4f03475c047b;p=civicrm-core.git diff --git a/js/Common.js b/js/Common.js index 8a53067637..ce8546c570 100644 --- a/js/Common.js +++ b/js/Common.js @@ -238,9 +238,13 @@ if (!CRM.vars) CRM.vars = {}; }; var scriptsLoaded = {}; - CRM.loadScript = function(url) { + CRM.loadScript = function(url, appendCacheCode) { if (!scriptsLoaded[url]) { - var script = document.createElement('script'); + var script = document.createElement('script'), + src = url; + if (appendCacheCode !== false) { + src += (_.includes(url, '?') ? '&r=' : '?r=') + CRM.config.resourceCacheCode; + } scriptsLoaded[url] = $.Deferred(); script.onload = function () { // Give the script time to execute @@ -256,7 +260,7 @@ if (!CRM.vars) CRM.vars = {}; CRM.CMSjQuery = window.jQuery; window.jQuery = CRM.$; } - script.src = url; + script.src = src; document.getElementsByTagName("head")[0].appendChild(script); } return scriptsLoaded[url]; @@ -466,7 +470,7 @@ if (!CRM.vars) CRM.vars = {}; var entity = $(this).data('api-entity') || ''; $(this) .off('.crmEntity') - .removeClass('crm-form-entityref crm-' + entity.toLowerCase() + '-ref') + .removeClass('crm-form-entityref crm-' + _.kebabCase(entity) + '-ref') .crmSelect2('destroy'); }); } @@ -475,13 +479,17 @@ if (!CRM.vars) CRM.vars = {}; return $(this).each(function() { var $el = $(this).off('.crmEntity'), - entity = options.entity || $el.data('api-entity') || 'contact', + entity = options.entity || $el.data('api-entity') || 'Contact', selectParams = {}; + // Legacy: fix entity name if passed in as snake case + if (entity.charAt(0).toUpperCase() !== entity.charAt(0)) { + entity = _.capitalize(_.camelCase(entity)); + } $el.data('api-entity', entity); $el.data('select-params', $.extend({}, $el.data('select-params') || {}, options.select)); $el.data('api-params', $.extend(true, {}, $el.data('api-params') || {}, options.api)); $el.data('create-links', options.create || $el.data('create-links')); - $el.addClass('crm-form-entityref crm-' + entity.toLowerCase() + '-ref'); + $el.addClass('crm-form-entityref crm-' + _.kebabCase(entity) + '-ref'); var settings = { // Use select2 ajax helper instead of CRM.api3 because it provides more value ajax: { @@ -527,7 +535,7 @@ if (!CRM.vars) CRM.vars = {}; } }; // Create new items inline - works for tags - if ($el.data('create-links') && entity.toLowerCase() === 'tag') { + if ($el.data('create-links') && entity === 'Tag') { selectParams.createSearchChoice = function(term, data) { if (!_.findKey(data, {label: term})) { return {id: "0", term: term, label: term + ' (' + ts('new tag') + ')'}; @@ -578,11 +586,13 @@ if (!CRM.vars) CRM.vars = {}; formUrl = $(this).attr('href') + '&returnExtra=display_name,sort_name' + (extra ? (',' + extra) : ''); $el.select2('close'); CRM.loadForm(formUrl, { - dialog: {width: 500, height: 220} + dialog: {width: '50%', height: 220} }).on('crmFormSuccess', function(e, data) { if (data.status === 'success' && data.id) { - data.label = data.extra.sort_name; - CRM.status(ts('%1 Created', {1: data.extra.display_name})); + if (!data.crmMessages) { + CRM.status(ts('%1 Created', {1: data.label || data.extra.display_name})); + } + data.label = data.label || data.extra.sort_name; if ($el.select2('container').hasClass('select2-container-multi')) { var selection = $el.select2('data'); selection.push(data); @@ -646,153 +656,12 @@ if (!CRM.vars) CRM.vars = {}; return combined; } - function copyAttributes($source, $target, attributes) { + CRM.utils.copyAttributes = function ($source, $target, attributes) { _.each(attributes, function(name) { if ($source.attr(name) !== undefined) { $target.attr(name, $source.attr(name)); } }); - } - - /** - * @see http://wiki.civicrm.org/confluence/display/CRMDOC/crmDatepicker - */ - $.fn.crmDatepicker = function(options) { - return $(this).each(function() { - if ($(this).is('.crm-form-date-wrapper .crm-hidden-date')) { - // Already initialized - destroy - $(this) - .off('.crmDatepicker') - .css('display', '') - .removeClass('crm-hidden-date') - .siblings().remove(); - $(this).unwrap(); - } - if (options === 'destroy') { - return; - } - var - $dataField = $(this).wrap(''), - settings = _.cloneDeep(options || {}), - $dateField = $(), - $timeField = $(), - $clearLink = $(), - hasDatepicker = settings.date !== false && settings.date !== 'yy', - type = hasDatepicker ? 'text' : 'number'; - - if (settings.allowClear !== undefined ? settings.allowClear : !$dataField.is('.required, [required]')) { - $clearLink = $('') - .insertAfter($dataField); - } - if (settings.time !== false) { - $timeField = $('').insertAfter($dataField); - copyAttributes($dataField, $timeField, ['class', 'disabled']); - $timeField - .addClass('crm-form-text crm-form-time') - .attr('placeholder', $dataField.attr('time-placeholder') === undefined ? ts('Time') : $dataField.attr('time-placeholder')) - .attr('aria-label', $dataField.attr('time-placeholder') === undefined ? ts('Time') : $dataField.attr('time-placeholder')) - .change(updateDataField) - .timeEntry({ - spinnerImage: '', - show24Hours: settings.time === true || settings.time === undefined ? CRM.config.timeIs24Hr : settings.time == '24' - }); - } - if (settings.date !== false) { - // Render "number" field for year-only format, calendar popup for all other formats - $dateField = $('').insertAfter($dataField); - copyAttributes($dataField, $dateField, ['placeholder', 'style', 'class', 'disabled', 'aria-label']); - $dateField.addClass('crm-form-' + type); - if (hasDatepicker) { - settings.minDate = settings.minDate ? CRM.utils.makeDate(settings.minDate) : null; - settings.maxDate = settings.maxDate ? CRM.utils.makeDate(settings.maxDate) : null; - settings.dateFormat = typeof settings.date === 'string' ? settings.date : CRM.config.dateInputFormat; - settings.changeMonth = _.includes(settings.dateFormat, 'm'); - settings.changeYear = _.includes(settings.dateFormat, 'y'); - if (!settings.yearRange && settings.minDate !== null && settings.maxDate !== null) { - settings.yearRange = '' + CRM.utils.formatDate(settings.minDate, 'yy') + ':' + CRM.utils.formatDate(settings.maxDate, 'yy'); - } - $dateField.addClass('crm-form-date').datepicker(settings); - } else { - $dateField.attr('min', settings.minDate ? CRM.utils.formatDate(settings.minDate, 'yy') : '1000'); - $dateField.attr('max', settings.maxDate ? CRM.utils.formatDate(settings.maxDate, 'yy') : '4000'); - } - $dateField.change(updateDataField); - } - // Rudimentary validation. TODO: Roll into use of jQUery validate and ui.datepicker.validation - function isValidDate() { - // FIXME: parseDate doesn't work with incomplete date formats; skip validation if no month, day or year in format - var lowerFormat = settings.dateFormat.toLowerCase(); - if (lowerFormat.indexOf('y') < 0 || lowerFormat.indexOf('m') < 0 || !dateHasDay()) { - return true; - } - try { - $.datepicker.parseDate(settings.dateFormat, $dateField.val()); - return true; - } catch (e) { - return false; - } - } - - /** - * Does the date format contain the day. - * - * @returns {boolean} - */ - function dateHasDay() { - var lowerFormat = settings.dateFormat.toLowerCase(); - if (lowerFormat.indexOf('d') < 0) { - return false; - } - return true; - } - function updateInputFields(e, context) { - var val = $dataField.val(), - time = null; - if (context !== 'userInput' && context !== 'crmClear') { - if (hasDatepicker) { - $dateField.datepicker('setDate', _.includes(val, '-') ? $.datepicker.parseDate('yy-mm-dd', val) : null); - } else if ($dateField.length) { - $dateField.val(val.slice(0, 4)); - } - if ($timeField.length) { - if (val.length === 8) { - time = val; - } else if (val.length === 19) { - time = val.split(' ')[1]; - } - $timeField.timeEntry('setTime', time); - } - } - $clearLink.css('visibility', val ? 'visible' : 'hidden'); - } - function updateDataField(e, context) { - // The crmClear event wipes all the field values anyway, so no need to respond - if (context !== 'crmClear') { - var val = ''; - if ($dateField.val()) { - if (hasDatepicker && isValidDate() && dateHasDay()) { - val = $.datepicker.formatDate('yy-mm-dd', $dateField.datepicker('getDate')); - $dateField.removeClass('crm-error'); - } else if (!hasDatepicker) { - val = $dateField.val() + '-01-01'; - } - else if (!dateHasDay()) { - // This would be a Year-month date (yyyy-mm) - // it could be argued it should not use a datepicker.... - val = $dateField.val() + '-01'; - } else { - $dateField.addClass('crm-error'); - } - } - if ($timeField.val()) { - val += (val ? ' ' : '') + $timeField.timeEntry('getTime').toTimeString().substr(0, 8); - } - $dataField.val(val).trigger('change', ['userInput']); - } - } - $dataField.hide().addClass('crm-hidden-date').on('change.crmDatepicker', updateInputFields); - updateInputFields(); - }); }; CRM.utils.formatSelect2Result = function (row) { @@ -824,33 +693,18 @@ if (!CRM.vars) CRM.vars = {}; var createLinks = $el.data('create-links'), params = getEntityRefApiParams($el).params, + entity = $el.data('api-entity'), markup = ''; return markup; @@ -858,19 +712,20 @@ if (!CRM.vars) CRM.vars = {}; function getEntityRefFilters($el) { var - entity = $el.data('api-entity').toLowerCase(), - filters = $.extend([], CRM.config.entityRef.filters[entity] || []), + entity = $el.data('api-entity'), + filters = CRM.config.entityRef.filters[entity] || [], params = $.extend({params: {}}, $el.data('api-params') || {}).params, result = []; - $.each(filters, function() { - var filter = $.extend({type: 'select', 'attributes': {}, entity: entity}, this); - if (typeof params[filter.key] === 'undefined') { + _.each(filters, function(filter) { + _.defaults(filter, {type: 'select', 'attributes': {}, entity: entity}); + if (!params[filter.key]) { + // Filter out options if params don't match its condition + if (filter.condition && !_.isMatch(params, _.pick(filter.condition, _.keys(params)))) { + return; + } result.push(filter); } else if (filter.key == 'contact_type' && typeof params.contact_sub_type === 'undefined') { - filter.options = _.remove(filter.options, function(option) { - return option.key.indexOf(params.contact_type + '__') === 0; - }); result.push(filter); } }); @@ -913,11 +768,7 @@ if (!CRM.vars) CRM.vars = {}; attrs += ' ' + attr + '="' + val + '"'; }); if (filterSpec.type === 'select') { - markup = ''; - if (filterSpec.options) { - markup += CRM.utils.renderOptions(filterSpec.options, filter.value); - } - markup += ''; + markup = ''; } else { markup = ''; } @@ -938,7 +789,7 @@ if (!CRM.vars) CRM.vars = {}; $('.crm-entityref-filter-value', '#select2-drop').remove(); $valField = $(entityRefFilterValueMarkup(filter, filterSpec)); $keyField.after($valField); - if (filterSpec.type === 'select' && !filterSpec.options) { + if (filterSpec.type === 'select') { loadEntityRefFilterOptions(filter, filterSpec, $valField, $el); } } else { @@ -947,24 +798,38 @@ if (!CRM.vars) CRM.vars = {}; } /** - * Fetch options for a filter via ajax api + * Fetch options for a filter from cache or ajax api */ function loadEntityRefFilterOptions(filter, filterSpec, $valField, $el) { - $valField.prop('disabled', true); // Fieldname may be prefixed with joins - strip those out - var fieldName = _.last(filter.key.split('.')); + var fieldName = _.last(filter.key.split('.')), + params = $.extend({params: {}}, $el.data('api-params') || {}).params; + if (filterSpec.options) { + setEntityRefFilterOptions($valField, fieldName, params, filterSpec); + return; + } + $('.crm-entityref-filters select', '#select2-drop').prop('disabled', true); CRM.api3(filterSpec.entity, 'getoptions', {field: fieldName, context: 'search', sequential: 1}) .done(function(result) { - var entity = $el.data('api-entity').toLowerCase(), - globalFilterSpec = _.find(CRM.config.entityRef.filters[entity], {key: filter.key}) || {}; + var entity = $el.data('api-entity').toLowerCase(); // Store options globally so we don't have to look them up again - globalFilterSpec.options = result.values; - $valField.prop('disabled', false); - CRM.utils.setOptions($valField, result.values); + filterSpec.options = result.values; + $('.crm-entityref-filters select', '#select2-drop').prop('disabled', false); + setEntityRefFilterOptions($valField, fieldName, params, filterSpec); $valField.val(filter.value || ''); }); } + function setEntityRefFilterOptions($valField, fieldName, params, filterSpec) { + var values = _.cloneDeep(filterSpec.options); + if (fieldName === 'contact_type' && params.contact_type) { + values = _.remove(values, function(option) { + return option.key.indexOf(params.contact_type + '__') === 0; + }); + } + CRM.utils.setOptions($valField, values); + } + //CRM-15598 - Override url validator method to allow relative url's (e.g. /index.htm) $.validator.addMethod("url", function(value, element) { if (/^\//.test(value)) { @@ -1639,7 +1504,7 @@ if (!CRM.vars) CRM.vars = {}; // Determine if a user has a given permission. // @see CRM_Core_Resources::addPermissions CRM.checkPerm = function(perm) { - return CRM.permissions[perm]; + return CRM.permissions && CRM.permissions[perm]; }; // Round while preserving sigfigs @@ -1686,4 +1551,11 @@ if (!CRM.vars) CRM.vars = {}; return (yiq >= 128) ? 'black' : 'white'; }; + // CVE-2015-9251 - Prevent auto-execution of scripts when no explicit dataType was provided + $.ajaxPrefilter(function(s) { + if (s.crossDomain) { + s.contents.script = false; + } + }); + })(jQuery, _);