Skip to content

Commit

Permalink
Merge pull request #24512 from colemanw/contactTypeSearch
Browse files Browse the repository at this point in the history
AdminUI - Add SearchKit-based screen for Administer Contact Types
  • Loading branch information
eileenmcnaughton authored Sep 14, 2022
2 parents 2a534f6 + bb23116 commit 06ddfae
Show file tree
Hide file tree
Showing 22 changed files with 284 additions and 13 deletions.
7 changes: 7 additions & 0 deletions CRM/Admin/Form/ContactType.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
*/
class CRM_Admin_Form_ContactType extends CRM_Admin_Form {

public function preProcess(): void {
CRM_Utils_Request::retrieve('action', 'String', $this);
CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0);
$this->set('BAOName', 'CRM_Contact_BAO_ContactType');
parent::preProcess();
}

/**
* Build the form object.
*/
Expand Down
5 changes: 2 additions & 3 deletions CRM/Admin/Page/ContactType.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function &links() {
self::$_links = [
CRM_Core_Action::UPDATE => [
'name' => ts('Edit'),
'url' => 'civicrm/admin/options/subtype',
'url' => 'civicrm/admin/options/subtype/edit',
'qs' => 'action=update&id=%%id%%&reset=1',
'title' => ts('Edit Contact Type'),
],
Expand All @@ -66,7 +66,7 @@ public function &links() {
],
CRM_Core_Action::DELETE => [
'name' => ts('Delete'),
'url' => 'civicrm/admin/options/subtype',
'url' => 'civicrm/admin/options/subtype/edit',
'qs' => 'action=delete&id=%%id%%',
'title' => ts('Delete Contact Type'),
],
Expand All @@ -81,7 +81,6 @@ public function &links() {
public function run() {
$action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 0);
$this->assign('action', $action);
$id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0);
if (!$action) {
$this->browse();
}
Expand Down
20 changes: 20 additions & 0 deletions CRM/Contact/BAO/ContactType.php
Original file line number Diff line number Diff line change
Expand Up @@ -886,4 +886,24 @@ public static function getAllContactTypes() {
return $contactTypes;
}

/**
* @param string $entityName
* @param string $action
* @param array $record
* @param $userID
* @return bool
* @see CRM_Core_DAO::checkAccess
*/
public static function _checkAccess(string $entityName, string $action, array $record, $userID): bool {
// Only records with a parent may be deleted
if ($action === 'delete') {
if (!array_key_exists('parent_id', $record)) {
$record['parent_id'] = CRM_Core_DAO::getFieldValue(parent::class, $record['id'], 'parent_id');
}
return (bool) $record['parent_id'];
}
// Gatekeeper permissions suffice for everything else
return TRUE;
}

}
13 changes: 12 additions & 1 deletion CRM/Contact/DAO/ContactType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*
* Generated from xml/schema/CRM/Contact/ContactType.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
* (GenCodeChecksum:13c81c203681009e8f71fd8387ebdbc2)
* (GenCodeChecksum:9858d69cd4bfdc5a3ce45c77eecd1145)
*/

/**
Expand All @@ -30,6 +30,17 @@ class CRM_Contact_DAO_ContactType extends CRM_Core_DAO {
*/
public static $_log = FALSE;

/**
* Paths for accessing this entity in the UI.
*
* @var string[]
*/
protected static $_paths = [
'add' => 'civicrm/admin/options/subtype/edit?action=add&reset=1',
'update' => 'civicrm/admin/options/subtype/edit?action=update&id=[id]&reset=1',
'delete' => 'civicrm/admin/options/subtype/edit?action=delete&id=[id]&reset=1',
];

/**
* Contact Type ID
*
Expand Down
5 changes: 5 additions & 0 deletions CRM/Core/xml/Menu/Admin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@
<adminGroup>Customize Data and Screens</adminGroup>
<weight>40</weight>
</item>
<item>
<path>civicrm/admin/options/subtype/edit</path>
<title>Edit Contact Type</title>
<page_callback>CRM_Admin_Form_ContactType</page_callback>
</item>
<item>
<path>civicrm/admin/options/gender</path>
<title>Gender Options</title>
Expand Down
2 changes: 1 addition & 1 deletion CRM/Utils/Check/Component/ContactTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function checkContactTypeIcons() {
'fa-picture-o'
);
foreach ($contactTypesWithImages as $contactType) {
$message->addAction($contactType['label'], FALSE, 'href', ['path' => 'civicrm/admin/options/subtype', 'query' => ['action' => 'update', 'id' => $contactType['id'], 'reset' => 1]], 'fa-pencil');
$message->addAction($contactType['label'], FALSE, 'href', ['path' => 'civicrm/admin/options/subtype/edit', 'query' => ['action' => 'update', 'id' => $contactType['id'], 'reset' => 1]], 'fa-pencil');
}
$messages[] = $message;
}
Expand Down
2 changes: 1 addition & 1 deletion Civi/Api4/ContactType.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*
* @see https://docs.civicrm.org/user/en/latest/organising-your-data/contacts/#contact-subtypes
* @see \Civi\Api4\Contact
* @searchable none
* @searchable secondary
* @since 5.19
* @package Civi\Api4
*/
Expand Down
4 changes: 3 additions & 1 deletion Civi/Api4/Generic/DAODeleteAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ public function _run(Result $result) {
$items = $this->getBatchRecords();

if ($this->getCheckPermissions()) {
$idField = CoreUtil::getIdFieldName($this->getEntityName());
foreach ($items as $key => $item) {
if (!CoreUtil::checkAccessRecord($this, $item, \CRM_Core_Session::getLoggedInContactID() ?: 0)) {
// Don't pass the entire item because only the id is a trusted value
if (!CoreUtil::checkAccessRecord($this, [$idField => $item[$idField]], \CRM_Core_Session::getLoggedInContactID() ?: 0)) {
throw new UnauthorizedException("ACL check failed");
}
$items[$key]['check_permissions'] = TRUE;
Expand Down
9 changes: 9 additions & 0 deletions ext/civicrm_admin_ui/ang/afsearchAdminContactTypes.aff.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div af-fieldset="">
<div class="af-markup">
<div class="help">
{{:: ts('CiviCRM comes with 3 basic (built-in) contact types: Individual, Household, and Organization. You can create additional contact types based on these basic types to further differentiate contacts (for example you might create Student, Parent, Staff, and /or Volunteer "subtypes" from the basic Individual type...). You can also re-name the built-in types. Contact subtypes are especially useful when you need to collect and display different sets of custom data for different types of contacts.') }}
<a href="https://docs.civicrm.org/user/en/latest/organising-your-data/contacts/" target="_blank" class="crm-doc-link no-popup">{{:: ts('Learn more...') }}</a>
</div>
</div>
<crm-search-display-table search-name="Administer_Contact_Types" display-name="Contact_Types_Table"></crm-search-display-table>
</div>
8 changes: 8 additions & 0 deletions ext/civicrm_admin_ui/ang/afsearchAdminContactTypes.aff.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "search",
"title": "Contact Types",
"description": "Administer contact types and sub-types",
"icon": "fa-list-alt",
"server_route": "civicrm/admin/options/subtype",
"permission": "access CiviCRM"
}
151 changes: 151 additions & 0 deletions ext/civicrm_admin_ui/managed/SavedSearch_Administer_Contact_Types.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php
return [
[
'name' => 'SavedSearch_Administer_Contact_Types',
'entity' => 'SavedSearch',
'cleanup' => 'always',
'update' => 'unmodified',
'params' => [
'version' => 4,
'values' => [
'name' => 'Administer_Contact_Types',
'label' => 'Administer Contact Types',
'form_values' => NULL,
'mapping_id' => NULL,
'search_custom_id' => NULL,
'api_entity' => 'ContactType',
'api_params' => [
'version' => 4,
'select' => [
'label',
'parent_id:label',
'description',
],
'orderBy' => [],
'where' => [],
'groupBy' => [],
'join' => [],
'having' => [],
],
'expires_date' => NULL,
'description' => NULL,
],
],
],
[
'name' => 'SavedSearch_Administer_Contact_Types_SearchDisplay_Contact_Types_Table',
'entity' => 'SearchDisplay',
'cleanup' => 'always',
'update' => 'unmodified',
'params' => [
'version' => 4,
'values' => [
'name' => 'Contact_Types_Table',
'label' => 'Contact Types Table',
'saved_search_id.name' => 'Administer_Contact_Types',
'type' => 'table',
'settings' => [
'actions' => FALSE,
'limit' => 50,
'classes' => [
'table',
'table-striped',
],
'pager' => [
'show_count' => TRUE,
],
'placeholder' => 5,
'sort' => [
[
'parent_id:label',
'ASC',
],
[
'label',
'ASC',
],
],
'columns' => [
[
'type' => 'field',
'key' => 'label',
'dataType' => 'String',
'label' => 'Label',
'sortable' => TRUE,
'icons' => [
[
'field' => 'icon',
'side' => 'left',
],
],
'editable' => TRUE,
],
[
'type' => 'field',
'key' => 'parent_id:label',
'dataType' => 'Integer',
'label' => 'Parent',
'sortable' => TRUE,
'icons' => [
[
'icon' => 'fa-lock',
'side' => 'left',
'if' => [
'parent_id:label',
'IS EMPTY',
],
],
],
],
[
'type' => 'field',
'key' => 'description',
'dataType' => 'Text',
'label' => 'Description',
'sortable' => TRUE,
'editable' => TRUE,
],
[
'size' => 'btn-sm',
'links' => [
[
'entity' => 'ContactType',
'action' => 'update',
'join' => '',
'target' => 'crm-popup',
'icon' => 'fa-pencil',
'text' => 'Edit',
'style' => 'default',
'path' => '',
'condition' => [],
],
[
'entity' => 'ContactType',
'action' => 'delete',
'join' => '',
'target' => 'crm-popup',
'icon' => 'fa-trash',
'text' => 'Delete',
'style' => 'danger',
'path' => '',
'condition' => [
'parent_id:label',
'IS NOT EMPTY',
],
],
],
'type' => 'buttons',
'alignment' => 'text-right',
],
],
'addButton' => [
'path' => 'civicrm/admin/options/subtype/edit?action=add&reset=1',
'text' => 'Add Contact Type',
'icon' => 'fa-plus',
],
],
'acl_bypass' => FALSE,
],
],
],
];
Original file line number Diff line number Diff line change
Expand Up @@ -509,18 +509,21 @@ private function getLinkPath($link, $data = NULL, $index = 0) {
if ($prefix) {
$path = str_replace('[', '[' . $prefix, $path);
}
// Check access for edit/update links
// Check access for edit/update/delete links
// (presumably if a record is shown in SearchKit the user already has view access, and the check is expensive)
if ($path && isset($data) && !in_array($link['action'], ['view', 'preview'], TRUE)) {
$id = $data[$prefix . $idKey] ?? NULL;
$id = is_array($id) ? $id[$index] ?? NULL : $id;
if ($id) {
$values = [$idField => $id];
// If not aggregated, add other values to help checkAccess be efficient
if (!is_array($data[$prefix . $idKey])) {
$values += \CRM_Utils_Array::filterByPrefix($data, $prefix);
}
$access = civicrm_api4($link['entity'], 'checkAccess', [
// Fudge links with funny action names to check 'update'
'action' => $link['action'] === 'delete' ? 'delete' : 'update',
'values' => [
$idField => $id,
],
'values' => $values,
], 0)['access'];
if (!$access) {
return NULL;
Expand Down
5 changes: 5 additions & 0 deletions ext/search_kit/Civi/Search/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Civi\Api4\Query\SqlFunction;
use Civi\Api4\SearchDisplay;
use Civi\Api4\Tag;
use Civi\Api4\Utils\CoreUtil;
use CRM_Search_ExtensionUtil as E;

/**
Expand Down Expand Up @@ -136,6 +137,10 @@ public static function getSchema(): array {
if ($links) {
$entity['links'] = array_values($links);
}
$paths = CoreUtil::getInfoItem($entity['name'], 'paths');
if (!empty($paths['add'])) {
$entity['addPath'] = $paths['add'];
}
$getFields = civicrm_api4($entity['name'], 'getFields', [
'select' => ['name', 'title', 'label', 'description', 'type', 'options', 'input_type', 'input_attrs', 'data_type', 'serialize', 'entity', 'fk_entity', 'readonly', 'operators', 'suffixes', 'nullable'],
'where' => [['name', 'NOT IN', ['api_key', 'hash']]],
Expand Down
3 changes: 3 additions & 0 deletions ext/search_kit/ang/crmSearchAdmin.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@
}
return {
getEntity: getEntity,
getBaseEntity: function() {
return getEntity(searchEntity);
},
getField: function(fieldName, entityName) {
return getFieldAndJoin(fieldName, entityName || searchEntity).field;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,25 @@
});
};

this.toggleAddButton = function() {
if (ctrl.display.settings.addButton && ctrl.display.settings.addButton.path) {
delete ctrl.display.settings.addButton;
} else {
var entity = searchMeta.getBaseEntity();
ctrl.display.settings.addButton = {
path: entity.addPath || 'civicrm/',
text: ts('Add %1', {1: entity.title}),
icon: 'fa-plus'
};
}
};

this.onChangeAddButtonPath = function() {
if (!ctrl.display.settings.addButton.path) {
delete ctrl.display.settings.addButton;
}
};

// Helper function to sort active from hidden columns and initialize each column with defaults
this.initColumns = function(defaults) {
if (!ctrl.display.settings.columns) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,12 @@
</div>
<input type="text" ng-show="$ctrl.display.settings.button" ng-model="$ctrl.display.settings.button" ng-model-options="{updateOn: 'blur'}" class="form-control" title="{{:: ts('Search button text') }}" placeholder="{{:: ts('Search button text') }}">
</div>
<div class="input-group">
<div class="checkbox-inline form-control" title="{{:: ts('Display a button for creating a new record') }}">
<label>
<input type="checkbox" ng-checked="$ctrl.display.settings.addButton.path" ng-click="$ctrl.parent.toggleAddButton()">
<span>{{:: ts('"Add New" Button') }}</span>
</label>
</div>
</div>
<input class="form-control" ng-if="$ctrl.display.settings.addButton.path" ng-model="$ctrl.display.settings.addButton.path" ng-change="$ctrl.onChangeAddButtonPath()" ng-model-options="{updateOn: 'blur'}" title="{{:: ts('Path') }}" placeholder="{{:: ts('Path') }}">
Loading

0 comments on commit 06ddfae

Please sign in to comment.