From d84ae5f67322209857c240950f703b80e86a950b Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 14 May 2018 16:32:41 +1200 Subject: [PATCH] Support custom data on the Relationship Type form IF enabled via extension. This is an effort at a way to genericise core forms that basically exist to do crud on an entity. We have a number of fairly straight forward forms of this type in core and, in order to allow extensions to use custom fields on a range of entities we should support editing them on these core crud forms. To add custom data support to an entity we need to a) add the custom data to the form using CRM_Custom_Form_CustomData::addToForm($this); b) ensure that the entity is saved using an api call not a BAO call c) add the custom data block to the tpl file - ie {include file="CRM/common/customDataBlock.tpl"} (the above is possible due to previous work to add support & simplify) In this PR the adding of the customData is done in the EntityFormTrait, the api is previously converted and the custom data is included by using a generic tpl to support metadata applied to the form. By using metadata on the form we can also give extension writers a lot more control over what is on the form as they can add to, alter, or remove the metadata in a php hook. This is the crux of this issue https://github.com/civicrm/civicrm-core/pull/12078 & it likewise is looking to use a generic field template to add fields based on metadata. A key difference between the 2 prs is that one uses divs & the other a table & that might preclude close sharing of the approach. Note this PR DOES have the impact of adding translate links to 2 localisable relationship type fields that did not currently have them - I think this is a good thing? Example of how to enable custom fields for RelationshipType in an extension. ``` civicrm_api3('OptionValue', 'create', [ 'option_group_id' => 'cg_extend_objects', 'name' => 'civicrm_relationship_type', 'label' => ts('Relationship Type'), 'value' => 'RelationshipType', ]); ``` --- CRM/Admin/Form/RelationshipType.php | 66 +++++-- CRM/Core/Form.php | 10 ++ CRM/Core/Form/EntityFormTrait.php | 169 ++++++++++++++++++ templates/CRM/Admin/Form/RelationshipType.tpl | 42 +---- templates/CRM/Core/Form/EntityForm.tpl | 46 +++++ templates/CRM/Core/Form/Field.tpl | 39 ++++ 6 files changed, 319 insertions(+), 53 deletions(-) create mode 100644 CRM/Core/Form/EntityFormTrait.php create mode 100644 templates/CRM/Core/Form/EntityForm.tpl create mode 100644 templates/CRM/Core/Form/Field.tpl diff --git a/CRM/Admin/Form/RelationshipType.php b/CRM/Admin/Form/RelationshipType.php index 2c07ed8863..ed13ef49a3 100644 --- a/CRM/Admin/Form/RelationshipType.php +++ b/CRM/Admin/Form/RelationshipType.php @@ -36,6 +36,50 @@ */ class CRM_Admin_Form_RelationshipType extends CRM_Admin_Form { + use CRM_Core_Form_EntityFormTrait; + + /** + * Fields for the entity to be assigned to the template. + * + * Fields may have keys + * - name (required to show in tpl from the array) + * - description (optional, will appear below the field) + * - not-auto-addable - this class will not attempt to add the field using addField. + * (this will be automatically set if the field does not have html in it's metadata + * or is not a core field on the form's entity). + * - help (option) add help to the field - e.g ['id' => 'id-source', 'file' => 'CRM/Contact/Form/Contact']] + * - template - use a field specific template to render this field + * @var array + */ + protected $entityFields = []; + + /** + * Set entity fields to be assigned to the form. + */ + protected function setEntityFields() { + $this->entityFields = [ + 'label_a_b' => [ + 'name' => 'label_a_b', + 'description' => ts("Label for the relationship from Contact A to Contact B. EXAMPLE: Contact A is 'Parent of' Contact B.") + ], + 'label_b_a' => [ + 'name' => 'label_b_a', + 'description' => ts("Label for the relationship from Contact B to Contact A. EXAMPLE: Contact B is 'Child of' Contact A. You may leave this blank for relationships where the name is the same in both directions (e.g. Spouse).") + ], + 'description' => ['name' => 'description'], + 'contact_types_a' => ['name' => 'contact_types_a', 'not-auto-addable' => TRUE], + 'contact_types_b' => ['name' => 'contact_types_b', 'not-auto-addable' => TRUE], + 'is_active' => ['name' => 'is_active'], + ]; + } + + /** + * Deletion message to be assigned to the form. + * + * @var string + */ + protected $deleteMessage; + /** * Explicitly declare the entity api name. */ @@ -43,21 +87,25 @@ class CRM_Admin_Form_RelationshipType extends CRM_Admin_Form { return 'RelationshipType'; } + /** + * Set the delete message. + * + * We do this from the constructor in order to do a translation. + */ + public function setDeleteMessage() { + $this->deleteMessage = ts('WARNING: Deleting this option will result in the loss of all Relationship records of this type.') . ts('This may mean the loss of a substantial amount of data, and the action cannot be undone.') . ts('Do you want to continue?'); + } + /** * Build the form object. */ public function buildQuickForm() { - parent::buildQuickForm(); - $this->setPageTitle(ts('Relationship Type')); + self::buildQuickEntityForm(); if ($this->_action & CRM_Core_Action::DELETE) { return; } - $this->applyFilter('__ALL__', 'trim'); - - $this->addField('label_a_b'); - $this->addField('label_b_a'); $this->addRule('label_a_b', ts('Label already exists in Database.'), 'objectExists', array('CRM_Contact_DAO_RelationshipType', $this->_id, 'label_a_b') ); @@ -65,8 +113,6 @@ class CRM_Admin_Form_RelationshipType extends CRM_Admin_Form { 'objectExists', array('CRM_Contact_DAO_RelationshipType', $this->_id, 'label_b_a') ); - $this->addField('description'); - $contactTypes = CRM_Contact_BAO_ContactType::getSelectElements(FALSE, TRUE, '__'); // add select for contact type @@ -81,8 +127,6 @@ class CRM_Admin_Form_RelationshipType extends CRM_Admin_Form { ) + $contactTypes ); - $this->addField('is_active'); - //only selected field should be allow for edit, CRM-4888 if ($this->_id && CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', $this->_id, 'is_reserved') @@ -96,8 +140,6 @@ class CRM_Admin_Form_RelationshipType extends CRM_Admin_Form { $this->freeze(); } - $this->assign('relationship_type_id', $this->_id); - } /** diff --git a/CRM/Core/Form.php b/CRM/Core/Form.php index 58c332f48b..15a0cd245f 100644 --- a/CRM/Core/Form.php +++ b/CRM/Core/Form.php @@ -269,8 +269,18 @@ class CRM_Core_Form extends HTML_QuickForm_Page { $this->addClass(CRM_Utils_System::getClassName($this)); $this->assign('snippet', CRM_Utils_Array::value('snippet', $_GET)); + $this->setTranslatedFields(); } + /** + * Set translated fields. + * + * This function is called from the class constructor, allowing us to set + * fields on the class that can't be set as properties due to need for + * translation or other non-input specific handling. + */ + protected function setTranslatedFields() {} + /** * Add one or more css classes to the form. * diff --git a/CRM/Core/Form/EntityFormTrait.php b/CRM/Core/Form/EntityFormTrait.php new file mode 100644 index 0000000000..c102c94e7f --- /dev/null +++ b/CRM/Core/Form/EntityFormTrait.php @@ -0,0 +1,169 @@ +entityFields; + } + + /** + * Explicitly declare the form context. + */ + public function getDefaultContext() { + return 'create'; + } + + /** + * Get entity fields for the entity to be added to the form. + * + * @var array + */ + public function getDeleteMessage() { + return $this->deleteMessage; + } + + /** + * Get the entity id being edited. + * + * @return int|null + */ + public function getEntityId() { + return $this->_id; + } + /** + * If the custom data is in the submitted data (eg. added via ajax loaded form) add to form. + */ + public function addCustomDataToForm() { + $customisableEntities = CRM_Core_SelectValues::customGroupExtends(); + if (isset($customisableEntities[$this->getDefaultEntity()])) { + CRM_Custom_Form_CustomData::addToForm($this); + } + } + + /** + * Build the form object. + */ + public function buildQuickEntityForm() { + if ($this->_action & CRM_Core_Action::DELETE) { + $this->buildDeleteForm(); + return; + } + $this->applyFilter('__ALL__', 'trim'); + $this->addEntityFieldsToTemplate(); + $this->assign('entityFields', $this->entityFields); + $this->assign('entityID', $this->getEntityId()); + $this->assign('entityInClassFormat', strtolower(str_replace('_', '-', $this->getDefaultEntity()))); + $this->assign('entityTable', CRM_Core_DAO_AllCoreTables::getTableForClass(CRM_Core_DAO_AllCoreTables::getFullName($this->getDefaultEntity()))); + $this->addCustomDataToForm(); + $this->addFormButtons(); + } + + /** + * Build the form for any deletion. + */ + protected function buildDeleteForm() { + $this->assign('deleteMessage', $this->getDeleteMessage()); + $this->addFormButtons(); + } + + /** + * Add relevant buttons to the form. + */ + protected function addFormButtons() { + if ($this->_action & CRM_Core_Action::VIEW || $this->_action & CRM_Core_Action::PREVIEW) { + $this->addButtons(array( + array( + 'type' => 'cancel', + 'name' => ts('Done'), + 'isDefault' => TRUE, + ), + ) + ); + } + else { + $this->addButtons(array( + array( + 'type' => 'next', + 'name' => $this->_action & CRM_Core_Action::DELETE ? ts('Delete') : ts('Save'), + 'isDefault' => TRUE, + ), + array( + 'type' => 'cancel', + 'name' => ts('Cancel'), + ), + ) + ); + } + } + + /** + * Set translated fields. + * + * This function is called from the class constructor, allowing us to set + * fields on the class that can't be set as properties due to need for + * translation or other non-input specific handling. + */ + protected function setTranslatedFields() { + $this->setEntityFields(); + $this->setDeleteMessage(); + $metadata = civicrm_api3($this->getDefaultEntity(), 'getfields', ['action' => 'create']); + $this->metadata = $metadata['values']; + foreach ($this->metadata as $fieldName => $spec) { + if (isset($this->entityFields[$fieldName])) { + if ($spec['localizable']) { + $this->entityFields[$fieldName]['is_add_translate_dialog'] = TRUE; + } + if (empty($spec['html'])) { + $this->entityFields[$fieldName]['not-auto-addable'] = TRUE; + } + } + } + } + + /** + * Add defined entity field to template. + */ + protected function addEntityFieldsToTemplate() { + foreach ($this->getEntityFields() as $fieldSpec) { + if (empty($fieldSpec['not-auto-addable'])) { + $this->addField($fieldSpec['name']); + } + } + } + +} diff --git a/templates/CRM/Admin/Form/RelationshipType.tpl b/templates/CRM/Admin/Form/RelationshipType.tpl index 63b86f289e..ffd91f73d3 100644 --- a/templates/CRM/Admin/Form/RelationshipType.tpl +++ b/templates/CRM/Admin/Form/RelationshipType.tpl @@ -24,44 +24,4 @@ +--------------------------------------------------------------------+ *} {* this template is used for adding/editing relationship types *} -
-
{include file="CRM/common/formButtons.tpl" location="top"}
- {if $action eq 8} -
-
- {ts}WARNING: Deleting this option will result in the loss of all Relationship records of this type.{/ts} {ts}This may mean the loss of a substantial amount of data, and the action cannot be undone.{/ts} {ts}Do you want to continue?{/ts} - - -
- {else} - - - - - - - - - - - - - - - - - - - - - - - - - -
{$form.label_a_b.label} {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_relationship_type' field='label_a_b' id=$relationship_type_id}{/if}{$form.label_a_b.html}
- {ts}Label for the relationship from Contact A to Contact B. EXAMPLE: Contact A is 'Parent of' Contact B.{/ts}
{$form.label_b_a.label} {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_relationship_type' field='label_b_a' id=$relationship_type_id}{/if}{$form.label_b_a.html}
- {ts}Label for the relationship from Contact B to Contact A. EXAMPLE: Contact B is 'Child of' Contact A. You may leave this blank for relationships where the name is the same in both directions (e.g. Spouse).{/ts}
{$form.contact_types_a.label}{$form.contact_types_a.html}
{$form.contact_types_b.label}{$form.contact_types_b.html}
{$form.description.label} {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_relationship_type' field='description' id=$relationship_type_id}{/if}{$form.description.html}
{$form.is_active.label}{$form.is_active.html}
- {/if} -
{include file="CRM/common/formButtons.tpl" location="bottom"}
-
+{include file="CRM/Core/Form/EntityForm.tpl"} diff --git a/templates/CRM/Core/Form/EntityForm.tpl b/templates/CRM/Core/Form/EntityForm.tpl new file mode 100644 index 0000000000..c59b0d2cf5 --- /dev/null +++ b/templates/CRM/Core/Form/EntityForm.tpl @@ -0,0 +1,46 @@ +{* + +--------------------------------------------------------------------+ + | CiviCRM version 5 | + +--------------------------------------------------------------------+ + | Copyright CiviCRM LLC (c) 2004-2018 | + +--------------------------------------------------------------------+ + | This file is a part of CiviCRM. | + | | + | CiviCRM is free software; you can copy, modify, and distribute it | + | under the terms of the GNU Affero General Public License | + | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | + | | + | CiviCRM is distributed in the hope that it will be useful, but | + | WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | + | See the GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public | + | License and the CiviCRM Licensing Exception along | + | with this program; if not, contact CiviCRM LLC | + | at info[AT]civicrm[DOT]org. If you have questions about the | + | GNU Affero General Public License or the licensing of CiviCRM, | + | see the CiviCRM license FAQ at http://civicrm.org/licensing | + +--------------------------------------------------------------------+ +*} +{* this template is used for adding/editing entities *} +
+
{include file="CRM/common/formButtons.tpl" location="top"}
+ {if $action eq 8} +
+
+ {$deleteMessage|escape} +
+ {else} + + {foreach from=$entityFields item=fieldSpec} + {assign var=fieldName value=$fieldSpec.name} + + {include file="CRM/Core/Form/Field.tpl"} + + {/foreach} +
+ {include file="CRM/common/customDataBlock.tpl"} + {/if} +
{include file="CRM/common/formButtons.tpl" location="bottom"}
+
diff --git a/templates/CRM/Core/Form/Field.tpl b/templates/CRM/Core/Form/Field.tpl new file mode 100644 index 0000000000..79d68bda40 --- /dev/null +++ b/templates/CRM/Core/Form/Field.tpl @@ -0,0 +1,39 @@ +{* + +--------------------------------------------------------------------+ + | CiviCRM version 5 | + +--------------------------------------------------------------------+ + | Copyright CiviCRM LLC (c) 2004-2018 | + +--------------------------------------------------------------------+ + | This file is a part of CiviCRM. | + | | + | CiviCRM is free software; you can copy, modify, and distribute it | + | under the terms of the GNU Affero General Public License | + | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | + | | + | CiviCRM is distributed in the hope that it will be useful, but | + | WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | + | See the GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public | + | License and the CiviCRM Licensing Exception along | + | with this program; if not, contact CiviCRM LLC | + | at info[AT]civicrm[DOT]org. If you have questions about the | + | GNU Affero General Public License or the licensing of CiviCRM, | + | see the CiviCRM license FAQ at http://civicrm.org/licensing | + +--------------------------------------------------------------------+ +*} +{if $fieldSpec.template} + {include file=$fieldSpec.template} +{else} + {$form.$fieldName.label} + {if $fieldSpec.help}{assign var=help value=$fieldSpec.help}{capture assign=helpFile}{if $fieldSpec.help} + {$fieldSpec.help} + {else}''{/if} + {/capture}{help id=$help.id file=$help.file}{/if} + {if $action == 2 && $fieldSpec.is_add_translate_dialog}{include file='CRM/Core/I18n/Dialog.tpl' table=$entityTable field=$fieldName id=$entityID}{/if} + + {if $form.$fieldName.html}{if $fieldSpec.formatter === 'crmMoney'}{$form.$fieldName.html|crmMoney}{else}{$form.$fieldName.html}{/if}{else}{$fieldSpec.place_holder}{/if}
+ {if $fieldSpec.description}{$fieldSpec.description}{/if} + +{/if} -- 2.25.1