Skip to content
This repository has been archived by the owner on Jun 25, 2021. It is now read-only.

Commit

Permalink
Add settings page, include forceRecurring, implement forceRecurring fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich Lott / Artful Robot committed Nov 18, 2020
1 parent eddc0b2 commit 0fd2d59
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 94 deletions.
19 changes: 0 additions & 19 deletions CRM/GoCardless/Page/WebhookHelper.php

This file was deleted.

88 changes: 88 additions & 0 deletions CRM/GoCardlessUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,92 @@ public static function completeRedirectFlowCiviCore($deets) {
*/
public static function loadLibraries() {}

/**
* Get our specific settings.
*
* @return Array
*/
public static function getSettings() {
$json = Civi::settings()->get('gocardless', NULL);
if ($json) {
$settings = json_decode($json, TRUE);
}
if (empty($settings)) {
$settings = [];
}
$defaults = [
'forceRecurring' => FALSE,
];
// Ensure defaults
$settings += $defaults;

return $settings;
}
/**
* The thank you page is the page the redirect flow redirects to.
*
* We validate some stuff we need then pas on to completeRedirectFlowCiviCore()
*
* Complete a GoCardless redirect flow before we present the thank you page.
*
* - call GC API to complete the mandate.
* - find details of the contribution: how much, how often, day of month, 'name'
* - set up a GC Subscription.
* - set trxn_id to the subscription ID in the contribution table.
* - if recurring: set trxn_id, "In Progress", start date in contribution_recur table.
* - if membership: set membership end date to start date + interval.
*/
public static function handleContributeFormThanks() {
// We have a redirect_flow_id.
$redirect_flow_id = $_GET['redirect_flow_id'];
$sesh = CRM_Core_Session::singleton();
$sesh_store = $sesh->get('redirect_flows', 'GoCardless');
if (empty($sesh_store[$redirect_flow_id])) {
// When would this happen?
// - Back button.
// - Hacks?
// - Something else that lost the session.
//
// Anyway, in all cases let's assume that we are unable to proceed.
CRM_Core_Error::fatal('Sorry there was an error setting up your Direct Debit. This could be caused by your browser not allowing cookies.');
return;
}

// Validate the session_token.
if (empty($_GET['qfKey']) || empty($sesh_store[$redirect_flow_id]['session_token'])
|| $_GET['qfKey'] != $sesh_store[$redirect_flow_id]['session_token']) {

// @todo throw something that generates a server error 500 page.
CRM_Core_Error::fatal('Sorry, the session tokens did not match. This should not happen.');
return;
}

// Complete the redirect flow with GC.
try {
$params = [ 'redirect_flow_id' => $redirect_flow_id ] + $sesh_store[$redirect_flow_id];
CRM_GoCardlessUtils::completeRedirectFlowCiviCore($params);
}
catch (Exception $e) {
CRM_Core_Error::fatal('Sorry there was an error setting up your Direct Debit. Please contact us so we can look into what went wrong.');
}
}
/**
* Do our forceRecurring magic, if configured to do so.
*/
public static function handleContributeFormHacks() {
$settings = CRM_GoCardlessUtils::getSettings();
if ($settings['forceRecurring']) {
// Get GC processor IDs.
$paymentProcessorsIDs = \Civi\Api4\PaymentProcessor::get(FALSE)
->addSelect('id')
->addWhere('payment_processor_type_id:name', '=', 'GoCardless')
->addWhere('is_active', '=', TRUE)
->addWhere('is_test', 'IS NOT NULL')
->execute()
->column('id');
$js = file_get_contents(E::path('js/gcform.js'));
$js = str_replace('var goCardlessProcessorIDs = [];', 'var goCardlessProcessorIDs = ' . json_encode($paymentProcessorsIDs) . ';', $js);
CRM_Core_Region::instance('page-body')->add(['markup' => "<script>$js</script>"]);
}
}
}
23 changes: 23 additions & 0 deletions ang/gocardless.ang.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
// This file declares an Angular module which can be autoloaded
// in CiviCRM. See also:
// \https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules/n
return [
'js' => [
'ang/gocardless.js',
'ang/gocardless/*.js',
'ang/gocardless/*/*.js',
],
'css' => [
'ang/gocardless.css',
],
'partials' => [
'ang/gocardless',
],
'requires' => [
'crmUi',
'crmUtil',
'ngRoute',
],
'settings' => [],
];
1 change: 1 addition & 0 deletions ang/gocardless.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Add any CSS rules for Angular module "gocardless" */
4 changes: 4 additions & 0 deletions ang/gocardless.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(function(angular, $, _) {
// Declare a list of dependencies.
angular.module('gocardless', CRM.angRequires('gocardless'));
})(angular, CRM.$, CRM._);
58 changes: 58 additions & 0 deletions ang/gocardless/GoCardlessSettings.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<div class="crm-container">
<h1 crm-page-title>{{ts('GoCardless settings')}}</h1>

<form name="myForm" crm-ui-id-scope>
<!--
<div class="help">
<p>{{ts('This is an auto-generated skeletal page.')}}</p>
<p>{{ts('To view more debugging information, edit the URL and include "?angularDebug=1".')}}</p>
</div>
<div crm-ui-accordion="{title: ts('Greeting')}">
<p ng-show="$ctrl.myContact.first_name">{{ ts('Hello, %1.', {1: $ctrl.myContact.first_name}) }}</p>
<p ng-show="!$ctrl.myContact.first_name">{{ ts('Hello, stranger.') }}</p>
</div>
-->
<div class="crm-block">
<div class="crm-group">
<label>
<input
type="checkbox"
crm-ui-id="myForm.forceRecurring"
name="forceRecurring"
ng-model="gcSettings.forceRecurring"
class="crm-form-checkbox"
/>
&nbsp; {{ts('Force recurring contributions when a GoCardless payment processor is chosen')}}
</label>
</div>
</div>

<div>
<button ng-click="$ctrl.save()">{{ts('Save')}}</button>
</div>
<h2>Active Payment processors</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Test Webhook endpoint URL</th>
<th>Live Webhook endpoint URL</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="pp in ppTable" >
<td>{{pp.name}}</td>
<td><input type="text" value="{{pp.urlTest}}"/></td>
<td><input type="text" value="{{pp.urlLive}}"/></td>
</tr>
</tbody>
</table>
</div>

</form>

</div>
93 changes: 93 additions & 0 deletions ang/gocardless/GoCardlessSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
(function(angular, $, _) {

angular.module('gocardless').config(function($routeProvider) {
$routeProvider.when('/gocardless', {
controller: 'GocardlessGoCardlessSettings',
controllerAs: '$ctrl',
templateUrl: '~/gocardless/GoCardlessSettings.html',

// If you need to look up data when opening the page, list it out
// under "resolve".
resolve: {
various: function(crmApi4) {
return crmApi4({
gcSettings: ['Setting', 'get', {select: ['gocardless']}, 0],
paymentProcessors: ['PaymentProcessor', 'get', {
where: [
["payment_processor_type_id:name", '=', 'GoCardless'],
["is_test", 'IS NOT NULL'], // api4 seems to skip test ones otherwise
],
orderBy: {
is_active: 'DESC',
name: 'ASC'
}
}],
});
},
}
});
}
);

// The controller uses *injection*. This default injects a few things:
// $scope -- This is the set of variables shared between JS and HTML.
// crmApi, crmStatus, crmUiHelp -- These are services provided by civicrm-core.
angular.module('gocardless').controller('GocardlessGoCardlessSettings', function($scope, crmApi4, crmStatus, crmUiHelp, various) {
// The ts() and hs() functions help load strings for this module.
var ts = $scope.ts = CRM.ts('uk.artfulrobot.civicrm.gocardless');
var hs = $scope.hs = crmUiHelp({file: 'CRM/gocardless/GoCardlessSettings'}); // See: templates/CRM/gocardless/GoCardlessSettings.hlp
// Local variable for this controller (needed when inside a callback fn where `this` is not available).
var ctrl = this;

// Parse settings
var gcSettings = {};
console.log("GOT ", various);
if (various.gcSettings.value) {
gcSettings = JSON.parse(various.gcSettings.value);
if (!gcSettings) {
gcSettings = {};
}
}
// Check settings have defaults.
const defaults = {
forceRecurring: false
};
Object.keys(defaults).forEach(k => {
if (!(k in gcSettings)) {
gcSettings[k] = defaults[k];
}
});
$scope.gcSettings = gcSettings;

// Make pay processors accessible
var ppTable = [];
var ppNames = {};
var urlStub = window.location.href.replace(/^(https?:\/\/[^/]+).*$/, '$1');
various.paymentProcessors.forEach(pp => {
var k;
if (!(pp.name in ppNames)) {
ppNames[pp.name] = { name: pp.name, p: pp};
}
if (pp.is_test) {
k = 'urlTest';
}
else {
k = 'urlLive';
}

// Make URL.
ppNames[pp.name][k] = urlStub + CRM.url('civicrm/payment/ipn/' + pp.id, null, 'front');
});
$scope.ppTable = Object.values(ppNames);

this.save = function() {
return crmStatus(
// Status messages. For defaults, just use "{}"
{start: ts('Saving...'), success: ts('Saved')},
// The save action. Note that crmApi() returns a promise.
crmApi4('Setting', 'set', { values: { gocardless: JSON.stringify($scope.gcSettings)} })
);
};
});

})(angular, CRM.$, CRM._);
22 changes: 11 additions & 11 deletions gocardless.civix.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
* extension.
*/
class CRM_GoCardless_ExtensionUtil {
const SHORT_NAME = "gocardless";
const LONG_NAME = "uk.artfulrobot.civicrm.gocardless";
const CLASS_PREFIX = "CRM_GoCardless";
const SHORT_NAME = 'gocardless';
const LONG_NAME = 'uk.artfulrobot.civicrm.gocardless';
const CLASS_PREFIX = 'CRM_GoCardless';

/**
* Translate a string using the extension's domain.
Expand Down Expand Up @@ -193,8 +193,9 @@ function _gocardless_civix_civicrm_disable() {
* @param $op string, the type of operation being performed; 'check' or 'enqueue'
* @param $queue CRM_Queue_Queue, (for 'enqueue') the modifiable list of pending up upgrade tasks
*
* @return mixed based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending)
* for 'enqueue', returns void
* @return mixed
* based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending)
* for 'enqueue', returns void
*
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
*/
Expand Down Expand Up @@ -225,7 +226,7 @@ function _gocardless_civix_upgrader() {
* @param string $dir base dir
* @param string $pattern , glob pattern, eg "*.txt"
*
* @return array(string)
* @return array
*/
function _gocardless_civix_find_files($dir, $pattern) {
if (is_callable(['CRM_Utils_File', 'findFiles'])) {
Expand All @@ -244,7 +245,7 @@ function _gocardless_civix_find_files($dir, $pattern) {
if ($dh = opendir($subdir)) {
while (FALSE !== ($entry = readdir($dh))) {
$path = $subdir . DIRECTORY_SEPARATOR . $entry;
if ($entry{0} == '.') {
if ($entry[0] == '.') {
}
elseif (is_dir($path)) {
$todos[] = $path;
Expand All @@ -255,6 +256,7 @@ function _gocardless_civix_find_files($dir, $pattern) {
}
return $result;
}

/**
* (Delegated) Implements hook_civicrm_managed().
*
Expand Down Expand Up @@ -362,7 +364,7 @@ function _gocardless_civix_civicrm_themes(&$themes) {
* @link http://php.net/glob
* @param string $pattern
*
* @return array, possibly empty
* @return array
*/
function _gocardless_civix_glob($pattern) {
$result = glob($pattern);
Expand Down Expand Up @@ -470,8 +472,6 @@ function _gocardless_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL
*
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
*/

function _gocardless_civix_civicrm_entityTypes(&$entityTypes) {
$entityTypes = array_merge($entityTypes, array (
));
$entityTypes = array_merge($entityTypes, []);
}
Loading

0 comments on commit 0fd2d59

Please sign in to comment.