Merge remote-tracking branch 'systopia/issue/42'
[#42] "Data too long" when saving many profiles
This commit is contained in:
commit
85081dc3ab
7 changed files with 149 additions and 163 deletions
|
@ -18,7 +18,8 @@ use CRM_Twingle_ExtensionUtil as E;
|
|||
class CRM_Twingle_Page_Profiles extends CRM_Core_Page {
|
||||
|
||||
public function run() {
|
||||
$profiles = array();
|
||||
CRM_Utils_System::setTitle(E::ts("Twingle API Profiles"));
|
||||
$profiles = [];
|
||||
foreach (CRM_Twingle_Profile::getProfiles() as $profile_name => $profile) {
|
||||
$profiles[$profile_name]['name'] = $profile_name;
|
||||
foreach (CRM_Twingle_Profile::allowedAttributes() as $attribute) {
|
||||
|
@ -26,6 +27,7 @@ class CRM_Twingle_Page_Profiles extends CRM_Core_Page {
|
|||
}
|
||||
}
|
||||
$this->assign('profiles', $profiles);
|
||||
$this->assign('profile_stats', CRM_Twingle_Profile::getProfileStats());
|
||||
|
||||
parent::run();
|
||||
}
|
||||
|
|
|
@ -21,12 +21,6 @@ use CRM_Twingle_ExtensionUtil as E;
|
|||
*/
|
||||
class CRM_Twingle_Profile {
|
||||
|
||||
/**
|
||||
* @var CRM_Twingle_Profile[] $_profiles
|
||||
* Caches the profile objects.
|
||||
*/
|
||||
protected static $_profiles = NULL;
|
||||
|
||||
/**
|
||||
* @var string $name
|
||||
* The name of the profile.
|
||||
|
@ -56,6 +50,20 @@ class CRM_Twingle_Profile {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs (production) access to this profile
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function logAccess() {
|
||||
CRM_Core_DAO::executeQuery("
|
||||
UPDATE civicrm_twingle_profile
|
||||
SET
|
||||
last_access = NOW(),
|
||||
access_counter = access_counter + 1
|
||||
WHERE name = %1", [1 => [$this->name, 'String']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the profile's selector matches the given project ID.
|
||||
*
|
||||
|
@ -79,7 +87,7 @@ class CRM_Twingle_Profile {
|
|||
* The profile's configured custom field mapping
|
||||
*/
|
||||
public function getCustomFieldMapping() {
|
||||
$custom_field_mapping = array();
|
||||
$custom_field_mapping = [];
|
||||
if (!empty($custom_field_definition = $this->getAttribute('custom_field_mapping'))) {
|
||||
foreach (preg_split('/\r\n|\r|\n/', $custom_field_definition, -1, PREG_SPLIT_NO_EMPTY) as $custom_field_map) {
|
||||
list($twingle_field_name, $custom_field_name) = explode("=", $custom_field_map);
|
||||
|
@ -144,7 +152,7 @@ class CRM_Twingle_Profile {
|
|||
*/
|
||||
public function setAttribute($attribute_name, $value) {
|
||||
if (!in_array($attribute_name, self::allowedAttributes())) {
|
||||
throw new Exception(E::ts('Unknown attribute %1.', array(1 => $attribute_name)));
|
||||
throw new Exception(E::ts('Unknown attribute %1.', [1 => $attribute_name]));
|
||||
}
|
||||
// TODO: Check if value is acceptable.
|
||||
$this->data[$attribute_name] = $value;
|
||||
|
@ -182,17 +190,36 @@ class CRM_Twingle_Profile {
|
|||
* Persists the profile within the CiviCRM settings.
|
||||
*/
|
||||
public function saveProfile() {
|
||||
self::$_profiles[$this->getName()] = $this;
|
||||
// make sure it's valid
|
||||
$this->verifyProfile();
|
||||
self::storeProfiles();
|
||||
|
||||
// check if the profile exists
|
||||
$profile_id = CRM_Core_DAO::singleValueQuery(
|
||||
"SELECT id FROM civicrm_twingle_profile WHERE name = %1", [1 => [$this->name, 'String']]);
|
||||
if ($profile_id) {
|
||||
// existing profile -> just update the config
|
||||
CRM_Core_DAO::executeQuery(
|
||||
"UPDATE civicrm_twingle_profile SET config = %2 WHERE name = %1",
|
||||
[
|
||||
1 => [$this->name, 'String'],
|
||||
2 => [json_encode($this->data), 'String']
|
||||
]);
|
||||
} else {
|
||||
// new profile -> add new entry to the DB
|
||||
CRM_Core_DAO::executeQuery(
|
||||
"INSERT IGNORE INTO civicrm_twingle_profile(name,config,last_access,access_counter) VALUES (%1, %2, null, 0)",
|
||||
[
|
||||
1 => [$this->name, 'String'],
|
||||
2 => [json_encode($this->data), 'String']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the profile from the CiviCRM settings.
|
||||
* Deletes the profile from the database
|
||||
*/
|
||||
public function deleteProfile() {
|
||||
unset(self::$_profiles[$this->getName()]);
|
||||
self::storeProfiles();
|
||||
CRM_Core_DAO::executeQuery("DELETE FROM civicrm_twingle_profile WHERE name = %1", [1 => [$this->name, 'String']]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,7 +229,7 @@ class CRM_Twingle_Profile {
|
|||
*/
|
||||
public static function allowedAttributes() {
|
||||
return array_merge(
|
||||
array(
|
||||
[
|
||||
'selector',
|
||||
'xcm_profile',
|
||||
'location_type_id',
|
||||
|
@ -228,7 +255,7 @@ class CRM_Twingle_Profile {
|
|||
'membership_postprocess_call',
|
||||
'newsletter_double_opt_in',
|
||||
'required_address_components',
|
||||
),
|
||||
],
|
||||
// Add payment methods.
|
||||
array_keys(static::paymentInstruments()),
|
||||
|
||||
|
@ -245,7 +272,7 @@ class CRM_Twingle_Profile {
|
|||
* @return array
|
||||
*/
|
||||
public static function paymentInstruments() {
|
||||
return array(
|
||||
return [
|
||||
'pi_banktransfer' => E::ts('Bank transfer'),
|
||||
'pi_debit_manual' => E::ts('Debit manual'),
|
||||
'pi_debit_automatic' => E::ts('Debit automatic'),
|
||||
|
@ -257,7 +284,7 @@ class CRM_Twingle_Profile {
|
|||
'pi_paydirekt' => E::ts('paydirekt'),
|
||||
'pi_applepay' => E::ts('Apple Pay'),
|
||||
'pi_googlepay' => E::ts('Google Pay'),
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -269,7 +296,7 @@ class CRM_Twingle_Profile {
|
|||
* @return CRM_Twingle_Profile
|
||||
*/
|
||||
public static function createDefaultProfile($name = 'default') {
|
||||
return new CRM_Twingle_Profile($name, array(
|
||||
return new CRM_Twingle_Profile($name, [
|
||||
'selector' => '',
|
||||
'xcm_profile' => '',
|
||||
'location_type_id' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK,
|
||||
|
@ -307,7 +334,7 @@ class CRM_Twingle_Profile {
|
|||
'city',
|
||||
'country',
|
||||
],
|
||||
)
|
||||
]
|
||||
// Add contribution status for all payment methods.
|
||||
+ array_fill_keys(array_map(function($attribute) {
|
||||
return $attribute . '_status';
|
||||
|
@ -339,54 +366,56 @@ class CRM_Twingle_Profile {
|
|||
/**
|
||||
* Retrieves the profile with the given name.
|
||||
*
|
||||
* @param $name
|
||||
* @param string $name
|
||||
*
|
||||
* @return CRM_Twingle_Profile | NULL
|
||||
*/
|
||||
public static function getProfile($name) {
|
||||
$profiles = self::getProfiles();
|
||||
if (isset($profiles[$name])) {
|
||||
return $profiles[$name];
|
||||
if (!empty($name)) {
|
||||
$profile_data = CRM_Core_DAO::singleValueQuery("SELECT config FROM civicrm_twingle_profile WHERE name = %1", [
|
||||
1 => [$name, 'String']]);
|
||||
if ($profile_data) {
|
||||
return new CRM_Twingle_Profile($name, json_decode($profile_data, 1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of all profiles persisted within the current CiviCRM
|
||||
* settings, including the default profile.
|
||||
*
|
||||
* @return CRM_Twingle_Profile[]
|
||||
* @return array
|
||||
* profile_name => CRM_Twingle_Profile
|
||||
*/
|
||||
public static function getProfiles() {
|
||||
if (self::$_profiles === NULL) {
|
||||
self::$_profiles = array();
|
||||
if ($profiles_data = Civi::settings()->get('twingle_profiles')) {
|
||||
foreach ($profiles_data as $profile_name => $profile_data) {
|
||||
self::$_profiles[$profile_name] = new CRM_Twingle_Profile($profile_name, $profile_data);
|
||||
// todo: cache?
|
||||
$profiles = [];
|
||||
$profile_data = CRM_Core_DAO::executeQuery("SELECT name, config FROM civicrm_twingle_profile");
|
||||
while ($profile_data->fetch()) {
|
||||
$profiles[$profile_data->name] = new CRM_Twingle_Profile($profile_data->name, json_decode($profile_data->config, 1));
|
||||
}
|
||||
return $profiles;
|
||||
}
|
||||
}
|
||||
|
||||
// Include the default profile if it was not overridden within the settings.
|
||||
if (!isset(self::$_profiles['default'])) {
|
||||
self::$_profiles['default'] = self::createDefaultProfile();
|
||||
self::storeProfiles();
|
||||
}
|
||||
|
||||
return self::$_profiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Persists the list of profiles into the CiviCRM settings.
|
||||
* Get the stats (access_count, last_access) for all twingle profiles
|
||||
*
|
||||
* @return CRM_Twingle_Profile[]
|
||||
*/
|
||||
public static function storeProfiles() {
|
||||
$profile_data = array();
|
||||
foreach (self::$_profiles as $profile_name => $profile) {
|
||||
$profile_data[$profile_name] = $profile->data;
|
||||
public static function getProfileStats() {
|
||||
$stats = [];
|
||||
$profile_data = CRM_Core_DAO::executeQuery("SELECT name, last_access, access_counter FROM civicrm_twingle_profile");
|
||||
while ($profile_data->fetch()) {
|
||||
$stats[$profile_data->name] = [
|
||||
'name' => $profile_data->name,
|
||||
'last_access' => $profile_data->last_access,
|
||||
'last_access_txt' => $profile_data->last_access ? date('Y-m-d H:i:s', strtotime($profile_data->last_access)) : E::ts("never"),
|
||||
'access_counter' => $profile_data->access_counter,
|
||||
'access_counter_txt' => $profile_data->access_counter ? ((int) $profile_data->access_counter) . 'x' : E::ts("never"),
|
||||
];
|
||||
}
|
||||
Civi::settings()->set('twingle_profiles', $profile_data);
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,32 +6,15 @@ use CRM_Twingle_ExtensionUtil as E;
|
|||
*/
|
||||
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.
|
||||
*
|
||||
* Installer script
|
||||
*/
|
||||
public function install() {
|
||||
$this->executeSqlFile('sql/myinstall.sql');
|
||||
}
|
||||
// create a DB table for the twingle profiles
|
||||
$this->executeSqlFile('sql/civicrm_twingle_profile.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),
|
||||
));
|
||||
// add a default profile
|
||||
CRM_Twingle_Profile::createDefaultProfile()->saveProfile();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,96 +24,6 @@ class CRM_Twingle_Upgrader extends CRM_Twingle_Upgrader_Base {
|
|||
$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.
|
||||
*/
|
||||
|
@ -163,4 +56,32 @@ class CRM_Twingle_Upgrader extends CRM_Twingle_Upgrader_Base {
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrading to 1.4.0 needs to convert the profiles into the new infrastructure
|
||||
*
|
||||
* @return TRUE on success
|
||||
* @throws Exception
|
||||
*/
|
||||
public function upgrade_5140() {
|
||||
$this->ctx->log->info('Converting twingle profiles.');
|
||||
|
||||
// create a DB table for the twingle profiles
|
||||
$this->executeSqlFile('sql/civicrm_twingle_profile.sql');
|
||||
|
||||
// migrate the current profiles
|
||||
if ($profiles_data = Civi::settings()->get('twingle_profiles')) {
|
||||
foreach ($profiles_data as $profile_name => $profile_data) {
|
||||
$profile = new CRM_Twingle_Profile($profile_name, $profile_data);
|
||||
$data = json_encode($profile->getData());
|
||||
CRM_Core_DAO::executeQuery(
|
||||
"INSERT IGNORE INTO civicrm_twingle_profile(name,config,last_access,access_counter) VALUES (%1, %2, NOW(), 0)",
|
||||
[
|
||||
1 => [$profile_name, 'String'],
|
||||
2 => [$data, 'String']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,6 +278,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
|
|||
// Get the profile defined for the given form ID, or the default profile
|
||||
// if none matches.
|
||||
$profile = CRM_Twingle_Profile::getProfileForProject($params['project_id']);
|
||||
$profile->logAccess();
|
||||
|
||||
// Validate submitted parameters
|
||||
CRM_Twingle_Submission::validateSubmission($params, $profile);
|
||||
|
|
16
sql/civicrm_twingle_profile.sql
Normal file
16
sql/civicrm_twingle_profile.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
-- /*******************************************************
|
||||
-- ** civicrm_twingle_profile
|
||||
-- **
|
||||
-- ** stores twingle profile data v1.4+
|
||||
-- ********************************************************/
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `civicrm_twingle_profile`(
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(255) COMMENT 'configuration name, i.e. internal ID',
|
||||
`config` text COMMENT 'JSON encoded configuration',
|
||||
`last_access` datetime COMMENT 'timestamp of the last access (through the api)',
|
||||
`access_counter` int unsigned COMMENT 'number of accesses (through the api)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
||||
|
|
@ -26,6 +26,8 @@
|
|||
<tr>
|
||||
<th>{ts domain="de.systopia.twingle"}Profile name{/ts}</th>
|
||||
<th>{ts domain="de.systopia.twingle"}Properties{/ts}</th>
|
||||
<th>{ts domain="de.systopia.twingle"}Used{/ts}</th>
|
||||
<th>{ts domain="de.systopia.twingle"}Last Used{/ts}</th>
|
||||
<th>{ts domain="de.systopia.twingle"}Operations{/ts}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -37,6 +39,8 @@
|
|||
<td>
|
||||
<div><strong>{ts domain="de.systopia.twingle"}Selector{/ts}:</strong> {$profile.selector}</div>
|
||||
</td>
|
||||
<td>{ts domain="de.systopia.twingle"}{$profile_stats.$profile_name.access_counter_txt}{/ts}</td>
|
||||
<td>{ts domain="de.systopia.twingle"}{$profile_stats.$profile_name.last_access_txt}{/ts}</td>
|
||||
<td>
|
||||
<a href="{crmURL p="civicrm/admin/settings/twingle/profile" q="op=edit&name=$profile_name"}" title="{ts domain="de.systopia.twingle" 1=$profile.name}Edit profile %1{/ts}" class="action-item crm-hover-button">{ts domain="de.systopia.twingle"}Edit{/ts}</a>
|
||||
<a href="{crmURL p="civicrm/admin/settings/twingle/profile" q="op=copy&source_name=$profile_name"}" title="{ts domain="de.systopia.twingle" 1=$profile.name}Copy profile %1{/ts}" class="action-item crm-hover-button">{ts domain="de.systopia.twingle"}Copy{/ts}</a>
|
||||
|
|
13
twingle.php
13
twingle.php
|
@ -153,6 +153,19 @@ function twingle_civicrm_alterAPIPermissions($entity, $action, &$params, &$permi
|
|||
$permissions['twingle_donation']['endrecurring'] = array('access Twingle API');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure, that the last_access and access_counter column is not logged
|
||||
*
|
||||
* @param array $logTableSpec
|
||||
*/
|
||||
function twingle_civicrm_alterLogTables(&$logTableSpec)
|
||||
{
|
||||
if (isset($logTableSpec['civicrm_twingle_profile'])) {
|
||||
$logTableSpec['civicrm_twingle_profile']['exceptions'] = ['last_access', 'access_counter'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- Functions below this ship commented out. Uncomment as required. ---
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue