diff --git a/CRM/Twingle/Form/Profile.php b/CRM/Twingle/Form/Profile.php index efffdce..d7fe333 100644 --- a/CRM/Twingle/Form/Profile.php +++ b/CRM/Twingle/Form/Profile.php @@ -32,6 +32,12 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { */ protected $profile; + /** + * The ID of this profile. + * @var int|NULL + */ + protected $profile_id = NULL; + /** * @var string * @@ -142,10 +148,11 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { $this->_op = 'create'; } - // Verify that a profile with the given name exists. - $profile_name = CRM_Utils_Request::retrieve('name', 'String', $this); - if (!$this->profile = CRM_Twingle_Profile::getProfile($profile_name)) { - $profile_name = NULL; + // Verify that a profile with the given id exists. + + if ($this->_op != 'copy') { + $this->profile_id = CRM_Utils_Request::retrieve('id', 'Int', $this); + $this->profile = CRM_Twingle_Profile::getProfile($this->profile_id); } // Set redirect destination. @@ -153,12 +160,12 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { switch ($this->_op) { case 'delete': - if ($profile_name) { - CRM_Utils_System::setTitle(E::ts('Delete Twingle API profile %1', [1 => $profile_name])); + if ($this->profile_id) { + CRM_Utils_System::setTitle(E::ts('Delete Twingle API profile %1', [1 => $this->profile->getName()])); $this->addButtons([ [ 'type' => 'submit', - 'name' => ($profile_name == 'default' ? E::ts('Reset') : E::ts('Delete')), + 'name' => ($this->profile->getName() == 'default' ? E::ts('Reset') : E::ts('Delete')), 'isDefault' => TRUE, ], ]); @@ -166,40 +173,37 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form { parent::buildQuickForm(); return; - case 'edit': - // When editing without a valid profile name, edit the default profile. - if (!$profile_name) { - $profile_name = 'default'; - $this->profile = CRM_Twingle_Profile::getProfile($profile_name); - } - CRM_Utils_System::setTitle(E::ts('Edit Twingle API profile %1', [1 => $this->profile->getName()])); - break; - case 'copy': // Retrieve the source profile name. - $profile_name = CRM_Utils_Request::retrieve('source_name', 'String', $this); - // When copying without a valid profile name, copy the default profile. - if (!$profile_name) { - $profile_name = 'default'; + $source_id = CRM_Utils_Request::retrieve('source_id', 'Int', $this); + // When copying without a valid profile id, copy the default profile. + if (!$source_id) { + $this->profile = CRM_Twingle_Profile::createDefaultProfile(); + } else { + $source_profile = CRM_Twingle_Profile::getProfile($source_id); + $this->profile = $source_profile->copy(); } - $this->profile = clone CRM_Twingle_Profile::getProfile($profile_name); - // Propose a new name for this profile. - $profile_name = $profile_name . '_copy'; + $profile_name = $this->profile->getName() . '_copy'; $this->profile->setName($profile_name); CRM_Utils_System::setTitle(E::ts('New Twingle API profile')); break; + case 'edit': + CRM_Utils_System::setTitle(E::ts('Edit Twingle API profile %1', [1 => $this->profile->getName()])); + break; + case 'create': // Load factory default profile values. - $this->profile = CRM_Twingle_Profile::createDefaultProfile($profile_name); + $this->profile = CRM_Twingle_Profile::createDefaultProfile(E::ts('New Profile')); CRM_Utils_System::setTitle(E::ts('New Twingle API profile')); break; } // Assign template variables. $this->assign('op', $this->_op); - $this->assign('profile_name', $profile_name); + $this->assign('profile_name', $this->profile->getName()); + $this->assign('is_default', $this->profile->is_default()); // Add form elements. $is_default = $profile_name == 'default'; diff --git a/CRM/Twingle/Page/Profiles.php b/CRM/Twingle/Page/Profiles.php index e8f014b..919965d 100644 --- a/CRM/Twingle/Page/Profiles.php +++ b/CRM/Twingle/Page/Profiles.php @@ -22,10 +22,11 @@ class CRM_Twingle_Page_Profiles extends CRM_Core_Page { public function run() { 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::getProfiles() as $profile_id => $profile) { + $profiles[$profile_id]['id'] = $profile_id; + $profiles[$profile_id]['name'] = $profile->getName(); foreach (CRM_Twingle_Profile::allowedAttributes() as $attribute) { - $profiles[$profile_name][$attribute] = $profile->getAttribute($attribute); + $profiles[$profile_id][$attribute] = $profile->getAttribute($attribute); } } $this->assign('profiles', $profiles); diff --git a/CRM/Twingle/Profile.php b/CRM/Twingle/Profile.php index a78f576..ae62782 100644 --- a/CRM/Twingle/Profile.php +++ b/CRM/Twingle/Profile.php @@ -25,7 +25,13 @@ use Civi\Twingle\Exceptions\ProfileException as ProfileException; class CRM_Twingle_Profile { /** - * @var string + * @var int $id + * The id of the profile. + */ + protected $id = NULL; + + /** + * @var string $name * The name of the profile. */ protected $name = NULL; @@ -43,8 +49,10 @@ class CRM_Twingle_Profile { * The name of the profile. * @param array $data * The properties of the profile + * @param int|NULL $id */ - public function __construct($name, $data) { + public function __construct($name, $data, $id = NULL) { + $this->id = $id; $this->name = $name; $allowed_attributes = self::allowedAttributes(); $this->data = $data + array_combine( @@ -65,6 +73,18 @@ class CRM_Twingle_Profile { WHERE name = %1', [1 => [$this->name, 'String']]); } + /** + * Copy this profile by returning a clone with all unique information removed. + * + * @return CRM_Twingle_Profile + */ + public function copy() { + $copy = clone $this; + $copy->id = NULL; + $copy->data['selector'] = NULL; + return $copy; + } + /** * Checks whether the profile's selector matches the given project ID. * @@ -107,6 +127,24 @@ class CRM_Twingle_Profile { return $this->data; } + /** + * Retrieves the profile id. + * + * @return int + */ + public function getId() { + return $this->id; + } + + /** + * Set the profile id. + * + * @param int $id + */ + public function setId(int $id) { + $this->id = $id; + } + /** * Retrieves the profile name. * @@ -119,9 +157,9 @@ class CRM_Twingle_Profile { /** * Sets the profile name. * - * @param $name + * @param string $name */ - public function setName($name) { + public function setName(string $name) { $this->name = $name; } @@ -193,37 +231,76 @@ class CRM_Twingle_Profile { // make sure it's valid $this->verifyProfile(); - // 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'], - ]); + try { + if ($this->id !== NULL) { + // existing profile -> just update the config + CRM_Core_DAO::executeQuery( + "UPDATE civicrm_twingle_profile SET config = %2 WHERE id = %1", + [ + 1 => [$this->id, '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'], + ]); + } } - 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'], - ]); + catch (Exception $e) { + throw new ProfileException( + E::ts("Could not save/update profile: %1", [1 => $e->getMessage()]), + ProfileException::ERROR_CODE_COULD_NOT_SAVE_PROFILE + ); } } /** * Deletes the profile from the database + * + * @throws \CRM_Twingle_Exceptions_ProfileException */ public function deleteProfile() { - CRM_Core_DAO::executeQuery( - 'DELETE FROM civicrm_twingle_profile WHERE name = %1', - [1 => [$this->name, 'String']] - ); + // Do only reset default profile + if ($this->getName() == 'default') { + try { + $default_profile = CRM_Twingle_Profile::createDefaultProfile(); + $default_profile->setId($this->getId()); + $default_profile->saveProfile(); + + // Reset counter + CRM_Core_DAO::executeQuery("UPDATE civicrm_twingle_profile SET access_counter = 0, last_access = NULL WHERE id = %1", [ + 1 => [ + $this->id, + 'Integer' + ] + ]); + } catch (Exception $e) { + throw new ProfileException( + E::ts("Could not reset default profile: %1", [1 => $e->getMessage()]), + ProfileException::ERROR_CODE_COULD_NOT_RESET_PROFILE + ); + } + } + else { + try { + CRM_Core_DAO::executeQuery("DELETE FROM civicrm_twingle_profile WHERE id = %1", [ + 1 => [ + $this->id, + 'Integer' + ] + ]); + } catch (Exception $e) { + throw new ProfileException( + E::ts("Could not delete profile: %1", [1 => $e->getMessage()]), + ProfileException::ERROR_CODE_COULD_NOT_RESET_PROFILE + ); + } + } } /** @@ -305,9 +382,9 @@ class CRM_Twingle_Profile { */ public static function createDefaultProfile($name = 'default') { return new CRM_Twingle_Profile($name, [ - 'selector' => '', - 'xcm_profile' => '', - 'location_type_id' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK, + 'selector' => NULL, + 'xcm_profile' => '', + 'location_type_id' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK, 'location_type_id_organisation' => CRM_Twingle_Submission::LOCATION_TYPE_ID_WORK, // "Donation" 'financial_type_id' => 1, @@ -391,20 +468,19 @@ class CRM_Twingle_Profile { } /** - * Retrieves the profile with the given name. + * Retrieves the profile with the given ID. * - * @param string $name + * @param int|NULL $id * * @return CRM_Twingle_Profile | NULL + * @throws \Civi\Core\Exception\DBQueryException */ - public static function getProfile($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)); + public static function getProfile(int $id = NULL) { + if (!empty($id)) { + $profile_data = CRM_Core_DAO::executeQuery("SELECT id, name, config FROM civicrm_twingle_profile WHERE id = %1", + [1 => [$id, 'Integer']]); + if ($profile_data->fetch()) { + return new CRM_Twingle_Profile($profile_data->name, json_decode($profile_data->config, 1), (int) $profile_data->id); } } return NULL; @@ -416,16 +492,14 @@ class CRM_Twingle_Profile { * * @return array * profile_name => CRM_Twingle_Profile + * @throws \Civi\Core\Exception\DBQueryException */ public static function getProfiles() { // todo: cache? $profiles = []; - $profile_data = CRM_Core_DAO::executeQuery('SELECT name, config FROM civicrm_twingle_profile'); + $profile_data = CRM_Core_DAO::executeQuery("SELECT id, 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) - ); + $profiles[$profile_data->id] = new CRM_Twingle_Profile($profile_data->name, json_decode($profile_data->config, 1), (int) $profile_data->id); } return $profiles; } diff --git a/templates/CRM/Twingle/Page/Profiles.tpl b/templates/CRM/Twingle/Page/Profiles.tpl index b5b869e..cf3351e 100644 --- a/templates/CRM/Twingle/Page/Profiles.tpl +++ b/templates/CRM/Twingle/Page/Profiles.tpl @@ -33,6 +33,7 @@ {foreach from=$profiles item=profile} + {assign var="profile_id" value=$profile.id} {assign var="profile_name" value=$profile.name} {$profile.name} @@ -42,12 +43,12 @@ {ts domain="de.systopia.twingle"}{$profile_stats.$profile_name.access_counter_txt}{/ts} {ts domain="de.systopia.twingle"}{$profile_stats.$profile_name.last_access_txt}{/ts} - {ts domain="de.systopia.twingle"}Edit{/ts} - {ts domain="de.systopia.twingle"}Copy{/ts} + {ts domain="de.systopia.twingle"}Edit{/ts} + {ts domain="de.systopia.twingle"}Copy{/ts} {if $profile_name == 'default'} - {ts domain="de.systopia.twingle"}Reset{/ts} + {ts domain="de.systopia.twingle"}Reset{/ts} {else} - {ts domain="de.systopia.twingle"}Delete{/ts} + {ts domain="de.systopia.twingle"}Delete{/ts} {/if}