Merge branch 'dev_1'

[#1] Allow different financial types for one time and recurring contributions
This commit is contained in:
Jens Schuppe 2019-08-05 11:40:15 +02:00
commit 876a2c5cee
9 changed files with 620 additions and 16 deletions

View file

@ -136,6 +136,13 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
$this->getFinancialTypes(), // list of options $this->getFinancialTypes(), // list of options
TRUE // is required TRUE // is required
); );
$this->add(
'select', // field type
'financial_type_id_recur', // field name
E::ts('Financial type (recurring)'), // field label
$this->getFinancialTypes(), // list of options
TRUE // is required
);
$this->add( $this->add(
'select', 'select',

View file

@ -170,6 +170,7 @@ class CRM_Twingle_Profile {
'location_type_id', 'location_type_id',
'location_type_id_organisation', 'location_type_id_organisation',
'financial_type_id', 'financial_type_id',
'financial_type_id_recur',
'pi_banktransfer', 'pi_banktransfer',
'pi_debit_manual', 'pi_debit_manual',
'pi_debit_automatic', 'pi_debit_automatic',
@ -228,6 +229,7 @@ class CRM_Twingle_Profile {
'location_type_id' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK, 'location_type_id' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK,
'location_type_id_organisation' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK, 'location_type_id_organisation' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK,
'financial_type_id' => 1, // "Donation" 'financial_type_id' => 1, // "Donation"
'financial_type_id_recur' => 1, // "Donation"
'pi_banktransfer' => 5, // "EFT" 'pi_banktransfer' => 5, // "EFT"
'pi_debit_manual' => NULL, 'pi_debit_manual' => NULL,
'pi_debit_automatic' => 3, // Debit 'pi_debit_automatic' => 3, // Debit

146
CRM/Twingle/Upgrader.php Normal file
View file

@ -0,0 +1,146 @@
<?php
use CRM_Twingle_ExtensionUtil as E;
/**
* Collection of upgrade steps.
*/
class CRM_Twingle_Upgrader extends CRM_Twingle_Upgrader_Base {
// By convention, functions that look like "function upgrade_NNNN()" are
// upgrade tasks. They are executed in order (like Drupal's hook_update_N).
/**
* Example: Run an external SQL script when the module is installed.
*
public function install() {
$this->executeSqlFile('sql/myinstall.sql');
}
/**
* Example: Work with entities usually not available during the install step.
*
* This method can be used for any post-install tasks. For example, if a step
* of your installation depends on accessing an entity that is itself
* created during the installation (e.g., a setting or a managed entity), do
* so here to avoid order of operation problems.
*
public function postInstall() {
$customFieldId = civicrm_api3('CustomField', 'getvalue', array(
'return' => array("id"),
'name' => "customFieldCreatedViaManagedHook",
));
civicrm_api3('Setting', 'create', array(
'myWeirdFieldSetting' => array('id' => $customFieldId, 'weirdness' => 1),
));
}
/**
* Example: Run an external SQL script when the module is uninstalled.
*
public function uninstall() {
$this->executeSqlFile('sql/myuninstall.sql');
}
/**
* Example: Run a simple query when a module is enabled.
*
public function enable() {
CRM_Core_DAO::executeQuery('UPDATE foo SET is_active = 1 WHERE bar = "whiz"');
}
/**
* Example: Run a simple query when a module is disabled.
*
public function disable() {
CRM_Core_DAO::executeQuery('UPDATE foo SET is_active = 0 WHERE bar = "whiz"');
}
/**
* Example: Run a couple simple queries.
*
* @return TRUE on success
* @throws Exception
*
public function upgrade_4200() {
$this->ctx->log->info('Applying update 4200');
CRM_Core_DAO::executeQuery('UPDATE foo SET bar = "whiz"');
CRM_Core_DAO::executeQuery('DELETE FROM bang WHERE willy = wonka(2)');
return TRUE;
} // */
/**
* Example: Run an external SQL script.
*
* @return TRUE on success
* @throws Exception
public function upgrade_4201() {
$this->ctx->log->info('Applying update 4201');
// this path is relative to the extension base dir
$this->executeSqlFile('sql/upgrade_4201.sql');
return TRUE;
} // */
/**
* Example: Run a slow upgrade process by breaking it up into smaller chunk.
*
* @return TRUE on success
* @throws Exception
public function upgrade_4202() {
$this->ctx->log->info('Planning update 4202'); // PEAR Log interface
$this->addTask(E::ts('Process first step'), 'processPart1', $arg1, $arg2);
$this->addTask(E::ts('Process second step'), 'processPart2', $arg3, $arg4);
$this->addTask(E::ts('Process second step'), 'processPart3', $arg5);
return TRUE;
}
public function processPart1($arg1, $arg2) { sleep(10); return TRUE; }
public function processPart2($arg3, $arg4) { sleep(10); return TRUE; }
public function processPart3($arg5) { sleep(10); return TRUE; }
// */
/**
* Example: Run an upgrade with a query that touches many (potentially
* millions) of records by breaking it up into smaller chunks.
*
* @return TRUE on success
* @throws Exception
public function upgrade_4203() {
$this->ctx->log->info('Planning update 4203'); // PEAR Log interface
$minId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(min(id),0) FROM civicrm_contribution');
$maxId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(max(id),0) FROM civicrm_contribution');
for ($startId = $minId; $startId <= $maxId; $startId += self::BATCH_SIZE) {
$endId = $startId + self::BATCH_SIZE - 1;
$title = E::ts('Upgrade Batch (%1 => %2)', array(
1 => $startId,
2 => $endId,
));
$sql = '
UPDATE civicrm_contribution SET foobar = whiz(wonky()+wanker)
WHERE id BETWEEN %1 and %2
';
$params = array(
1 => array($startId, 'Integer'),
2 => array($endId, 'Integer'),
);
$this->addTask($title, 'executeSql', $sql, $params);
}
return TRUE;
} // */
/**
* Copy financial_type_id setting to new setting financial_type_id_recur.
*/
public function upgrade_4000() {
$this->ctx->log->info('Applying update 4000: Copying Financial type to new setting Financial type (recurring).');
foreach (CRM_Twingle_Profile::getProfiles() as $profile) {
$profile->setAttribute('financial_type_id_recur', $profile->getAttribute('financial_type_id'));
$profile->saveProfile();
}
return TRUE;
}
}

View file

@ -0,0 +1,376 @@
<?php
// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
use CRM_Twingle_ExtensionUtil as E;
/**
* Base class which provides helpers to execute upgrade logic
*/
class CRM_Twingle_Upgrader_Base {
/**
* @var varies, subclass of this
*/
static $instance;
/**
* @var CRM_Queue_TaskContext
*/
protected $ctx;
/**
* @var string, eg 'com.example.myextension'
*/
protected $extensionName;
/**
* @var string, full path to the extension's source tree
*/
protected $extensionDir;
/**
* @var array(revisionNumber) sorted numerically
*/
private $revisions;
/**
* @var boolean
* Flag to clean up extension revision data in civicrm_setting
*/
private $revisionStorageIsDeprecated = FALSE;
/**
* Obtain a reference to the active upgrade handler.
*/
static public function instance() {
if (!self::$instance) {
// FIXME auto-generate
self::$instance = new CRM_Twingle_Upgrader(
'de.systopia.twingle',
realpath(__DIR__ . '/../../../')
);
}
return self::$instance;
}
/**
* Adapter that lets you add normal (non-static) member functions to the queue.
*
* Note: Each upgrader instance should only be associated with one
* task-context; otherwise, this will be non-reentrant.
*
* @code
* CRM_Twingle_Upgrader_Base::_queueAdapter($ctx, 'methodName', 'arg1', 'arg2');
* @endcode
*/
static public function _queueAdapter() {
$instance = self::instance();
$args = func_get_args();
$instance->ctx = array_shift($args);
$instance->queue = $instance->ctx->queue;
$method = array_shift($args);
return call_user_func_array(array($instance, $method), $args);
}
public function __construct($extensionName, $extensionDir) {
$this->extensionName = $extensionName;
$this->extensionDir = $extensionDir;
}
// ******** Task helpers ********
/**
* Run a CustomData file.
*
* @param string $relativePath the CustomData XML file path (relative to this extension's dir)
* @return bool
*/
public function executeCustomDataFile($relativePath) {
$xml_file = $this->extensionDir . '/' . $relativePath;
return $this->executeCustomDataFileByAbsPath($xml_file);
}
/**
* Run a CustomData file
*
* @param string $xml_file the CustomData XML file path (absolute path)
*
* @return bool
*/
protected static function executeCustomDataFileByAbsPath($xml_file) {
$import = new CRM_Utils_Migrate_Import();
$import->run($xml_file);
return TRUE;
}
/**
* Run a SQL file.
*
* @param string $relativePath the SQL file path (relative to this extension's dir)
*
* @return bool
*/
public function executeSqlFile($relativePath) {
CRM_Utils_File::sourceSQLFile(
CIVICRM_DSN,
$this->extensionDir . DIRECTORY_SEPARATOR . $relativePath
);
return TRUE;
}
/**
* @param string $tplFile
* The SQL file path (relative to this extension's dir).
* Ex: "sql/mydata.mysql.tpl".
* @return bool
*/
public function executeSqlTemplate($tplFile) {
// Assign multilingual variable to Smarty.
$upgrade = new CRM_Upgrade_Form();
$tplFile = CRM_Utils_File::isAbsolute($tplFile) ? $tplFile : $this->extensionDir . DIRECTORY_SEPARATOR . $tplFile;
$smarty = CRM_Core_Smarty::singleton();
$smarty->assign('domainID', CRM_Core_Config::domainID());
CRM_Utils_File::sourceSQLFile(
CIVICRM_DSN, $smarty->fetch($tplFile), NULL, TRUE
);
return TRUE;
}
/**
* Run one SQL query.
*
* This is just a wrapper for CRM_Core_DAO::executeSql, but it
* provides syntatic sugar for queueing several tasks that
* run different queries
*/
public function executeSql($query, $params = array()) {
// FIXME verify that we raise an exception on error
CRM_Core_DAO::executeQuery($query, $params);
return TRUE;
}
/**
* Syntatic sugar for enqueuing a task which calls a function in this class.
*
* The task is weighted so that it is processed
* as part of the currently-pending revision.
*
* After passing the $funcName, you can also pass parameters that will go to
* the function. Note that all params must be serializable.
*/
public function addTask($title) {
$args = func_get_args();
$title = array_shift($args);
$task = new CRM_Queue_Task(
array(get_class($this), '_queueAdapter'),
$args,
$title
);
return $this->queue->createItem($task, array('weight' => -1));
}
// ******** Revision-tracking helpers ********
/**
* Determine if there are any pending revisions.
*
* @return bool
*/
public function hasPendingRevisions() {
$revisions = $this->getRevisions();
$currentRevision = $this->getCurrentRevision();
if (empty($revisions)) {
return FALSE;
}
if (empty($currentRevision)) {
return TRUE;
}
return ($currentRevision < max($revisions));
}
/**
* Add any pending revisions to the queue.
*/
public function enqueuePendingRevisions(CRM_Queue_Queue $queue) {
$this->queue = $queue;
$currentRevision = $this->getCurrentRevision();
foreach ($this->getRevisions() as $revision) {
if ($revision > $currentRevision) {
$title = ts('Upgrade %1 to revision %2', array(
1 => $this->extensionName,
2 => $revision,
));
// note: don't use addTask() because it sets weight=-1
$task = new CRM_Queue_Task(
array(get_class($this), '_queueAdapter'),
array('upgrade_' . $revision),
$title
);
$this->queue->createItem($task);
$task = new CRM_Queue_Task(
array(get_class($this), '_queueAdapter'),
array('setCurrentRevision', $revision),
$title
);
$this->queue->createItem($task);
}
}
}
/**
* Get a list of revisions.
*
* @return array(revisionNumbers) sorted numerically
*/
public function getRevisions() {
if (!is_array($this->revisions)) {
$this->revisions = array();
$clazz = new ReflectionClass(get_class($this));
$methods = $clazz->getMethods();
foreach ($methods as $method) {
if (preg_match('/^upgrade_(.*)/', $method->name, $matches)) {
$this->revisions[] = $matches[1];
}
}
sort($this->revisions, SORT_NUMERIC);
}
return $this->revisions;
}
public function getCurrentRevision() {
$revision = CRM_Core_BAO_Extension::getSchemaVersion($this->extensionName);
if (!$revision) {
$revision = $this->getCurrentRevisionDeprecated();
}
return $revision;
}
private function getCurrentRevisionDeprecated() {
$key = $this->extensionName . ':version';
if ($revision = CRM_Core_BAO_Setting::getItem('Extension', $key)) {
$this->revisionStorageIsDeprecated = TRUE;
}
return $revision;
}
public function setCurrentRevision($revision) {
CRM_Core_BAO_Extension::setSchemaVersion($this->extensionName, $revision);
// clean up legacy schema version store (CRM-19252)
$this->deleteDeprecatedRevision();
return TRUE;
}
private function deleteDeprecatedRevision() {
if ($this->revisionStorageIsDeprecated) {
$setting = new CRM_Core_BAO_Setting();
$setting->name = $this->extensionName . ':version';
$setting->delete();
CRM_Core_Error::debug_log_message("Migrated extension schema revision ID for {$this->extensionName} from civicrm_setting (deprecated) to civicrm_extension.\n");
}
}
// ******** Hook delegates ********
/**
* @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
*/
public function onInstall() {
$files = glob($this->extensionDir . '/sql/*_install.sql');
if (is_array($files)) {
foreach ($files as $file) {
CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file);
}
}
$files = glob($this->extensionDir . '/sql/*_install.mysql.tpl');
if (is_array($files)) {
foreach ($files as $file) {
$this->executeSqlTemplate($file);
}
}
$files = glob($this->extensionDir . '/xml/*_install.xml');
if (is_array($files)) {
foreach ($files as $file) {
$this->executeCustomDataFileByAbsPath($file);
}
}
if (is_callable(array($this, 'install'))) {
$this->install();
}
}
/**
* @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall
*/
public function onPostInstall() {
$revisions = $this->getRevisions();
if (!empty($revisions)) {
$this->setCurrentRevision(max($revisions));
}
if (is_callable(array($this, 'postInstall'))) {
$this->postInstall();
}
}
/**
* @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
*/
public function onUninstall() {
$files = glob($this->extensionDir . '/sql/*_uninstall.mysql.tpl');
if (is_array($files)) {
foreach ($files as $file) {
$this->executeSqlTemplate($file);
}
}
if (is_callable(array($this, 'uninstall'))) {
$this->uninstall();
}
$files = glob($this->extensionDir . '/sql/*_uninstall.sql');
if (is_array($files)) {
foreach ($files as $file) {
CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file);
}
}
}
/**
* @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
*/
public function onEnable() {
// stub for possible future use
if (is_callable(array($this, 'enable'))) {
$this->enable();
}
}
/**
* @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
*/
public function onDisable() {
// stub for possible future use
if (is_callable(array($this, 'disable'))) {
$this->disable();
}
}
public function onUpgrade($op, CRM_Queue_Queue $queue = NULL) {
switch ($op) {
case 'check':
return array($this->hasPendingRevisions());
case 'enqueue':
return $this->enqueuePendingRevisions($queue);
default:
}
}
}

View file

@ -36,16 +36,18 @@ The *default* profile is used whenever the plugin cannot match the Twingle
project ID from any other profile. Therefore the default profile will be used project ID from any other profile. Therefore the default profile will be used
for all newly created Twingle projects. for all newly created Twingle projects.
| Label | Description | | Label | Description |
|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| |---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Profile name | Internal name, used inside the extension. | | Profile name | Internal name, used inside the extension. |
| Project IDs | Twingle project IDs. Separate multiple IDs with commas. | | Project IDs | Twingle project IDs. Separate multiple IDs with commas. |
| Location type | Specify how the address data sent by the form should be categorised in CiviCRM. The list is based on your CiviCRM configuration. | | Location type | Specify how the address data sent by the form should be categorised in CiviCRM. The list is based on your CiviCRM configuration. |
| Financial type | Specify which financial type incoming donations should be recorded with in CiviCRM. The list is based on your CiviCRM configuration. | | Location type for organisations | Specify how the address data sent by the form should be categorised in CiviCRM for organisational donations. The list is based on your CiviCRM configuration. |
| Gender options | Specify which CiviCRM gender option the incoming Twingle gender value should be mapped to. The list is based on your CiviCRM configuration. | | Financial type | Specify which financial type incoming one-time donations should be recorded with in CiviCRM. The list is based on your CiviCRM configuration. |
| Record *Payment method* as | Specifiy the payment methods mapping for incoming donations for each Twingle payment method. | | Financial type (recurring) | Specify which financial type incoming recurring donations should be recorded with in CiviCRM. The list is based on your CiviCRM configuration. |
| CiviSEPA creditor | When enabled to integrate with CiviSEPA, specify the CiviSEPA creditor to use. | | Gender options | Specify which CiviCRM gender option the incoming Twingle gender value should be mapped to. The list is based on your CiviCRM configuration. |
| Sign up for groups | Whenever the donor checked the newsletter/postal mailing/donation receipt checkbox on the Twingle form, the contact will be added to the groups listed here. | | Record *Payment method* as | Specifiy the payment methods mapping for incoming donations for each Twingle payment method. |
| CiviSEPA creditor | When enabled to integrate with CiviSEPA, specify the CiviSEPA creditor to use. |
| Sign up for groups | Whenever the donor checked the newsletter/postal mailing/donation receipt checkbox on the Twingle form, the contact will be added to the groups listed here. |
## API documentation ## API documentation

View file

@ -454,7 +454,6 @@ function civicrm_api3_twingle_donation_Submit($params) {
'contact_id' => (isset($organisation_id) ? $organisation_id : $contact_id), 'contact_id' => (isset($organisation_id) ? $organisation_id : $contact_id),
'currency' => $params['currency'], 'currency' => $params['currency'],
'trxn_id' => $params['trx_id'], 'trxn_id' => $params['trx_id'],
'financial_type_id' => $profile->getAttribute('financial_type_id'),
'payment_instrument_id' => $params['payment_instrument_id'], 'payment_instrument_id' => $params['payment_instrument_id'],
'amount' => $params['amount'] / 100, 'amount' => $params['amount'] / 100,
'total_amount' => $params['amount'] / 100, 'total_amount' => $params['amount'] / 100,
@ -517,6 +516,10 @@ function civicrm_api3_twingle_donation_Submit($params) {
// Add cycle day for recurring contributions. // Add cycle day for recurring contributions.
if ($params['donation_rhythm'] != 'one_time') { if ($params['donation_rhythm'] != 'one_time') {
$mandate_data['cycle_day'] = CRM_Twingle_Submission::getSEPACycleDay($params['confirmed_at'], $creditor_id); $mandate_data['cycle_day'] = CRM_Twingle_Submission::getSEPACycleDay($params['confirmed_at'], $creditor_id);
$mandate_data['financial_type_id'] = $profile->getAttribute('financial_type_id_recur');
}
else {
$mandate_data['financial_type_id'] = $profile->getAttribute('financial_type_id');
} }
// Let CiviSEPA set the correct payment instrument depending on the // Let CiviSEPA set the correct payment instrument depending on the
@ -537,6 +540,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
+ array( + array(
'contribution_status_id' => 'Pending', 'contribution_status_id' => 'Pending',
'start_date' => $params['confirmed_at'], 'start_date' => $params['confirmed_at'],
'financial_type_id' => $profile->getAttribute('financial_type_id_recur'),
) )
+ CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']); + CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']);
$contribution_recur = civicrm_api3('contributionRecur', 'create', $contribution_recur_data); $contribution_recur = civicrm_api3('contributionRecur', 'create', $contribution_recur_data);
@ -547,6 +551,10 @@ function civicrm_api3_twingle_donation_Submit($params) {
); );
} }
$contribution_data['contribution_recur_id'] = $contribution_recur['id']; $contribution_data['contribution_recur_id'] = $contribution_recur['id'];
$contribution_data['financial_type_id'] = $contribution_recur_data['financial_type_id'];
}
else {
$contribution_data['financial_type_id'] = $profile->getAttribute('financial_type_id');
} }
// Create contribution. // Create contribution.

View file

@ -17,5 +17,13 @@
{/htxt} {/htxt}
{htxt id='id-location_type_id_organisation'} {htxt id='id-location_type_id_organisation'}
{ts domain="de.systopia.twingle"}Select which location type to use for addresses for organisations and shared organisation addresses for individual contacts{/ts} {ts domain="de.systopia.twingle"}Select which location type to use for addresses for organisations and shared organisation addresses for individual contacts.{/ts}
{/htxt}
{htxt id='id-financial_type_id'}
{ts domain="de.systopia.twingle"}Select which financial type to use for one-time contributions.{/ts}
{/htxt}
{htxt id='id-financial_type_id_recur'}
{ts domain="de.systopia.twingle"}Select which financial type to use for recurring contributions.{/ts}
{/htxt} {/htxt}

View file

@ -77,10 +77,49 @@
</tr> </tr>
<tr class="crm-section"> <tr class="crm-section">
<td class="label">{$form.financial_type_id.label}</td> <td class="label">
{$form.financial_type_id.label}
<a
onclick='
CRM.help(
"{ts domain="de.systopia.twingle"}Financial type{/ts}",
{literal}{
"id": "id-financial_type_id",
"file": "CRM\/Twingle\/Form\/Profile"
}{/literal}
);
return false;
'
href="#"
title="{ts domain="de.systopia.twingle"}Help{/ts}"
class="helpicon"
></a>
</td>
<td class="content">{$form.financial_type_id.html}</td> <td class="content">{$form.financial_type_id.html}</td>
</tr> </tr>
<tr class="crm-section">
<td class="label">
{$form.financial_type_id_recur.label}
<a
onclick='
CRM.help(
"{ts domain="de.systopia.twingle"}Financial type (recurring){/ts}",
{literal}{
"id": "id-financial_type_id_recur",
"file": "CRM\/Twingle\/Form\/Profile"
}{/literal}
);
return false;
'
href="#"
title="{ts domain="de.systopia.twingle"}Help{/ts}"
class="helpicon"
></a>
</td>
<td class="content">{$form.financial_type_id_recur.html}</td>
</tr>
{if isset($form.sepa_creditor_id)} {if isset($form.sepa_creditor_id)}
<tr class="crm-section"> <tr class="crm-section">
<td class="label">{$form.sepa_creditor_id.label}</td> <td class="label">{$form.sepa_creditor_id.label}</td>

View file

@ -263,16 +263,17 @@ function _twingle_civix_find_files($dir, $pattern) {
*/ */
function _twingle_civix_civicrm_managed(&$entities) { function _twingle_civix_civicrm_managed(&$entities) {
$mgdFiles = _twingle_civix_find_files(__DIR__, '*.mgd.php'); $mgdFiles = _twingle_civix_find_files(__DIR__, '*.mgd.php');
sort($mgdFiles);
foreach ($mgdFiles as $file) { foreach ($mgdFiles as $file) {
$es = include $file; $es = include $file;
foreach ($es as $e) { foreach ($es as $e) {
if (empty($e['module'])) { if (empty($e['module'])) {
$e['module'] = E::LONG_NAME; $e['module'] = E::LONG_NAME;
} }
$entities[] = $e;
if (empty($e['params']['version'])) { if (empty($e['params']['version'])) {
$e['params']['version'] = '3'; $e['params']['version'] = '3';
} }
$entities[] = $e;
} }
} }
} }
@ -352,8 +353,10 @@ function _twingle_civix_glob($pattern) {
* Inserts a navigation menu item at a given place in the hierarchy. * Inserts a navigation menu item at a given place in the hierarchy.
* *
* @param array $menu - menu hierarchy * @param array $menu - menu hierarchy
* @param string $path - path where insertion should happen (ie. Administer/System Settings) * @param string $path - path to parent of this item, e.g. 'my_extension/submenu'
* @param array $item - menu you need to insert (parent/child attributes will be filled for you) * 'Mailing', or 'Administer/System Settings'
* @param array $item - the item to insert (parent/child attributes will be
* filled for you)
*/ */
function _twingle_civix_insert_navigation_menu(&$menu, $path, $item) { function _twingle_civix_insert_navigation_menu(&$menu, $path, $item) {
// If we are done going down the path, insert menu // If we are done going down the path, insert menu
@ -443,3 +446,16 @@ function _twingle_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
$metaDataFolders[] = $settingsDir; $metaDataFolders[] = $settingsDir;
} }
} }
/**
* (Delegated) Implements hook_civicrm_entityTypes().
*
* Find any *.entityType.php files, merge their content, and return.
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_entityTypes
*/
function _twingle_civix_civicrm_entityTypes(&$entityTypes) {
$entityTypes = array_merge($entityTypes, array (
));
}