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

Upgrade script for dev/core#1046 #15556

Merged
Merged
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
204 changes: 200 additions & 4 deletions CRM/Upgrade/Incremental/php/FiveTwenty.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,21 @@ class CRM_Upgrade_Incremental_php_FiveTwenty extends CRM_Upgrade_Incremental_Bas
* @param null $currentVer
*/
public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) {
// Example: Generate a pre-upgrade message.
// if ($rev == '5.12.34') {
// $preUpgradeMessage .= '<p>' . ts('A new permission, "%1", has been added. This permission is now used to control access to the Manage Tags screen.', array(1 => ts('manage tags'))) . '</p>';
// }
if ($rev == '5.20.alpha1') {
if (CRM_Core_DAO::checkTableExists('civicrm_persistent') && CRM_Core_DAO::checkTableHasData('civicrm_persistent')) {
$preUpgradeMessage .= '<br/>' . ts("WARNING: The table \"<code>civicrm_persistent</code>\" is flagged for removal because all official records show it being unused. However, the upgrader has detected data in this copy of \"<code>civicrm_persistent</code>\". Please <a href='%1' target='_blank'>report</a> anything you can about the usage of this table. In the mean-time, the data will be preserved.", [
1 => 'https://civicrm.org/bug-reporting',
]);
}

$config = CRM_Core_Config::singleton();
if (in_array('CiviCase', $config->enableComponents)) {
// Do dry-run to get warning messages.
$messages = self::_changeCaseTypeLabelToName(TRUE);
foreach ($messages as $message) {
$preUpgradeMessage .= "<p>{$message}</p>\n";
}
}
}
}

Expand Down Expand Up @@ -99,6 +104,7 @@ public function upgrade_5_20_alpha1($rev) {
$config = CRM_Core_Config::singleton();
if (in_array('CiviCase', $config->enableComponents)) {
$this->addTask('Change direction of autoassignees in case type xml', 'changeCaseTypeAutoassignee');
$this->addTask('Change labels back to names in case type xml', 'changeCaseTypeLabelToName');
}
$this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev);
$this->addTask('Add "Template" contribution status', 'templateStatus');
Expand Down Expand Up @@ -236,4 +242,194 @@ private static function isBidirectionalRelationship($relationshipTypeId) {
return FALSE;
}

/**
* Change labels in case type xml definition back to names. (dev/core#1046)
* ONLY for ones using database storage - don't want to "fork" case types
* that aren't currently forked.
*
* @return bool
*/
public static function changeCaseTypeLabelToName() {
self::_changeCaseTypeLabelToName(FALSE);
return TRUE;
}

/**
* Change labels in case type xml definition back to names. (dev/core#1046)
* ONLY for ones using database storage - don't want to "fork" case types
* that aren't currently forked.
*
* @param $isDryRun bool
* If TRUE then don't actually change anything just report warnings.
*
* @return array List of warning messages.
*/
public static function _changeCaseTypeLabelToName($isDryRun = FALSE) {
$messages = [];
self::$relationshipTypes = civicrm_api3('RelationshipType', 'get', [
'options' => ['limit' => 0],
])['values'];

// Get all case types definitions that are using db storage
$dao = CRM_Core_DAO::executeQuery("SELECT id FROM civicrm_case_type WHERE definition IS NOT NULL AND definition <> ''");
while ($dao->fetch()) {
// array_merge so that existing numeric keys don't get overwritten
$messages = array_merge($messages, self::_processCaseTypeLabelName($isDryRun, $dao->id));
}
return $messages;
}

/**
* Process a single case type for _changeCaseTypeLabelToName()
*
* @param $isDryRun bool
* If TRUE then don't actually change anything just report warnings.
* @param $caseTypeId int
*/
private static function _processCaseTypeLabelName($isDryRun, $caseTypeId) {
$messages = [];
$isDirty = FALSE;

// Get the case type definition
$caseType = civicrm_api3(
'CaseType',
'get',
['id' => $caseTypeId]
)['values'][$caseTypeId];

foreach ($caseType['definition']['caseRoles'] as $roleSequenceId => $role) {
// First double-check that there is a unique match on label so we
// don't get it wrong.
// There's maybe a fancy way to do this with array_XXX functions but
// need to take into account edge cases where bidirectional but name
// is different, or where somehow two labels are the same across types,
// so do old-fashioned loop.

$cantConvertMessage = NULL;
$foundName = NULL;
foreach (self::$relationshipTypes as $relationshipType) {
// does it match one of our existing labels
if ($relationshipType['label_a_b'] === $role['name'] || $relationshipType['label_b_a'] === $role['name']) {
// So either it's ambiguous, in which case exit loop with a message,
// or we have the name, so exit loop with that.
$cantConvertMessage = self::checkAmbiguous($relationshipType, $caseType['name'], $role['name']);
if (empty($cantConvertMessage)) {
// not ambiguous, so note the corresponding name for the direction
$foundName = ($relationshipType['label_a_b'] === $role['name']) ? $relationshipType['name_a_b'] : $relationshipType['name_b_a'];
}
break;
}
}

if (empty($foundName) && empty($cantConvertMessage)) {
// It's possible we went through all relationship types and didn't
// find any match, so don't change anything.
$cantConvertMessage = ts("Case Type '%1', role '%2' doesn't seem to be a valid role. See the administration console status messages for more info.", [
1 => htmlspecialchars($caseType['name']),
2 => htmlspecialchars($role['name']),
]);
}
// Only two possibilities now are we have a name, or we have a message.
// So the if($foundName) is redundant, but seems clearer somehow.
if ($foundName && empty($cantConvertMessage)) {
// If name and label are the same don't need to update anything.
if ($foundName !== $role['name']) {
$caseType['definition']['caseRoles'][$roleSequenceId]['name'] = $foundName;
$isDirty = TRUE;
}
}
else {
$messages[] = $cantConvertMessage;
}

// end looping thru all roles in definition
}

// If this is a dry run during preupgrade checks we can skip this and
// just return any messages.
// If for real, then update the case type and here if there's errors
// we don't really have a choice but to stop the entire upgrade
// completely. There's no way to just send back messages during a queue
// run. But we can log a message to error log so that the user has a
// little more specific info about which case type.
if ($isDirty && !$isDryRun) {
$exception = NULL;
try {
$api_result = civicrm_api3('CaseType', 'create', $caseType);
}
catch (Exception $e) {
$exception = $e;
$errorMessage = ts("Error updating case type '%1': %2", [
1 => htmlspecialchars($caseType['name']),
2 => htmlspecialchars($e->getMessage()),
]);
CRM_Core_Error::debug_log_message($errorMessage);
}
if (!empty($api_result['is_error'])) {
$errorMessage = ts("Error updating case type '%1': %2", [
1 => htmlspecialchars($caseType['name']),
2 => htmlspecialchars($api_result['error_message']),
]);
CRM_Core_Error::debug_log_message($errorMessage);
$exception = new Exception($errorMessage);
}
// We need to rethrow the error which unfortunately stops the
// entire upgrade including any further tasks. But otherwise
// the only way to notify the user something went wrong is with a
// crazy workaround.
if ($exception) {
throw $exception;
}
}

return $messages;
}

/**
* Helper for _processCaseTypeLabelName to check if a label can't be
* converted unambiguously to name.
*
* If it's bidirectional, we can't convert it if there's an edge case
* where the two names are different.
*
* If it's unidirectional, we can't convert it if there's an edge case
* where there's another type that has the same label.
*
* @param $relationshipType array
* @param $caseTypeName string
* @param $xmlRoleName string
*
* @return string|NULL
*/
private static function checkAmbiguous($relationshipType, $caseTypeName, $xmlRoleName) {
$cantConvertMessage = NULL;
if ($relationshipType['label_a_b'] === $relationshipType['label_b_a']) {
// bidirectional, so check if names are different for some reason
if ($relationshipType['name_a_b'] !== $relationshipType['name_b_a']) {
$cantConvertMessage = ts("Case Type '%1', role '%2' has an ambiguous configuration and can't be automatically updated. See the administration console status messages for more info.", [
1 => htmlspecialchars($caseTypeName),
2 => htmlspecialchars($xmlRoleName),
]);
}
}
else {
// Check if it matches either label_a_b or label_b_a for another type
foreach (self::$relationshipTypes as $innerLoopId => $innerLoopType) {
if ($innerLoopId == $relationshipType['id']) {
// Only check types that aren't the same one we're on.
// Sidenote: The loop index is integer but the 'id' member is string
continue;
}
if ($innerLoopType['label_a_b'] === $xmlRoleName || $innerLoopType['label_b_a'] === $xmlRoleName) {
$cantConvertMessage = ts("Case Type '%1', role '%2' has an ambiguous configuration where the role matches multiple labels and so can't be automatically updated. See the administration console status messages for more info.", [
1 => htmlspecialchars($caseTypeName),
2 => htmlspecialchars($xmlRoleName),
]);
break;
}
}
}
return $cantConvertMessage;
}

}
Loading