Skip to content

Fix_person_roles_picker #275

Merged
merged 6 commits into from
Feb 4, 2025
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
3 changes: 3 additions & 0 deletions app/resources/locales/en_US/menu.po
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ msgstr "Toggle menu collapse button"
msgid "options"
msgstr "Options"

msgid "person.canvas"
msgstr "Person Canvas"

msgid "registries"
msgstr "Available {0} Registries"

Expand Down
3 changes: 3 additions & 0 deletions app/resources/locales/en_US/operation.po
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ msgstr "show more"
msgid "autocomplete.people.desc"
msgstr "Begin typing to find a person (use at least {0} characters from a name, email address, or identifier)"

msgid "autocomplete.people.field.desc"
msgstr "Begin typing to find a person (use characters from a name, email address, or identifier)"

msgid "autocomplete.people.label"
msgstr "Search for a person"

Expand Down
2 changes: 1 addition & 1 deletion app/src/Lib/Traits/SearchFilterTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ public function getSearchableAttributes(string $controller, \DateTimeZone $vv_tz
// Picker configuration
if(isset($f['picker'])) {
$autocompleteArgs = [
'type' => 'default',
'type' => 'search',
'fieldName' => $field,
'personType' => $f['picker']['type'],
'htmlId' => $field, // This is the input ID
Expand Down
15 changes: 15 additions & 0 deletions app/src/Lib/Util/StringUtilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,21 @@ public static function foreignKeyToClassName(string $s): string {
return Inflector::camelize(Inflector::pluralize(substr($s, 0, strlen($s)-3)));
}

/**
* Determine the controller name from a foreign key (eg: report_id -> reports).
*
* @since COmanage Registry v5.1.0
* @param string $s Foreign Key name
* @return string Class name
*/

public static function foreignKeyToController(string $s): string {
if($s === 'affiliation_type_id') {
$s = 'type_id';
}
return Inflector::underscore(Inflector::pluralize(substr($s, 0, strlen($s)-3)));
}

/**
* Localize a controller name, accounting for plugins.
*
Expand Down
11 changes: 11 additions & 0 deletions app/src/Model/Table/PersonRolesTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,17 @@ public function beforeMarshal(EventInterface $event, \ArrayObject $data, \ArrayO
}
}
}

$re = '/^.*\(ID: (\d+)\)$/m';
if(!empty($data['sponsor_person_id'])) {
preg_match_all($re, $data['sponsor_person_id'], $matchesSponsor, PREG_SET_ORDER, 0);
$data['sponsor_person_id'] = $matchesSponsor[0][1];
}

if(!empty($data['manager_person_id'])) {
preg_match_all($re, $data['manager_person_id'], $matchesManager, PREG_SET_ORDER, 0);
$data['manager_person_id'] = $matchesManager[0][1];
}
}

/**
Expand Down
21 changes: 11 additions & 10 deletions app/src/Model/Table/PetitionHistoryRecordsTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,29 +132,30 @@ public function generateDisplayField(\App\Model\Entity\JobHistoryRecord $entity)

return __d('controller', 'PetitionHistoryRecords', [1]);
}

/**
* Record a Petition History Record.
*
* @since COmanage Registry v5.0.0
* @param int $petitionId Petition ID
* @param string $enrollmentFlowStepId Enrollment Flow Step ID, or null for start or finalize
* @param string $action PetitionActionEnum
* @param string $comment Comment
* @param int $actorPersonId Actor Person ID
* @param int $petitionId Petition ID
* @param int|null $enrollmentFlowStepId Enrollment Flow Step ID, or null for start or finalize
* @param string $action PetitionActionEnum
* @param string $comment Comment
* @param int|null $actorPersonId Actor Person ID
*
* @return int Petition History Record ID
* @since COmanage Registry v5.0.0
*/

public function record(
int $petitionId,
?int $enrollmentFlowStepId=null,
?int $enrollmentFlowStepId,
string $action,
string $comment,
?int $actorPersonId=null
?int $actorPersonId = null
): int {
$obj = $this->newEntity([
'petition_id' => $petitionId,
'enrollment_flow_step_id' => $enrollmentFlowStepId,
'enrollment_flow_step_id' => $enrollmentFlowStepId ?? null,
'action' => $action,
'comment' => $comment,
'actor_person_id' => $actorPersonId
Expand Down
88 changes: 77 additions & 11 deletions app/src/View/Helper/FieldHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@

namespace App\View\Helper;

use App\Lib\Enum\DateTypeEnum;
use App\Lib\Util\StringUtilities;
use Cake\I18n\FrozenTime;
use Cake\Utility\Inflector;
use Cake\View\Helper;
use App\Lib\Enum\DateTypeEnum;
use App\Lib\Util\StringUtilities;
use DOMDocument;

class FieldHelper extends Helper {
public $helpers = ['Form', 'Html'];
Expand Down Expand Up @@ -243,15 +244,31 @@ public function constructSPAField(string $element, string $vueElementName): stri
// Parse the Class attribute
$regexClass = '/class="(.*?)"/m';
preg_match_all($regexClass, $element, $matchesClass, PREG_SET_ORDER, 0);

// Parse the Value attribute
// XXX This will not work properly if the input element is a select element
if (!empty($matchesClass[0][1])
&& !str_contains($matchesClass[0][1], 'select')
) {
$regexClass = '/value="(.*?)"/m';
preg_match_all($regexClass, $element, $matchesValue, PREG_SET_ORDER, 0);
}

if(!empty($matchesId[0][1]) && !empty($matchesName[0][1])) {
return $this->getView()->element($vueElementName, [
$vueElementProperties = [
'htmlId' => $matchesId[0][1],
'fieldName' => $matchesName[0][1],
'containerClasses' => $matchesClass[0][1],
'type' => 'field',
// we want the label to be an empty string to hide the default label introduced by the module.
'label' => ''
]);
];

if (isset($matchesValue[0][1])) {
$vueElementProperties['inputValue'] = $matchesValue[0][1];
}

return $this->getView()->element($vueElementName, $vueElementProperties);
}

// Fallback to an error element
Expand Down Expand Up @@ -279,14 +296,18 @@ public function dateField(string $fieldName,
$dateTitle = $dateType === DateTypeEnum::DateOnly ? 'datepicker.enterDate' : 'datepicker.enterDateTime';
$datePattern = $dateType === DateTypeEnum::DateOnly ? '\d{4}-\d{2}-\d{2}' : '\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}';
$queryParams = $this->getView()->getRequest()->getQueryParams();
$date_object = !empty($queryParams[$fieldName])
? FrozenTime::parse($queryParams[$fieldName])
: $this->getEntity()?->$fieldName;

// Petition Attribute Collection use case
if($date_object === null && !empty($fieldArgs['default'])) {
$date_object = $fieldArgs['default'];
}
$date_object = match(true) {
// filtering block
!empty($queryParams[$fieldName]) => FrozenTime::parse($queryParams[$fieldName]),
// Petition View/ Value saved as string
isset($fieldArgs['default']) && is_string($fieldArgs['default']) => FrozenTime::parse($fieldArgs['default']),
// Petition View/ Value saved a FronzenTime
isset($fieldArgs['default'])
&& is_a($fieldArgs['default'], 'Cake\I18n\FrozenTime') => $fieldArgs['default'],
// Table record/ Retrieve it from the Entity object
default => $this->getEntity()?->$fieldName,
};
// Create the options array for the (text input) form control
$coptions = [];

Expand Down Expand Up @@ -397,6 +418,8 @@ public function formField(string $fieldName,
|| ($fieldName == 'plugin' && $this->action == 'edit');

// Selects, Checkboxes, and Radio Buttons use "disabled"
// XXX For this use case we need to add a hidden input field. If we do not we will not be able
// to post the value
$fieldArgs['disabled'] = $fieldArgs['readonly'];

// required can be overridden by the fields.inc, but start with the default expectation
Expand Down Expand Up @@ -625,4 +648,47 @@ public function sourceLink($entity): string
return $link;
}

/**
* Iterate over form arguments, parse the generated HTML element using XMLReader,
* and inject a hidden input field if the element (e.g., select, checkbox, radio) is disabled.
* Finally, outputs the original HTML element.
*
* @param string $element
* @param array $formArguments An array containing options/attributes for the HTML form element.
* @return void
* @since COmanage Registry v5.1.0
*/
public function getElementsForDisabledInput(string $element, array $formArguments): void
{
$orginalElement = $this->getView()->element($element, ['arguments' => $formArguments]);
if ($orginalElement) {
$htmlObj = new DOMDocument();
$htmlObj->loadHTML($orginalElement, LIBXML_NOERROR);
if($htmlObj->getElementsByTagName('select')->length > 0) {
// Check if it is disabled. If it is then print a hidden element
foreach($htmlObj->getElementsByTagName('select')->item(0)->attributes as $attr) {
if($attr->name == 'disabled' && $attr->value == 'disabled') {
print $this->getView()->Form->hidden($formArguments['fieldName'], ['value' => $formArguments["fieldOptions"]["default"]]);
}
}
} elseif ($htmlObj->getElementsByTagName('radio')->length) {
// Check if it is disabled. If it is then print a hidden element
foreach($htmlObj->getElementsByTagName('radio')->item(0)->attributes as $attr) {
if($attr->name == 'disabled' && $attr->value == 'disabled') {
print $this->getView()->Form->hidden($formArguments['fieldName'], ['value' => $formArguments["fieldOptions"]["default"]]);
}
}
} elseif ($htmlObj->getElementsByTagName('checkbox')->length) {
// Check if it is disabled. If it is then print a hidden element
foreach($htmlObj->getElementsByTagName('checkbox')->item(0)->attributes as $attr) {
if($attr->name == 'disabled' && $attr->value == 'disabled') {
print $this->getView()->Form->hidden($formArguments['fieldName'], ['value' => $formArguments["fieldOptions"]["default"]]);
}
}
}
}

// Print the original element
print $orginalElement;
}
}
53 changes: 51 additions & 2 deletions app/src/View/Helper/PetitionHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@
namespace App\View\Helper;

use App\Lib\Util\StringUtilities;
use App\Lib\Util\TableUtilities;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Utility\Inflector;
use Cake\Validation\Validator;
use Cake\View\Helper;
use CoreEnroller\Model\Table\EnrollmentAttributesTable;

Expand Down Expand Up @@ -95,4 +93,55 @@ public function getTable(string $tableName): Table
{
return TableRegistry::getTableLocator()->get($tableName);
}

/**
* Fetch a record by its ID and optionally include related data.
*
* This method retrieves a specific record from the database using its ID
* and foreign key. If optional related data (associations) need to be loaded,
* they can be specified with the `$contains` parameter.
*
* @param string $foreignKey
* @param int|string $id
* @param array $contains
*
* @return array
* @since COmanage Registry v5.1.0
*/
public function getRecordForId(string $foreignKey, int|string $id, array $contains = []): array
{
$tableName = StringUtilities::foreignKeyToClassName($foreignKey);
if($tableName === 'AffiliationTypes') {
$tableName = 'Types';
}
$table = $this->getTable($tableName);
$query = $table->find()
->where([$tableName . '.id' => $id]);
if(!empty($contains)) {
return $query
->contain($contains)
->first()
->toArray();
}
return $query->first()->toArray();
}

/**
* Transform an enrollment attribute name into a class postfix
*
* This method modifies the given attribute name by converting it
* to a format suitable for use as a CSS class postfix.
*
* @param string $attributeName Attribute name to transform
* @return string Transformed class postfix
* @since COmanage Registry v5.1.0
*/
public function getClassPostfixFromAttributeName(string $attributeName): string
{
if(str_ends_with($attributeName, '_id')) {
$attributeName = substr($attributeName, 0, -3);
}
$attributeName = Inflector::underscore($attributeName);
return str_replace('_', '-', $attributeName);
}
}
8 changes: 8 additions & 0 deletions app/src/View/Helper/VueHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,25 @@ class VueHelper extends Helper {
'report.for',
'value.copied',
],
'menu' => [
'person.canvas'
],
'operation' => [
'add',
'add.member',
'add.owner',
'autocomplete.pager.show.more',
'autocomplete.people.desc',
'autocomplete.people.field.desc',
'autocomplete.people.label',
'autocomplete.people.placeholder',
'close',
'copy',
'copy.value',
'edit',
'primary',
'remove',
'view',
'visit.link',
],
'result' => [
Expand Down
28 changes: 8 additions & 20 deletions app/templates/PersonRoles/fields.inc
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,11 @@ if($vv_action == 'add' || $vv_action == 'edit' || $vv_action == 'view') {
// For now, we render sponsor and manager as read only.
// XXX Need People Picker (CFM-150)
foreach(['sponsor', 'manager'] as $f) {
$fp = $f."_person";

$fname = "";
$flink = [];

if(!empty($vv_obj->$fp->names[0])) {
$fname = $vv_obj->$fp->names[0]->full_name;
$fid = $vv_obj->$fp->id;
$flink = ['url' => ['controller' => 'people', 'action' => 'edit', $vv_obj->$fp->id]];
$formParams = [
'value' => $fid,
'fullName' => $fname,
'link' => $flink,
];
$this->set('formParams', $formParams);
}

$fp = $f . '_person';

$vv_autocomplete_arguments = [
'fieldName' => $f.'_person_id',
'status' => $fname,
'link' => $flink,
'fieldLabel' => __d('field', $f),
'fieldOptions' => [],
'autocomplete' => [
'configuration' => [
'action' => 'GET',
Expand All @@ -90,6 +72,12 @@ if($vv_action == 'add' || $vv_action == 'edit' || $vv_action == 'view') {
]
];

if(!empty($vv_obj->$fp->names[0])) {
$vv_autocomplete_arguments['fieldOptions'] = [
'default' => $vv_obj->$fp->id
];
}

print $this->element('form/listItem', ['arguments' => $vv_autocomplete_arguments]);
}

Expand Down
8 changes: 3 additions & 5 deletions app/templates/element/filter/peoplePicker.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,9 @@
}

// Get the Field configuration
$formParams = $this->Filter->calculateFieldParams($key, $label);
if(!empty($formParams['value']) && $key == 'person_id') {
$formParams['fullName'] = $this->Filter->getFullName((int)$formParams['value']);
}
$vv_autocomplete_arguments['formParams'] = $formParams;
$vv_autocomplete_arguments['fieldOptions'] = $this->Filter->calculateFieldParams($key, $label);
// Update the view var
$this->set('vv_autocomplete_arguments', $vv_autocomplete_arguments);

?>

Expand Down
Loading