Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #26: support recurring contributions. #37

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions api/v3/RemoteFormContributionPage/Submit.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -77,13 +78,82 @@ 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'],
'payment_processor' => $ppid,
);
}

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(
Expand Down
110 changes: 103 additions & 7 deletions remoteform.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 <div> element containing fields related to recurring contribution options.
*
*/
function createRecurringFieldsDiv(fields) {
// Create 'recur' checkbox; code borrowed heavily from createCheckboxesOrRadios,
// but without wrapping everyithing in <div>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
*
Expand Down Expand Up @@ -1157,7 +1253,7 @@ function remoteForm(config) {
}

optionInput.value = optionId;

// Create the label.
var optionLabel = document.createElement('label');
optionLabel.for = optionInput.id;
Expand All @@ -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;

Expand Down
4 changes: 4 additions & 0 deletions remoteform.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down