diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e9d587fb1..062c2e88f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,6 +12,9 @@ on: coreprurl: description: (optional) Core PR URL required: false + drupal8prurl: + description: (optional) Drupal 8 PR URL + required: false jobs: phpunit: @@ -20,25 +23,22 @@ jobs: fail-fast: false matrix: include: - - drupal: '9.4.*' - civicrm: '5.57.*' - php: '7.4' - - drupal: '9.5.*' - civicrm: '5.58.*' - php: '7.4' - - drupal: '9.5.*' - civicrm: '5.60.x-dev' - php: '7.4' - - drupal: '9.5.*' - civicrm: 'dev-master' - php: '7.4' - - drupal: '10.0.*' + - drupal: '10.2.*' + civicrm: '5.78.*' + php: '8.2' + - drupal: '10.2.*' + civicrm: '5.79.*' + php: '8.2' + - drupal: '10.2.*' + civicrm: '5.80.x-dev' + php: '8.2' + - drupal: '10.3.*' civicrm: 'dev-master' - php: '8.1' + php: '8.3' name: Drupal ${{ matrix.drupal }} | CiviCRM ${{ matrix.civicrm }} services: mysql: - image: mysql:5.7 + image: mysql:8.0 env: MYSQL_ALLOW_EMPTY_PASSWORD: yes MYSQL_DATABASE: db @@ -46,7 +46,7 @@ jobs: - 3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} @@ -56,7 +56,7 @@ jobs: - name: Get composer cache directory id: composercache run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-${{ matrix.drupal }}-composer-${{ hashFiles('**/composer.json') }} @@ -68,7 +68,15 @@ jobs: # - CiviCRM requires `compile-mode: all` - name: Setup sendmail run: | + sudo apt-get update sudo apt-get install sendmail + # Temp thing + - name: Downgrade chrome + run: | + curl -L -o chrome.deb http://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_127.0.6533.119-1_amd64.deb + sudo apt-get install libu2f-udev + sudo dpkg -i chrome.deb + google-chrome-stable --version - name: Setup Drupal run: | COMPOSER_MEMORY_LIMIT=-1 composer create-project drupal/recommended-project:${{ matrix.drupal }} ~/drupal --no-interaction --no-install @@ -98,18 +106,10 @@ jobs: run: | cd ~/drupal COMPOSER_MEMORY_LIMIT=-1 composer require civicrm/civicrm-asset-plugin:'~1.1' civicrm/civicrm-{core,packages,drupal-8}:${{ matrix.civicrm }} -W - - name: Ensure Webform ^6.2 - if: ${{ matrix.drupal == '10.0.*' }} + - name: Download Webform run: | cd ~/drupal - COMPOSER_MEMORY_LIMIT=-1 composer require 'drupal/webform:^6.2@beta' - - name: Ensure Webform ^6.0 - if: ${{ matrix.drupal != '10.0.*' }} - run: | - cd ~/drupal - #COMPOSER_MEMORY_LIMIT=-1 composer require cweagans/composer-patches - #jq '.extra.patches = {"drupal/webform": {"Regression": "https://www.drupal.org/files/issues/2021-12-13/3254028-2.patch"}}' composer.json > temp.json && mv temp.json composer.json - COMPOSER_MEMORY_LIMIT=-1 composer require 'drupal/webform:6.x-dev@dev' + COMPOSER_MEMORY_LIMIT=-1 composer require 'drupal/webform:^6.2' - name: Install webform_civicrm run: | cd ~/drupal @@ -134,6 +134,12 @@ jobs: cd ~/drupal/vendor/civicrm/civicrm-core curl -L -o prpatch.patch ${{ github.event.inputs.coreprurl }}.patch git am prpatch.patch + - name: Optionally Apply Drupal 8 PR + if: ${{ github.event.inputs.drupal8prurl != 0 }} + run: | + cd ~/drupal/web/modules/contrib/civicrm + curl -L -o prpatch.patch ${{ github.event.inputs.drupal8prurl }}.patch + git am prpatch.patch - name: Do a fake temp install # so that we can use civi api to get extensions with a version appropriate to the installed civi version run: | @@ -145,51 +151,63 @@ jobs: ./vendor/drush/drush/drush -y -l http://civi.localhost site-install standard --db-url='mysql://root:@127.0.0.1:${{ job.services.mysql.ports[3306] }}/fakedb' --site-name=FakeCivi chmod +w web/sites/default /home/runner/civicrm-cv/cv core:install --cms-base-url=http://civi.localhost - - name: Download Civi extensions from git - if: ${{ matrix.drupal == '10.0.*' }} + - name: Download Civi extensions run: | mkdir -p ~/drupal/web/sites/default/files/civicrm/ext cd ~/drupal/web/sites/default/files/civicrm/ext - /home/runner/civicrm-cv/cv api3 Extension.download install=0 key=com.aghstrategies.uscounties - # Let's get latest git versions of these - git clone https://lab.civicrm.org/extensions/mjwshared.git - git clone https://lab.civicrm.org/extensions/firewall.git - git clone https://lab.civicrm.org/extensions/stripe.git - git clone https://github.com/iATSPayments/com.iatspayments.civicrm.git - - name: Download Civi extensions normal - if: ${{ matrix.drupal != '10.0.*' }} - run: | - mkdir -p ~/drupal/web/sites/default/files/civicrm/ext - cd ~/drupal/web/sites/default/files/civicrm/ext - # Normally we'll just let civi decide which version to download. + # Allow unapproved extensions + /home/runner/civicrm-cv/cv ev '\Civi::settings()->set("ext_repo_url", "https://civicrm.org/extdir/ver={ver}|cms={uf}|ready=");' + /home/runner/civicrm-cv/cv ev '\Civi::settings()->set("http_timeout", 60);' # Apparently we have to install it, otherwise stripe gives a dependency error even with install=0. I think that's a bug, but let's just do it. This is a fake install anyway. /home/runner/civicrm-cv/cv api3 Extension.download install=1 key=mjwshared /home/runner/civicrm-cv/cv api3 Extension.download install=1 key=firewall + /home/runner/civicrm-cv/cv api3 Extension.download install=1 key=mjwpaymentapi /home/runner/civicrm-cv/cv api3 Extension.download install=1 key=com.drastikbydesign.stripe /home/runner/civicrm-cv/cv api3 Extension.download install=0 key=com.iatspayments.civicrm /home/runner/civicrm-cv/cv api3 Extension.download install=0 key=com.aghstrategies.uscounties + # temporary iats patches for undeclared vars + cd com.iatspayments.civicrm + curl -L -O https://patch-diff.githubusercontent.com/raw/iATSPayments/com.iatspayments.civicrm/pull/455.diff + curl -L -O https://patch-diff.githubusercontent.com/raw/iATSPayments/com.iatspayments.civicrm/pull/456.diff + curl -L -O https://patch-diff.githubusercontent.com/raw/iATSPayments/com.iatspayments.civicrm/pull/457.diff + curl -L -O https://patch-diff.githubusercontent.com/raw/iATSPayments/com.iatspayments.civicrm/pull/458.diff + git apply 455.diff + git apply 456.diff + git apply 457.diff + git apply 458.diff - uses: nanasess/setup-chromedriver@master + with: + # temporary to match downgraded chrome + chromedriver-version: '127.0.6533.119' - name: Run chromedriver - run: chromedriver & + run: chromedriver --port=9515 & - name: Run php built-in server run: php -S 127.0.0.1:8080 -t ~/drupal/web & - name: Run PHPUnit run: | - mkdir $BROWSERTEST_OUTPUT_DIRECTORY + mkdir -p $BROWSERTEST_OUTPUT_DIRECTORY cd ~/drupal/web - ../vendor/bin/phpunit -c core modules/contrib/webform_civicrm + ../vendor/bin/phpunit --verbose -c core modules/contrib/webform_civicrm env: SYMFONY_DEPRECATIONS_HELPER: 999999 SIMPLETEST_DB: mysql://root:@127.0.0.1:${{ job.services.mysql.ports[3306] }}/db SIMPLETEST_BASE_URL: http://127.0.0.1:8080 - MINK_DRIVER_ARGS_WEBDRIVER: '["chrome", {"browserName":"chrome","chromeOptions":{"args":["--disable-gpu", "--no-sandbox", "--headless"]}}, "http://127.0.0.1:9515"]' - BROWSERTEST_OUTPUT_DIRECTORY: '${{ runner.temp }}/browser_output' + MINK_DRIVER_ARGS_WEBDRIVER: '["chrome", {"browserName":"chrome","goog:chromeOptions":{"args":["--disable-gpu", "--no-sandbox", "--headless"]}}, "http://127.0.0.1:9515"]' + BROWSERTEST_OUTPUT_DIRECTORY: '/home/runner/drupal/web/sites/simpletest/browser_output' DEV_EXTENSION_DIR: /home/runner/drupal/web/sites/default/files/civicrm/ext DEV_EXTENSION_URL: http://127.0.0.1:8080/sites/default/files/civicrm/ext - - uses: actions/upload-artifact@v3 + - name: Helper to make unique name for upload + if: ${{ failure() || success() }} + run: | + # doing this on multiple lines to avoid quote-hell + cd ${{ runner.temp }} + echo '${{ matrix.drupal }}_${{ matrix.civicrm }}_${{ matrix.php }}' > upload_helper.txt + sed -i -e 's/[^0-9a-zA-Z_.\-]//g' upload_helper.txt + echo "UPLOADNAME=$(cat upload_helper.txt)" >> $GITHUB_ENV + - uses: actions/upload-artifact@v4 if: ${{ failure() || success() }} with: - name: phpunit_browser_output + name: screenshots.${{ env.UPLOADNAME }} # For some reason Drupal prints here and not our specified BROWSERTEST_OUTPUT_DIRECTORY. path: '/home/runner/drupal/web/sites/simpletest/browser_output' retention-days: 7 diff --git a/js/jquery.tokeninput.js b/js/jquery.tokeninput.js index 4b69d8239..41a5951a3 100644 --- a/js/jquery.tokeninput.js +++ b/js/jquery.tokeninput.js @@ -468,7 +468,7 @@ hiddenInput.val(""); var li_data = $(input).data("settings").prePopulate || hiddenInput.data("pre"); - if ($(input).data("settings").processPrePopulate && $.isFunction($(input).data("settings").onResult)) { + if ($(input).data("settings").processPrePopulate && (typeof $(input).data("settings").onResult === "function")) { li_data = $(input).data("settings").onResult.call(hiddenInput, li_data); } @@ -581,14 +581,14 @@ } function add_freetagging_tokens() { - var value = $.trim(input_box.val()); + var value = input_box.val().trim(); var tokens = value.split($(input).data("settings").tokenDelimiter); $.each(tokens, function(i, token) { if (!token) { return; } - if ($.isFunction($(input).data("settings").onFreeTaggingAdd)) { + if (typeof $(input).data("settings").onFreeTaggingAdd === "function") { token = $(input).data("settings").onFreeTaggingAdd.call(hiddenInput, token); } var object = {}; @@ -684,7 +684,7 @@ hide_dropdown(); // Execute the onAdd callback if defined - if($.isFunction(callback)) { + if (typeof callback === "function") { callback.call(hiddenInput,item); } } @@ -774,7 +774,7 @@ } // Execute the onDelete callback if defined - if($.isFunction(callback)) { + if (typeof callback === "function") { callback.call(hiddenInput,token_data); } } @@ -973,7 +973,7 @@ var cache_key = query + computeURL(); var cached_results = cache.get(cache_key); if (cached_results) { - if ($.isFunction($(input).data("settings").onCachedResult)) { + if (typeof $(input).data("settings").onCachedResult === "function") { cached_results = $(input).data("settings").onCachedResult.call(hiddenInput, cached_results); } populateDropdown(query, cached_results); @@ -1022,7 +1022,7 @@ // Attach the success callback ajax_params.success = function(results) { cache.add(cache_key, $(input).data("settings").jsonContainer ? results[$(input).data("settings").jsonContainer] : results); - if($.isFunction($(input).data("settings").onResult)) { + if (typeof $(input).data("settings").onResult === "function") { results = $(input).data("settings").onResult.call(hiddenInput, results); } @@ -1046,7 +1046,7 @@ }); cache.add(cache_key, results); - if($.isFunction($(input).data("settings").onResult)) { + if (typeof $(input).data("settings").onResult === "function") { results = $(input).data("settings").onResult.call(hiddenInput, results); } populateDropdown(query, results); diff --git a/js/webform_civicrm_admin.js b/js/webform_civicrm_admin.js index 174182e37..f9a960e7d 100644 --- a/js/webform_civicrm_admin.js +++ b/js/webform_civicrm_admin.js @@ -269,7 +269,7 @@ var wfCiviAdmin = (function (D, $, once) { if ($('select[name$="_contact_sub_type[]"]', context).val()) { var first = true; $('select[name$="_contact_sub_type[]"] option:selected', context).each(function() { - label += (first ? ' (' : ', ') + $.trim($(this).text()); + label += (first ? ' (' : ', ') + $(this).text().trim(); first = false; }); label += ')'; @@ -325,7 +325,7 @@ var wfCiviAdmin = (function (D, $, once) { $(once('wf-civi', 'details#edit-additional-options', context)).drupalSetSummary(function (context) { var label = []; $(':checked', context).each(function() { - label.push($.trim($(this).siblings('label').contents().first().text())); + label.push($(this).siblings('label').contents().first().text().trim()); }); return label.join(', ') || Drupal.t('- None -'); }); @@ -500,25 +500,6 @@ var wfCiviAdmin = (function (D, $, once) { changeContactLabel.call(this); }); - // Contribution honoree fields - $(once('crm-contrib', 'select[name$=contribution_honor_contact_id]', context)).change(function() { - if ($(this).val() == '0') { - $('.form-item-civicrm-1-contribution-1-contribution-honor-type-id').hide(); - } - else { - $('.form-item-civicrm-1-contribution-1-contribution-honor-type-id').show(); - } - }).change(); - $(once('crm-contrib', 'select[name$=contribution_honor_type_id]', context)).change(function() { - var $label = $('.form-item-civicrm-1-contribution-1-contribution-honor-contact-id label'); - if ($(this).val() == 'create_civicrm_webform_element') { - $label.html(Drupal.t('In Honor/Memory of')); - } - else { - $label.html($('option:selected', this).html()); - } - }).change(); - // Membership constraints $(once('crm-mem-date', 'select[name$=_membership_num_terms]', context)).change(function(e, type) { var $dateWrappers = $(this).parent().siblings('[class$="-date"]').not('[class$="-status-override-end-date"]'); diff --git a/js/webform_civicrm_contact.js b/js/webform_civicrm_contact.js index 7143419ca..df90e3d72 100644 --- a/js/webform_civicrm_contact.js +++ b/js/webform_civicrm_contact.js @@ -14,17 +14,22 @@ hintText: field.data('search-prompt'), noResultsText: field.data('none-prompt'), resultsFormatter: formatChoices, - searchingText: "Searching..." + searchingText: "Searching...", + enableHTML: true }; - wfCivi.existingInit( - field, - field.data('civicrm-contact'), - field.data('form-id'), - autocompleteUrl, - toHide, - tokenValues - ); } + else { + var tokenValues = false; + } + + wfCivi.existingInit( + field, + field.data('civicrm-contact'), + field.data('form-id'), + autocompleteUrl, + toHide, + tokenValues + ); field.change(function () { wfCivi.existingSelect( diff --git a/js/webform_civicrm_forms.js b/js/webform_civicrm_forms.js index bb2355913..f586be0cd 100644 --- a/js/webform_civicrm_forms.js +++ b/js/webform_civicrm_forms.js @@ -164,6 +164,9 @@ var wfCivi = (function (D, $, drupalSettings, once) { $(':input[id$="month"]', $wrapper).val(parseInt(date[1], 10)).trigger('change', 'webform_civicrm:autofill'); $(':input[id$="day"]', $wrapper).val(parseInt(date[2], 10)).trigger('change', 'webform_civicrm:autofill'); } + else { + $(':input', this).val('').trigger('change', 'webform_civicrm:reset');; + } } else { $(':input', this).not(':radio, :checkbox, :button, :submit, :file, .form-file').each(function() { @@ -174,7 +177,7 @@ var wfCivi = (function (D, $, drupalSettings, once) { }); $('.civicrm-remove-file', this).click(); $('input:checkbox, input:radio', this).each(function() { - $(this).removeAttr('checked').trigger('change', 'webform_civicrm:reset'); + $(this).prop('checked', false).trigger('change', 'webform_civicrm:reset'); }); } } @@ -325,20 +328,21 @@ var wfCivi = (function (D, $, drupalSettings, once) { } function fillOptions(element, data) { + var sortedData = Object.entries(data).sort(([,a],[,b]) => a > b); var $el = $(element), value = $el.attr('data-val') ? $el.attr('data-val') : $el.val(); $el.find('option').remove(); - if (!$.isEmptyObject(data || [])) { + if (!sortedData.length == 0) { if (!data['']) { var text = $el.hasClass('required') ? Drupal.t('- Select -') : Drupal.t('- None -'); $el.append(''); } - $.each(data, function(key, val) { - $el.append(''); - }); - if (value in data) { - $el.val(value); - } + for (let i = 0; i < sortedData.length; i++) { + $el.append(''); + if (sortedData[i][0] == value) { + $el.val(value); + } + }; } else { $el.append(''); @@ -348,7 +352,7 @@ var wfCivi = (function (D, $, drupalSettings, once) { function sharedAddress(item, action, speed) { var name = parseName($(item).attr('name')); - var fields = $(item).parents('form.webform-submission-form').find('[name*="'+(name.replace(/master_id.*$/, ''))+'"').not('[name*=location_type_id]').not('[name*=master_id]').not('[type="hidden"]'); + var fields = $(item).parents('form.webform-submission-form').find('[name*="'+(name.replace(/master_id.*$/, ''))+'"]').not('[name*=location_type_id]').not('[name*=master_id]').not('[type="hidden"]'); if (action === 'hide') { fields.parent().hide(speed, function() {$(this).css('display', 'none');}); fields.prop('disabled', true); diff --git a/js/webform_civicrm_payment.js b/js/webform_civicrm_payment.js index 0adf0e976..f2efb5acf 100644 --- a/js/webform_civicrm_payment.js +++ b/js/webform_civicrm_payment.js @@ -13,7 +13,7 @@ function loadBillingBlock() { var type = getPaymentProcessor(); - if (type && type !== '0') { + if (type) { $.ajax({ url: setting.contributionCallback + '&' + setting.processor_id_key + '=' + type, success: function(data) { diff --git a/src/AdminForm.php b/src/AdminForm.php index e8a8d08ef..852cd7b78 100644 --- a/src/AdminForm.php +++ b/src/AdminForm.php @@ -30,6 +30,11 @@ class AdminForm implements AdminFormInterface { private $settings; private $data; + /** + * @var \Drupal\webform_civicrm\UtilsInterface + */ + protected $utils; + /** * The shim allowing us to slowly port this code. * @@ -41,6 +46,11 @@ class AdminForm implements AdminFormInterface { */ public static $fieldset_entities = ['contact', 'billing_1_number_of_billing', 'activity', 'case', 'grant']; + /** + * @var bool + */ + public $confirmPage; + public function __construct(UtilsInterface $utils) { $this->utils = $utils; } @@ -233,7 +243,7 @@ private function buildFormIntro() { '#type' => 'select', '#title' => t('Number of Contacts'), '#default_value' => count($this->data['contact']), - '#options' => array_combine(range(1, 40), range(1, 40)), + '#options' => array_combine(range(1, 50), range(1, 50)), ]; $this->form['change_form_settings'] = [ '#type' => 'button', @@ -525,6 +535,7 @@ private function buildActivityTab() { 'entire_result' => t('Include entire webform submission in activity details'), 'view_link' => t('Include link to view webform submission in activity details'), 'edit_link' => t('Include link to edit webform submission in activity details'), + 'view_link_secure' => t('Include secure (tokenised) link to view webform submission in activity details'), 'update_existing' => t('Update the details when an existing activity is updated'), ], '#default_value' => wf_crm_aval($this->data, "activity:$n:details", ['view_link'], TRUE), @@ -938,9 +949,15 @@ private function buildParticipantTab() { '#title' => t('Allow events to be autoloaded from URL'), '#default_value' => (bool) wf_crm_aval($this->data, 'reg_options:allow_url_load'), ]; + $this->form['participant']['reg_options']['disable_primary_participant'] = [ + '#type' => 'checkbox', + '#title' => t('Disable Contact 1 to be stored as Primary Participant'), + '#default_value' => (bool) wf_crm_aval($this->data, 'reg_options:disable_primary_participant'), + ]; $this->help($this->form['participant']['reg_options']['block_form'], 'reg_options_block_form'); $this->help($this->form['participant']['reg_options']['disable_unregister'], 'reg_options_disable_unregister'); $this->help($this->form['participant']['reg_options']['allow_url_load'], 'reg_options_allow_url_load'); + $this->help($this->form['participant']['reg_options']['disable_primary_participant'], 'reg_options_disable_primary_participant'); $this->addAjaxItem('participant', 'participant_reg_type', 'participants'); $this->addAjaxItem('participant', 'event_type', 'participants'); $this->addAjaxItem('participant', 'show_past_events', 'participants'); @@ -1121,6 +1138,13 @@ private function buildContributionTab() { } if (isset($set['fields'])) { foreach ($set['fields'] as $fid => $field) { + // Display receive date only if processor = 'Pay Later' or '- User Select -' + if ($fid == 'contribution_receive_date') { + $pp = wf_crm_aval($this->data, "contribution:1:contribution:1:payment_processor_id", 'create_civicrm_webform_element', TRUE); + if (!empty($pp) && $pp !== 'create_civicrm_webform_element') { + continue; + } + } $fid = "civicrm_1_contribution_1_$fid"; if (strpos($sid, 'cg') === 0) { $this->form['contribution']['sets']['custom'][$sid][$fid] = $this->addItem($fid, $field); @@ -1133,12 +1157,13 @@ private function buildContributionTab() { } } $this->addAjaxItem("contribution:sets:contribution", "civicrm_1_contribution_1_contribution_financial_type_id", "..:custom"); + $this->addAjaxItem("contribution:sets:contribution", "civicrm_1_contribution_1_contribution_payment_processor_id", "..:contribution"); //Add Currency. $this->form['contribution']['sets']['contribution']['contribution_1_settings_currency'] = [ '#type' => 'select', '#title' => t('Currency'), - '#default_value' => wf_crm_aval($this->data, "contribution:1:currency"), + '#default_value' => wf_crm_aval($this->data, "contribution:1:currency", $this->utils->wf_crm_get_civi_setting('defaultCurrency')), '#options' => \CRM_Core_OptionGroup::values('currencies_enabled'), '#required' => TRUE, ]; @@ -1548,6 +1573,9 @@ private function addItem($fid, $field) { if ($field['type'] != 'hidden') { $options += ['create_civicrm_webform_element' => t('- User Select -')]; } + if ($name == 'group') { + $options += ['public_groups' => t('- User Select - (public groups)')]; + } $options += $this->utils->wf_crm_field_options($field, 'config_form', $this->data); $item += [ '#type' => 'select', @@ -1918,7 +1946,9 @@ public function postProcess() { } elseif (!isset($enabled[$key])) { $val = (array) $val; - if (in_array('create_civicrm_webform_element', $val, TRUE) || (!empty($val[0]) && $field['type'] == 'hidden')) { + if (in_array('create_civicrm_webform_element', $val, TRUE) + || (!empty($val[0]) && $field['type'] == 'hidden') + || (preg_match('/_group$/', $key) && in_array('public_groups', $val, TRUE))) { // Restore disabled component if (isset($disabled[$key])) { webform_component_update($disabled[$key]); @@ -1951,9 +1981,9 @@ public function postProcess() { $created[] = $field['name']; } // @todo: Update Conditionals as per Drupal 9 standards. - // if (isset($field['civicrm_condition'])) { - // $this->addConditionalRule($field, $enabled); - // } + if (isset($field['civicrm_condition'])) { + $this->addConditionalRule($field, $enabled); + } } } } @@ -2164,7 +2194,7 @@ private function addConditionalRule(&$field, &$enabled) { if (isset($options[$value])) { $field['states'] = [ 'visible' => [ - ":input[name='{$source_id}']" => ['value' => $value], + ':input[name="' . $source_key . '"]' => ['value' => $value], ], ]; unset($field['civicrm_condition']); @@ -2194,7 +2224,7 @@ private function getFieldsToDelete($fields) { // Find fields to delete foreach ($fields as $key => $val) { $val = (array) wf_crm_aval($this->settings, $key); - if (((in_array('create_civicrm_webform_element', $val, TRUE)) && $this->settings['nid']) + if (((in_array('create_civicrm_webform_element', $val, TRUE) || in_array('public_groups', $val, TRUE)) && $this->settings['nid']) || strpos($key, 'fieldset') !== FALSE) { unset($fields[$key]); } diff --git a/src/AdminHelp.php b/src/AdminHelp.php index 428581971..722f0a03d 100644 --- a/src/AdminHelp.php +++ b/src/AdminHelp.php @@ -15,6 +15,11 @@ */ class AdminHelp implements AdminHelpInterface { + /** + * @var \Drupal\webform_civicrm\UtilsInterface + */ + protected $utils; + public function __construct(UtilsInterface $utils) { $this->utils = $utils; } @@ -269,6 +274,15 @@ protected function reg_options_allow_url_load() { '/{node.nid}?c1event1={event1.event_id},{event2.event_id}&c2event1={event3.event_id}

'; } + /** + * Help text for disable primary setting. + */ + protected function reg_options_disable_primary_participant() { + return '

' . + t('If enabled, Contact 1 will not be stored as primary participant for multiple registrations.') . + '

'; + } + protected function reg_options_show_past_events() { return '

' . t('To also display events that have ended, choose an option for how far in the past to search.') . diff --git a/src/ContactComponent.php b/src/ContactComponent.php index 154aff580..9a6110366 100644 --- a/src/ContactComponent.php +++ b/src/ContactComponent.php @@ -13,6 +13,11 @@ */ class ContactComponent implements ContactComponentInterface { + /** + * UtilsInterface object + */ + protected $utils; + public function __construct(UtilsInterface $utils) { $this->utils = $utils; } @@ -102,6 +107,7 @@ function wf_crm_contact_search($node, $element, $params, $contacts, $str = NULL) 'country' => ['address', 'country_id:label'], 'county' => ['address', 'county_id:label'], 'postal_code' => ['address', 'postal_code'], + 'street_address' => ['address', 'street_address'] ]; $joinedTables = []; foreach ($fieldMappings as $field => $type) { @@ -216,19 +222,10 @@ function wf_crm_contact_access($component, $filters, $cid) { if ($cid == $this->utils->wf_crm_user_cid()) { $filters['checkPermissions'] = FALSE; } - if (!empty($filters['checkPermissions'])) { - // If we have a valid checksum for this contact, bypass other permission checks - // For legacy reasons we support "cid" param as an alias of "cid1" - // ToDo use: \Drupal::request()->query->all(); - if (wf_crm_aval($_GET, "cid$c") == $cid || ($c == 1 && wf_crm_aval($_GET, "cid") == $cid)) { - // For legacy reasons we support "cs" param as an alias of "cs1" - if (!empty($_GET['cs']) && $c == 1 && \CRM_Contact_BAO_Contact_Utils::validChecksum($cid, $_GET['cs'])) { - $filters['checkPermissions'] = FALSE; - } - elseif (!empty($_GET["cs$c"]) && \CRM_Contact_BAO_Contact_Utils::validChecksum($cid, $_GET["cs$c"])) { - $filters['checkPermissions'] = FALSE; - } - } + // If checksum is included in the URL, bypass the permission. + $checksumValid = $this->utils->checksumUserAccess($c, $cid); + if (!empty($filters['checkPermissions']) && $checksumValid) { + $filters['checkPermissions'] = FALSE; } // Fetch contact name with filters applied $result = $this->utils->wf_civicrm_api4('Contact', 'get', $filters)[0] ?? []; @@ -281,13 +278,13 @@ function wf_crm_find_relations($cid, $types = [], $current = TRUE) { // Put current employer first in the list if ($type == $employer_type && $current) { $search_key = $a == 'b' ? 'id' : 'employer_id'; - // Note: inconsistency in api3 - search key is "employer_id" but return key is "current_employer_id" - $employer = $this->utils->wf_crm_apivalues('contact', 'get', [ - $search_key => $cid, - 'sequential' => 1, - ], $a == 'b' ? 'current_employer_id' : 'id'); + $employer = $this->utils->wf_civicrm_api4('Contact', 'get', [ + 'where' => [ + [$search_key, '=', $cid], + ], + ])->first()[$a == 'b' ? 'employer_id' : 'id'] ?? NULL; if ($employer) { - $found[$employer[0]] = $employer[0]; + $found[$employer] = $employer; } } $type_ids[] = $type; diff --git a/src/Element/CivicrmSelectOptions.php b/src/Element/CivicrmSelectOptions.php index 43e9160c3..b0fcfec68 100644 --- a/src/Element/CivicrmSelectOptions.php +++ b/src/Element/CivicrmSelectOptions.php @@ -104,37 +104,39 @@ public static function processSelectOptions(&$element, FormStateInterface $form_ $element['options']['#tabledrag'] = []; $element['options']['#tableselect'] = FALSE; } - if (strpos($element['#form_key'], 'address_state_province_id') !== false || strpos($key, 'address_county_id') !== false) { + if (strpos($element['#form_key'], 'address_state_province_id') !== false || strpos($element['#form_key'], 'address_county_id') !== false) { $parent_label = (strpos($element['#form_key'], 'address_state_province_id') !== false) ? 'Country' : 'State/Province'; $element['options']['#empty'] = t('Options are loaded dynamically on the webform based on the value selected in @key field.', ['@key' => $parent_label]); } - $current_options = $element['#default_value']; $weight = 0; $webform = $form_state->getFormObject()->getWebform(); $data = $webform->getHandler('webform_civicrm')->getConfiguration()['settings']['data'] ?? []; - $field_options = static::getFieldOptions($element['#form_key'], $data); - // Sort the field options by the current options. + // $current_options is an array of [value => webform_label] listed in the order defined in the webform. + // Options disabled in the webform are absent from this array. + $current_options = $element['#default_value']; + if (!$element['#civicrm_live_options']) { - uasort($field_options, function ($a, $b) use ($current_options) { - $current_options = array_flip($current_options); - $weight_values = array_flip(array_values(array_flip($current_options))); + // $all_options is an array of [value => civi_label] listed in the order defined in civicrm. + // Options disabed in civi are absent from this array, but it includes options disabled in the webform. + $all_options = static::getFieldOptions($element['#form_key'], $data); - if (!isset($current_options[$b]) && isset($current_options[$a])) { - return -1; - } - if (!isset($current_options[$a]) && isset($current_options[$b])) { - return 1; - } + // build the $field_options array using the order of $current_options, using the labels specified in the webform. + foreach ($current_options as $key => $option) { + $field_options[$key] = $all_options[$key]; + } - $a_weight = $weight_values[$a] ?? 0; - $b_weight = $weight_values[$b] ?? 0; - if ($a_weight == $b_weight) { - return 0; + // Add to the $field_options array any options that are disabled in the webform. + // The order of the disabled options cannot be changed, and they will always + // appear below the enabled options. + foreach ($all_options as $key => $option) { + if (!isset($field_options[$key])) { + $field_options[$key] = $all_options[$key]; } - return ($a_weight < $b_weight) ? -1 : 1; - }); + } + } else { // static options + $field_options = static::getFieldOptions($element['#form_key'], $data); } foreach ($field_options as $key => $option) { @@ -183,6 +185,17 @@ public static function processSelectOptions(&$element, FormStateInterface $form_ '#default_value' => $weight, '#attributes' => ['class' => ['weight']], '#access' => !$element['#civicrm_live_options'], + + // delta theoretically should control the number of items in the weight dropdown for each option, but + // in reality that weight range seems to be fixed at -10 to +10. When there are more than 10 options present, + // the weight dropdown therefore cannot be used to move an option below the 10th spot. In addition, + // a bug related to this fixed -10 to +10 range prevents dragging options below the 22nd spot. + // Therefore, when there are more than 10 options present, it's desireable to switch from a weight + // listbox to an integer edit box. This is accomplished by setting + // delta to a value greater than Drupal::config('system.site')->get('weight_select_max') (default value 100) + // See Drupal\Core\Render\Element\Weight::processWeight() + // Other than that threshold, the value specified here for delta is not significent. + '#delta' => !$element['#civicrm_live_options'] && sizeof($all_options) > 10 ? '101' : "10", ]; $weight++; } diff --git a/src/FieldOptions.php b/src/FieldOptions.php index 7c6f2b61d..38169536b 100644 --- a/src/FieldOptions.php +++ b/src/FieldOptions.php @@ -67,7 +67,12 @@ public function get($field, $context, $data) { $ret = $utils->wf_crm_get_tags($ent, wf_crm_aval($split, 1)); } elseif (isset($field['table']) && $field['table'] === 'group') { - $ret = $utils->wf_crm_apivalues('group', 'get', ['is_hidden' => 0], 'title'); + $params = ['is_hidden' => 0]; + $options = wf_crm_aval($data, "contact:$c:other:1:group"); + if (!empty($options) && !empty($options['public_groups'])) { + $params['visibility'] = "Public Pages"; + } + $ret = $utils->wf_crm_apivalues('group', 'get', $params, 'title'); } elseif ($name === 'survey_id') { $ret = $utils->wf_crm_get_surveys(wf_crm_aval($data, "activity:$c:activity:1", [])); @@ -104,12 +109,17 @@ public function get($field, $context, $data) { else { $params = ['field' => $name, 'context' => 'create']; // Special case for contribution_recur fields - if ($table == 'contribution' && strpos($name, 'frequency_') === 0) { - $table = 'contribution_recur'; - } - if ($table == 'contribution' && strpos($name, 'billing_address_') === 0) { - $table = 'address'; - $params['field'] = str_replace('billing_address_', '', $params['field']); + if ($table === 'contribution') { + if (str_starts_with($name, 'frequency_')) { + $table = 'contribution_recur'; + } + elseif ($name === 'soft_credit_type_id') { + $table = 'contribution_soft'; + } + elseif (str_starts_with($name, 'billing_address_')) { + $table = 'address'; + $params['field'] = str_replace('billing_address_', '', $params['field']); + } } // Use the Contribution table to pull up financial type id-s if ($table == 'membership' && $name == 'financial_type_id') { diff --git a/src/Fields.php b/src/Fields.php index 9c4c23b18..88a788f50 100644 --- a/src/Fields.php +++ b/src/Fields.php @@ -11,6 +11,11 @@ class Fields implements FieldsInterface { */ protected $fieldMetadata = []; + /** + * @var \Drupal\webform_civicrm\UtilsInterface + */ + protected $utils; + public function __construct(UtilsInterface $utils) { $this->utils = $utils; } @@ -81,7 +86,7 @@ protected function getSets(array $components): array { } } $conditional_sets = [ - 'CiviCase' => ['entity_type' => 'case', 'label' => t('Case'), 'max_instances' => 30], + 'CiviCase' => ['entity_type' => 'case', 'label' => t('Case'), 'max_instances' => 50], 'CiviEvent' => ['entity_type' => 'participant', 'label' => t('Participant'), 'max_instances' => 9], 'CiviContribute' => ['entity_type' => 'contribution', 'label' => t('Contribution')], 'CiviMember' => ['entity_type' => 'membership', 'label' => t('Membership'), 'custom_fields' => 'combined'], @@ -255,11 +260,13 @@ protected function wf_crm_get_fields($var = 'fields') { 'type' => 'select', 'default_value' => $this->utils->wf_crm_get_civi_setting('lcMessages', 'en_US'), ]; - $default_communication_style = $this->utils->wf_crm_apivalues('OptionValue', 'get', [ - 'sequential' => 1, - 'option_group_id' => "communication_style", - 'is_default' => 1, - ], 'value')[0] ?? NULL; + $default_communication_style = $this->utils->wf_civicrm_api4('OptionValue', 'get', [ + 'where' => [ + ['option_group_id.name', '=', 'communication_style'], + ['is_default', '=', TRUE], + ], + 'select' => ['value'], + ])->first()['value'] ?? NULL; $fields['contact_communication_style_id'] = [ 'name' => t('Communication Style'), 'type' => 'select', @@ -572,8 +579,8 @@ protected function wf_crm_get_fields($var = 'fields') { ]; // Fetch case roles $sets['caseRoles'] = ['entity_type' => 'case', 'label' => t('Case Roles')]; - foreach ($this->utils->wf_crm_apivalues('case_type', 'get') as $case_type) { - foreach ($case_type['definition']['caseRoles'] as $role) { + foreach ($this->utils->wf_crm_apivalues('case_type', 'get', ['is_active' => 1]) as $case_type) { + foreach ($case_type['definition']['caseRoles'] ?? [] as $role) { foreach ($this->utils->wf_crm_get_relationship_types() as $rel_type) { if (in_array($role['name'], [$rel_type['name_b_a'], $rel_type['label_b_a']])) { $case_role_fields_key = 'case_role_' . $rel_type['id']; @@ -597,8 +604,9 @@ protected function wf_crm_get_fields($var = 'fields') { } } } + $tag_display_field = $this->utils->tag_display_field(); $all_tagsets = $this->utils->wf_crm_apivalues('tag', 'get', [ - 'return' => ['id', 'name', 'used_for'], + 'return' => ['id', $tag_display_field, 'used_for'], 'is_tagset' => 1, 'parent_id' => ['IS NULL' => 1], ]); @@ -607,7 +615,7 @@ protected function wf_crm_get_fields($var = 'fields') { $tagsets = ['' => t('Tag(s)')]; foreach ($all_tagsets as $set) { if (strpos($set['used_for'], $table_name) !== FALSE) { - $tagsets[$set['id']] = $set['name']; + $tagsets[$set['id']] = $set[$tag_display_field]; } } foreach ($tagsets as $pid => $name) { @@ -713,33 +721,29 @@ protected function wf_crm_get_fields($var = 'fields') { 'type' => 'textarea', 'parent' => 'contribution_pagebreak', ]; - $fields['contribution_soft'] = [ - 'name' => t('Soft Credit To'), - 'type' => 'select', - 'expose_list' => TRUE, - 'extra' => ['multiple' => TRUE], - 'data_type' => 'ContactReference', - 'parent' => 'contribution_pagebreak', - ]; - $fields['contribution_honor_contact_id'] = [ - 'name' => t('In Honor/Memory of'), - 'type' => 'select', - 'expose_list' => TRUE, - 'empty_option' => t('No One'), - 'data_type' => 'ContactReference', - 'parent' => 'contribution_pagebreak', - ]; - $fields['contribution_honor_type_id'] = [ - 'name' => t('Honoree Type'), - 'type' => 'select', - 'expose_list' => TRUE, - 'parent' => 'contribution_pagebreak', - ]; $fields['contribution_source'] = [ 'name' => t('Contribution Source'), 'type' => 'textfield', 'parent' => 'contribution_pagebreak', ]; + $fields['contribution_receive_date'] = [ + 'name' => t('Contribution Receive Date'), + 'type' => 'datetime', + 'parent' => 'contribution_pagebreak', + 'default_value' => 'now', + 'date_date_min' => 'today', + 'date_time_element' => 'timepicker', + 'civicrm_condition' => [ + 'andor' => 'or', + 'action' => 'show', + 'rules' => [ + 'contribution_payment_processor_id' => [ + 'values' => '0', + 'operator' => 'equal', + ], + ], + ], + ]; $donationFinancialType = current($this->utils->wf_crm_apivalues('FinancialType', 'get', [ 'return' => 'id', 'name' => 'Donation', @@ -769,6 +773,28 @@ protected function wf_crm_get_fields($var = 'fields') { 'set' => 'line_items', 'fid' => 'contribution_financial_type_id', ]; + + // Soft Credit + $sets['contributionSoft'] = ['entity_type' => 'contribution', 'label' => t('Soft Credit')]; + $fields['contribution_soft'] = [ + 'name' => t('Soft Credit To'), + 'type' => 'select', + 'expose_list' => TRUE, + 'extra' => ['multiple' => TRUE], + 'data_type' => 'ContactReference', + 'parent' => 'contribution_pagebreak', + 'set' => 'contributionSoft', + ]; + $fields['contribution_soft_credit_type_id'] = [ + 'name' => t('Soft Credit Type'), + 'type' => 'select', + 'expose_list' => TRUE, + 'civicrm_live_options' => TRUE, + 'empty_option' => t('None'), + 'parent' => 'contribution_pagebreak', + 'set' => 'contributionSoft', + ]; + $sets['contributionRecur'] = ['entity_type' => 'contribution', 'label' => t('Recurring Contribution')]; $fields['contribution_frequency_unit'] = [ 'name' => t('Frequency of Installments'), diff --git a/src/Plugin/WebformElement/CivicrmContact.php b/src/Plugin/WebformElement/CivicrmContact.php index e3d8a240b..10c6ea6c7 100644 --- a/src/Plugin/WebformElement/CivicrmContact.php +++ b/src/Plugin/WebformElement/CivicrmContact.php @@ -2,7 +2,6 @@ namespace Drupal\webform_civicrm\Plugin\WebformElement; -use CRM_Core_BAO_Tag; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Xss; use Drupal\Core\Form\FormStateInterface; @@ -400,12 +399,11 @@ public function form(array $form, FormStateInterface $form_state) { '#default_value' => $element_properties['group'], '#description' => $this->t('Listed contacts must be members of at least one of the selected groups (leave blank to not filter by group).'), ]; - $tags = []; $form['filters']['tag'] = [ '#type' => 'select', '#multiple' => TRUE, '#title' => $this->t('Tags'), - '#options' => ['' => '- ' . $this->t('None') . ' -'] + CRM_Core_BAO_Tag::getTags('civicrm_contact', $tags, NULL, '- '), + '#options' => ['' => '- ' . $this->t('None') . ' -'] + $utils->wf_crm_get_tags('contact'), '#default_value' => $element_properties['tag'], '#description' => $this->t('Listed contacts must be have at least one of the selected tags (leave blank to not filter by tag).'), ]; @@ -525,6 +523,7 @@ function wf_crm_results_display_options($contact_type) { 'state_province' => t("State/Province"), 'country' => t("Country"), 'postal_code' => t("Postal Code"), + 'street_address' => t("Street Address"), 'phone' => t("Phone"), ]; return $options; diff --git a/src/Plugin/WebformElement/CivicrmOptions.php b/src/Plugin/WebformElement/CivicrmOptions.php index e8cd2c58c..14cd9de79 100644 --- a/src/Plugin/WebformElement/CivicrmOptions.php +++ b/src/Plugin/WebformElement/CivicrmOptions.php @@ -321,4 +321,33 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form } } + /** + * @inheritDoc + */ + protected function format($type, array &$element, WebformSubmissionInterface $webform_submission, array $options = []) { + $value = parent::format($type, $element, $webform_submission, $options); + $format = $this->getItemFormat($element); + if (!str_ends_with($element['#form_key'], '_address_state_province_id')) { + return $value; + } + if ($type === 'Text') { + $state_id = $value; + } + else { + $state_id = $value['#plain_text'] ?? $value['#markup'] ?? NULL; + } + if ($format === 'raw' || empty($state_id) || !is_numeric($state_id)) { + return $value; + } + $utils = \Drupal::service('webform_civicrm.utils'); + $state = $utils->wf_crm_apivalues('state_province', 'get', ['id' => $state_id], 'name'); + if (!empty($state[$state_id])) { + if ($type === 'Text') { + return $state[$state_id]; + } + $value['#plain_text'] = $state[$state_id]; + } + return $value; + } + } diff --git a/src/Plugin/WebformHandler/CivicrmWebformHandler.php b/src/Plugin/WebformHandler/CivicrmWebformHandler.php index 5c3a07911..556ba99ff 100644 --- a/src/Plugin/WebformHandler/CivicrmWebformHandler.php +++ b/src/Plugin/WebformHandler/CivicrmWebformHandler.php @@ -31,12 +31,20 @@ class CivicrmWebformHandler extends WebformHandlerBase { */ protected $civicrm; + /** + * The Token Manager service. + * + * @var \Drupal\webform\WebformTokenManager + */ + public $tokenManager; + /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); $instance->civicrm = $container->get('civicrm'); + $instance->tokenManager = $container->get('webform.token_manager'); return $instance; } diff --git a/src/Utils.php b/src/Utils.php index 0fb062dba..f99c7437d 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -8,10 +8,28 @@ */ use Drupal\Component\Utility\Html; use Drupal\Core\Render\Markup; +use Symfony\Component\HttpFoundation\RequestStack; use Drupal\webform\WebformInterface; class Utils implements UtilsInterface { + /** + * The related request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + private $requestStack; + + /** + * Constructs a utils object. + * + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. + */ + function __construct(RequestStack $requestStack) { + $this->requestStack = $requestStack; + } + /** * Explodes form key into an array and verifies that it is in the right format * @@ -113,11 +131,15 @@ public function wf_crm_get_states($param = NULL) { * @return array */ function wf_crm_get_events($reg_options, $context) { - $ret = []; + static $ret = []; + if ($ret && $context !== 'config_form') { + return $ret; + } $format = wf_crm_aval($reg_options, 'title_display', 'title'); $sort_field = wf_crm_aval($reg_options, 'event_sort_field', 'start_date'); $sort_order = ($context == 'config_form' && $sort_field === 'start_date') ? ' DESC' : ''; $params = [ + 'return' => ['id', 'title', 'start_date', 'end_date', 'event_type_id', 'max_participants'], 'is_template' => 0, 'is_active' => 1, ]; @@ -226,14 +248,15 @@ function wf_crm_get_tags($used_for, $parent_id = NULL) { 'parent_id' => $parent_id ?: ['IS NULL' => 1], 'options' => ['sort' => 'name'], ]; - $tags = $this->wf_crm_apivalues('Tag', 'get', $params, 'name'); + $tag_display_field = $this->tag_display_field(); + $tags = $this->wf_crm_apivalues('Tag', 'get', $params, $tag_display_field); // Tagsets cannot be nested so no need to fetch children if ($parent_id || !$tags) { return $tags; } // Fetch child tags unset($params['parent_id']); - $params += ['return' => ['name', 'parent_id'], 'parent_id.is_tagset' => 0, 'parent_id.is_selectable' => 1, 'parent_id.used_for' => $params['used_for']]; + $params += ['return' => [$tag_display_field, 'parent_id'], 'parent_id.is_tagset' => 0, 'parent_id.is_selectable' => 1, 'parent_id.used_for' => $params['used_for']]; $unsorted = $this->wf_crm_apivalues('Tag', 'get', $params); $parents = array_fill_keys(array_keys($tags), ['depth' => 1]); // Place children under their parents. @@ -244,7 +267,7 @@ function wf_crm_get_tags($used_for, $parent_id = NULL) { foreach ($unsorted as $id => $tag) { $parent = $tag['parent_id']; if (isset($parents[$parent])) { - $name = str_repeat('- ', $parents[$parent]['depth']) . $tag['name']; + $name = str_repeat('- ', $parents[$parent]['depth']) . $tag[$tag_display_field]; $pos = array_search($parents[$parent]['child'] ?? $parent, array_keys($tags)) + 1; $tags = array_slice($tags, 0, $pos, TRUE) + [$id => $name] + array_slice($tags, $pos, NULL, TRUE); $parents[$id] = ['depth' => $parents[$parent]['depth'] + 1]; @@ -603,27 +626,13 @@ function wf_crm_array2str($arr) { } /** - * Wrapper for all CiviCRM APIv4 calls - * - * @param string $entity - * API entity - * @param string $operation - * API operation - * @param array $params - * API params - * @param string|int|array $index - * Controls the Result array format. - * - * @return array - * Result of API call + * @inheritDoc */ function wf_civicrm_api4($entity, $operation, $params, $index = NULL) { if (!$entity) { return []; } - $params += [ - 'checkPermissions' => FALSE, - ]; + $params['checkPermissions'] = FALSE; $result = civicrm_api4($entity, $operation, $params, $index); return $result; } @@ -647,9 +656,7 @@ function wf_civicrm_api($entity, $operation, $params) { return []; } - $params += [ - 'check_permissions' => FALSE, - ]; + $params['check_permissions'] = FALSE; if ($operation == 'transact') { $utils = \Drupal::service('webform_civicrm.utils'); $result = $utils->wf_civicrm_api3_contribution_transact($params); @@ -1021,4 +1028,90 @@ public function hasMultipleValues($element) { return FALSE; } + /** + * @inheritDoc + */ + public function checksumUserAccess($c, $cid) { + $request = $this->requestStack->getCurrentRequest(); + $urlCidN = $urlChecksumN = NULL; + $session = \CRM_Core_Session::singleton(); + $urlCid1 = $request->query->get('cid'); + $urlChecksum1 = $request->query->get('cs'); + + $urlCidN = $request->query->get("cid$c"); + $urlChecksumN = $request->query->get("cs$c"); + + $cs = NULL; + if ($c == 1 && !empty($urlChecksum1)) { + $cs = $urlChecksum1; + } + elseif (!empty($urlChecksumN)) { + $cs = $urlChecksumN; + } + if ($cs && (($c == 1 && $urlCid1 == $cid) || $urlCidN == $cid)) { + $check_access = $this->wf_civicrm_api4('Contact', 'validateChecksum', [ + 'contactId' => $cid, + 'checksum' => $cs, + ])[0] ?? []; + if ($check_access['valid']) { + if ($c == 1) { + $session->set('userID', $cid); + } + return TRUE; + } + } + // If access is checked for non primary contact, check if c1 has access to view it. + elseif ($c != 1 && $this->isContactAccessible($cid)) { + return TRUE; + } + // If no checksum is passed and user is anonymous, reset prev checksum session values if any. + if (\Drupal::currentUser()->isAnonymous() && $session->get('userID') && $c == 1 && empty($urlChecksum1)) { + $session->reset(); + } + return FALSE; + } + + /** + * @inheritDoc + */ + public function isContactAccessible($cid) { + $access = $this->wf_civicrm_api4('Contact', 'checkAccess', [ + 'action' => 'get', + 'values' => [ + 'id' => $cid, + ], + ], 0); + if (!empty($access['access'])) { + return TRUE; + } + + $request = $this->requestStack->getCurrentRequest(); + $urlCid1 = $request->query->get('cid') ?? $request->query->get('cid1') ?? NULL; + $urlChecksum1 = $request->query->get('cs') ?? $request->query->get('cs1') ?? NULL; + + if (!empty($urlChecksum1) && !empty($urlCid1)) { + $valid = $this->wf_civicrm_api4('Contact', 'validateChecksum', [ + 'contactId' => $urlCid1, + 'checksum' => $urlChecksum1, + ])[0] ?? []; + if ($valid['valid']) { + // checkAccess v4 api does not check for access via relationship. + if (\CRM_Contact_BAO_Contact_Permission::allow($cid)) { + return TRUE; + } + } + } + return FALSE; + } + + /** + * @return string Which field is the tag display field in this version of civi? + */ + public function tag_display_field(): string { + if (version_compare(\CRM_Core_BAO_Domain::version(), '5.68.alpha1', '>=')) { + return 'label'; + } + return 'name'; + } + } diff --git a/src/UtilsInterface.php b/src/UtilsInterface.php index 1e4523026..60e7073fa 100644 --- a/src/UtilsInterface.php +++ b/src/UtilsInterface.php @@ -318,4 +318,44 @@ function wf_crm_custom_types_map_array(); */ function wf_crm_get_civi_setting($setting_name, $default_value = NULL); + /** + * Check if user checksum is available in the URL. + * Set checksum user in the session. + * + * @param int $c + * @param int $cid + * + * @return boolean + * TRUE if checksum is valid. + */ + function checksumUserAccess($c, $cid); + + /** + * Wrapper for all CiviCRM APIv4 calls + * + * @param string $entity + * API entity + * @param string $operation + * API operation + * @param array $params + * API params + * @param string|int|array $index + * Controls the Result array format. + * + * @return array|\Civi\Api4\Generic\Result + * Result of API call + */ + function wf_civicrm_api4($entity, $operation, $params, $index = NULL); + + /** + * Check if logged in user or the checksum user + * is allowed to view a contact. + * + * @param int $cid + * + * @return boolean + * TRUE if checksum user is allowed to view $cid. + */ + function isContactAccessible($cid); + } diff --git a/src/WebformAjax.php b/src/WebformAjax.php index a0d5fc78b..a3cee8176 100644 --- a/src/WebformAjax.php +++ b/src/WebformAjax.php @@ -21,6 +21,11 @@ class WebformAjax extends WebformCivicrmBase implements WebformAjaxInterface { private $requestStack; + /** + * @var \Drupal\webform_civicrm\UtilsInterface + */ + protected $utils; + function __construct(RequestStack $requestStack, UtilsInterface $utils) { $this->requestStack = $requestStack; $this->utils = $utils; diff --git a/src/WebformCivicrmBase.php b/src/WebformCivicrmBase.php index cc4059cc7..a3e17e08a 100644 --- a/src/WebformCivicrmBase.php +++ b/src/WebformCivicrmBase.php @@ -59,8 +59,7 @@ function __get($name) { return $this->_payment_processor; case 'tax_rate': - $taxSettings = $this->utils->wf_crm_get_civi_setting('contribution_invoice_settings'); - if (is_array($taxSettings) && !empty($taxSettings['invoicing'])) { + if (\Civi::settings()->get('invoicing')) { $contribution_enabled = wf_crm_aval($this->data, 'contribution:1:contribution:1:enable_contribution'); if ($contribution_enabled) { // tax integration @@ -555,9 +554,13 @@ protected function getRelationship($r_types, $cid1, $cid2, $active_only = FALSE) protected function getExposedOptions($field_key, $exclude = []) { $field = $this->getComponent($field_key); - if ($field && $field['#type'] == 'hidden') { + if ($field && ($field['#type'] == 'hidden' || !empty($field['#civicrm_live_options']))) { // Fetch live options - $exposed = $this->utils->wf_crm_field_options($field, 'civicrm_live_options', $this->data); + $params = [ + 'extra' => wf_crm_aval($field, 'extra', []) + wf_crm_aval($field, '#extra', []), + 'form_key' => $field['#form_key'], + ]; + $exposed = $this->utils->wf_crm_field_options($params, 'civicrm_live_options', $this->data); foreach ($exclude as $i) { unset($exposed[$i]); } @@ -776,13 +779,18 @@ function addPaymentJs() { * Copies a drupal file into the Civi file system * * @param int $id: drupal file id + * @param string $filename drupal filename * @return int|null Civi file id */ - public static function saveDrupalFileToCivi($id) { + public static function saveDrupalFileToCivi($id, $filename = NULL) { $file = File::load($id); if ($file) { $config = \CRM_Core_Config::singleton(); - $path = \Drupal::service('file_system')->copy($file->getFileUri(), $config->customFileUploadDir); + $copyTo = $config->customFileUploadDir; + if(isset($filename)) { + $copyTo .= '/' . $filename; + } + $path = \Drupal::service('file_system')->copy($file->getFileUri(), $copyTo); if ($path) { $result = \Drupal::service('webform_civicrm.utils')->wf_civicrm_api('file', 'create', [ 'uri' => str_replace($config->customFileUploadDir, '', $path), diff --git a/src/WebformCivicrmConfirmForm.php b/src/WebformCivicrmConfirmForm.php index b642072f6..0e53b2eae 100644 --- a/src/WebformCivicrmConfirmForm.php +++ b/src/WebformCivicrmConfirmForm.php @@ -11,6 +11,11 @@ class WebformCivicrmConfirmForm implements WebformCivicrmConfirmFormInterface { */ private $form_state; + /** + * @var \Drupal\webform_civicrm\UtilsInterface + */ + protected $utils; + /** * Static cache. * diff --git a/src/WebformCivicrmPostProcess.php b/src/WebformCivicrmPostProcess.php index d9c1ae72e..90b8a864e 100644 --- a/src/WebformCivicrmPostProcess.php +++ b/src/WebformCivicrmPostProcess.php @@ -47,10 +47,21 @@ class WebformCivicrmPostProcess extends WebformCivicrmBase implements WebformCiv */ private $database; + /** + * @var \Drupal\webform_civicrm\Plugin\WebformHandler + */ + private $handler; + /** * @var \Drupal\webform\WebformSubmissionInterface */ private $submission; + + /** + * @var \Drupal\webform_civicrm\UtilsInterface + */ + protected $utils; + private $all_fields; private $all_sets; private $shared_address = []; @@ -79,10 +90,10 @@ function initialize(WebformSubmissionInterface $webform_submission) { $handler_collection = $this->node->getHandlers('webform_civicrm'); $instance_ids = $handler_collection->getInstanceIds(); - $handler = $handler_collection->get(reset($instance_ids)); + $this->handler = $handler_collection->get(reset($instance_ids)); $this->database = \Drupal::database(); - $this->settings = $handler->getConfiguration()['settings']; + $this->settings = $this->handler->getConfiguration()['settings']; $this->data = $this->settings['data']; $this->enabled = $this->utils->wf_crm_enabled_fields($this->node); $this->all_fields = $this->utils->wf_crm_get_fields(); @@ -164,7 +175,7 @@ protected function modifyWebformSubmissionData(WebformSubmissionInterface $webfo $webform = $webform_submission->getWebform(); foreach ($data as $field_key => $val) { $element = $webform->getElement($field_key); - if ($element['#type'] == 'civicrm_options' && is_array($val) && count(array_filter(array_keys($val), 'is_string')) > 0) { + if ($element && $element['#type'] == 'civicrm_options' && is_array($val) && count(array_filter(array_keys($val), 'is_string')) > 0) { $data[$field_key] = array_values($val); } } @@ -1190,7 +1201,9 @@ private function processParticipants($c, $cid) { unset($params['status_id']); } // Set the currency of the result to the currency type that was submitted. - $params['fee_currency'] = $this->data['contribution'][$n]['currency']; + if (isset($this->data['contribution'][$n]['currency'])) { + $params['fee_currency'] = $this->data['contribution'][$n]['currency']; + } $result = $this->utils->wf_civicrm_api('participant', 'create', $params); $this->ent['participant'][$n]['id'] = $result['id']; @@ -1205,7 +1218,7 @@ private function processParticipants($c, $cid) { } } // When registering contact 1, store id to apply to other contacts - if ($c == 1) { + if ($c == 1 && empty($this->data['reg_options']['disable_primary_participant'])) { $registered_by_id[$e][$i] = $result['id']; } } @@ -1614,7 +1627,10 @@ private function formatSubmissionDetails(&$params, $activity_number) { if (!empty($this->data['activity'][$activity_number]['details']['view_link'])) { $params['details'] .= '

' . $this->submission->toLink(t('View Webform Submission'), 'canonical', [ 'absolute' => TRUE, - ])->toString() . '

'; + ])->toString() . '

' . \Drupal\Core\Link::fromTextAndUrl('View Webform Submission', $this->submission->getTokenUrl('view'))->toString(); + } + if (!empty($this->data['activity'][$activity_number]['details']['view_link_secure'])) { + $params['details'] .= '

' . \Drupal\Core\Link::fromTextAndUrl('View Webform Submission', $this->submission->getTokenUrl('view'))->toString() . '

'; } if (!empty($this->data['activity'][$activity_number]['details']['edit_link'])) { $params['details'] .= '

' . $this->submission->toLink(t('Edit Submission'), 'edit-form', [ @@ -1670,6 +1686,7 @@ private function processGrants() { * Calculate line-items for this webform submission */ private function tallyLineItems() { + $submittedFormValues = $this->form_state->getUserInput(); // Contribution $fid = 'civicrm_1_contribution_1_contribution_total_amount'; if (isset($this->enabled[$fid]) || $this->getData($fid) > 0) { @@ -1687,7 +1704,7 @@ private function tallyLineItems() { if (isset($this->enabled[$fid])) { foreach ($this->data['lineitem'][1]['contribution'] as $n => $lineitem) { $fid = "civicrm_1_lineitem_{$n}_contribution_line_total"; - if ($this->getData($fid) != 0) { + if (!isset($submittedFormValues[$fid]) || $this->getData($fid) != 0) { $this->line_items[] = [ 'qty' => 1, 'unit_price' => $lineitem['line_total'], @@ -1722,6 +1739,7 @@ private function tallyLineItems() { }; if ($price) { + $member_name = NULL; if (!empty($this->data['contact'][$c]['contact'][$n])) { $member_contact = $this->data['contact'][$c]['contact'][$n]; if (!empty($member_contact['first_name']) && !empty($member_contact['last_name'])) { @@ -1908,7 +1926,6 @@ private function createBillingContact() { // Current employer must wait for ContactRef ids to be filled unset($contact['contact'][1]['employer_id']); $cid = $this->createContact($contact); - $this->billing_contact = $cid; } else { foreach (['address', 'email'] as $loc) { @@ -2112,6 +2129,7 @@ private function submitIPNPayment() { } } } + // Ideally we would pass the correct id for the test processor through but that seems not to be the // case so load it here. if (!empty($params['is_test'])) { @@ -2121,6 +2139,28 @@ private function submitIPNPayment() { $i = $this->getContributionContactIndex(); $contact = $this->utils->wf_civicrm_api('contact', 'getsingle', ['id' => $this->ent['contact'][$i]['id']]); $params += $contact; + + // contact provides 'country' and 'country_id', but doPayment using PropertyBag expects 'billingCountry' with an iso_code + $countryName = $params['country'] ?? NULL; + $countryId = $params['country_id'] ?? NULL; + // providing country name throws deprecation warnings, + // which break the transaction so remove it + unset($params['country']); + + // country id seems more reliable, so use that first + if ($countryId) { + $params['billingCountry'] = $this->utils->wf_civicrm_api4('Country', 'get', [ + 'select' => ['iso_code'], + 'where' => [['id', '=', $countryId]] + ])->first()['iso_code'] ?? ''; + } + elseif ($countryName) { + $params['billingCountry'] = $this->utils->wf_civicrm_api4('Country', 'get', [ + 'select' => ['iso_code'], + 'where' => [['name', '=', $countryName]] + ])->first()['iso_code'] ?? ''; + } + $params['contributionID'] = $params['id'] = $this->ent['contribution'][1]['id']; if (!empty($this->ent['contribution_recur'][1]['id'])) { $params['is_recur'] = TRUE; @@ -2240,27 +2280,6 @@ private function contributionParams() { $params[$key] = $value; } } - - // Fix bug for testing. - // @todo Pay Later causes issues as it returns `0`. - if ($params['is_test'] == 1 && $params['payment_processor_id'] !== '0') { - $liveProcessorName = $this->utils->wf_civicrm_api('payment_processor', 'getvalue', [ - 'id' => $params['payment_processor_id'], - 'return' => 'name', - ]); - // Lookup current domain for multisite support - static $domain = 0; - if (!$domain) { - $domain = $this->utils->wf_civicrm_api('domain', 'get', ['current_domain' => 1, 'return' => 'id']); - $domain = wf_crm_aval($domain, 'id', 1); - } - $params['payment_processor_id'] = $this->utils->wf_civicrm_api('payment_processor', 'getvalue', [ - 'return' => 'id', - 'name' => $liveProcessorName, - 'is_test' => 1, - 'domain_id' => $domain, - ]); - } if (empty($params['payment_instrument_id']) && !empty($params['payment_processor_id'])) { $params['payment_instrument_id'] = $this->getPaymentInstrument($params['payment_processor_id']); } @@ -2283,7 +2302,7 @@ private function contributionParams() { } // Save this stuff for later - unset($params['soft'], $params['honor_contact_id'], $params['honor_type_id']); + unset($params['soft'], $params['soft_credit_type_id']); return $params; } @@ -2312,21 +2331,10 @@ private function processContribution() { 'contribution_id' => $id, 'amount' => $amount, 'currency' => wf_crm_aval($this->data, "contribution:1:currency"), - 'soft_credit_type_id' => $default_soft_credit_type['value'], + 'soft_credit_type_id' => $contribution['soft_credit_type_id'] ?? $default_soft_credit_type['value'], ]); } } - // Save honoree - // FIXME: these api params were deprecated in 4.5, should be switched to use soft-credits when we drop support for 4.4 - if (!empty($contribution['honor_contact_id']) && !empty($contribution['honor_type_id'])) { - $this->utils->wf_civicrm_api('contribution', 'create', [ - 'id' => $id, - 'total_amount' => $contribution['total_amount'], - 'honor_contact_id' => $contribution['honor_contact_id'], - 'honor_type_id' => $contribution['honor_type_id'], - ]); - } - $contributionResult = \CRM_Contribute_BAO_Contribution::getValues(['id' => $id], \CRM_Core_DAO::$_nullArray, \CRM_Core_DAO::$_nullArray); // Save line-items @@ -2514,6 +2522,9 @@ private function fillDataFromSubmission() { } if (substr($name, 0, 6) === 'custom' || ($table == 'other' && in_array($name, ['group', 'tag']))) { $val = array_filter($val); + if ($name === 'group') { + unset($val['public_groups']); + } } // We need to handle items being de-selected too and provide an array to pass to Entity.create API @@ -2542,7 +2553,11 @@ private function fillDataFromSubmission() { } } elseif ($dataType == 'File') { - if (empty($val[0]) || !($val = $this->saveDrupalFileToCivi($val[0]))) { + // Replace filename (with tokens) if set. + if (isset($component['#file_name']) && $component['#file_name']) { + $newFilename = $this->handler->tokenManager->replace($component['#file_name'], $this->submission); + } + if (empty($val[0]) || !($val = $this->saveDrupalFileToCivi($val[0], $newFilename))) { // This field can't be emptied due to the nature of file uploads continue; } @@ -2708,7 +2723,9 @@ protected function submissionValue($fid, $value = NULL) { $data = $webform_submission->getData(); } else { + $webform_submission = $this->submission; $data = $this->submission->getData(); + $webform_submission = $this->submission; } if (!isset($data[$fid])) { diff --git a/src/WebformCivicrmPreProcess.php b/src/WebformCivicrmPreProcess.php index 0e40fcf99..762247727 100644 --- a/src/WebformCivicrmPreProcess.php +++ b/src/WebformCivicrmPreProcess.php @@ -21,7 +21,7 @@ use Drupal\Core\Datetime\DrupalDateTime; use Drupal\webform\Utility\WebformHtmlHelper; use Drupal\webform\Utility\WebformXss; - +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; class WebformCivicrmPreProcess extends WebformCivicrmBase implements WebformCivicrmPreProcessInterface { @@ -31,6 +31,11 @@ class WebformCivicrmPreProcess extends WebformCivicrmBase implements WebformCivi private $all_fields; private $all_sets; + /** + * @var \Drupal\webform_civicrm\UtilsInterface + */ + protected $utils; + public function __construct(UtilsInterface $utils) { $this->utils = $utils; } @@ -103,6 +108,14 @@ public function alterForm() { $this->form['#attributes']['data-form-defaults'] = Json::encode($this->getWebformDefaults()); // Early return if the form (or page) was already submitted $triggering_element = $this->form_state->getTriggeringElement(); + + // When user uploads a file using a managed_file element, avoid making any change to $this->form. + if ($this->form_state->hasFileElement() + && is_array($triggering_element['#submit']) + && in_array('file_managed_file_submit', $triggering_element['#submit'], TRUE)) { + return; + } + if ($triggering_element && $triggering_element['#id'] == 'edit-wizard-prev' || (empty($this->form_state->isRebuilding()) && !empty($this->form_state->getValues()) && empty($this->form['#submission']->is_draft)) // When resuming from a draft @@ -154,8 +167,7 @@ public function alterForm() { } if ($this->settings['block_unknown_users']) { $this->form['submitted']['#access'] = $this->form['actions']['#access'] = FALSE; - $this->setMessage(t('Sorry, you do not have permission to access this form.'), 'warning'); - return; + throw new AccessDeniedHttpException(); } } if (!empty($this->data['participant_reg_type'])) { @@ -428,7 +440,7 @@ private function loadURLEvents($c) { else { $urlParam = "c{$c}event{$e}"; } - foreach (explode(',', wf_crm_aval($_GET, $urlParam)) as $url_param_value) { + foreach (explode(',', wf_crm_aval($_GET, $urlParam, '')) as $url_param_value) { if (isset($eids[$url_param_value])) { $event_ids[] = $eids[$url_param_value]; } @@ -557,6 +569,19 @@ private function fillForm(&$elements, $submitted = []) { $options = $this->utils->wf_crm_field_options($element, '', $this->data); $val = wf_crm_aval($options, $val); } + //Ensure value from webform default is loaded when the field is null in civicrm. + if (!empty($element['#options']) && isset($val)) { + if (!is_array($val) && !isset($element['#options'][$val])) { + $val = NULL; + } + if ((is_null($val) || (is_array($val) && empty(array_filter($val)))) && !empty($this->form['#attributes']['data-form-defaults'])) { + $formDefaults = Json::decode($this->form['#attributes']['data-form-defaults']); + $key = str_replace('_', '-', $element['#form_key']); + if (isset($formDefaults[$key])) { + $val = $formDefaults[$key]; + } + } + } // Contact image & custom file fields if ($dt == 'File') { $fileInfo = $this->getFileInfo($name, $val, $ent, $n); @@ -639,10 +664,8 @@ private function displayLineItems() { if ($itemTaxRate !== NULL) { // Change the line item label to display the tax rate it contains - $taxSettings = $this->utils->wf_crm_get_civi_setting('contribution_invoice_settings'); - - if (($itemTaxRate !== 0) && ($taxSettings['tax_display_settings'] !== 'Do_not_show')) { - $item['label'] .= ' (' . t('includes @rate @tax', ['@rate' => (float) $itemTaxRate . '%', '@tax' => $taxSettings['tax_term']]) . ')'; + if (($itemTaxRate !== 0) && (\Civi::settings()->get('tax_display_settings') !== 'Do_not_show')) { + $item['label'] .= ' (' . t('includes @rate @tax', ['@rate' => (float) $itemTaxRate . '%', '@tax' => \Civi::settings()->get('tax_term')]) . ')'; } // Add calculation for financial type that contains tax diff --git a/templates/webform-civicrm-contact.html.twig b/templates/webform-civicrm-contact.html.twig index a0df9ff18..df32cbbca 100644 --- a/templates/webform-civicrm-contact.html.twig +++ b/templates/webform-civicrm-contact.html.twig @@ -17,8 +17,8 @@ */ #} -{% if attributes['type'] == 'hidden' and attributes['title'] %} - +{% if attributes['type'] == 'hidden' and attributes['title'] and element['#title_display'] != 'none' %} + {{ attributes['title'] }} {% endif %} {% if description_display == 'before' and description.content %} diff --git a/tests/modules/webform_civicrm_test/config/install/webform.webform.update_contact_details.yml b/tests/modules/webform_civicrm_test/config/install/webform.webform.update_contact_details.yml index 1d3dcc226..c449491ad 100644 --- a/tests/modules/webform_civicrm_test/config/install/webform.webform.update_contact_details.yml +++ b/tests/modules/webform_civicrm_test/config/install/webform.webform.update_contact_details.yml @@ -2,11 +2,11 @@ uuid: null langcode: en status: open dependencies: + module: + - webform_civicrm enforced: module: - webform_civicrm_test - module: - - webform_civicrm_test weight: 0 open: null close: null @@ -16,7 +16,7 @@ archive: false id: update_contact_details title: 'Update Contact Details' description: '' -category: '' +categories: { } elements: |- basic_information: '#type': webform_wizard_page @@ -59,6 +59,47 @@ elements: |- width: 20 '#parent': civicrm_1_contact_1_fieldset_fieldset '#title': 'Last Name' + civicrm_1_contact_1_email_email: + '#type': email + '#form_key': civicrm_1_contact_1_email_email + '#extra': + width: 20 + '#parent': civicrm_1_contact_1_fieldset_fieldset + '#title': Email + civicrm_2_contact_1_fieldset_fieldset: + '#type': fieldset + '#title': 'Contact 2' + '#form_key': civicrm_2_contact_1_fieldset_fieldset + civicrm_2_contact_1_contact_existing: + '#type': civicrm_contact + '#title': 'Existing Contact' + '#widget': hidden + '#none_prompt': '+ Create new +' + '#results_display': + display_name: display_name + '#default': relationship + '#default_relationship_to': '1' + '#default_relationship': + 8_b: 8_b + '#filter_relationship_contact': '1' + '#filter_relationship_types': + 8_b: 8_b + '#allow_create': 1 + '#contact_type': household + '#form_key': civicrm_2_contact_1_contact_existing + '#parent': civicrm_2_contact_1_fieldset_fieldset + '#extra': { } + civicrm_2_contact_1_contact_household_name: + '#type': textfield + '#counter_type': character + '#counter_maximum': 128 + '#counter_maximum_message': ' ' + '#contact_type': household + '#form_key': civicrm_2_contact_1_contact_household_name + '#extra': + width: 20 + '#parent': civicrm_2_contact_1_fieldset_fieldset + '#title': 'Household Name' address_information: '#type': webform_wizard_page '#title': 'Address Information' @@ -469,12 +510,12 @@ elements: |- 1111: Kazakhstan 1112: Kenya 1113: Kiribati - 1114: 'Korea, Democratic People''s Republic of' + 1114: "Korea, Democratic People's Republic of" 1115: 'Korea, Republic of' 1251: Kosovo 1116: Kuwait 1117: Kyrgyzstan - 1118: 'Lao People''s Democratic Republic' + 1118: "Lao People's Democratic Republic" 1119: Latvia 1120: Lebanon 1121: Lesotho @@ -822,11 +863,10 @@ handlers: weight: null settings: nid: 1 - number_of_contacts: '1' + number_of_contacts: '2' 1_contact_type: individual 1_webform_label: 'Contact 1' - civicrm_1_contact_1_contact_contact_sub_type: - '': '' + civicrm_1_contact_1_contact_contact_sub_type: { } civicrm_1_contact_1_contact_existing: create_civicrm_webform_element civicrm_1_contact_1_contact_prefix_id: 0 civicrm_1_contact_1_contact_first_name: create_civicrm_webform_element @@ -867,20 +907,45 @@ handlers: civicrm_1_contact_1_address_country_id: create_civicrm_webform_element civicrm_1_contact_1_address_state_province_id: create_civicrm_webform_element civicrm_1_contact_1_address_county_id: 0 + civicrm_1_contact_1_address_master_id: 0 civicrm_1_contact_1_address_location_type_id: '1' civicrm_1_contact_1_address_is_primary: '1' contact_1_number_of_phone: '0' - contact_1_number_of_email: '0' + contact_1_number_of_email: '1' + civicrm_1_contact_1_email_email: create_civicrm_webform_element + civicrm_1_contact_1_email_location_type_id: '1' + civicrm_1_contact_1_email_is_primary: '1' contact_1_number_of_website: '0' contact_1_number_of_im: '0' - contact_1_number_of_cg1: '0' - contact_1_number_of_cg2: '0' + 2_contact_type: household + 2_webform_label: 'Contact 2' + civicrm_2_contact_1_contact_contact_sub_type: { } + civicrm_2_contact_1_contact_existing: create_civicrm_webform_element + civicrm_2_contact_1_contact_household_name: create_civicrm_webform_element + civicrm_2_contact_1_contact_nick_name: 0 + civicrm_2_contact_1_contact_preferred_communication_method: 0 + civicrm_2_contact_1_contact_privacy: 0 + civicrm_2_contact_1_contact_preferred_language: 0 + civicrm_2_contact_1_contact_communication_style_id: 0 + civicrm_2_contact_1_contact_image_url: 0 + civicrm_2_contact_1_contact_contact_id: 0 + civicrm_2_contact_1_contact_user_id: 0 + civicrm_2_contact_1_contact_external_identifier: 0 + civicrm_2_contact_1_contact_source: 0 + civicrm_2_contact_1_contact_cs: 0 + contact_2_settings_matching_rule: Unsupervised + contact_2_number_of_other: '0' + contact_2_number_of_address: '0' + contact_2_number_of_phone: '0' + contact_2_number_of_email: '0' + contact_2_number_of_website: '0' + contact_2_number_of_im: '0' + contact_2_number_of_relationship: '0' prefix_known: '' prefix_unknown: '' toggle_message: 0 message: '' activity_number_of_activity: '0' - case_number_of_case: '0' participant_reg_type: '0' reg_options: event_type: @@ -897,8 +962,8 @@ handlers: disable_unregister: 0 allow_url_load: 0 membership_1_number_of_membership: '0' + membership_2_number_of_membership: '0' civicrm_1_contribution_1_contribution_enable_contribution: '0' - grant_number_of_grant: '0' checksum_text: '' create_fieldsets: 1 confirm_subscription: 1 @@ -918,24 +983,38 @@ handlers: number_of_other: '0' number_of_address: '1' number_of_phone: '0' - number_of_email: '0' + number_of_email: '1' number_of_website: '0' number_of_im: '0' - number_of_cg1: '0' - number_of_cg2: '0' address: 1: location_type_id: '1' is_primary: '1' + email: + 1: + location_type_id: '1' + is_primary: '1' + 2: + contact: + 1: + contact_type: household + contact_sub_type: { } + webform_label: 'Contact 2' + matching_rule: Unsupervised + number_of_other: '0' + number_of_address: '0' + number_of_phone: '0' + number_of_email: '0' + number_of_website: '0' + number_of_im: '0' + number_of_relationship: '0' activity: number_of_activity: '0' - case: - number_of_case: '0' membership: 1: number_of_membership: '0' - grant: - number_of_grant: '0' + 2: + number_of_membership: '0' participant_reg_type: '0' reg_options: event_type: diff --git a/tests/src/FunctionalJavascript/ActivitySubmissionTest.php b/tests/src/FunctionalJavascript/ActivitySubmissionTest.php index b03991209..4a0822c7f 100644 --- a/tests/src/FunctionalJavascript/ActivitySubmissionTest.php +++ b/tests/src/FunctionalJavascript/ActivitySubmissionTest.php @@ -11,6 +11,11 @@ */ final class ActivitySubmissionTest extends WebformCivicrmTestBase { + /** + * @var array + */ + private $_contacts; + /** * {@inheritdoc} */ @@ -81,12 +86,11 @@ private function addActivityFields($num = 1, $select_activity = FALSE) { $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption('number_of_contacts', $num); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); $this->getSession()->getPage()->clickLink('Activities'); $this->getSession()->getPage()->selectFieldOption('activity_number_of_activity', 1); - $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertSession()->waitForField('civicrm_1_activity_1_activity_subject'); $this->htmlOutput(); if ($select_activity) { diff --git a/tests/src/FunctionalJavascript/AttachmentTest.php b/tests/src/FunctionalJavascript/AttachmentTest.php index 6049549dc..12d72c527 100644 --- a/tests/src/FunctionalJavascript/AttachmentTest.php +++ b/tests/src/FunctionalJavascript/AttachmentTest.php @@ -13,6 +13,11 @@ final class AttachmentTest extends WebformCivicrmTestBase { protected static $filePrefix = NULL; + /** + * @var array + */ + private $fileParams; + /** * {@inheritdoc} */ @@ -23,6 +28,9 @@ final class AttachmentTest extends WebformCivicrmTestBase { 'file', ]; + private $_cg = []; + private $_cf = []; + protected function setUp(): void { parent::setUp(); $this->cleanupFiles(); @@ -101,11 +109,11 @@ public function testSubmitWebform() { ])); $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption("contact_1_number_of_cg{$this->_cg[1]['id']}", 'Yes'); - $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->wait(1000); $this->htmlOutput(); $this->getSession()->getPage()->selectFieldOption("contact_1_number_of_cg{$this->_cg[2]['id']}", 'Yes'); - $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->wait(1000); $this->htmlOutput(); // Enable custom fields. @@ -119,7 +127,7 @@ public function testSubmitWebform() { $this->assertPageNoErrorMessages(); // Ensure all files are loaded on the form. - foreach ($this->fileParams as $name => $file) { + foreach ($this->fileParams as $file) { $this->assertSession()->pageTextContains($file['name']); } diff --git a/tests/src/FunctionalJavascript/CaseSubmissionTest.php b/tests/src/FunctionalJavascript/CaseSubmissionTest.php index 850ce2756..a5c13171b 100644 --- a/tests/src/FunctionalJavascript/CaseSubmissionTest.php +++ b/tests/src/FunctionalJavascript/CaseSubmissionTest.php @@ -11,6 +11,11 @@ */ final class CaseSubmissionTest extends WebformCivicrmTestBase { + /** + * @var array + */ + private $_caseContact; + protected function setUp(): void { parent::setUp(); $this->enableComponent('CiviCase'); @@ -58,17 +63,17 @@ public function testCaseSubmission() { * Test Case Submission and update with non admin user. */ public function testCaseSubmissionWithNonAdminUser() { - $this->testUser = $this->createUser([ + $testUser = $this->createUser([ 'access content', ]); - $ufContact = $this->getUFMatchRecord($this->testUser->id()); + $ufContact = $this->getUFMatchRecord($testUser->id()); $this->_caseContact = $this->utils->wf_civicrm_api('Contact', 'create', [ 'id' => $ufContact['contact_id'], 'first_name' => 'Mark', 'last_name' => 'Gibson', ])['values'][$ufContact['contact_id']]; - $this->drupalLogin($this->testUser); + $this->drupalLogin($testUser); $caseSubject = "Test Case create with authenticated user"; $this->submitCaseAndVerifyResult($caseSubject, FALSE); diff --git a/tests/src/FunctionalJavascript/ContactDedupeTest.php b/tests/src/FunctionalJavascript/ContactDedupeTest.php index d693c2858..84a86cc4e 100644 --- a/tests/src/FunctionalJavascript/ContactDedupeTest.php +++ b/tests/src/FunctionalJavascript/ContactDedupeTest.php @@ -18,6 +18,11 @@ final class ContactDedupeTest extends WebformCivicrmTestBase { */ protected $dedupeRuleGroupId; + /** + * @var int + */ + private $cfID; + private function createContactSubtype() { $params = [ 'name' => "Student", @@ -29,12 +34,12 @@ private function createContactSubtype() { $this->assertEquals(1, $result['count']); // Create custom group for Student. - $this->cgID = $this->createCustomGroup([ + $cgID = $this->createCustomGroup([ 'title' => "Student Extras", 'extends_entity_column_value' => ['Student'], ])['id']; $this->cfID = $this->utils->wf_civicrm_api('CustomField', 'create', [ - 'custom_group_id' => $this->cgID, + 'custom_group_id' => $cgID, 'label' => 'Advisor Name', 'html_type' => "Text", ])['id']; diff --git a/tests/src/FunctionalJavascript/ContactRelationshipTest.php b/tests/src/FunctionalJavascript/ContactRelationshipTest.php index cf0d443d7..597286b78 100644 --- a/tests/src/FunctionalJavascript/ContactRelationshipTest.php +++ b/tests/src/FunctionalJavascript/ContactRelationshipTest.php @@ -47,7 +47,6 @@ public function testRelationshipRemoval() { $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption('number_of_contacts', 2); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); $this->getSession()->getPage()->clickLink('2. Contact 2'); @@ -171,7 +170,6 @@ public function testSubmitWebform() { $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption('number_of_contacts', 2); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); // Configuring Contact 1 - Student @@ -331,11 +329,9 @@ function testSubTypeRelationship() { $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption("number_of_contacts", 3); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); foreach ([1, 2, 3] as $c) { $this->getSession()->getPage()->clickLink("Contact {$c}"); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->getPage()->selectFieldOption("{$c}_contact_type", 'Organization'); $this->assertSession()->assertWaitOnAjaxRequest(); diff --git a/tests/src/FunctionalJavascript/ContactSubmissionTest.php b/tests/src/FunctionalJavascript/ContactSubmissionTest.php index 160f02779..f3463cc6a 100644 --- a/tests/src/FunctionalJavascript/ContactSubmissionTest.php +++ b/tests/src/FunctionalJavascript/ContactSubmissionTest.php @@ -11,6 +11,16 @@ */ final class ContactSubmissionTest extends WebformCivicrmTestBase { + /** + * @var array + */ + private $group; + + /** + * @var array + */ + private $contacts; + /** * {@inheritdoc} */ @@ -284,7 +294,6 @@ public function testDraftSubmission() { $this->htmlOutput(); $this->getSession()->getPage()->selectFieldOption("draft", 'authenticated'); $this->getSession()->getPage()->pressButton('Save'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->drupalGet($this->webform->toUrl('canonical')); $this->assertPageNoErrorMessages(); @@ -406,6 +415,51 @@ public function testSubmitWebformWithContactSubtype() { $this->assertEquals('First_Contact', implode($contact['contact_sub_type'])); } + /** + * Ensure "sticky" star in webform results works. + */ + public function testSubmitWebformSticky() { + $params = [ + 'name' => "First Contact", + 'is_active' => 1, + 'parent_id' => "Individual", + ]; + $result = $this->utils->wf_civicrm_api('ContactType', 'create', $params); + $this->assertEquals(0, $result['is_error']); + $this->assertEquals(1, $result['count']); + + $this->drupalLogin($this->adminUser); + $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ + 'webform' => $this->webform->id(), + ])); + $this->enableCivicrmOnWebform(); + $this->getSession()->getPage()->selectFieldOption('civicrm_1_contact_1_contact_contact_sub_type[]', 'create_civicrm_webform_element'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $this->saveCiviCRMSettings(); + + $this->drupalLogout(); + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertPageNoErrorMessages(); + + $this->getSession()->getPage()->checkField('First Contact'); + $this->assertSession()->checkboxChecked("First Contact"); + $this->getSession()->getPage()->fillField('First Name', 'Frederick'); + $this->getSession()->getPage()->fillField('Last Name', 'Pabst'); + + $this->getSession()->getPage()->pressButton('Submit'); + $this->assertPageNoErrorMessages(); + + // Make sure the "sticky" AJAX works. + $this->drupalLogin($this->adminUser); + $this->drupalGet($this->webform->toUrl('results-submissions')); + $stickyLink = $this->assertSession()->elementExists('css', "#webform-submission-1-sticky"); + $this->assertSession()->elementExists('css', '#webform-submission-1-sticky .webform-icon-sticky--off'); + $stickyLink->click(); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertSession()->elementExists('css', '#webform-submission-1-sticky .webform-icon-sticky--on'); + } + /** * Test submitting a contact. * @@ -479,7 +533,7 @@ public function testSubmitWebform($contact_type, array $contact_values) { foreach ($field_value as $key => $value) { $selector = "civicrm_1_contact_1_{$entity_type}_{$key}"; $this->addFieldValue($selector, $value); - $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->wait(1000); } } else { diff --git a/tests/src/FunctionalJavascript/ContributionDummyTest.php b/tests/src/FunctionalJavascript/ContributionDummyTest.php index af539979e..36d6c74da 100644 --- a/tests/src/FunctionalJavascript/ContributionDummyTest.php +++ b/tests/src/FunctionalJavascript/ContributionDummyTest.php @@ -144,7 +144,7 @@ public function testBillingSameAs() { $this->getSession()->getPage()->fillField('City', $billingValues['city']); $this->getSession()->getPage()->fillField('Postal Code', $billingValues['postal_code']); $this->getSession()->getPage()->selectFieldOption('Country', $billingValues['country_id']); - $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->wait(1000); $this->getSession()->getPage()->selectFieldOption('State/Province', $billingValues['state_province_id']); $this->getSession()->getPage()->pressButton('Next >'); @@ -182,7 +182,7 @@ public function testSubmitContribution() { $this->setupSalesTax(5, $accountParams = []); // Create a second individual contact cid2 - $this->cid2 = $this->createIndividual(['first_name' => 'Mark', 'last_name' => 'Cooper']); + $cid2 = $this->createIndividual(['first_name' => 'Mark', 'last_name' => 'Cooper'])['id']; $this->drupalLogin($this->rootUser); $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ @@ -191,7 +191,6 @@ public function testSubmitContribution() { $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption('number_of_contacts', 2); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); $this->getSession()->getPage()->clickLink('2. Contact 2'); $this->getSession()->getPage()->checkField("civicrm_2_contact_1_contact_existing"); @@ -231,7 +230,7 @@ public function testSubmitContribution() { $this->saveCiviCRMSettings(); - $this->drupalGet($this->webform->toUrl('canonical', ['query' => ['cid2' => $this->cid2['id']]])); + $this->drupalGet($this->webform->toUrl('canonical', ['query' => ['cid2' => $cid2]])); $this->assertPageNoErrorMessages(); $this->assertSession()->waitForField('First Name'); @@ -277,7 +276,7 @@ public function testSubmitContribution() { $this->assertEquals($adminCid, $membership[0]['contact_id']); $this->assertEquals('Basic', $membership[0]['membership_name']); - $this->assertEquals($this->cid2['id'], $membership[1]['contact_id']); + $this->assertEquals($cid2, $membership[1]['contact_id']); $this->assertEquals('Advanced', $membership[1]['membership_name']); $api_result = $this->utils->wf_civicrm_api('contribution', 'get', [ @@ -361,7 +360,6 @@ public function testCurrentEmployer() { $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption('number_of_contacts', 2); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); $this->getSession()->getPage()->clickLink('2. Contact 2'); $this->getSession()->getPage()->selectFieldOption('2_contact_type', 'organization'); @@ -643,7 +641,6 @@ public function testAssignContributionSecondContactSelectByUserPaymentProcessor( // Enable email field for contact 2. $this->getSession()->getPage()->clickLink("Contact 2"); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->getPage()->selectFieldOption('contact_2_number_of_email', 1); $this->assertSession()->assertWaitOnAjaxRequest(); @@ -691,7 +688,6 @@ public function testAssignContributionSecondContactSelectByUserPaymentProcessor( $this->assertSession()->elementTextContains('css', '#wf-crm-billing-total', '10.00'); $this->htmlOutput(); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->fillCardAndSubmit(); $api_result_contribution = $this->utils->wf_civicrm_api('contribution', 'get', [ diff --git a/tests/src/FunctionalJavascript/ContributionIatsTest.php b/tests/src/FunctionalJavascript/ContributionIatsTest.php index 7807ac423..97956554f 100644 --- a/tests/src/FunctionalJavascript/ContributionIatsTest.php +++ b/tests/src/FunctionalJavascript/ContributionIatsTest.php @@ -13,6 +13,22 @@ */ final class ContributionIatsTest extends WebformCivicrmTestBase { + /** + * @var array + */ + private $payment_processor_legacy; + + /** + * @var array + */ + private $payment_processor_legacy_acheft; + + /** + * @var array + * @todo the test that uses this is commented out. Is it still needed? + */ + private $payment_processor_faps; + /** * {@inheritdoc} */ @@ -39,8 +55,7 @@ protected function setUp(): void { 'payment_type' => 1, 'payment_instrument_id' => 'Credit Card', ]; - $utils = \Drupal::service('webform_civicrm.utils'); - $result = $utils->wf_civicrm_api('payment_processor', 'create', $params); + $result = $this->utils->wf_civicrm_api('payment_processor', 'create', $params); $this->assertEquals(0, $result['is_error']); $this->assertEquals(1, $result['count']); $this->payment_processor_legacy = current($result['values']); @@ -63,8 +78,7 @@ protected function setUp(): void { 'payment_type' => 1, 'payment_instrument_id' => 'Debit Card', ]; - $utils = \Drupal::service('webform_civicrm.utils'); - $result = $utils->wf_civicrm_api('payment_processor', 'create', $params); + $result = $this->utils->wf_civicrm_api('payment_processor', 'create', $params); $this->assertEquals(0, $result['is_error']); $this->assertEquals(1, $result['count']); $this->payment_processor_legacy_acheft = current($result['values']); @@ -87,8 +101,7 @@ protected function setUp(): void { 'payment_type' => 1, 'payment_instrument_id' => 'Credit Card', ]; - $utils = \Drupal::service('webform_civicrm.utils'); - $result = $utils->wf_civicrm_api('payment_processor', 'create', $params); + $result = $this->utils->wf_civicrm_api('payment_processor', 'create', $params); $this->assertEquals(0, $result['is_error']); $this->assertEquals(1, $result['count']); $this->payment_processor_faps = current($result['values']); @@ -156,8 +169,7 @@ protected function setUp(): void { $this->assertPageNoErrorMessages(); // ToDo: load the Contribution and check the values - $utils = \Drupal::service('webform_civicrm.utils'); - $api_result = $utils->wf_civicrm_api('contribution', 'get', [ + $api_result = $this->utils->wf_civicrm_api('contribution', 'get', [ 'sequential' => 1, ]); @@ -200,29 +212,23 @@ private function filliATSCryptogram() { $this->getSession()->switchToIFrame(); } - public function testSubmitContribution() { - $financialAccount = $this->setupSalesTax(2, $accountParams = []); - + /** + * Create a webform with contribution. + */ + public function configureWebform() { + $this->setupSalesTax(2); $this->drupalLogin($this->adminUser); $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ 'webform' => $this->webform->id(), ])); // The label has a

in it which can cause weird failures here. - $this->assertSession()->waitForText('Enable CiviCRM Processing'); - $this->assertSession()->waitForField('nid'); - $this->getSession()->getPage()->checkField('nid'); - $this->getSession()->getPage()->clickLink('Contribution'); - $this->getSession()->getPage()->selectFieldOption('civicrm_1_contribution_1_contribution_enable_contribution', 1); - $this->assertSession()->assertWaitOnAjaxRequest(); - $this->assertSession()->pageTextContains('You must enable an email field for Contact 1 in order to process transactions.'); - $this->getSession()->getPage()->pressButton('Enable It'); - $this->assertSession()->assertWaitOnAjaxRequest(); + $this->enableCivicrmOnWebform(); + $params = [ + 'payment_processor_id' => $this->payment_processor_legacy['id'], + ]; + $this->configureContributionTab($params); $this->getSession()->getPage()->checkField('Contribution Amount'); - $this->getSession()->getPage()->selectFieldOption('Currency', 'USD'); - $this->getSession()->getPage()->selectFieldOption('Financial Type', 1); - $this->assertCount(5, $this->getOptions('Payment Processor')); - $this->getSession()->getPage()->selectFieldOption('Payment Processor', $this->payment_processor_legacy['id']); $this->enableBillingSection(); $this->getSession()->getPage()->selectFieldOption('lineitem_1_number_of_lineitem', 2); @@ -235,9 +241,13 @@ public function testSubmitContribution() { // Set the Financial Type for the second line item to Member Dues (which has Sales Tax on it). $this->getSession()->getPage()->selectFieldOption('civicrm_1_lineitem_2_contribution_financial_type_id', 2); - $this->getSession()->getPage()->pressButton('Save Settings'); - $this->assertSession()->pageTextContains('Saved CiviCRM settings'); + $this->saveCiviCRMSettings(); + } + /** + * Submit the form using iATS card details. + */ + public function submitWebForm() { $this->drupalGet($this->webform->toUrl('canonical')); $this->assertPageNoErrorMessages(); $this->getSession()->getPage()->fillField('First Name', 'Frederick'); @@ -247,6 +257,7 @@ public function testSubmitContribution() { $this->getSession()->getPage()->fillField('Line Item Amount 2', '5.00'); $this->getSession()->getPage()->pressButton('Next >'); + $this->assertSession()->waitForField('Contribution Amount'); $this->getSession()->getPage()->fillField('Contribution Amount', '3.00'); $this->assertSession()->elementExists('css', '#wf-crm-billing-items'); $this->htmlOutput(); @@ -271,14 +282,17 @@ public function testSubmitContribution() { $this->fillBillingFields($billingValues); $this->getSession()->getPage()->pressButton('Submit'); // throw new \Exception(var_export($this->htmlOutputDirectory, TRUE)); - $this->createScreenshot($this->htmlOutputDirectory . '/legacy289.png'); $this->htmlOutput(); $this->assertPageNoErrorMessages(); - $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); + $this->assertSession()->waitForText('New submission added to CiviCRM Webform Test.'); + } - $utils = \Drupal::service('webform_civicrm.utils'); - $api_result = $utils->wf_civicrm_api('contribution', 'get', [ + /** + * Verify payment values in CiviCRM. + */ + public function verifyResults() { + $api_result = $this->utils->wf_civicrm_api('contribution', 'get', [ 'sequential' => 1, ]); @@ -294,12 +308,12 @@ public function testSubmitContribution() { $this->assertEquals('USD', $contribution['currency']); // Also retrieve tax_amount (have to ask for it to be returned): - $api_result = $utils->wf_civicrm_api('contribution', 'get', [ + $api_result = $this->utils->wf_civicrm_api('contribution', 'get', [ 'sequential' => 1, 'return' => ['tax_amount', 'payment_instrument_id'], ]); $contribution = reset($api_result['values']); - $creditCardID = $utils->wf_civicrm_api('OptionValue', 'getvalue', [ + $creditCardID = $this->utils->wf_civicrm_api('OptionValue', 'getvalue', [ 'return' => "value", 'label' => "Credit Card", 'option_group_id' => "payment_instrument", @@ -308,13 +322,13 @@ public function testSubmitContribution() { $this->assertEquals($creditCardID, $contribution['payment_instrument_id']); $tax_total_amount = $contribution['tax_amount']; - $contriPriceFieldID = $utils->wf_civicrm_api('PriceField', 'get', [ + $contriPriceFieldID = $this->utils->wf_civicrm_api('PriceField', 'get', [ 'sequential' => 1, 'price_set_id' => 'default_contribution_amount', 'options' => ['limit' => 1], ])['id'] ?? NULL; - $api_result = $utils->wf_civicrm_api('line_item', 'get', [ + $api_result = $this->utils->wf_civicrm_api('line_item', 'get', [ 'sequential' => 1, ]); @@ -337,6 +351,30 @@ public function testSubmitContribution() { $this->assertEquals($contribution_total_amount, $sum_line_total + $sum_tax_amount); } + /** + * Test Payment using iATS processor. + */ + public function testSubmitContribution() { + $this->configureWebform(); + $this->submitWebForm(); + $this->verifyResults(); + } + + /** + * Test Payment on AJAX webform. + */ + public function testSubmitContributionAjaxEnabled() { + $this->configureWebform(); + // Enable AJAX on the form. + $this->drupalGet($this->webform->toUrl('settings')); + $this->htmlOutput(); + $this->getSession()->getPage()->checkField('Use Ajax'); + $this->getSession()->getPage()->pressButton('Save'); + + $this->submitWebForm(); + $this->verifyResults(); + } + public function testSubmitACHEFTContribution() { $this->drupalLogin($this->adminUser); $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ @@ -381,7 +419,7 @@ public function testSubmitACHEFTContribution() { // Wait for the ACHEFT form to load in. $this->assertSession()->waitForField('account_holder'); $this->getSession()->getPage()->fillField('Account Holder', 'CiviCRM user'); - $this->getSession()->getPage()->fillField('Bank Account Number', '12345678'); + $this->getSession()->getPage()->fillField('Account No.', '12345678'); $this->getSession()->getPage()->fillField('Bank Identification Number', '111111111'); $this->getSession()->getPage()->fillField('Bank Name', 'Bank of CiviCRM'); $this->getSession()->getPage()->selectFieldOption('bank_account_type', 'Savings'); diff --git a/tests/src/FunctionalJavascript/ContributionPayLaterTest.php b/tests/src/FunctionalJavascript/ContributionPayLaterTest.php index 8c598f7de..eeed20631 100644 --- a/tests/src/FunctionalJavascript/ContributionPayLaterTest.php +++ b/tests/src/FunctionalJavascript/ContributionPayLaterTest.php @@ -3,8 +3,8 @@ namespace Drupal\Tests\webform_civicrm\FunctionalJavascript; use Civi\Api4\Contribution; +use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Url; -use Drupal\webform\Entity\Webform; /** * Tests submitting a Webform with CiviCRM: Contribution with Pay later @@ -13,6 +13,19 @@ */ final class ContributionPayLaterTest extends WebformCivicrmTestBase { + private $_customGroup = []; + private $_customFields = []; + + /** + * @var string + */ + private $country; + + /** + * @var string + */ + private $state; + public function testReceiptParams() { $this->drupalLogin($this->rootUser); $this->redirectEmailsToDB(); @@ -37,6 +50,7 @@ public function testReceiptParams() { $this->getSession()->getPage()->selectFieldOption('Enable Billing Address?', 'No'); $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->getPage()->checkField('Contribution Amount'); + $this->getSession()->getPage()->checkField('Contribution Receive Date'); $this->saveCiviCRMSettings(); @@ -50,6 +64,10 @@ public function testReceiptParams() { $this->assertPageNoErrorMessages(); $this->getSession()->getPage()->fillField('Contribution Amount', '30'); + $futureReceiveDate = new DrupalDateTime('+1 month'); + $this->getSession()->getPage()->fillField('civicrm_1_contribution_1_contribution_receive_date[date]', $futureReceiveDate->format('m-d-Y')); + $this->getSession()->getPage()->fillField('civicrm_1_contribution_1_contribution_receive_date[time]', '07:15:00'); + $this->assertSession()->elementExists('css', '#wf-crm-billing-items'); $this->htmlOutput(); $this->assertSession()->elementTextContains('css', '#wf-crm-billing-total', '30.00'); @@ -60,7 +78,7 @@ public function testReceiptParams() { $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); $contribution = Contribution::get() - ->addSelect('source', 'total_amount', 'contribution_status_id:label', 'currency', 'financial_type_id:label') + ->addSelect('source', 'total_amount', 'contribution_status_id:label', 'currency', 'financial_type_id:label', 'receive_date') ->setLimit(1) ->execute() ->first(); @@ -68,6 +86,9 @@ public function testReceiptParams() { $this->assertEquals('Pending', $contribution['contribution_status_id:label']); $this->assertEquals('Member Dues', $contribution['financial_type_id:label']); $this->assertEquals('USD', $contribution['currency']); + $verifyDate = $futureReceiveDate->format('Y-m-d'); + $contributionDate = date('Y-m-d', strtotime($contribution['receive_date'])); + $this->assertEquals("{$verifyDate} 07:15:00", "{$contributionDate} 07:15:00"); $sent_email = $this->getMostRecentEmail(); $this->assertStringContainsString('From: Admin ', $sent_email); @@ -261,7 +282,6 @@ private function verifyResult() { $this->utils->wf_civicrm_api('contribution', 'delete', [ 'id' => $contribution['id'], ]); - $this->contribution_id = $contribution['id']; $address = $this->utils->wf_civicrm_api('Address', 'get', [ 'sequential' => 1, diff --git a/tests/src/FunctionalJavascript/ContributionSoftTest.php b/tests/src/FunctionalJavascript/ContributionSoftTest.php new file mode 100644 index 000000000..2eb66cdad --- /dev/null +++ b/tests/src/FunctionalJavascript/ContributionSoftTest.php @@ -0,0 +1,69 @@ +drupalLogin($this->rootUser); + $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ + 'webform' => $this->webform->id(), + ])); + $this->enableCivicrmOnWebform(); + $this->getSession()->getPage()->selectFieldOption('number_of_contacts', 2); + $this->htmlOutput(); + + $params = [ + 'payment_processor_id' => 'Pay Later', + 'soft' => 'Contact 2', + 'soft_credit_type_id' => 'In Memory of', + ]; + $this->configureContributionTab($params); + + $this->getSession()->getPage()->selectFieldOption('Enable Billing Address?', 'No'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->checkField('Contribution Amount'); + + $this->saveCiviCRMSettings(); + + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertPageNoErrorMessages(); + + $this->getSession()->getPage()->fillField('civicrm_1_contact_1_contact_first_name', 'Frederick'); + $this->getSession()->getPage()->fillField('civicrm_1_contact_1_contact_last_name', 'Pabst'); + $this->getSession()->getPage()->fillField('civicrm_1_contact_1_email_email', 'fred@example.com'); + + // Second contact to assign the soft credit. + $this->getSession()->getPage()->fillField('civicrm_2_contact_1_contact_first_name', 'Max'); + $this->getSession()->getPage()->fillField('civicrm_2_contact_1_contact_last_name', 'Plank'); + + $this->getSession()->getPage()->pressButton('Next >'); + $this->assertPageNoErrorMessages(); + $this->getSession()->getPage()->fillField('Contribution Amount', '20'); + + $this->assertSession()->elementExists('css', '#wf-crm-billing-items'); + $this->htmlOutput(); + $this->assertSession()->elementTextContains('css', '#wf-crm-billing-total', '20.00'); + + $this->getSession()->getPage()->pressButton('Submit'); + $this->assertPageNoErrorMessages(); + $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); + + $contribution = Contribution::get(TRUE) + ->addSelect('contribution_soft.amount', 'contribution_soft.soft_credit_type_id:label', 'contribution_soft.contact_id.display_name', 'contact_id.display_name') + ->addJoin('ContributionSoft AS contribution_soft', 'LEFT') + ->execute() + ->first(); + $this->assertEquals('Frederick Pabst', $contribution['contact_id.display_name']); + $this->assertEquals('20', $contribution['contribution_soft.amount']); + $this->assertEquals('In Memory of', $contribution['contribution_soft.soft_credit_type_id:label']); + $this->assertEquals('Max Plank', $contribution['contribution_soft.contact_id.display_name']); + } + +} diff --git a/tests/src/FunctionalJavascript/CustomFieldSubmissionTest.php b/tests/src/FunctionalJavascript/CustomFieldSubmissionTest.php index d77c7dcab..e10c55f0d 100644 --- a/tests/src/FunctionalJavascript/CustomFieldSubmissionTest.php +++ b/tests/src/FunctionalJavascript/CustomFieldSubmissionTest.php @@ -11,6 +11,11 @@ */ final class CustomFieldSubmissionTest extends WebformCivicrmTestBase { + /** + * @var array + */ + private $_customFields; + private function createCustomFields() { $this->_customFields = []; $result = $this->createCustomGroup(); @@ -329,10 +334,9 @@ public function testDynamicCustomFields() { 'query' => ['reset' => 1, 'action' => 'update', 'gid' => 1, 'id' => $this->_customFields['color_checkboxes']] ])->toString(); $this->drupalGet($fieldURL); - $this->getSession()->getPage()->uncheckField('Active?'); + $this->getSession()->getPage()->uncheckField(version_compare(\CRM_Core_BAO_Domain::version(), '5.75.alpha1', '<') ? 'Active?' : 'Active'); // $this->createScreenshot($this->htmlOutputDirectory . '/custom_field.png'); $this->getSession()->getPage()->pressButton('_qf_Field_done-bottom'); - $this->assertSession()->assertWaitOnAjaxRequest(); //Reload the webform page - the custom field should be removed. $this->drupalGet($this->webform->toUrl('canonical')); @@ -344,9 +348,8 @@ public function testDynamicCustomFields() { //Re-enable the field. $this->drupalGet($fieldURL); - $this->getSession()->getPage()->checkField('Active?'); + $this->getSession()->getPage()->checkField(version_compare(\CRM_Core_BAO_Domain::version(), '5.75.alpha1', '<') ? 'Active?' : 'Active'); $this->getSession()->getPage()->pressButton('_qf_Field_done-bottom'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->drupalGet($this->webform->toUrl('canonical')); $this->htmlOutput(); @@ -514,6 +517,58 @@ public function testSubmitWebform() { $this->assertSession()->assertWaitOnAjaxRequest(); } + /** + * Ensure webform default values are loaded when the contact + * in civicrm does not have a value set on it. + */ + public function testCustomFieldWebformDefaults() { + $this->createCustomFields(); + $createParams = [ + 'first_name' => 'Frederick', + 'last_name' => 'Pabst', + 'custom_' . $this->_customFields['text'] => 'Lorem Ipsum', + ]; + $contactID = $this->createIndividual($createParams)['id']; + + $this->drupalLogin($this->rootUser); + $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ + 'webform' => $this->webform->id(), + ])); + $this->enableCivicrmOnWebform(); + + $this->getSession()->getPage()->selectFieldOption('contact_1_number_of_cg1', 'Yes'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->htmlOutput(); + + // Enable custom fields. + foreach ($this->_customFields as $name => $id) { + $this->getSession()->getPage()->checkField("civicrm_1_contact_1_cg1_custom_{$id}"); + $this->assertSession()->checkboxChecked("civicrm_1_contact_1_cg1_custom_{$id}"); + } + $this->saveCiviCRMSettings(); + + $this->drupalGet($this->webform->toUrl('edit-form')); + + $this->setDefaultValue("edit-webform-ui-elements-civicrm-1-contact-1-cg1-custom-{$this->_customFields['test_radio_2']}-operations", 3); + $this->setDefaultValue("edit-webform-ui-elements-civicrm-1-contact-1-cg1-custom-{$this->_customFields['color_checkboxes']}-operations", 2); + $this->setDefaultValue("edit-webform-ui-elements-civicrm-1-contact-1-cg1-custom-{$this->_customFields['fruits']}-operations", 'Mango, Orange'); + + $this->drupalGet($this->webform->toUrl('canonical', ['query' => ['cid1' => $contactID]])); + $this->htmlOutput(); + $this->assertPageNoErrorMessages(); + + // Ensure default values are loaded. + $this->assertFieldValue("edit-civicrm-1-contact-1-cg1-custom-{$this->_customFields['text']}", 'Lorem Ipsum'); + + // This is loaded from webform default since no value is set in civi. + $this->assertSession()->checkboxNotChecked("Red"); + $this->assertSession()->checkboxChecked("Green"); + + $this->assertSession()->checkboxChecked("Mango"); + $this->assertSession()->checkboxChecked("Orange"); + $this->assertSession()->checkboxNotChecked("Apple"); + } + /** * Test Contact Values loaded via ajax, i.e, * on selecting a contact from autocomplete, select, etc. diff --git a/tests/src/FunctionalJavascript/EventTest.php b/tests/src/FunctionalJavascript/EventTest.php index 7ff9de9a2..3ae7790b6 100644 --- a/tests/src/FunctionalJavascript/EventTest.php +++ b/tests/src/FunctionalJavascript/EventTest.php @@ -3,7 +3,6 @@ namespace Drupal\Tests\webform_civicrm\FunctionalJavascript; use Drupal\Core\Url; -use Drupal\FunctionalJavascriptTests\DrupalSelenium2Driver; /** * Tests submitting a Webform with CiviCRM: Contact with Event. @@ -12,6 +11,25 @@ */ final class EventTest extends WebformCivicrmTestBase { + private $_customFields = []; + + /** + * @var array + * custom group + */ + private $cg; + + /** + * @var array + * financial type + */ + private $ft; + + /** + * @var array + */ + private $_event; + protected function setUp(): void { parent::setUp(); $this->ft = $this->utils->wf_civicrm_api('FinancialType', 'get', [ @@ -75,7 +93,6 @@ function testParticipantContactReference() { ])); $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption('Number of Contacts', 2); - $this->assertSession()->assertWaitOnAjaxRequest(); //Configure Event tab. $this->getSession()->getPage()->clickLink('Event Registration'); @@ -94,7 +111,6 @@ function testParticipantContactReference() { $this->getSession()->getPage()->selectFieldOption('Payment Processor', 'Pay Later'); $this->saveCiviCRMSettings(); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->drupalGet($this->webform->toUrl('canonical')); $this->htmlOutput(); @@ -115,7 +131,6 @@ function testParticipantContactReference() { $refName = 'civicrm_1_participant_1_cg' . $this->cg['id'] . '_custom_' . $this->_customFields['participant_contact_ref']['id']; $this->getSession()->getPage()->selectFieldOption($refName, 2); $this->getSession()->getPage()->pressButton('Next >'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->assertPageNoErrorMessages(); $this->htmlOutput(); @@ -124,7 +139,6 @@ function testParticipantContactReference() { $this->assertSession()->elementTextContains('css', '#wf-crm-billing-total', '40.00'); $this->getSession()->getPage()->pressButton('Submit'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->assertPageNoErrorMessages(); $this->htmlOutput(); @@ -140,6 +154,98 @@ function testParticipantContactReference() { $this->assertEquals($contactRef['id'], $participant["{$customKey}_id"]); } + /** + * Submit the form with values. + */ + function submitWebform() { + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertPageNoErrorMessages(); + $edit = [ + 'civicrm_1_contact_1_contact_first_name' => 'Frederick', + 'civicrm_1_contact_1_contact_last_name' => 'Pabst', + 'civicrm_2_contact_1_contact_first_name' => 'Mark', + 'civicrm_2_contact_1_contact_last_name' => 'Anthony' + ]; + $this->postSubmission($this->webform, $edit); + } + + /** + * Verify submission results. + * + * @param boolean $primary + * false if primary participant setting is disabled on the webform. + */ + function verifyResults($primary = true) { + // Ensure both contacts are added to the event. + $api_result = $this->utils->wf_civicrm_api('participant', 'get', [ + 'sequential' => 1, + ]); + $this->assertEquals(0, $api_result['is_error']); + $this->assertEquals(2, $api_result['count']); + + $values = $api_result['values']; + $this->assertEquals($this->_event['id'], $values[0]['event_id']); + $this->assertEquals($this->_event['id'], $values[1]['event_id']); + if ($primary) { + $this->assertEquals($values[0]['id'], $values[1]['participant_registered_by_id']); + } + else { + $this->assertEmpty($values[0]['participant_registered_by_id']); + $this->assertEmpty($values[1]['participant_registered_by_id']); + } + // Delete participants. + $this->utils->wf_civicrm_api('participant', 'delete', [ + 'id' => $values[0]['id'], + ]); + $this->utils->wf_civicrm_api('participant', 'delete', [ + 'id' => $values[1]['id'], + ]); + } + + /** + * Verify the submission of multiple participants. + */ + function testMultipleParticipants() { + $this->drupalLogin($this->adminUser); + $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ + 'webform' => $this->webform->id(), + ])); + $this->enableCivicrmOnWebform(); + + $this->getSession()->getPage()->selectFieldOption('number_of_contacts', 2); + $this->htmlOutput(); + + $this->getSession()->getPage()->clickLink('Event Registration'); + + // Configure Event tab. + $this->getSession()->getPage()->selectFieldOption('participant_reg_type', 'all'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->htmlOutput(); + $this->getSession()->getPage()->selectFieldOption('participant_1_number_of_participant', 1); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->htmlOutput(); + $this->getSession()->getPage()->selectFieldOption('civicrm_1_participant_1_participant_event_id[]', 'Test Event'); + + $this->saveCiviCRMSettings(); + + $this->submitWebform(); + + // Ensure both contacts are added to the event. + $this->verifyResults(); + + // Disable primary participant on the webform. + $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ + 'webform' => $this->webform->id(), + ])); + $this->getSession()->getPage()->clickLink('Event Registration'); + $this->getSession()->getPage()->checkField('Disable Contact 1 to be stored as Primary Participant'); + $this->saveCiviCRMSettings(); + + // Resubmit the form and verify the results. + $this->submitWebform(); + $this->verifyResults(false); + } + /** * Event Participant submission. */ @@ -207,8 +313,7 @@ function testSubmitEventParticipant() { $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); //Assert if recur is attached to the created membership. - $utils = \Drupal::service('webform_civicrm.utils'); - $api_result = $utils->wf_civicrm_api('participant', 'get', [ + $api_result = $this->utils->wf_civicrm_api('participant', 'get', [ 'sequential' => 1, ]); $this->assertEquals(0, $api_result['is_error']); @@ -217,9 +322,10 @@ function testSubmitEventParticipant() { } /** - * Test the working of 'Show Full Events'. + * Test the working of 'Show Full Events' + * and default URL load of events. */ - function testMaxParticipant() { + function testMaxParticipantAndEventUrlDefault() { $event = $this->utils->wf_civicrm_api('Event', 'create', [ 'event_type_id' => "Conference", 'title' => "Test Event 2", @@ -229,7 +335,7 @@ function testMaxParticipant() { ]); $this->assertEquals(0, $event['is_error']); $this->assertEquals(1, $event['count']); - $this->_event2 = reset($event['values']); + $event2 = reset($event['values']); $event = $this->utils->wf_civicrm_api('Event', 'create', [ 'event_type_id' => "Conference", @@ -239,7 +345,6 @@ function testMaxParticipant() { ]); $this->assertEquals(0, $event['is_error']); $this->assertEquals(1, $event['count']); - $this->_event3 = reset($event['values']); // Enable waitlist on the event with max participant = 2. $this->utils->wf_civicrm_api('Event', 'create', [ @@ -266,7 +371,7 @@ function testMaxParticipant() { // Register only 1 particpant to event 2 so that 1 seat is available. $this->utils->wf_civicrm_api('Participant', 'create', [ 'contact_id' => $indiv1, - 'event_id' => $this->_event2['id'], + 'event_id' => $event2['id'], 'status_id' => "Registered", 'role_id' => "Attendee", ]); @@ -278,7 +383,6 @@ function testMaxParticipant() { ])); $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption('number_of_contacts', 1); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); // Configure Event tab. @@ -287,6 +391,7 @@ function testMaxParticipant() { $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); $this->getSession()->getPage()->selectFieldOption('reg_options[show_remaining]', 'always'); + $this->getSession()->getPage()->checkField('reg_options[allow_url_load]'); $this->getSession()->getPage()->selectFieldOption('participant_1_number_of_participant', 1); $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); @@ -318,6 +423,25 @@ function testMaxParticipant() { $this->assertSession()->pageTextNotContains('Test Event 1'); $this->assertSession()->pageTextContains('Test Event 2'); $this->assertSession()->pageTextContains('Test Event 3'); + + // Ensure URL events are set as default on the event field. + $this->drupalGet($this->webform->toUrl('canonical', ['query' => ['event1' => $event2['id']]])); + $this->assertSession()->checkboxChecked('Test Event 2'); + + // Create new event and load it from the URL. + $event = $this->utils->wf_civicrm_api('Event', 'create', [ + 'event_type_id' => "Conference", + 'title' => "Test Event 4", + 'start_date' => date('Y-m-d'), + 'financial_type_id' => $this->ft['id'], + ]); + $this->assertEquals(0, $event['is_error']); + $this->assertEquals(1, $event['count']); + $event4 = reset($event['values']); + + $this->drupalGet($this->webform->toUrl('canonical', ['query' => ['event1' => $event4['id']]])); + $this->assertSession()->pageTextContains('Test Event 4'); + $this->assertSession()->checkboxChecked('Test Event 4'); } } diff --git a/tests/src/FunctionalJavascript/ExistingContactElementTest.php b/tests/src/FunctionalJavascript/ExistingContactElementTest.php index b3548cc2a..ef0d88f04 100644 --- a/tests/src/FunctionalJavascript/ExistingContactElementTest.php +++ b/tests/src/FunctionalJavascript/ExistingContactElementTest.php @@ -4,6 +4,9 @@ use Drupal\Core\Url; use Drupal\Core\Test\AssertMailTrait; +use Drupal\webform\Entity\WebformSubmission; +use Drupal\webform\Entity\Webform; +use Drupal\Core\Serialization\Yaml; /** * Tests submitting a Webform with CiviCRM: existing contact element. @@ -64,9 +67,9 @@ function testRenderingOfExistingContactElement() { 'first_name' => 'Fred', 'last_name' => 'Pinto', ]; - $this->childContact = $this->createIndividual($childContact); + $childContactId = $this->createIndividual($childContact)['id']; $this->utils->wf_civicrm_api('Relationship', 'create', [ - 'contact_id_a' => $this->childContact['id'], + 'contact_id_a' => $childContactId, 'contact_id_b' => $this->rootUserCid, 'relationship_type_id' => "Child of", ]); @@ -82,12 +85,10 @@ function testRenderingOfExistingContactElement() { $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->getPage()->selectFieldOption("number_of_contacts", 4); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); foreach ([2, 3, 4] as $c) { $this->getSession()->getPage()->clickLink("Contact {$c}"); - $this->assertSession()->assertWaitOnAjaxRequest(); //Make second contact as household contact. if ($c == 2) { $this->getSession()->getPage()->selectFieldOption("{$c}_contact_type", 'Household'); @@ -319,6 +320,12 @@ public function testTokensInEmail() { $this->assertSession()->checkboxChecked("civicrm_1_contact_1_email_email"); $this->getSession()->getPage()->selectFieldOption('civicrm_1_contact_1_email_location_type_id', 'Main'); + // Enable Address fields. + $this->getSession()->getPage()->selectFieldOption('contact_1_number_of_address', 1); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->checkField('Country'); + $this->assertSession()->checkboxChecked('Country'); + $this->getSession()->getPage()->clickLink('Activities'); $this->getSession()->getPage()->selectFieldOption('activity_number_of_activity', 2); $this->assertSession()->assertWaitOnAjaxRequest(); @@ -328,13 +335,14 @@ public function testTokensInEmail() { $email = [ 'to_mail' => '[webform_submission:values:civicrm_1_contact_1_email_email:raw]', - 'body' => 'Submitted Values Are - [webform_submission:values] Existing Contact - [webform_submission:values:civicrm_1_contact_1_contact_existing]. Activity 1 ID - [webform_submission:activity-id:1]. Activity 2 ID - [webform_submission:activity-id:2]. Webform CiviCRM Contacts IDs - [webform_submission:contact-id:1]. Webform CiviCRM Contacts Links - [webform_submission:contact-link:1].', + 'body' => 'Submitted Values Are - [webform_submission:values] Existing Contact - [webform_submission:values:civicrm_1_contact_1_contact_existing]. Activity 1 ID - [webform_submission:activity-id:1]. Activity 2 ID - [webform_submission:activity-id:2]. Webform CiviCRM Contacts IDs - [webform_submission:contact-id:1]. Webform CiviCRM Contacts Links - [webform_submission:contact-link:1] Country - [webform_submission:values:civicrm_1_contact_1_address_country_id]. State/Province - [webform_submission:values:civicrm_1_contact_1_address_state_province_id].', ]; $this->addEmailHandler($email); $this->drupalGet($this->webform->toUrl('handlers')); - $civicrm_handler = $this->assertSession()->elementExists('css', '[data-webform-key="webform_civicrm"] a.tabledrag-handle'); + // tabledrag results into a console js error, possibly a drupal core bug. + // $civicrm_handler = $this->assertSession()->elementExists('css', '[data-webform-key="webform_civicrm"] a.tabledrag-handle'); // Move up to be the top-most handler. - $this->sendKeyPress($civicrm_handler, 38); + // $this->sendKeyPress($civicrm_handler, 38); $this->getSession()->getPage()->pressButton('Save handlers'); $this->assertSession()->assertWaitOnAjaxRequest(); @@ -343,6 +351,24 @@ public function testTokensInEmail() { $this->getSession()->getPage()->fillField('Last Name', 'Pabst'); $this->getSession()->getPage()->fillField('Email', 'frederick@pabst.io'); + $countryID = $this->utils->wf_civicrm_api4('Country', 'get', [ + 'where' => [ + ['name', '=', 'United States'], + ], + ], 0)['id']; + $stateProvinceID = $this->utils->wf_civicrm_api4('StateProvince', 'get', [ + 'where' => [ + ['abbreviation', '=', 'NJ'], + ['country_id', '=', $countryID], + ], + ], 0)['id']; + $this->getSession()->getPage()->fillField('Street Address', '123 Milwaukee Ave'); + $this->getSession()->getPage()->fillField('City', 'Milwaukee'); + $this->getSession()->getPage()->fillField('Postal Code', '53177'); + $this->getSession()->getPage()->selectFieldOption('Country', $countryID); + $this->getSession()->wait(1000); + $this->getSession()->getPage()->selectFieldOption('State/Province', $stateProvinceID); + $this->getSession()->getPage()->pressButton('Submit'); $this->assertPageNoErrorMessages(); $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); @@ -355,12 +381,18 @@ public function testTokensInEmail() { // Check if email was sent to contact 1. $this->assertStringContainsString('frederick@pabst.io', $sent_email[0]['to']); + // Something new in 10.3 + $weirdoExtraSpaces = version_compare(\Drupal::VERSION, '10.3.2', '>=') ? ' ' : ''; + // And now there is no longer a newline + $weirdoNewline = version_compare(\Drupal::VERSION, '10.3.2', '<') ? "\n" : ''; + // Verify tokens are rendered correctly. - if (version_compare(\Drupal::VERSION, '10', '>=')) { - $this->assertEquals("Submitted Values Are - + // We ignore newlines so that the length of the website's URL (appearing in + // $this->cidURL) doesn't cause a failure due to variations in line + // wrapping. + $this->assertEquals(strtr("Submitted Values Are - --------- Contact 1 ------------------------------------------------------------ +-------- Contact 1 {$weirdoNewline}----------------------------------------------------------- *Existing Contact* Frederick Pabst @@ -368,36 +400,638 @@ public function testTokensInEmail() { Frederick *Last Name* Pabst +*Street Address* +123 Milwaukee Ave +*City* +Milwaukee +*Postal Code* +53177 +*Country* +United States +*State/Province* +New Jersey *Email* frederick@pabst.io [1] -Existing Contact - Frederick Pabst. Activity 1 ID - {$actID1}. Activity 2 ID - {$actID2}. -Webform CiviCRM Contacts IDs - {$this->rootUserCid}. Webform CiviCRM Contacts Links - -{$cidURL}. +Existing Contact - Frederick Pabst. Activity 1 ID - {$actID1}. Activity 2 ID - {$actID2}.{$weirdoExtraSpaces} +Webform CiviCRM Contacts IDs - {$this->rootUserCid}. Webform CiviCRM Contacts Links -{$weirdoExtraSpaces} +{$cidURL} Country - United{$weirdoExtraSpaces} +States. State/Province - New Jersey. [1] mailto:frederick@pabst.io -", $sent_email[0]['body']); +", "\n", ' '), strtr($sent_email[0]['body'], "\n", ' ')); + } + + /** + * Define test-contact parameters and create a subset of them in Civi. + * + * @return array + * contains parameter arrays for each test-contact + */ + private function addcontactinfo2() { + $contact = [ + 0 => [ // cid = 3 (will overwrite existing contact) + 'contact_id' => 3, + 'first_name' => 'Jimmy', + 'last_name' => 'Page', + 'job_title' => "Guitarist", + 'contact_type' => 'Individual' + ], + 1 => [ // cid = 4 + 'first_name' => 'Robert', + 'last_name' => 'Plant', + 'job_title' => "Vocalist", + 'contact_type' => 'Individual' + ], + 2 => [ // cid = 5 + 'first_name' => 'John Paul', + 'last_name' => 'Jones', + 'job_title' => "Bassist", + 'contact_type' => 'Individual' + ], + 3 => [ // cid = 6 + 'first_name' => 'John', + 'last_name' => 'Bonham', + 'job_title' => "Drummer", + 'contact_type' => 'Individual' + ], + 4 => [ // cid = 7 + 'first_name' => 'Janis', + 'last_name' => 'Joplin', + 'job_title' => "Singer", + 'contact_type' => 'Individual' + ], + 5 => [ // not initiallly created + 'first_name' => 'Marvin', + 'last_name' => 'Gaye', + 'job_title' => "Vocals", + 'contact_type' => 'Individual' + ], + 6 => [ // not initiallly created + 'first_name' => 'Bob', + 'last_name' => 'Dylan', + 'job_title' => "Vocals, Harmonica", + 'contact_type' => 'Individual' + ], + 7 => [ // null contact, not initiallly created + 'first_name' => '', + 'last_name' => '', + 'job_title' => '', + 'contact_type' => 'Individual' + ], + 8 => [ // cid = 8 + 'first_name' => 'Prince', + 'last_name' => '', + 'job_title' => "Guitar, vocals", + 'contact_type' => 'Individual' + ], + 9 => [ // cid = 9 + 'first_name' => 'Madona', + 'last_name' => '', + 'job_title' => "Vocals, drummer", + 'contact_type' => 'Individual' + ], + ]; + $utils = \Drupal::service('webform_civicrm.utils'); + foreach ($contact as $key => $c) { + if (in_array($key, [0, 1, 2, 3, 4, 8, 9])) { + $result = $utils->wf_civicrm_api('Contact', 'create', $c); + $this->assertEquals(0, $result['is_error']); + $this->assertEquals(1, $result['count']); + } } - else { - // Verify tokens are rendered correctly. - $this->assertEquals("Submitted Values Are - --------- Contact 1 ------------------------------------------------------------ + return $contact; + } -*Existing Contact* -Frederick Pabst -*First Name* -Frederick -*Last Name* -Pabst -*Email* -frederick@pabst.io [1] -Existing Contact - Frederick Pabst. Activity 1 ID - {$actID1}. Activity 2 ID - {$actID2}. -Webform CiviCRM Contacts IDs - {$this->rootUserCid}. Webform CiviCRM Contacts Links - -{$cidURL}. + /** + * Sets the contact fields used by testNextPrevSaveLoad() + * + * @param array $contact + * contact parameters to be set + */ + private function setContactFields($contact) { + $this->getSession()->getPage()->fillField('First Name', $contact['first_name']); + $this->getSession()->getPage()->fillField('Last Name', $contact['last_name']); + $this->getSession()->getPage()->fillField('Job Title', $contact['job_title']); + } -[1] mailto:frederick@pabst.io -", $sent_email[0]['body']); - } + /** + * Checks the contact fields used by testNextPrevSaveLoad() + * + * @param array $contact + * contact parameters to be checked + */ + private function checkContactFields($contact) { + $this->assertSession()->fieldValueEquals('First Name', $contact['first_name']); + $this->assertSession()->fieldValueEquals('Last Name', $contact['last_name']); + $this->assertSession()->fieldValueEquals('Job Title', $contact['job_title']); } + /** + * Test locked/unlocked and blank/filled fields during Next/Previous/Save Draft/Load Draft/Submit operations + */ + public function testNextPrevSaveLoad() { + if (version_compare(\Drupal::VERSION, '10.3', '>=')) { + $this->markTestSkipped('retrieving $elements gives blank in 10.3 for some reason'); + return; + } + + $contact = $this->addcontactinfo2(); + + $this->drupalLogin($this->rootUser); + + $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ + 'webform' => $this->webform->id(), + ])); + $this->enableCivicrmOnWebform(); + + // Enable 3 contacts each with first name, last name, job title + $this->getSession()->getPage()->selectFieldOption("number_of_contacts", 3); + foreach ([1, 2, 3] as $c) { + $this->getSession()->getPage()->clickLink("Contact {$c}"); + $this->getSession()->getPage()->checkField("civicrm_{$c}_contact_1_contact_existing"); + $this->assertSession()->checkboxChecked("civicrm_{$c}_contact_1_contact_existing"); + $this->getSession()->getPage()->checkField("civicrm_{$c}_contact_1_contact_job_title"); + $this->assertSession()->checkboxChecked("civicrm_{$c}_contact_1_contact_job_title"); + } + + $this->saveCiviCRMSettings(); + + $this->drupalGet($this->webform->toUrl('edit-form')); + + // Edit contact element 1. + $editContact = [ + 'selector' => 'edit-webform-ui-elements-civicrm-1-contact-1-contact-existing-operations', + 'title' => 'Contact 1', + 'widget' => 'Select List', + 'hide_fields' => 'Name', + 'hide_method' => 'Disabled', + 'no_hide_blank' => TRUE, + 'submit_disabled' => TRUE, + 'default' => 'Specified Contact', + 'default_contact_id' => 3 + ]; + $this->editContactElement($editContact); + + // Edit contact element 2. + $editContact = [ + 'selector' => 'edit-webform-ui-elements-civicrm-2-contact-1-contact-existing-operations', + 'title' => 'Contact 2', + 'widget' => 'Select List', + 'hide_fields' => 'Name', + 'hide_method' => 'Disabled', + 'no_hide_blank' => TRUE, + 'submit_disabled' => TRUE, + 'default' => 'None', + //'default_contact_id' => 4 + ]; + $this->editContactElement($editContact); + + // Edit contact element 3. + $editContact = [ + 'selector' => 'edit-webform-ui-elements-civicrm-3-contact-1-contact-existing-operations', + 'title' => 'Contact 3', + 'widget' => 'Select List', + 'hide_fields' => 'Name', + 'hide_method' => 'Disabled', + 'no_hide_blank' => TRUE, + 'submit_disabled' => TRUE, + 'default' => 'Specified Contact', + 'default_contact_id' => 5 + ]; + $this->editContactElement($editContact); + + // Make first/last name required for all contacts + $this->getSession()->getPage()->checkField("webform_ui_elements[civicrm_1_contact_1_contact_first_name][required]"); + $this->getSession()->getPage()->checkField("webform_ui_elements[civicrm_2_contact_1_contact_first_name][required]"); + $this->getSession()->getPage()->checkField("webform_ui_elements[civicrm_3_contact_1_contact_first_name][required]"); + $this->getSession()->getPage()->checkField("webform_ui_elements[civicrm_1_contact_1_contact_last_name][required]"); + $this->getSession()->getPage()->checkField("webform_ui_elements[civicrm_2_contact_1_contact_last_name][required]"); + $this->getSession()->getPage()->checkField("webform_ui_elements[civicrm_3_contact_1_contact_last_name][required]"); + $this->getSession()->getPage()->pressButton('Save elements'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $this->drupalGet($this->webform->toUrl('edit-form')); + $this->htmlOutput(); + + // Place fields for each contact on their own page and enable saving drafts + $webform = Webform::load($this->webform->getOriginalId()); + $elements = Yaml::decode($webform->get('elements')); + $elements_new = [ + 'page1' => ['#type' => 'webform_wizard_page', '#title' => 'Page 1', 'civicrm_1_contact_1_fieldset_fieldset' => $elements["civicrm_1_contact_1_fieldset_fieldset"]], + 'page2' => ['#type' => 'webform_wizard_page', '#title' => 'Page 2', 'civicrm_2_contact_1_fieldset_fieldset' => $elements["civicrm_2_contact_1_fieldset_fieldset"]], + 'page3' => ['#type' => 'webform_wizard_page', '#title' => 'Page 3', 'civicrm_3_contact_1_fieldset_fieldset' => $elements["civicrm_3_contact_1_fieldset_fieldset"]], + ]; + $webform->set('elements', Yaml::encode($elements_new)); + $webform->setSetting('draft', 'all'); + $webform->save(); + + $this->drupalGet($this->webform->toUrl('edit-form')); + $this->htmlOutput(); + + $this->drupalGet($this->webform->toUrl('canonical')); + + $this->assertPageNoErrorMessages(); + $this->htmlOutput(); + + + //** Setup complete Begin tests. ** + // "{Contacts: x, y, z}" below refers to the current form contents (three elements of $contacts[] array) + + // Page 1 {Contacts: 0, none, 2}: Check initial values. + $this->checkContactFields($contact[0]); + + // Confirm first name is disabled + $field_disabled = $this->getSession()->evaluateScript("document.getElementById('edit-civicrm-1-contact-1-contact-first-name').disabled"); + $this->assertEquals(true, $field_disabled, 'First name is disabled'); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 0, none, 2}: Check initial values. + $this->checkContactFields($contact[7]); // 7 is the blank contact + + // Page 2 {Contacts: 0, none, 2}: Confirm that locked blank fields can be modified + $this->getSession()->getPage()->fillField('First Name', 'FIRST'); + $this->assertSession()->fieldValueEquals('First Name', 'FIRST'); + + // Page 2 {Contacts: 0, none, 2}: Select $contact[1]. + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "{$contact[1]['first_name']} {$contact[1]['last_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->checkContactFields($contact[1]); + + // Page 2 {Contacts: 0, 1, 2}: Test that locked nonblank fields are disabled. + $field_disabled = $this->getSession()->evaluateScript("document.getElementById('edit-civicrm-2-contact-1-contact-first-name').disabled"); + $this->assertEquals(true, $field_disabled, 'First name is disabled'); + $this->getSession()->getPage()->pressButton('Next >'); + return; // @TODO: Additional parts of this test will be enabled in susbequent PRs + $this->assertPageNoErrorMessages(); + + // Page 3 {Contacts: 0, 1, 2}: Check initial values. + $this->checkContactFields($contact[2]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 2 {Contacts: 0, 1, 2}: Check entered contact data ($contact[1]). + $this->checkContactFields($contact[1]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 1 {Contacts: 0, 1, 2}: check initial values. + $this->checkContactFields($contact[0]); + + // Page 1 {Contacts: 0, 1, 2}: Select $contact[3] + $this->getSession()->getPage()->selectFieldOption('civicrm_1_contact_1_contact_existing', "{$contact[3]['first_name']} {$contact[3]['last_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->checkContactFields($contact[3]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 3, 1, 2}: Check still has $contact[1] + $this->checkContactFields($contact[1]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 1: {Contacts: 3, 1, 2}: Check still has $contact[3] + $this->checkContactFields($contact[3]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 3, 1, 2}: Check still has $contact[1] + $this->checkContactFields($contact[1]); + + // Page 2 {Contacts: 3, 1, 2}: Create a new contact ($contact[4]) + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "+ Create new +"); + $this->setContactFields($contact[4]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 1 {Contacts: 3, 4, 2}: check still has $contact[3] + $this->checkContactFields($contact[3]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 3, 4, 2}: Check still has $contact[4] + $this->checkContactFields($contact[4]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 3 {Contacts: 3, 4, 2}: Check initial state + $this->checkContactFields($contact[2]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 2 {Contacts: 3, 4, 2}: check still has $contact[4] + $this->checkContactFields($contact[4]); + + // Page 2 {Contacts: 3, 4, 2}: Create a new contact ($contact[5]) + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "+ Create new +"); + $this->setContactFields($contact[5]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 3 {Contacts: 3, 5, 2}: Check initial state + $this->checkContactFields($contact[2]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 2 {Contacts: 3, 5, 2}: check still has $contact[5] + $this->checkContactFields($contact[5]); + + // Page 2 {Contacts: 3, 5, 2}: Create a new contact ($contact[6]) + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "+ Create new +"); + $this->setContactFields($contact[6]); + + // Page 2 {Contacts: 3, 6, 2}: Save draft + $this->getSession()->getPage()->pressButton('Save Draft'); + $this->assertSession()->pageTextContains('Submission saved. You may return to this form later and it will restore the current values.'); + + // Page 2 {Contacts: 3, 6, 2}: Reload form, check still has $contact[6] + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertSession()->pageTextContains('A partially-completed form was found. Please complete the remaining portions.'); + $this->checkContactFields($contact[6]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 1 {Contacts: 3, 6, 2}: Check still has $contact[3] + $this->checkContactFields($contact[3]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 3, 6, 2}: Check still has $contact[6] + $this->checkContactFields($contact[6]); + + + //*** Test sequence: modify, prev, save draft, load, next, next, *** + // Page 2 {Contacts: 3, 6, 2}: Select $contact[1] + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "{$contact[1]['first_name']} {$contact[1]['last_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->checkContactFields($contact[1]); + + // Page 2 {Contacts: 3, 6, 2}: Modify the job field + $contact['1m'] = $contact[1]; + $contact['1m']['job_title'] = 'MODIFIED JOB TITLE 1'; + $this->getSession()->getPage()->fillField('Job Title', $contact['1m']['job_title']); + $this->getSession()->getPage()->pressButton('< Prev'); + $this->checkContactFields($contact[3]); + + // Page 1 {Contacts: 3, 1m, 2}: Save/load the draft + $this->getSession()->getPage()->pressButton('Save Draft'); + $this->assertSession()->pageTextContains('Submission saved. You may return to this form later and it will restore the current values.'); + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertSession()->pageTextContains('A partially-completed form was found. Please complete the remaining portions.'); + + // Page 1 {Contacts: 3, 1m, 2}: Confirm contact + $this->checkContactFields($contact[3]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 3, 1m, 2}: Confirm modified contact + $this->checkContactFields($contact['1m']); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 3 {Contacts: 3, 1m, 2}: Confirm the job is still modified + $this->checkContactFields($contact[2]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 2 {Contacts: 3, 1m, 2}: Confirm the contact + $this->checkContactFields($contact['1m']); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 1 {Contacts: 3, 1m, 2}: Confirm the contact + $this->checkContactFields($contact[3]); + $this->getSession()->getPage()->pressButton('Next >'); + + + //*** Test sequence: modify, next, save, load draft, prev, prev, next, next *** + // Page 2 {Contacts: 3, 6, 2}: Select $contact[1] (must first select a different $contact) + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "{$contact[0]['first_name']} {$contact[0]['last_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "{$contact[1]['first_name']} {$contact[1]['last_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->checkContactFields($contact[1]); + + // Page 2 {Contacts: 3, 6, 2}: Modify the job field + $contact['1m'] = $contact[1]; + $contact['1m']['job_title'] = 'MODIFIED JOB TITLE 1A'; + $this->getSession()->getPage()->fillField('Job Title', $contact['1m']['job_title']); + $this->getSession()->getPage()->pressButton('Next >'); + $this->checkContactFields($contact[2]); + + // Page 3 {Contacts: 3, 1m, 2}: Save/load the draft + $this->getSession()->getPage()->pressButton('Save Draft'); + $this->assertSession()->pageTextContains('Submission saved. You may return to this form later and it will restore the current values.'); + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertSession()->pageTextContains('A partially-completed form was found. Please complete the remaining portions.'); + + // Page 3 {Contacts: 3, 1m, 2}: Confirm contact + $this->checkContactFields($contact[2]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 2 {Contacts: 3, 1m, 2}: Confirm modified contact + $this->checkContactFields($contact['1m']); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 1 {Contacts: 3, 1m, 2}: Confirm the job is still modified + $this->checkContactFields($contact[3]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 3, 1m, 2}: Confirm the contact + $this->checkContactFields($contact['1m']); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 3 {Contacts: 3, 1m, 2}: Confirm the job is still modified + $this->checkContactFields($contact[2]); + $this->getSession()->getPage()->pressButton('< Prev'); + + + // Page 2 {Contacts: 3, 6, 2}: Select $contact[4] + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "{$contact[4]['first_name']} {$contact[4]['last_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->checkContactFields($contact[4]); + + // Page 2 {Contacts: 3, 4, 2}: Save draft + $this->getSession()->getPage()->pressButton('Save Draft'); + $this->assertSession()->pageTextContains('Submission saved. You may return to this form later and it will restore the current values.'); + + // Page 2 {Contacts: 3, 4, 2}: Reload form, check still has $contact[4] + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertSession()->pageTextContains('A partially-completed form was found. Please complete the remaining portions.'); + $this->checkContactFields($contact[4]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 3 {Contacts: 3, 4, 2}: Check initial state + $this->checkContactFields($contact[2]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 2 {Contacts: 3, 4, 2}: Check still has $contact[4] + $this->checkContactFields($contact[4]); + + // Page 2 {Contacts: 3, 4, 2}: Create a new contact ($contact[5]) + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "+ Create new +"); + $this->setContactFields($contact[5]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 3 {Contacts: 3, 5, 2}: Check initial state + $this->checkContactFields($contact[2]); + + // Page 3 {Contacts: 3, 5, 2}: create a new contact ($contact[6]) + $this->getSession()->getPage()->selectFieldOption('civicrm_3_contact_1_contact_existing', "+ Create new +"); + $this->setContactFields($contact[6]); + + // Page 3 {Contacts: 3, 5, 6}: Submit + $this->getSession()->getPage()->pressButton('Submit'); + $this->assertPageNoErrorMessages(); + $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); + + // Confirm existing $contact[3] is unchanged, and $contact[5,6] have been created in Civi + foreach ([3,5,6] as $key) { + $result = $this->utils->wf_civicrm_api('Contact', 'get', [ + 'first_name' => $contact[$key]['first_name'], + 'last_name' => $contact[$key]['last_name'], + 'job_title' => $contact[$key]['job_title'], + ]); + $this->assertEquals(0, $result['is_error']); + $this->assertEquals(1, $result['count']); + } + + + //*** Check handling of existing contact with blank required field *** + $this->drupalGet($this->webform->toUrl('canonical')); + + // Page 1 {Contacts: 0, none, 2}: Check initial values. + $this->assertSession()->pageTextContains('You have already submitted this webform. View your previous submission.'); + $this->checkContactFields($contact[0]); + + // Page 1 {Contacts: 0, none, 2}: Select $contact[8] (no last name) + $this->getSession()->getPage()->selectFieldOption('civicrm_1_contact_1_contact_existing', "{$contact[8]['first_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->checkContactFields($contact[8]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 1 {Contacts: 8, none, 2}: Still on Page 1 because Last Name is blank and required + $this->checkContactFields($contact[8]); + $field_valid = $this->getSession()->evaluateScript("document.getElementById('edit-civicrm-1-contact-1-contact-last-name').reportValidity()"); + $this->assertEquals(false, $field_valid, 'Last Name field is not invalid.'); + + $contact['8m'] = $contact[8]; + $contact['8m']['last_name'] = 'CONTACT 8 LAST NAME'; + $this->getSession()->getPage()->fillField('Last Name', $contact['8m']['last_name']); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 8m, none, 2}: Check $contact[7] (null contact) + $this->checkContactFields($contact[7]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 1 {Contacts: 8m, none, 2}: Check $contact[8m] + $this->checkContactFields($contact['8m']); + + + //*** Check Draft Save/Load with blank required field *** + $this->drupalGet($this->webform->toUrl('canonical')); + + // Page 1 {Contacts: 0, none, 2}: Check initial values. + $this->assertSession()->pageTextContains('You have already submitted this webform. View your previous submission.'); + $this->checkContactFields($contact[0]); + + // Page 1 {Contacts: 0, none, 2}: Select $contact[8] (no last name) + $this->getSession()->getPage()->selectFieldOption('civicrm_1_contact_1_contact_existing', "{$contact[8]['first_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->checkContactFields($contact[8]); + $this->getSession()->getPage()->pressButton('Save Draft'); + $this->assertSession()->pageTextContains('Submission saved. You may return to this form later and it will restore the current values.'); + + // Page 1 {Contacts: 8, none, 2}: Reload form, check still has $contact[8] + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertSession()->pageTextContains('A partially-completed form was found. Please complete the remaining portions.'); + $this->checkContactFields($contact[8]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 1 {Contacts: 8, none, 2}: Still on Page 1 because Last Name is blank and required + $this->checkContactFields($contact[8]); + $field_valid = $this->getSession()->evaluateScript("document.getElementById('edit-civicrm-1-contact-1-contact-last-name').reportValidity()"); + $this->assertEquals(false, $field_valid, 'Last Name field is not invalid.'); + + // Page 1 {Contacts: 8, none, 2}: Add last name to $contact[8] + $contact['8m'] = $contact[8]; + $contact['8m']['last_name'] = 'CONTACT 8 LAST NAME'; + $this->getSession()->getPage()->fillField('Last Name', $contact['8m']['last_name']); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 8m, none, 2}: Check $contact[7] (null contact) + $this->checkContactFields($contact[7]); + $this->getSession()->getPage()->pressButton('< Prev'); + + // Page 1 {Contacts: 8m, none, 2}: Check $contact[8m] + $this->checkContactFields($contact['8m']); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 8m, none, 2}: Check $contact[7] (null contact) + $this->checkContactFields($contact[7]); + + // Page 2 {Contacts: 8m, none, 2}: Select $contact[5] + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "{$contact[5]['first_name']} {$contact[5]['last_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 3 {Contacts: 8m, 5, 2}: Check initial state + $this->checkContactFields($contact[2]); + + // Page 3 {Contacts: 8m, 5, 2}: Select $contact[9] and submit + $this->getSession()->getPage()->selectFieldOption('civicrm_3_contact_1_contact_existing', "{$contact[9]['first_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->checkContactFields($contact['9']); + $this->getSession()->getPage()->pressButton('Submit'); + + // Page 3 {Contacts: 8m, 5, 9}: Still on Page 3 because Last Name is blank and required + $this->checkContactFields($contact['9']); + $field_valid = $this->getSession()->evaluateScript("document.getElementById('edit-civicrm-3-contact-1-contact-last-name').reportValidity()"); + $this->assertEquals(false, $field_valid, 'Last Name field is not invalid.'); + + // Page 3 {Contacts: 8m, 5, 9}: Add last name and submit + $contact['9m'] = $contact[9]; + $contact['9m']['last_name'] = 'CONTACT 9 LAST NAME'; + $this->getSession()->getPage()->fillField('Last Name', $contact['9m']['last_name']); + $this->getSession()->getPage()->pressButton('Submit'); + $this->htmlOutput(); + + // Page 3 {Contacts: 8m, 5, 9m}: Confirm submit OK + $this->assertPageNoErrorMessages(); + $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); + + // Confirm existing $contact[5] is unchanged, and $contact[8,9] now have a last name. + foreach (['8m', 5, '9m'] as $key) { + $result = $this->utils->wf_civicrm_api('Contact', 'get', [ + 'first_name' => $contact[$key]['first_name'], + 'last_name' => $contact[$key]['last_name'], + 'job_title' => $contact[$key]['job_title'], + ]); + $this->assertEquals(0, $result['is_error']); + $this->assertEquals(1, $result['count']); + } + + + //*** Check Draft Save/Load, change selected contact, Submit *** + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertPageNoErrorMessages(); + + // Page 1 {Contacts: 0, none, 2}: Check initial values. + $this->checkContactFields($contact[0]); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 2 {Contacts: 0, none, 2}: Check initial values. + $this->checkContactFields($contact[7]); + + // Page 2 {Contacts: 0, none, 2}: Select $contact[5] + $this->getSession()->getPage()->selectFieldOption('civicrm_2_contact_1_contact_existing', "{$contact[5]['first_name']} {$contact[5]['last_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->pressButton('Next >'); + + // Page 3 {Contacts: 0, 5, 2}: Check initial state, select $contact[3], save draft + $this->checkContactFields($contact[2]); + $this->getSession()->getPage()->selectFieldOption('civicrm_3_contact_1_contact_existing', "{$contact[3]['first_name']} {$contact[3]['last_name']}"); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->checkContactFields($contact[3]); + $this->getSession()->getPage()->pressButton('Save Draft'); + $this->checkContactFields($contact[3]); + $this->assertSession()->pageTextContains('Submission saved. You may return to this form later and it will restore the current values.'); + $this->htmlOutput(); + + // Page 3 {Contacts: 0, 5, 3}: Reload form, check still has $contact[3] and submit + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertSession()->pageTextContains('A partially-completed form was found. Please complete the remaining portions.'); + $this->checkContactFields($contact[3]); + $this->getSession()->getPage()->pressButton('Submit'); + $this->assertPageNoErrorMessages(); + $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); + + $submission = WebformSubmission::load($this->getLastSubmissionId($this->webform)); + $sub_data = $submission->getData(); + $this->assertEquals($contact[3]['first_name'], $sub_data['civicrm_3_contact_1_contact_first_name'], 'Submission first name'); + $this->assertEquals($contact[3]['last_name'], $sub_data['civicrm_3_contact_1_contact_last_name'], 'Submission last name'); + $this->assertEquals($contact[3]['job_title'], $sub_data['civicrm_3_contact_1_contact_job_title'], 'Submission job title name'); + } } diff --git a/tests/src/FunctionalJavascript/GrantTest.php b/tests/src/FunctionalJavascript/GrantTest.php index 8356d236d..679947442 100644 --- a/tests/src/FunctionalJavascript/GrantTest.php +++ b/tests/src/FunctionalJavascript/GrantTest.php @@ -13,6 +13,11 @@ */ final class GrantTest extends WebformCivicrmTestBase { + /** + * @var int + */ + private $grant_type_id; + protected function setUp(): void { parent::setUp(); $civicrm_version = $this->utils->wf_crm_apivalues('System', 'get')[0]['version']; diff --git a/tests/src/FunctionalJavascript/GroupsTagsSubmissionTest.php b/tests/src/FunctionalJavascript/GroupsTagsSubmissionTest.php index d4757d390..8202a787f 100644 --- a/tests/src/FunctionalJavascript/GroupsTagsSubmissionTest.php +++ b/tests/src/FunctionalJavascript/GroupsTagsSubmissionTest.php @@ -11,6 +11,9 @@ */ final class GroupsTagsSubmissionTest extends WebformCivicrmTestBase { + private $groups = []; + private $tags = []; + /** * {@inheritdoc} */ @@ -23,6 +26,42 @@ protected function setUp(): void { } } + /** + * Test the display of public groups on webform. + */ + public function testPublicGroups() { + // Make GroupA and GroupB as public + $this->utils->wf_civicrm_api('Group', 'create', [ + 'id' => $this->groups['GroupA'], + 'visibility' => "Public Pages", + ]); + $this->utils->wf_civicrm_api('Group', 'create', [ + 'id' => $this->groups['GroupB'], + 'visibility' => "Public Pages", + ]); + + $this->drupalLogin($this->rootUser); + $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ + 'webform' => $this->webform->id(), + ])); + $this->enableCivicrmOnWebform(); + + // Enable Groups Field and then set it to -User Select (Public Group)- + $this->getSession()->getPage()->selectFieldOption('contact_1_number_of_other', 'Yes'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->selectFieldOption("civicrm_1_contact_1_other_group[]", 'public_groups'); + $this->htmlOutput(); + $this->saveCiviCRMSettings(); + + // Visit the form. + $this->drupalGet($this->webform->toUrl('canonical')); + $this->assertPageNoErrorMessages(); + + $this->assertSession()->pageTextContains('GroupA'); + $this->assertSession()->pageTextContains('GroupB'); + $this->assertSession()->pageTextNotContains('GroupC'); + } + public function testSubmitWebform() { $this->drupalLogin($this->rootUser); $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ @@ -54,11 +93,11 @@ public function testSubmitWebform() { $this->drupalGet($this->webform->toUrl('edit-form')); $this->htmlOutput(); - //Change type of group field to checkbox. + // Change type of group field to checkbox. $this->editCivicrmOptionElement('edit-webform-ui-elements-civicrm-1-contact-1-other-group-operations', FALSE, FALSE, NULL, 'checkboxes'); $majorDonorTagID = $this->utils->wf_civicrm_api('Tag', 'get', [ - 'name' => "Major Donor", + 'name' => (version_compare(\CRM_Core_BAO_Domain::version(), '5.68.alpha1', '<') ? "Major Donor" : "Major_Donor"), ])['id']; // Make Major Donor as the default option. $this->editCivicrmOptionElement('edit-webform-ui-elements-civicrm-1-contact-1-other-tag-operations', TRUE, FALSE, $majorDonorTagID); @@ -110,7 +149,7 @@ public function testSubmitWebform() { $this->assertTrue(in_array($this->groups['GroupB'], $contactGroups)); $this->assertTrue(in_array($this->groups['GroupC'], $contactGroups)); - $this->assertTrue(in_array('Major Donor', $contactTags)); + $this->assertTrue(in_array(version_compare(\CRM_Core_BAO_Domain::version(), '5.68.alpha1', '<') ? "Major Donor" : "Major_Donor", $contactTags)); $this->assertTrue(in_array('Volunteer', $contactTags)); // Ensure option labels are present on result page. @@ -149,11 +188,6 @@ public function testSubmitWebform() { $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); $contactID = $this->utils->wf_civicrm_api('Contact', 'get', $params)['id']; - $contactVal = $this->utils->wf_civicrm_api('Contact', 'get', [ - 'sequential' => 1, - 'return' => ["tag", "group"], - 'contact_id' => $contactID, - ]); $contact = $this->utils->wf_civicrm_api('Contact', 'get', [ 'sequential' => 1, 'return' => ["tag", "group"], @@ -161,7 +195,7 @@ public function testSubmitWebform() { ])['values'][0]; $contactTags = explode(',', $contact['tags']); $contactGroups = explode(',', $contact['groups']); - $this->assertTrue(in_array('Major Donor', $contactTags)); + $this->assertTrue(in_array(version_compare(\CRM_Core_BAO_Domain::version(), '5.68.alpha1', '<') ? "Major Donor" : "Major_Donor", $contactTags)); $this->assertFalse(in_array('Volunteer', $contactTags)); $this->assertTrue(in_array($this->groups['GroupA'], $contactGroups)); diff --git a/tests/src/FunctionalJavascript/LocationTypeTest.php b/tests/src/FunctionalJavascript/LocationTypeTest.php index e5538d428..e22f7e0a5 100644 --- a/tests/src/FunctionalJavascript/LocationTypeTest.php +++ b/tests/src/FunctionalJavascript/LocationTypeTest.php @@ -170,7 +170,11 @@ public function testAddressUpdateUsingChecksum() { 'first_name' => 'Pabst', 'last_name' => 'Anthony', ]); - $address = $this->utils->wf_civicrm_api('Address', 'create', [ + $this->utils->wf_civicrm_api('Email', 'create', [ + 'contact_id' => $contact['id'], + 'email' => "anthony.pabst@example.com", + ]); + $this->utils->wf_civicrm_api('Address', 'create', [ 'contact_id' => $contact['id'], 'location_type_id' => "Home", 'is_primary' => 1, @@ -180,7 +184,23 @@ public function testAddressUpdateUsingChecksum() { 'state_province_id' => "Alberta", 'postal_code' => 11111, ]); - $contact_cs = \CRM_Contact_BAO_Contact_Utils::generateChecksum($contact['id']); + $household = $this->createHousehold([ + 'household_name' => 'Anthony Family', + ]); + // Add relationship b/w the above 2 contacts and ensure + // contact has ability to view the household. + $this->utils->wf_civicrm_api4('Relationship', 'create', [ + 'values' => [ + 'contact_id_a' => $contact['id'], + 'relationship_type_id:name' => 'Household Member of', + 'contact_id_b' => $household['id'], + 'is_permission_a_b' => 2, + ], + ]); + + $contact_cs = $this->utils->wf_civicrm_api4('Contact', 'getChecksum', [ + 'contactId' => $contact['id'] + ], 0)['checksum']; $this->drupalGet($this->webform->toUrl('canonical', ['query' => ['cid1' => $contact['id'], 'cs' => $contact_cs]])); $this->assertPageNoErrorMessages(); @@ -188,9 +208,14 @@ public function testAddressUpdateUsingChecksum() { // Check if name fields are pre populated with existing values. $this->assertSession()->fieldValueEquals('First Name', $contact['first_name']); $this->assertSession()->fieldValueEquals('Last Name', $contact['last_name']); + $this->assertSession()->fieldValueEquals('Email', 'anthony.pabst@example.com'); - // Update the last name + // Verify if relationship contact is loaded on the form. + $this->assertSession()->fieldValueEquals('Household Name', 'Anthony Family'); + + // Update last name & email $this->getSession()->getPage()->fillField('Last Name', 'Morissette'); + $this->getSession()->getPage()->fillField('Email', 'anthony.pabst1@example.com'); $this->getSession()->getPage()->pressButton('Next >'); $this->assertPageNoErrorMessages(); $canada_id = $this->utils->wf_civicrm_api('Country', 'getvalue', [ @@ -210,10 +235,6 @@ public function testAddressUpdateUsingChecksum() { $this->assertSession()->fieldValueEquals('Postal Code', 11111); // Change the street & city value in the address fields. - $address = [ - 'Street Address' => '123 Defence Colony Updated', - 'City' => 'Calgary', - ]; $this->getSession()->getPage()->fillField('Street Address', '123 Defence Colony Updated'); $this->getSession()->getPage()->fillField('City', 'Calgary'); @@ -232,6 +253,7 @@ public function testAddressUpdateUsingChecksum() { $expected_values = [ 'first_name' => 'Pabst', 'last_name' => 'Morissette', + 'email' => 'anthony.pabst1@example.com', 'street_address' => "123 Defence Colony Updated", 'city' => "Calgary", 'country_id' => $canada_id, diff --git a/tests/src/FunctionalJavascript/MembershipSubmissionTest.php b/tests/src/FunctionalJavascript/MembershipSubmissionTest.php index 0287a4421..2427e8ece 100644 --- a/tests/src/FunctionalJavascript/MembershipSubmissionTest.php +++ b/tests/src/FunctionalJavascript/MembershipSubmissionTest.php @@ -122,11 +122,12 @@ public function testSubmitWebform() { $this->saveCiviCRMSettings(); + $adminUserCid = $this->getUFMatchRecord($this->adminUser->id())['contact_id']; // Create two memberships with the same status with the first membership // having an end date after the second membership's end date. $this->utils->wf_civicrm_api('membership', 'create', [ 'membership_type_id' => 'Basic', - 'contact_id' => 2, + 'contact_id' => $adminUserCid, 'join_date' => '08/10/21', 'start_date' => '08/10/21', 'end_date' => '08/10/22', @@ -136,7 +137,7 @@ public function testSubmitWebform() { $this->utils->wf_civicrm_api('membership', 'create', [ 'membership_type_id' => 'Basic', - 'contact_id' => 2, + 'contact_id' => $adminUserCid, 'join_date' => '01/01/21', 'start_date' => '01/01/21', 'end_date' => '01/01/22', @@ -178,7 +179,7 @@ public function testSubmitWebform() { $this->assertEquals($today, $membership['join_date']); $this->assertEquals($today, $membership['start_date']); - $this->assertEquals(date('Y-m-d', strtotime($today. ' +365 days')), $membership['end_date']); + $this->assertEquals(date('Y-m-d', strtotime('+1 year -1 day')), $membership['end_date']); } /** @@ -222,11 +223,13 @@ public function testSubmitMembershipQueryParams() { $fieldset->click(); $this->getSession()->getPage()->fillField('Default value', '[current-page:query:membership]'); $this->getSession()->getPage()->pressButton('Save'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + $this->getSession()->getPage()->pressButton('Save elements'); $this->drupalLogout(); $this->drupalGet($this->webform->toUrl('canonical', ['query' => ['membership' => 2]])); $this->htmlOutput(); - // ToDo -> $this->assertPageNoErrorMessages(); $this->assertSession()->waitForField('First Name'); @@ -235,7 +238,6 @@ public function testSubmitMembershipQueryParams() { $this->assertSession()->pageTextContains('Basic Plus'); $this->getSession()->getPage()->pressButton('Submit'); $this->htmlOutput(); - // ToDo -> $this->assertPageNoErrorMessages(); $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); diff --git a/tests/src/FunctionalJavascript/MultiCustomFieldsSubmissionTest.php b/tests/src/FunctionalJavascript/MultiCustomFieldsSubmissionTest.php index 9da9d6cdd..4261055e3 100644 --- a/tests/src/FunctionalJavascript/MultiCustomFieldsSubmissionTest.php +++ b/tests/src/FunctionalJavascript/MultiCustomFieldsSubmissionTest.php @@ -11,6 +11,27 @@ */ final class MultiCustomFieldsSubmissionTest extends WebformCivicrmTestBase { + /** + * @var int + */ + private $_totalMV; + + /** + * @var array + */ + private $_customFields; + + /** + * @var int + */ + private $_cgID; + + /** + * @var array + */ + private $_contact1; + private $_contact2; + private function createMultiValueCustomFields() { $this->_customFields = []; $params = [ @@ -195,7 +216,6 @@ public function testContactRefSubmission() { $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption("number_of_contacts", $this->_totalMV); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); $this->enableCustomFields(1); @@ -257,7 +277,6 @@ public function testSubmitWebform() { $this->enableCivicrmOnWebform(); $this->getSession()->getPage()->selectFieldOption("number_of_contacts", $this->_totalMV); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); $this->enableCustomFields(1); diff --git a/tests/src/FunctionalJavascript/SaveSettingsTest.php b/tests/src/FunctionalJavascript/SaveSettingsTest.php index b4c8fca2c..af86bbaac 100644 --- a/tests/src/FunctionalJavascript/SaveSettingsTest.php +++ b/tests/src/FunctionalJavascript/SaveSettingsTest.php @@ -60,7 +60,6 @@ function testPaging() { // Ensure no webform parent is added to the source yaml. $this->drupalGet($this->webform->toUrl('edit-form')); $this->getSession()->getPage()->clickLink('Source'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->assertSession()->pageTextNotContains('contact_pagebreak'); $this->assertSession()->pageTextNotContains('webform_parents'); @@ -82,7 +81,6 @@ function testPaging() { $this->assertElementsOnBuildForm($elements); $this->drupalGet($this->webform->toUrl('edit-form')); $this->getSession()->getPage()->clickLink('Source'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->assertSession()->pageTextContains('contact_pagebreak'); } @@ -97,7 +95,6 @@ function testDeleteField() { ])); $this->getSession()->getPage()->selectFieldOption('number_of_contacts', 2); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); $this->getSession()->getPage()->clickLink('Activities'); @@ -113,7 +110,6 @@ function testDeleteField() { // Cancel this action. $this->getSession()->getPage()->pressButton('edit-cancel'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->assertSession()->waitForField('nid'); $this->htmlOutput(); @@ -124,7 +120,6 @@ function testDeleteField() { // Repeat the step and delete activity type element from the page. $this->getSession()->getPage()->selectFieldOption('number_of_contacts', 2); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->htmlOutput(); $this->getSession()->getPage()->clickLink('Activities'); @@ -135,7 +130,6 @@ function testDeleteField() { $this->assertSession()->waitForField('edit-delete'); $this->getSession()->getPage()->pressButton('edit-delete'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->assertSession()->waitForField('nid'); $this->htmlOutput(); diff --git a/tests/src/FunctionalJavascript/StripeTest.php b/tests/src/FunctionalJavascript/StripeTest.php index 3fe7a8b71..400f8e473 100644 --- a/tests/src/FunctionalJavascript/StripeTest.php +++ b/tests/src/FunctionalJavascript/StripeTest.php @@ -10,22 +10,16 @@ * @group webform_civicrm */ final class StripeTest extends WebformCivicrmTestBase { - + protected $failOnJavascriptConsoleErrors = TRUE; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); - $this->setUpExtension('mjwshared,firewall,com.drastikbydesign.stripe'); + $this->setUpExtension('mjwshared,firewall,mjwpaymentapi,com.drastikbydesign.stripe'); - $params = []; - $result = $this->utils->wf_civicrm_api('Stripe', 'setuptest', $params); - $this->paymentProcessorID = $result['id']; - $this->utils->wf_civicrm_api('PaymentProcessor', 'create', [ - 'id' => $this->paymentProcessorID, - 'is_test' => 0, - ]); + $this->paymentProcessorID = $this->createStripeProcessor(); $this->utils->wf_civicrm_api('Setting', 'create', [ 'stripe_nobillingaddress' => 1, @@ -44,6 +38,8 @@ public function testSubmitContribution() { ])); $this->setUpSettings(); + $this->drupalLogout(); + $this->drupalGet($this->webform->toUrl('canonical')); $this->assertPageNoErrorMessages(); $edit = [ @@ -59,12 +55,10 @@ public function testSubmitContribution() { $this->assertSession()->elementExists('css', '#wf-crm-billing-items'); $this->htmlOutput(); $this->assertSession()->elementTextContains('css', '#wf-crm-billing-total', '59.50'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->fillStripeCardWidget(); $this->getSession()->getPage()->pressButton('Submit'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->assertPageNoErrorMessages(); $this->htmlOutput(); @@ -79,6 +73,9 @@ public function testSubmitContribution() { * Test webform submission using stripe processor with AJAX enabled. */ public function testAjaxSubmitContribution() { + // Stripe payment logs a console ajax error. + $this->failOnJavascriptConsoleErrors = FALSE; + $this->drupalLogin($this->adminUser); $this->drupalGet(Url::fromRoute('entity.webform.civicrm', [ 'webform' => $this->webform->id(), @@ -103,12 +100,10 @@ public function testAjaxSubmitContribution() { $this->assertSession()->elementExists('css', '#wf-crm-billing-items'); $this->htmlOutput(); $this->assertSession()->elementTextContains('css', '#wf-crm-billing-total', '59.50'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->fillStripeCardWidget(); $this->getSession()->getPage()->pressButton('Submit'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->assertPageNoErrorMessages(); $this->htmlOutput(); @@ -128,17 +123,13 @@ private function fillStripeCardWidget() { $stripeCardElement = $this->assertSession()->waitForElementVisible('xpath', '//div[contains(@class, "StripeElement")]/div/iframe'); $this->assertNotEmpty($stripeCardElement); $this->getSession()->switchToIFrame($stripeCardElement->getAttribute('name')); - $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->wait(3000); $this->assertSession()->waitForElementVisible('css', 'input[name="cardnumber"]'); $this->getSession()->getPage()->fillField('cardnumber', '4111 1111 1111 1111'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->getPage()->fillField('exp-date', '11 / ' . $expYear); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->getPage()->fillField('cvc', '123'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->getPage()->fillField('postal', '12345'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->switchToIFrame(); } @@ -223,4 +214,29 @@ protected function setUpSettings() { $this->saveCiviCRMSettings(); } + private function createStripeProcessor(): int { + $params = [ + 'name' => 'Stripe', + 'domain_id' => \CRM_Core_Config::domainID(), + 'payment_processor_type_id' => 'Stripe', + 'title' => 'Stripe', + 'is_active' => 1, + 'is_default' => 0, + 'is_test' => 0, + 'is_recur' => 1, + 'user_name' => \CRM_Utils_Constant::value('STRIPE_PK_TEST', 'pk_test_PNlMrGPvqOxwLK6Y3A9B2EFn'), + 'password' => \CRM_Utils_Constant::value('STRIPE_SK_TEST', 'sk_test_WHbZbmFH97YpY2y4OpVfry9W'), + 'url_site' => 'https://api.stripe.com/v1', + 'url_recur' => 'https://api.stripe.com/v1', + 'class_name' => 'Payment_Stripe', + 'billing_mode' => 1 + ]; + // First see if it already exists. + $result = $this->utils->wf_civicrm_api('PaymentProcessor', 'get', $params); + if ($result['count'] != 1) { + $result = $this->utils->wf_civicrm_api('PaymentProcessor', 'create', $params); + } + return $result['id']; + } + } diff --git a/tests/src/FunctionalJavascript/WebformCivicrmTestBase.php b/tests/src/FunctionalJavascript/WebformCivicrmTestBase.php index 246846b54..40dd7d41d 100644 --- a/tests/src/FunctionalJavascript/WebformCivicrmTestBase.php +++ b/tests/src/FunctionalJavascript/WebformCivicrmTestBase.php @@ -13,6 +13,7 @@ abstract class WebformCivicrmTestBase extends CiviCrmTestBase { use WebformBrowserTestTrait; use \Drupal\Tests\mink_civicrm_helpers\Traits\Utils; + use \Drupal\Tests\system\Traits\OffCanvasTestTrait; /** * {@inheritdoc} @@ -23,6 +24,7 @@ abstract class WebformCivicrmTestBase extends CiviCrmTestBase { 'webform_civicrm', 'token', 'ckeditor5', + 'off_canvas_test', 'mink_civicrm_helpers', ]; @@ -54,6 +56,16 @@ abstract class WebformCivicrmTestBase extends CiviCrmTestBase { */ protected $adminUser; + /** + * @var \Drupal\webform_civicrm\UtilsInterface + */ + protected $utils; + + /** + * @var int + */ + protected $rootUserCid; + /** * {@inheritdoc} */ @@ -88,13 +100,18 @@ protected function setUp(): void { 'id' => 'civicrm_webform_test', 'title' => 'CiviCRM Webform Test.' . $CiviCRM_version, ]); - $this->rootUserCid = $this->createIndividual()['id']; - // Create CiviCRM contact for rootUser. - $this->utils->wf_civicrm_api('UFMatch', 'create', [ - 'uf_id' => $this->rootUser->id(), - 'uf_name' => $this->rootUser->getAccountName(), - 'contact_id' => $this->rootUserCid, - ]); + if (version_compare(\CRM_Core_BAO_Domain::version(), '5.79.alpha1', '<')) { + $this->rootUserCid = $this->createIndividual()['id']; + // Create CiviCRM contact for rootUser. + $this->utils->wf_civicrm_api('UFMatch', 'create', [ + 'uf_id' => $this->rootUser->id(), + 'uf_name' => $this->rootUser->getAccountName(), + 'contact_id' => $this->rootUserCid, + ]); + } + else { + $this->rootUserCid = $this->getUFMatchRecord($this->rootUser->id())['contact_id']; + } } protected function tearDown(): void { @@ -119,7 +136,6 @@ public function redirectEmailsToDB() { $this->getSession()->getPage()->selectFieldOption('outBound_option', 5); $this->getSession()->getPage()->pressButton('_qf_Smtp_next'); - $this->assertSession()->assertWaitOnAjaxRequest(); } /** @@ -174,7 +190,7 @@ protected function setupSalesTax(int $financialTypeId, $accountParams = [], $tax 'tax_rate' => $tax_rate, 'is_active' => 1, ], $accountParams); - $account = \CRM_Financial_BAO_FinancialAccount::add($params); + $account = \CRM_Financial_BAO_FinancialAccount::writeRecord($params); $entityParams = [ 'entity_table' => 'civicrm_financial_type', 'entity_id' => $financialTypeId, @@ -234,6 +250,10 @@ protected function configureContributionTab($params = []) { if (!empty($params['payment_processor_id'])) { $this->getSession()->getPage()->selectFieldOption('Payment Processor', $params['payment_processor_id']); } + if (!empty($params['soft'])) { + $this->getSession()->getPage()->selectFieldOption('Soft Credit To', $params['soft']); + $this->getSession()->getPage()->selectFieldOption('Soft Credit Type', $params['soft_credit_type_id']); + } if (!empty($params['receipt'])) { $this->getSession()->getPage()->selectFieldOption('Enable Receipt?', 'Yes'); @@ -333,11 +353,13 @@ public function assertFieldValue($selector, $value, $isRadio = FALSE) { * TRUE if only one option is enabled on the element. * @param string $asList * TRUE if element need to be rendered as select element. + * @param string $secondarySelector + * optional secondary selector */ - protected function editCivicrmOptionElement($selector, $multiple = TRUE, $enableStatic = FALSE, $default = NULL, $type = NULL, $singleOption = FALSE, $asList = FALSE) { - $checkbox_edit_button = $this->assertSession()->elementExists('css', '[data-drupal-selector="' . $selector . '"] a.webform-ajax-link'); + protected function editCivicrmOptionElement($selector, $multiple = TRUE, $enableStatic = FALSE, $default = NULL, $type = NULL, $singleOption = FALSE, $asList = FALSE, $secondarySelector = 'li.edit') { + $checkbox_edit_button = $this->assertSession()->elementExists('css', '[data-drupal-selector="' . $selector . '"] ' . ($secondarySelector ? "$secondarySelector " : '') . 'a.webform-ajax-link'); $checkbox_edit_button->click(); - $this->assertSession()->waitForField('drupal-off-canvas'); + $this->waitForOffCanvasArea(); $this->htmlOutput(); if ($type) { $this->assertSession()->elementExists('css', '[data-drupal-selector="edit-change-type"]')->click(); @@ -423,7 +445,6 @@ public function enableCivicrmOnWebform() { $this->htmlOutput(); $this->getSession()->getPage()->checkField('nid'); $this->getSession()->getPage()->selectFieldOption('1_contact_type', 'individual'); - $this->assertSession()->assertWaitOnAjaxRequest(); } /** @@ -504,6 +525,9 @@ protected function editContactElement($params) { if (!empty($params['hide_fields'])) { $this->getSession()->getPage()->selectFieldOption('properties[hide_fields][]', $params['hide_fields']); } + if (!empty($params['hide_method'])) { + $this->getSession()->getPage()->selectFieldOption('properties[hide_method]', $params['hide_method']); + } if (!empty($params['submit_disabled'])) { $this->getSession()->getPage()->checkField("properties[submit_disabled]"); } @@ -540,6 +564,10 @@ protected function editContactElement($params) { $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->getPage()->selectFieldOption('Set default contact from', $params['default']); + if ($params['default'] == 'Specified Contact') { + $this->getSession()->getPage()->fillField('default-contact-id', $params['default_contact_id']); + } + if ($params['default'] == 'relationship') { $this->getSession()->getPage()->selectFieldOption('properties[default_relationship_to]', $params['default_relationship']['default_relationship_to']); $this->assertSession()->assertWaitOnAjaxRequest(); @@ -569,6 +597,13 @@ protected function editContactElement($params) { $this->getSession()->getPage()->checkField('properties[required]'); } + // Wait for ajax message from previous click of Save button to no longer be + // visible to avoid falling through the following waitForElementVisible + // prematurely. + do { + $ajax_message_visible = $this->assertSession()->waitForElementVisible('css', '.webform-ajax-messages', 100); + } while ($ajax_message_visible); + $this->getSession()->getPage()->pressButton('Save'); $this->assertSession()->waitForElementVisible('css', '.webform-ajax-messages'); } @@ -680,7 +715,7 @@ protected function fillBillingFields($params) { $this->getSession()->getPage()->fillField('City', $params['city']); $this->getSession()->getPage()->selectFieldOption('Country', $params['country']); - $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->wait(1000); $this->getSession()->getPage()->selectFieldOption('State/Province', $params['state_province']); $this->getSession()->getPage()->fillField('Postal Code', $params['postal_code']); @@ -695,7 +730,6 @@ protected function fillBillingFields($params) { protected function fillCardAndSubmit($billingValues = []) { if (!empty($billingValues)) { $this->getSession()->getPage()->checkField("civicrm_1_contribution_1_contribution_billing_address_same_as"); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->wait(1000); // Verify populated values for billing fields. @@ -727,7 +761,7 @@ protected function fillCardAndSubmit($billingValues = []) { $this->getSession()->getPage()->pressButton('Submit'); $this->htmlOutput(); $this->assertPageNoErrorMessages(); - $this->assertSession()->pageTextContains('New submission added to CiviCRM Webform Test.'); + $this->assertSession()->waitForText('New submission added to CiviCRM Webform Test.'); } /** @@ -752,18 +786,13 @@ public function fillCKEditor($locator, $value) { } // Fill value on the wysiwyg editor. - if (version_compare(\Drupal::VERSION, '10', '>=')) { - $this->getSession()->executeScript(" - const element = document.getElementById(\"$fieldId\"); - const editor = Drupal.CKEditor5Instances.get( - element.getAttribute('data-ckeditor5-id'), - ); - editor.setData(\"$value\"); - "); - } - else { - $this->getSession()->executeScript("CKEDITOR.instances[\"$fieldId\"].setData(\"$value\");"); - } + $this->getSession()->executeScript(" + const element = document.getElementById(\"$fieldId\"); + const editor = Drupal.CKEditor5Instances.get( + element.getAttribute('data-ckeditor5-id'), + ); + editor.setData(\"$value\"); + "); } /** @@ -778,10 +807,8 @@ protected function addEmailHandler($params) { } $this->getSession()->getPage()->selectFieldOption('edit-settings-body', '_other_'); - $this->assertSession()->assertWaitOnAjaxRequest(); $this->fillCKEditor('settings[body_custom_html][value]', $params['body']); $this->getSession()->getPage()->pressButton('Save'); - $this->assertSession()->assertWaitOnAjaxRequest(); } /** diff --git a/tests/src/Kernel/FieldOptionsTest.php b/tests/src/Kernel/FieldOptionsTest.php deleted file mode 100644 index b5fbf7f8a..000000000 --- a/tests/src/Kernel/FieldOptionsTest.php +++ /dev/null @@ -1,67 +0,0 @@ -getDatabaseConnectionInfo()['default']); - - } - - protected function setUp(): void { - $this->markTestSkipped('Requires MySQL'); - parent::setUp(); - - module_load_install('civicrm'); - civicrm_install(); - - $this->container->get('civicrm')->initialize(); - } - - /** - * {@inheritdoc} - */ - protected function tearDown(): void { - $conn = Database::getConnection('default', 'civicrm_test'); - $database = $conn->getConnectionOptions()['database']; - // Todo: get this working when db name passed in as an argument. - $conn->query("DROP DATABASE $database"); - $conn->destroy(); - parent::tearDown(); - } - - /** - * @dataProvider getDataprovider - */ - public function testGet(array $field, string $context, array $data) { - $field_options = $this->container->get('webform_civicrm.field_options'); - $options = $field_options->get($field, $context, $data); - } - - public function getDataprovider() { - yield [ - ['form_key' => 'civicrm_1_contact_1_email_email'], - 'live_options', - [] - ]; - } - -} diff --git a/tests/src/Unit/UtilsTest.php b/tests/src/Unit/UtilsTest.php index 95343f5c5..3313c388b 100644 --- a/tests/src/Unit/UtilsTest.php +++ b/tests/src/Unit/UtilsTest.php @@ -5,6 +5,7 @@ use Drupal\Tests\UnitTestCase; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\webform_civicrm\Utils; +use Symfony\Component\HttpFoundation\RequestStack; /** * @group webform_civicrm @@ -12,8 +13,11 @@ class UtilsTest extends UnitTestCase { public function testWfCrmExplodeKey() { - $utils = new Utils(); + $requestStack = new RequestStack(); $container = new ContainerBuilder(); + $container->set('request_stack', $requestStack); + $utils = new Utils($requestStack); + // Set the container for Drupal::service to work correctly. \Drupal::setContainer($container); $container->set('webform_civicrm.utils', $utils); diff --git a/webform_civicrm.info.yml b/webform_civicrm.info.yml index 410e05606..be24eb01b 100644 --- a/webform_civicrm.info.yml +++ b/webform_civicrm.info.yml @@ -5,4 +5,3 @@ core_version_requirement: ^9.4 || ^10 package: 'CiviCRM' dependencies: - drupal:webform - - civicrm diff --git a/webform_civicrm.install b/webform_civicrm.install index 26654d456..70e8f8290 100644 --- a/webform_civicrm.install +++ b/webform_civicrm.install @@ -397,7 +397,7 @@ function webform_civicrm_update_8006() { } } } - // Update data key in settings to have the correct case for subtype. + // Update data key in settings to have the correct case for subtype. $data = &$settings['data']; foreach ($data['contact'] as $key => &$contact) { if (!empty($contact['contact'][1]['contact_sub_type'])) { @@ -417,3 +417,29 @@ function webform_civicrm_update_8006() { } } } + +/** + * Fix old soft credit data. + */ +function webform_civicrm_update_8007() { + \Drupal::service('civicrm')->initialize(); + $webforms = Webform::loadMultiple(); + foreach ($webforms as $webform) { + $handler = $webform->getHandlers('webform_civicrm'); + $config = $handler->getConfiguration(); + if (empty($config['webform_civicrm'])) { + continue; + } + $data = &$config['webform_civicrm']['settings']['data']; + if (empty($data['contribution'][1]['contribution'][1])) { + continue; + } + $contribution = $data['contribution'][1]['contribution'][1]; + if (!empty($contribution['honor_contact_id']) && !empty($contribution['honor_type_id'])) { + $data['contribution'][1]['contribution'][1]['soft'][$contribution['honor_contact_id']] = $contribution['honor_contact_id']; + $data['contribution'][1]['contribution'][1]['soft_credit_type_id'] = $contribution['honor_type_id']; + $handler->setConfiguration($config); + $webform->save(); + } + } +} diff --git a/webform_civicrm.module b/webform_civicrm.module index 645ab9e9c..509ca53b9 100644 --- a/webform_civicrm.module +++ b/webform_civicrm.module @@ -171,7 +171,9 @@ function webform_civicrm_theme() { /** * Implements hook_entity_load() - * Display labels for civicrm option element. + * Display entity links on submission page. + * + * @param array $entities */ function webform_civicrm_webform_submission_load($entities) { foreach ($entities as $entity) { @@ -198,42 +200,44 @@ function _fillCiviCRMData($data, $webformSubmission) { } \Drupal::service('civicrm')->initialize(); $utils = \Drupal::service('webform_civicrm.utils'); - foreach ($data as $key => $val) { - $element = $webform->getElement($key); - if ($element && !empty($val) && $element['#type'] == 'civicrm_options') { - if (!empty($element['#webform_multiple'])) { - foreach ($val as $k => $v) { - if (isset($element['#options'][$v])) { - $data[$key]["{$k}_raw"] = $data[$key][$k]; - $data[$key][$k] = $element['#options'][$v]; + if (\Drupal::routeMatch()->getRouteName() == 'entity.webform.results_submissions') { + foreach ($data as $key => $val) { + $element = $webform->getElement($key); + if ($element && !empty($val) && $element['#type'] == 'civicrm_options') { + if (!empty($element['#webform_multiple'])) { + foreach ($val as $k => $v) { + if (isset($element['#options'][$v])) { + $data[$key]["{$k}_raw"] = $data[$key][$k]; + $data[$key][$k] = $element['#options'][$v]; + } } } - } - elseif (isset($element['#options'][$val])) { - $data["{$key}_raw"] = $data[$key]; - $data[$key] = $element['#options'][$val]; - } - elseif (strpos($key, 'state_province_id') !== false) { - $country_key = str_replace('state_province_id', 'country_id', $key); - $country_id = $data["{$country_key}_raw"] ?? $data[$country_key] ?? NULL; - $params = [ - 'sequential' => 1, - 'country_id' => $country_id, - ]; - $is_abbr = $utils->wf_civicrm_api4('StateProvince', 'get', [ - 'select' => ['row_count'], - 'where' => [['abbreviation', '=', $val]], - ])->count() > 0; - if (is_numeric($val)) { - $params['id'] = $val; + elseif (isset($element['#options'][$val])) { + $data["{$key}_raw"] = $data[$key]; + $data[$key] = $element['#options'][$val]; } - elseif ($is_abbr) { - $params['abbreviation'] = $val; - } - else { - continue; + elseif (strpos($key, 'state_province_id') !== false) { + $country_key = str_replace('state_province_id', 'country_id', $key); + $country_id = $data["{$country_key}_raw"] ?? $data[$country_key] ?? NULL; + $params = [ + 'sequential' => 1, + 'country_id' => $country_id, + ]; + $is_abbr = $utils->wf_civicrm_api4('StateProvince', 'get', [ + 'select' => ['row_count'], + 'where' => [['abbreviation', '=', $val]], + ])->count() > 0; + if (is_numeric($val)) { + $params['id'] = $val; + } + elseif ($is_abbr) { + $params['abbreviation'] = $val; + } + else { + continue; + } + $data[$key] = $utils->wf_crm_apivalues('StateProvince', 'get', $params, 'name')[0] ?? $data[$key]; } - $data[$key] = $utils->wf_crm_apivalues('StateProvince', 'get', $params, 'name')[0] ?? $data[$key]; } } } diff --git a/webform_civicrm.services.yml b/webform_civicrm.services.yml index a63fa6391..8bd0e6fde 100644 --- a/webform_civicrm.services.yml +++ b/webform_civicrm.services.yml @@ -1,6 +1,7 @@ services: webform_civicrm.utils: class: Drupal\webform_civicrm\Utils + arguments: ['@request_stack'] webform_civicrm.fields: class: Drupal\webform_civicrm\Fields arguments: ['@webform_civicrm.utils']