Skip to content

Commit

Permalink
PCHR-3465: Support inline relationship type editing
Browse files Browse the repository at this point in the history
Included in CiviCRM 5.2.0
Core PR: civicrm#11853
  • Loading branch information
mickadoo committed Apr 19, 2018
1 parent dd70ef8 commit 75bb74a
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 11 deletions.
71 changes: 70 additions & 1 deletion CRM/Contact/BAO/Relationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ public static function getContactRelationshipType(
$relationship->id = $relationshipId;
if ($relationship->find(TRUE)) {
$contact = new CRM_Contact_DAO_Contact();
$contact->id = ($relationship->contact_id_a === $contactId) ? $relationship->contact_id_b : $relationship->contact_id_a;
$contact->id = ($relationship->contact_id_a == $contactId) ? $relationship->contact_id_b : $relationship->contact_id_a;

if ($contact->find(TRUE)) {
$otherContactType = $contact->contact_type;
Expand Down Expand Up @@ -2149,4 +2149,73 @@ public static function getContactRelationshipSelector(&$params) {
return $relationshipsDT;
}

/**
* @inheritdoc
*/
public static function buildOptions($fieldName, $context = NULL, $props = array()) {
if ($fieldName === 'relationship_type_id') {
return self::buildRelationshipTypeOptions($props);
}

return parent::buildOptions($fieldName, $context, $props);
}

/**
* Builds a list of options available for relationship types
*
* @param array $params
* - contact_type: Limits by contact type on the "A" side
* - relationship_id: Used to find the value for contact type for "B" side.
* If contact_a matches provided contact_id then type of contact_b will
* be used. Otherwise uses type of contact_a. Must be used with contact_id
* - contact_id: Limits by contact types of this contact on the "A" side
* - is_form: Returns array with keys indexed for use in a quickform
* - relationship_direction: For relationship types with duplicate names
* on both sides, defines which option should be returned, a_b or b_a
*
* @return array
*/
public static function buildRelationshipTypeOptions($params = array()) {
$contactId = CRM_Utils_Array::value('contact_id', $params);
$direction = CRM_Utils_Array::value('relationship_direction', $params, 'a_b');
$relationshipId = CRM_Utils_Array::value('relationship_id', $params);
$contactType = CRM_Utils_Array::value('contact_type', $params);
$isForm = CRM_Utils_Array::value('is_form', $params);
$showAll = FALSE;

// getContactRelationshipType will return an empty set if these are not set
if (!$contactId && !$relationshipId && !$contactType) {
$showAll = TRUE;
}

$labels = self::getContactRelationshipType(
$contactId,
$direction,
$relationshipId,
$contactType,
$showAll,
'label'
);

if ($isForm) {
return $labels;
}

$names = self::getContactRelationshipType(
$contactId,
$direction,
$relationshipId,
$contactType,
$showAll,
'name'
);

// ensure $names contains only entries in $labels
$names = array_intersect_key($names, $labels);

$nameToLabels = array_combine($names, $labels);

return $nameToLabels;
}

}
20 changes: 17 additions & 3 deletions CRM/Contact/Form/Relationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,7 @@ public function buildQuickForm() {
// Select list
$relationshipList = CRM_Contact_BAO_Relationship::getContactRelationshipType($this->_contactId, $this->_rtype, $this->_relationshipId);

// Metadata needed on clientside
$this->assign('relationshipData', self::getRelationshipTypeMetadata($relationshipList));
$this->assign('contactTypes', CRM_Contact_BAO_ContactType::contactTypeInfo(TRUE));

foreach ($this->_allRelationshipNames as $id => $vals) {
if ($vals['name_a_b'] === 'Employee of') {
Expand All @@ -309,7 +308,22 @@ public function buildQuickForm() {
}
}

$this->addField('relationship_type_id', array('options' => array('' => ts('- select -')) + $relationshipList, 'class' => 'huge', 'placeholder' => '- select -'), TRUE);
$this->addField(
'relationship_type_id',
array(
'options' => array('' => ts('- select -')) + $relationshipList,
'class' => 'huge',
'placeholder' => '- select -',
'option_url' => 'civicrm/admin/reltype',
'option_context' => array(
'contact_id' => $this->_contactId,
'relationship_direction' => $this->_rtype,
'relationship_id' => $this->_relationshipId,
'is_form' => TRUE,
),
),
TRUE
);

$label = $this->_action & CRM_Core_Action::ADD ? ts('Contact(s)') : ts('Contact');
$contactField = $this->addField('related_contact_id', array('label' => $label, 'name' => 'contact_id_b', 'multiple' => TRUE, 'create' => TRUE), TRUE);
Expand Down
6 changes: 6 additions & 0 deletions CRM/Core/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,12 @@ public function &add(
}
}

// Add context for the editing of option groups
if (isset($extra['option_context'])) {
$context = json_encode($extra['option_context']);
$element->setAttribute('data-option-edit-context', $context);
}

return $element;
}

Expand Down
7 changes: 7 additions & 0 deletions api/v3/Generic.php
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,13 @@ function _civicrm_api3_generic_getoptions_spec(&$params, $apiRequest) {
}
}
}

$entityName = _civicrm_api_get_entity_name_from_camel($apiRequest['entity']);
$getOptionsSpecFunction = '_civicrm_api3_' . $entityName . '_getoptions_spec';

if (function_exists($getOptionsSpecFunction)) {
$getOptionsSpecFunction($params);
}
}

/**
Expand Down
50 changes: 50 additions & 0 deletions api/v3/Relationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,53 @@ function civicrm_api3_relationship_setvalue($params) {
}
return $result;
}

function _civicrm_api3_relationship_getoptions_spec(&$params) {
$params['field']['options']['relationship_type_id'] = ts('Relationship Type ID');

// Add parameters for limiting relationship type ID
$relationshipTypePrefix = ts('(For relationship_type_id only) ');
$params['contact_id'] = [
'title' => ts('Contact ID'),
'description' => $relationshipTypePrefix . ts('Limits options to those'
. ' available to give contact'),
'type' => CRM_Utils_Type::T_INT,
'FKClassName' => 'CRM_Contact_DAO_Contact',
'FKApiName' => 'Contact',
];
$params['relationship_direction'] = [
'title' => ts('Relationship Direction'),
'description' => $relationshipTypePrefix . ts('For relationships where the '
. 'name is the same for both sides (i.e. "Spouse Of") show the option '
. 'from "A" (origin) side or "B" (target) side of the relationship?'),
'type' => CRM_Utils_Type::T_STRING,
'options' => ['a_b' => 'a_b', 'b_a' => 'b_a'],
'api.default' => 'a_b',
];
$params['relationship_id'] = [
'title' => ts('Reference Relationship ID'),
'description' => $relationshipTypePrefix . ts('If provided alongside '
. 'contact ID it will be used to establish the contact type of the "B" '
. 'side of the relationship and limit options based on it. If the '
. 'provided contact ID does not match the "A" side of this relationship '
. 'then the "A" side of this relationship will be used to limit options'),
'type' => CRM_Utils_Type::T_INT,
'FKClassName' => 'CRM_Contact_DAO_Relationship',
'FKApiName' => 'Relationship',
];
$contactTypes = CRM_Contact_BAO_ContactType::contactTypes();
$params['contact_type'] = [
'title' => ts('Contact Type'),
'description' => $relationshipTypePrefix . ts('Limits options to those '
. 'available to this contact type. Overridden by the contact type of '
. 'contact ID (if provided)'),
'type' => CRM_Utils_Type::T_STRING,
'options' => array_combine($contactTypes, $contactTypes),
];
$params['is_form'] = [
'title' => ts('Is Form?'),
'description' => $relationshipTypePrefix . ts('Formats the options for use'
. ' in a form if true. The format is <id>_a_b => <label>'),
'type' => CRM_Utils_Type::T_BOOLEAN
];
}
7 changes: 5 additions & 2 deletions js/crm.optionEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ jQuery(function($) {
*/
function rebuildOptions($existing, rebuilder) {
if ($existing.data('api-entity') && $existing.data('api-field')) {
CRM.api3($existing.data('api-entity'), 'getoptions', {
var params = {
sequential: 1,
field: $existing.data('api-field')
})
};
$.extend(params, $existing.data('option-edit-context'));

CRM.api3($existing.data('api-entity'), 'getoptions', params)
.done(function(data) {
rebuilder($existing, data.values);
});
Expand Down
86 changes: 82 additions & 4 deletions templates/CRM/Contact/Form/Relationship.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,88 @@
CRM.$(function($) {
var
$form = $("form.{/literal}{$form.formClass}{literal}"),
relationshipData = {/literal}{$relationshipData|@json_encode}{literal};
$('[name=relationship_type_id]', $form).change(function() {
$relationshipTypeSelect = $('[name=relationship_type_id]', $form),
relationshipData = {},
contactTypes = {/literal}{$contactTypes|@json_encode}{literal};
(function init () {
// Refresh options if relationship types were edited
$('body').on('crmOptionsEdited', 'a.crm-option-edit-link', refreshRelationshipData);
// Initial load and trigger change on select
refreshRelationshipData().done(function() {
$relationshipTypeSelect.change();
});
$relationshipTypeSelect.change(function() {
var $select = $(this);
// ensure we have relationship data before changing anything
getRelationshipData().then(function() {
updateSelect($select);
})
});
})();
/**
* Fetch contact types and reset relationship data
*/
function refreshRelationshipData() {
// reset
relationshipData = {};
return getRelationshipData();
}
/**
* Fetches the relationship data using latest relationship types
*/
function getRelationshipData() {
var defer = $.Deferred();
if (!$.isEmptyObject(relationshipData)) {
defer.resolve(relationshipData);
}
CRM.api3("RelationshipType", "get", {"options": {"limit":0}})
.done(function (data) {
$.each(data.values, function (key, relType) {
// Loop over the suffixes for a relationship type
$.each(["a", "b"], function (index, suffix) {
var subtype = relType["contact_subtype_" + suffix];
var type = subtype || relType["contact_type_" + suffix];
var label = getContactTypeLabel(type) || "Contact";
label = label.toLowerCase();
relType["placeholder_" + suffix] = "- select " + label + " -";
});
relationshipData[relType["id"]] = relType;
});
defer.resolve(relationshipData);
});
return defer.promise();
}
/**
* Gets a contact type label based on a provided name
* @param {String} name - the name of the contact type
*/
function getContactTypeLabel(name) {
var label = "";
$.each(contactTypes, function(index, contactType) {
if (contactType.name === name) {
label = contactType.label;
return false;
}
});
return label;
}
function updateSelect($select) {
var
val = $(this).val(),
val = $select.val(),
$contactField = $('#related_contact_id[type=text]', $form);
if (!val && $contactField.length) {
$contactField
Expand Down Expand Up @@ -190,7 +268,7 @@
CRM.buildCustomData('Relationship', rType);
}
}).change();
}
});
{/literal}
</script>
Expand Down
8 changes: 7 additions & 1 deletion templates/CRM/common/enableDisableApi.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@
}

function refresh() {
$a.trigger('crmPopupFormSuccess');
// the opposite of the current status based on row class
var newStatus = $row.hasClass('disabled');
$a.trigger('crmPopupFormSuccess', {
'entity': info.entity,
'id': info.id,
'enabled': newStatus
});
CRM.refreshParent($row);
}

Expand Down

0 comments on commit 75bb74a

Please sign in to comment.