From 29f207fd55d07765e7cc50b4c009f0ed156a6e42 Mon Sep 17 00:00:00 2001 From: Allen Shaw Date: Tue, 29 Sep 2020 13:41:11 -0500 Subject: [PATCH] Fix #26: support recurring contributions. --- api/v3/RemoteFormContributionPage/Submit.php | 70 ++++++++++++ remoteform.js | 110 +++++++++++++++++-- remoteform.php | 4 + 3 files changed, 177 insertions(+), 7 deletions(-) diff --git a/api/v3/RemoteFormContributionPage/Submit.php b/api/v3/RemoteFormContributionPage/Submit.php index 5844f52..8cfe2a1 100644 --- a/api/v3/RemoteFormContributionPage/Submit.php +++ b/api/v3/RemoteFormContributionPage/Submit.php @@ -16,6 +16,7 @@ function _civicrm_api3_remote_form_contribution_page_Submit_spec(&$params, $apir _rf_add_page_details($contribution_page_id, $params, $test_mode); _rf_add_profile_fields($contribution_page_id, $params); _rf_add_price_fields($contribution_page_id, $params, $params['control']['currency']); + _rf_add_recur_fields($contribution_page_id, $params); // How do we handle credit card fields? Some processors may want to insert their // own. $cc_fields = _rf_include_credit_card_fields($contribution_page_id); @@ -77,6 +78,10 @@ function _rf_add_page_details($id, &$params, $test_mode = FALSE) { ); $params['control'] = array( 'is_active' => $values['is_active'], + 'is_recur' => $values['is_recur'], + 'recur_frequency_unit' => $values['recur_frequency_unit'], + 'is_recur_interval' => $values['is_recur_interval'], + 'is_recur_installments' => $values['is_recur_installments'], 'start_date' => $values['start_date'], 'currency' => $values['currency'], 'min_amount' => $values['min_amount'], @@ -84,6 +89,71 @@ function _rf_add_page_details($id, &$params, $test_mode = FALSE) { ); } +function _rf_add_recur_fields($id, &$params) { + if ($params['control']['is_recur']) { + $params['is_recur'] = array ( + 'name' => 'is_recur', + 'title' => E::ts('I want to contribute this amount every'), + 'entity' => 'ContributionPage', + 'html_type' => 'checkbox', + ); + + if ($params['control']['is_recur_interval']) { + $params['frequency_interval'] = array ( + 'name' => 'frequency_interval', + 'entity' => 'ContributionPage', + 'html_type' => 'text', + ); + $pluralizeUnits = TRUE; + } + else { + $params['frequency_interval'] = array ( + 'name' => 'frequency_interval', + 'entity' => 'ContributionPage', + 'html_type' => 'hidden', + 'defaul' => '1', + ); + $pluralizeUnits = FALSE; + } + + // Build frequency units array, pluralizing as needed. + $frequencyUnitOptions = []; + foreach ((array)$params['control']['recur_frequency_unit'] as $frequencyUnit) { + $label = $frequencyUnit; + if ($pluralizeUnits) { + $label .= '(s)'; + } + $frequencyUnitOptions[$frequencyUnit] = $label; + } + if (count($frequencyUnitOptions) > 1) { + $params['frequency_unit'] = array ( + 'name' => 'frequency_unit', + 'title' => E::ts('Repeat every'), + 'entity' => 'ContributionPage', + 'html_type' => 'select', + 'options' => $frequencyUnitOptions, + 'default' => 'week', + ); + } + else { + $params['frequency_unit'] = array ( + 'name' => 'frequency_unit', + 'entity' => 'ContributionPage', + 'html_type' => 'hidden', + 'default' => array_shift($frequencyUnitOptions), + ); + } + + if ($params['control']['is_recur_installments']) { + $params['installments'] = array ( + 'name' => 'installments', + 'entity' => 'ContributionPage', + 'html_type' => 'text', + ); + } + } +} + function _rf_add_profile_fields($id, &$params) { // Now get profile fields. $result = civicrm_api3('UFJoin', 'get', array( diff --git a/remoteform.js b/remoteform.js index 47b1fd5..22c2e9b 100644 --- a/remoteform.js +++ b/remoteform.js @@ -717,13 +717,26 @@ function remoteForm(config) { function buildForm(fields) { for (var key in fields) { if (fields.hasOwnProperty(key)) { - var def = fields[key]; - if (!def.entity) { + if (key == 'is_recur') { + var html = createRecurringFieldsDiv(fields, createField, wrapField); + } + else if ( + key == 'frequency_unit' + || key == 'frequency_interval' + || key == 'installments' + ) { + // These are all processed in createRecurringFields(), so skip them here. continue; } - var field; - var type = getType(def); - var html = cfg.createFieldDivFunc(key, def, type, createField, wrapField); + else { + var def = fields[key]; + if (!def.entity) { + continue; + } + var field; + var type = getType(def); + var html = cfg.createFieldDivFunc(key, def, type, createField, wrapField); + } if (html) { form.appendChild(html); } @@ -834,6 +847,89 @@ function remoteForm(config) { return wrapFieldFunc(key, def, field); } + /** + * ### createRecurringFieldsDiv + * + * ```createRecurringFieldsDiv(key, def, type, createFieldFunc, wrapFieldFunc)``` + * + * Build the form elements related to recurring contributions, if the contribution + * page is configured for such. Unlike other fields in RemoteForm displays, + * this collection of fields appears in a running line; therefore it needs + * special formatting, which this function provides. + * + * #### Parameters: + * + * - fields Array of form fields, as passed to buildForm() + * + * #### Returns: + * + * A
element containing fields related to recurring contribution options. + * + */ + function createRecurringFieldsDiv(fields) { + // Create 'recur' checkbox; code borrowed heavily from createCheckboxesOrRadios, + // but without wrapping everyithing in
s. + var optionId = '1'; + var def = fields.is_recur; + var optionInput = document.createElement('input'); + optionInput.type = 'checkbox'; + // We set an id so the label below can properly reference the right + // input. + optionInput.id = def.name + '-' + optionId; + optionInput.className = cfg.css.checkInput; + // We use the name field to find the values when we submit. This + // value has to be unique (in case we have multiple pricesets). + optionInput.name = 'is_recur'; + optionInput.value = optionId; + + var optionLabel = document.createElement('label'); + optionLabel.for = optionInput.id; + optionLabel.innerHTML = def.title; + optionLabel.className = cfg.css.checkLabel; + + var div = document.createElement('div'); + div.appendChild(optionInput); + div.appendChild(optionLabel); + + // Create 'interval' field; this will be a hidden field with value of '1' if + // 'support recurring intervals' is not enabled. Otherwise, it's a text input. + var intervalFieldType = getType(fields.frequency_interval); + if (intervalFieldType != 'hidden') { + var intervalField = createField('frequency_interval', fields.frequency_interval, intervalFieldType); + // Set styles so that this field is not 99% width. + intervalField.setAttribute('style', 'width:5em; margin: 0 .5em;'); + div.appendChild(intervalField); + } + + // Create a span element to hold remaining 'recur' fields and text. + var span = document.createElement('span'); + + // Create 'recurring unit' field; this will be a hidden field with a specific + // value if only one value is configured in 'support recurring units'; otherwise + // it's a select. + var unitFieldType = getType(fields.frequency_unit); + if (unitFieldType != 'hidden') { + var unitField = createField('frequency_unit', fields.frequency_unit, unitFieldType); + unitField.setAttribute('style', 'margin: 0 .5em;'); + div.appendChild(unitField); + } + else { + // If this is as hidden field, it means we only have one option for recurring + // units, so we should show that option as plain text. + span.innerHTML += ' ' + fields.frequency_unit.default; + } + + // Add 'installments' field, if so configured. + if (fields.installments !== undefined) { + var installmentsField = createField('installments', fields.installments, getType(fields.installments)); + installmentsField.setAttribute('style', 'width:5em; margin: 0 .5em;'); + span.innerHTML += 'for ' + installmentsField.outerHTML + ' installments'; + } + div.appendChild(span); + + return div; + } + /** * ### getType * @@ -1157,7 +1253,7 @@ function remoteForm(config) { } optionInput.value = optionId; - + // Create the label. var optionLabel = document.createElement('label'); optionLabel.for = optionInput.id; @@ -1166,7 +1262,7 @@ function remoteForm(config) { // options = [ { key: label }, { key: label} ] and also // complex options (used for price sets) which have more data: // options = [ {key: { label: label, amount: amount, name: name}, etc. - + optionLabel.innerHTML = optionDisplay; optionLabel.className = cfg.css.checkLabel; diff --git a/remoteform.php b/remoteform.php index 43ecd52..72524bf 100644 --- a/remoteform.php +++ b/remoteform.php @@ -377,6 +377,10 @@ function remoteform_get_contribution_page_details($id) { 'intro_text', 'thankyou_text', 'is_active', + 'is_recur', + 'recur_frequency_unit', + 'is_recur_interval', + 'is_recur_installments', 'start_date', 'currency', 'min_amount',