diff --git a/app/plugins/CoreEnroller/src/Model/Table/EnrollmentAttributesTable.php b/app/plugins/CoreEnroller/src/Model/Table/EnrollmentAttributesTable.php
index 3ca48ba2..ab46c269 100644
--- a/app/plugins/CoreEnroller/src/Model/Table/EnrollmentAttributesTable.php
+++ b/app/plugins/CoreEnroller/src/Model/Table/EnrollmentAttributesTable.php
@@ -165,6 +165,10 @@ public function initialize(array $config): void {
'type' => 'auxiliary',
'model' => 'CoSettings'
],
+ 'types' => [
+ 'type' => 'auxiliary',
+ 'model' => 'Types'
+ ],
]);
$this->setLayout([ 'index' => 'iframe',
diff --git a/app/plugins/CoreEnroller/templates/element/field.php b/app/plugins/CoreEnroller/templates/element/field.php
index 68e4be12..b5cd48d5 100644
--- a/app/plugins/CoreEnroller/templates/element/field.php
+++ b/app/plugins/CoreEnroller/templates/element/field.php
@@ -112,7 +112,12 @@
// HIDDEN Field
// We print directly, we do not delegate to the element for further processing
// In case this is a hidden field, we need to get only the value
- $attr->hidden && $hidden =>$this->Form->hidden($formArguments['fieldName'], ['value' => $options['default']]),
- // Default use case
- default => $this->element('form/listItem', ['arguments' => $formArguments])
+ $attr->hidden && $hidden => $this->Form->hidden($formArguments['fieldName'], ['value' => $options['default']]),
+ // For the case of xxx_person_id fields, we will render the People a Picker element.
+ str_ends_with($attr->attribute, 'person_id') => $this->element('CoreEnroller.spa-field', [
+ 'vueElementName' => 'peopleAutocomplete',
+ 'formArguments' => $formArguments
+ ]),
+// Default use case
+ default => $this->element('form/listItem', ['arguments' => $formArguments])
};
diff --git a/app/plugins/CoreEnroller/templates/element/spa-field.php b/app/plugins/CoreEnroller/templates/element/spa-field.php
new file mode 100644
index 00000000..d70069f9
--- /dev/null
+++ b/app/plugins/CoreEnroller/templates/element/spa-field.php
@@ -0,0 +1,61 @@
+
+
+
+ = $this->Field->constructSPAField(
+ // The Default field will be used to harvest the attributes
+ element: $this->Field->formField(...$formArguments),
+ // Vue/JS element
+ vueElementName: $vueElementName
+ ) ?>
+
+
+
diff --git a/app/resources/locales/en_US/field.po b/app/resources/locales/en_US/field.po
index f26b666d..312f3903 100644
--- a/app/resources/locales/en_US/field.po
+++ b/app/resources/locales/en_US/field.po
@@ -234,6 +234,9 @@ msgstr "IP Address"
msgid "required"
msgstr "Required"
+msgid "element_fallback"
+msgstr "Element ID not provided"
+
msgid "role_key"
msgstr "Role Key"
diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php
index 8fcf3ad6..90d6c0de 100644
--- a/app/src/View/Helper/FieldHelper.php
+++ b/app/src/View/Helper/FieldHelper.php
@@ -73,6 +73,7 @@ class FieldHelper extends Helper {
* @param array $config The configuration settings provided to this helper.
*
* @return void
+ * @since COmanage Registry v5.0.0
*/
public function initialize(array $config): void
{
@@ -94,6 +95,7 @@ public function initialize(array $config): void
* @param string $fieldName
*
* @return array
+ * @since COmanage Registry v5.0.0
*/
public function calculateLabelAndDescription(string $fieldName): array
{
@@ -180,6 +182,7 @@ public function calculateLabelAndDescription(string $fieldName): array
* Calculate the list of classes for the li element
*
* @return string
+ * @since COmanage Registry v5.0.0
*/
public function calculateLiClasses(): string
{
@@ -219,6 +222,42 @@ public function calculateLiClasses(): string
return $classes;
}
+ /**
+ * Construct the SPA field element
+ *
+ * @param string $element HTML element created with the CAKEPHP HTML Helper
+ * @param string $vueElementName The name of the JavaScript module
+ *
+ * @return string
+ * @since COmanage Registry v5.0.0
+ */
+ public function constructSPAField(string $element, string $vueElementName): string {
+ // Parse the ID attribute
+ $regexId = '/id="(.*?)"/m';
+ preg_match_all($regexId, $element, $matchesId, PREG_SET_ORDER, 0);
+
+ // Parse the Name attribute
+ $regexName = '/name="(.*?)"/m';
+ preg_match_all($regexName, $element, $matchesName, PREG_SET_ORDER, 0);
+
+ // Parse the Class attribute
+ $regexClass = '/class="(.*?)"/m';
+ preg_match_all($regexClass, $element, $matchesClass, PREG_SET_ORDER, 0);
+ if(!empty($matchesId[0][1]) && !empty($matchesName[0][1])) {
+ return $this->getView()->element($vueElementName, [
+ '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' => ''
+ ]);
+ }
+
+ // Fallback to an error element
+ return $this->getView()->element('elementFallback');
+ }
+
/**
* Emit a date/time form control.
* This is a wrapper function for $this->control()
diff --git a/app/templates/element/form/elementFallback.php b/app/templates/element/form/elementFallback.php
new file mode 100644
index 00000000..feb18104
--- /dev/null
+++ b/app/templates/element/form/elementFallback.php
@@ -0,0 +1,32 @@
+
+
+= __d('field', 'element_fallback') ?>
diff --git a/app/templates/element/peopleAutocomplete.php b/app/templates/element/peopleAutocomplete.php
index 7787bbf3..93666844 100644
--- a/app/templates/element/peopleAutocomplete.php
+++ b/app/templates/element/peopleAutocomplete.php
@@ -33,6 +33,7 @@
$htmlId = $htmlId ?? 'cmPersonPickerId';
$actionUrl = $actionUrl ?? []; // the url of the page to launch on select for a stand-alone picker
$viewConfigParameters = $viewConfigParameters ?? [];
+ $containerClasses = $containerClasses ?? 'cm-autocomplete-container';
// Get the CSRF Token in JavaScript
$token = $this->request->getAttribute('csrfToken');
@@ -132,7 +133,7 @@
}
// Mount the component and provide a global reference for this app instance.
- window.= $htmlId ?> = app.mount("#= $htmlId ?>-container");
+ window.= str_replace('-', '', $htmlId) ?> = app.mount("#= $htmlId ?>-container");
-
+
diff --git a/app/webroot/css/co-base.css b/app/webroot/css/co-base.css
index 38865977..05012797 100644
--- a/app/webroot/css/co-base.css
+++ b/app/webroot/css/co-base.css
@@ -2185,6 +2185,13 @@ td .alert {
bottom: 2px;
margin-right: -26px;
}
+.co-loading-mini-container.over-input {
+ display: none;
+ position: absolute;
+ right: 0.5em;
+ bottom: 0.7em;
+ z-index: 100;
+}
#co-loading span,
#co-loading-redirect span,
.co-loading-mini span {
diff --git a/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js b/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js
index 990378c2..209f2f16 100644
--- a/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js
+++ b/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js
@@ -177,7 +177,9 @@ export default {
return data?.People?.map((item) => {
return {
"value": item.id,
- "label": `${item?.primary_name?.given} ${item?.primary_name?.family}`,
+ // XXX The label is the value that autocomplete will use to update the input field value property.
+ "label": `${item?.primary_name?.given} ${item?.primary_name?.family} (ID: ${item?.id})`,
+ "fullName": `${item?.primary_name?.given} ${item?.primary_name?.family}`,
"itemId": `${item?.id}`,
"email": this.filterByEmailAddressType(item?.email_addresses),
"emailPretty": this.shortenString(this.constructEmailCsv(this.filterByEmailAddressType(item?.email_addresses))),
@@ -190,12 +192,8 @@ export default {
})
},
setPerson() {
- if(this.options.type == 'default') {
+ if(['default', 'field'].includes(this.options.type)) {
this.options.inputProps.dataPersonid = this.person.value
- } else if(this.options.type == 'field') {
- // The picker is part of a standard form field
- const field = document.getElementById(this.options.fieldName);
- field.value = this.person.value;
} else {
// The picker is stand-alone, and should render the configured page in a modal on @item-select
const urlForModal = this.options.actionUrl + '&person_id=' + this.person.value;
@@ -249,7 +247,7 @@ export default {
mounted() {
if(this.options.inputValue != undefined
&& this.options.inputValue != ''
- && this.options.htmlId == 'person_id') {
+ && this.options.htmlId.endsWith('person_id')) {
this.options.inputProps.value = `${this.options.formParams?.fullName} (ID: ${this.options.inputValue})`
}
},
@@ -272,11 +270,22 @@ export default {
}
// Otherwise return the default
return this.txt['autocomplete.people.label'];
+ },
+ hasAutoCompleteLabel: function() {
+ // Check to see if a label has been passed in
+ return this.options.label !== undefined && this.options.label !== ''
+ },
+ getMiniLoaderClasses: function() {
+ if(this.options.label !== undefined && this.options.label !== '') {
+ return "co-loading-mini-container d-inline ms-1"
+ } else {
+ return "co-loading-mini-container d-inline ms-1 over-input"
+ }
}
},
template: `
-
-
+
+