diff --git a/app/src/Controller/StandardController.php b/app/src/Controller/StandardController.php index 1e161626e..9be5585e0 100644 --- a/app/src/Controller/StandardController.php +++ b/app/src/Controller/StandardController.php @@ -126,7 +126,15 @@ public function add() { $this->set('vv_subtitle', $subtitle); // Let the view render - $this->render('/Standard/add-edit-view'); + if(in_array($this->name, [ + 'Cous', 'ApiUsers' + ]) + ) { + $this->render('/Standard/add-edit-view-new'); + } else { + // Let the view render + $this->render('/Standard/add-edit-view'); + } } /** @@ -419,9 +427,16 @@ public function edit(string $id) { $this->set('vv_title', $title); $this->set('vv_supertitle', $supertitle); $this->set('vv_subtitle', $subtitle); - - // Let the view render - $this->render('/Standard/add-edit-view'); + + if(in_array($this->name, [ + 'Cous' + ]) + ) { + $this->render('/Standard/add-edit-view-new'); + } else { + // Let the view render + $this->render('/Standard/add-edit-view'); + } } /** @@ -868,6 +883,14 @@ public function view($id = null) { $this->set('vv_subtitle', $subtitle); // Let the view render - $this->render('/Standard/add-edit-view'); + if(in_array($this->name, [ + 'Cous', 'ApiUsers' + ]) + ) { + $this->render('/Standard/add-edit-view-new'); + } else { + // Let the view render + $this->render('/Standard/add-edit-view'); + } } } \ No newline at end of file diff --git a/app/src/View/Helper/FieeldHelper.php b/app/src/View/Helper/FieeldHelper.php new file mode 100644 index 000000000..8882e2354 --- /dev/null +++ b/app/src/View/Helper/FieeldHelper.php @@ -0,0 +1,410 @@ +reqFields = $this->getView()->get('vv_required_fields'); + $this->modelName = $this->getView()->getName(); + $this->action = $this->getView()->get('vv_action'); + $this->editable = \in_array($this->action, ['add', 'edit']); + $this->pluginName = $this->getView()->getPlugin(); + $this->entity = $this->getView()->get('vv_obj'); + $this->fieldTypes = $this->getView()->get('vv_field_types'); + } + + + /** + * Emit a date/time form control. + * This is a wrapper function for $this->control() + * + * @param string $fieldName Form field + * @param string $dateType Standard, DateOnly, FromTime, ThroughTime + * @param array|null $queryParams Request Query parameters used by the filtering Blocks to get the date values + * @param string|null $label + * + * @return array HTML for control + * @since COmanage Registry v5.0.0 + */ + + public function dateField(string $fieldName, + string $dateType=DateTypeEnum::Standard, + array $queryParams=null, + string $label=null): string + { + // Initialize + $dateFormat = $dateType === DateTypeEnum::DateOnly ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss'; + $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}'; + $date_object = null; + + if(isset($queryParams)) { + if(!empty($queryParams[$fieldName])) { + $date_object = FrozenTime::parse($queryParams[$fieldName]); + } + } else { + // This is an entity view. We are getting the data from the object + $entity = $this->getView()->get('vv_obj'); + $date_object = $entity->$fieldName; + } + + // Create the options array for the (text input) form control + $coptions = []; + + // A datetime field will be rendered as a plain text input with adjacent date and time pickers + // that will interact with the field value. Allowing direct access to the input field is for + // accessibility purposes. + + // ACTION VIEW + if($this->action == 'view') { + // return the date as plaintext + $element = $this->notSetElement(); + if ($date_object !== null) { + // Adjust the time back to the user's timezone + $element = ''; + } + + // Return this to the generic control() function + return $element; + } + + // Special-case the very common "valid_from" and "valid_through" fields, so we won't need + // to specify their types in fields.inc. + $pickerType = match ($fieldName) { + 'valid_from' => DateTypeEnum::FromTime, + 'valid_through' => DateTypeEnum::ThroughTime, + default => $dateType + }; + + // Append the timezone to the label + $coptions['class'] = 'form-control datepicker'; + $coptions['placeholder'] = $dateFormat; + if(!empty($label)) { + $coptions['label'] = $label; + } + $coptions['pattern'] = $datePattern; + $coptions['title'] = __d('field', $dateTitle); + + $coptions['id'] = str_replace('_', '-', $fieldName); + + + // Default the picker date to today + $now = FrozenTime::now(); + $pickerDate = $now->i18nFormat($dateFormat); + + // Get the existing values, if present + if($date_object !== null) { + // Adjust the time back to the user's timezone + $coptions['value'] = $date_object->i18nFormat($dateFormat); + $pickerDate = $date_object->i18nFormat($dateFormat); + } + + // Set the date picker floor year value (-100 years)() + $pickerDateFT = new FrozenTime($pickerDate); + $pickerDateFT = $pickerDateFT->subYears(100); + $pickerFloor = $pickerDateFT->i18nFormat($dateFormat); + + $date_picker_args = [ + 'fieldName' => $fieldName, + 'pickerDate' => $pickerDate, + 'pickerType' => $pickerType, + 'pickerFloor' => $pickerFloor, + ]; + + // Create a text field to hold our value and call the datePicker + return $this->Form->text($fieldName, $coptions) . $this->getView()->element('datePicker', $date_picker_args); + } + + /** + * Create the actual Form element + * + * @param string $fieldName Form field + * @param array|null $options FormHelper control options + * By setting the options you are requesting a select field + * @param string|null $labelText + * @param string $prefix Field prefix - used for API Usernames + * @param string|null $fieldType + * + * @return string HTML for control + * @since COmanage Registry v5.0.0 + */ + public function formField(string $fieldName, + array $options = null, + string $labelText = null, + string $prefix = '', // api user + string $fieldType = null): string { + $fieldArgs = $options ?? []; + $fieldArgs['label'] = $options['label'] ?? false; + $fieldArgs['readonly'] = !$this->editable + || (isset($options['readonly']) && $options['readonly']) + || ($fieldName == 'plugin' && $this->action == 'edit'); + + // Selects, Checkboxes, and Radio Buttons use "disabled" + $fieldArgs['disabled'] = $fieldArgs['readonly']; + $fieldArgs['required'] = (bool)$this->getReqField($fieldName); + + // Get the field type from the map of fields (e.g. 'boolean', 'string', 'timestamp') + $fieldType = $fieldType ?? $this->getFieldType($fieldName); + + // Remove prefix from field value + if(!empty($prefix) && !empty($this->getEntity()->$fieldName)) { + $vv_obj = $this->getView()->get('vv_obj'); + $fieldValue = $vv_obj->$fieldName; + $fieldValueTemp = str_replace($prefix, '', $fieldValue); + $vv_obj->$fieldName = $fieldValueTemp; + $this->getView()->set('vv_obj', $vv_obj); + } + + if($fieldName !== 'status' + && !isset($options['empty']) + && (!isset($options['suppressBlank']) || !$options['suppressBlank'])) { + // Cause any select (except status) to render with a blank option, even + // if the field is required. This makes it clear when a value needs to be set. + // Note this will be ignored for non-select controls. + $fieldArgs['empty'] = true; + } + + // A boolean field is a checkbox. Set the label and class to improve rendering + // and accessibility. + if($fieldType === 'boolean') { + $fieldArgs['label'] = $labelText; + $fieldArgs['class'] = 'form-check-input'; + } elseif($fieldType === 'date') { + return $this->dateField($fieldName, DateTypeEnum::DateOnly); + } elseif($fieldType == 'datetime' || $fieldType == 'timestamp') { + return $this->dateField($fieldName); + } + + // Generate the form control or pass along the markup generated in a wrapper function + return $this->Form->control($fieldName, $fieldArgs); + + } + + + /** + * We autogenerate field labels and descriptions from the field name. + * + * @param string $fieldName + * + * @return array + */ + public function calculateLabelAndDescription(string $fieldName): array + { + $desc = null; + $label = null; + + // First, try to autogenerate the field label (if we weren't given one). + $pluginDomain = (!empty($this->getPluginName()) + ? Inflector::underscore($this->getPluginName()) + : null); + + $modelName = $this->getModelName(); + // We try to automagically determine if a description for the field exists by + // looking for the corresponding .desc language translation. + // We autogenerate field labels and descriptions from the field name. + + // We loop over the field generation logic twice, first for a plugin + // context (if set) and then generally (if no plugin localization was found). + + // We use $core as the variable for this loop, so the rest of the code + // is easier to read (!$core = plugin) + for($core = 0;$core < 2;$core++) { + if(!$core && empty($this->pluginName)) { + // No plugin set, go to the core field checks + continue; + } + + // Is there a model specific key? For plugins, this will be in field.Model.Field + + $key = (!$core ? 'field.' : '') . "$modelName.$fieldName"; + $label = __d(($core ? 'field' : $pluginDomain), $key); + + if($label === $key) { + // Model-specific label isn't found, try again for a general label + + $f = null; + + if(preg_match('/^(.*?)_id$/', $fieldName, $f)) { + // Map foreign keys (foo_id) to the controller label + $key = (!$core ? 'controller.' : '') . Inflector::camelize(Inflector::pluralize($f[1])); + $label = __d(($core ? 'controller' : $pluginDomain), $key, [1]); + + if($key !== $label) { + break; + } + } + + // Look up the key + $key = (!$core ? 'field.' : '') . $fieldName; + $label = __d(($core ? 'field' : $pluginDomain), $key); + + if($key !== $label) { + break; + } + } else { + // If we found a key, break the loop + break; + } + } + // We try to automagically determine if a description for the field exists by + // looking for the corresponding .desc language translation. + + for($core = 0;$core < 2;$core++) { + if(!$core && empty($this->pluginName)) { + // No plugin set, just go to the core field checks + continue; + } + + $key = (!$core ? 'field.' : '') . "$modelName.$fieldName.desc"; + $desc = __d(($core ? 'field' : $pluginDomain), $key); + + // If the description is the literal key we just generated, there is no description + if($desc === $key) { + $desc = null; + } else { + break; + } + } + + return [$label, $desc]; + } + + /** + * @return bool + */ + public function isEditable(): bool + { + return $this->editable; + } + + /** + * @return string|null + */ + public function getPluginName(): ?string + { + return $this->pluginName; + } + + /** + * @return string|null + */ + public function getModelName(): ?string + { + return $this->modelName; + } + + /** + * @return object|null + */ + public function getEntity(): ?object + { + return $this->entity; + } + + /** + * @return array + */ + public function getReqFields(): array + { + return $this->reqFields; + } + + /** + * @param string $field + * + * @return string|null + */ + public function getReqField(string $field): ?string + { + return $this->reqFields[$field] ?? null; + } + + + /** + * @return array + */ + public function getFieldTypes(): array + { + return $this->fieldTypes; + } + + /** + * @param string $field + * + * @return string|null + */ + public function getFieldType(string $field): ?string + { + return $this->fieldTypes[$field] ?? null; + } + + +} \ No newline at end of file diff --git a/app/templates/ApiUsers/fields.inc b/app/templates/ApiUsers/fields.inc index 2c3887e6e..007a2681b 100644 --- a/app/templates/ApiUsers/fields.inc +++ b/app/templates/ApiUsers/fields.inc @@ -28,16 +28,21 @@ // This view does not support read-only if($vv_action == 'add' || $vv_action == 'edit') { if($vv_cur_co->id == 1) { - print $this->Field->banner(__d('information', 'api.cmp')); + print $this->element('banner', __d('information', 'api.cmp')); } // AR-ApiUser-3 For namespacing purposes, API Users are named with a prefix consisting of the string "co_#.". - print $this->Field->control('username', prefix: 'co_' . $vv_cur_co->id . '.'); + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => 'username', // string + 'prefix' => 'co_' . $vv_cur_co->id . '.' + ] + ]); // We link to the "Generate" button on edit only $generateLink = []; $labelIsTextOnly = false; - + if(!empty($vv_obj->id)) { $generateLink = [ 'url' => [ @@ -49,22 +54,48 @@ if($vv_action == 'add' || $vv_action == 'edit') { 'class' => 'provisionbutton nospin btn btn-primary btn-sm', 'confirm' => __d('operation', 'api.key.generate.confirm') ]; - + $labelIsTextOnly = true; } - - print $this->Field->statusControl('api_key', - !empty($vv_obj->api_key) ? __d('enumeration', 'SetBooleanEnum.1') : __d('enumeration', 'SetBooleanEnum.0'), - $generateLink, - labelIsTextOnly: $labelIsTextOnly); - - print $this->Field->control('status', ['empty' => false]); - - print $this->Field->dateControl('valid_from'); - - print $this->Field->dateControl('valid_through'); - - print $this->Field->control('remote_ip'); - - print $this->Field->control('privileged'); + + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => 'api_key', + 'status' => !empty($vv_obj->api_key) ? __d('enumeration', 'SetBooleanEnum.1') : __d('enumeration', 'SetBooleanEnum.0'), + 'link' => $generateLink, + 'labelIsTextOnly' => $labelIsTextOnly + ] + ]); + + + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => 'status', // select + ] + ]); + + + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => 'valid_from', // timestamp + ] + ]); + + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => 'valid_through', // timestamp + ] + ]); + + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => 'remote_ip', // string + ] + ]); + + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => 'privileged', // boolean + ] + ]); } diff --git a/app/templates/Cous/fields.inc b/app/templates/Cous/fields.inc index 89aa3fd11..bdce4df30 100644 --- a/app/templates/Cous/fields.inc +++ b/app/templates/Cous/fields.inc @@ -27,11 +27,20 @@ // This view does not support read-only if($vv_action == 'add' || $vv_action == 'edit') { - print $this->Field->control('name'); - - print $this->Field->control('description'); + print $this->element('form/listItem', [ + 'arguments' => ['fieldName' => 'name'] + ]); + print $this->element('form/listItem', [ + 'arguments' => ['fieldName' => 'description'] + ]); if(!empty($parents)) { - print $this->Field->control('parent_id', ['empty' => true], __d('field', 'parent_id')); + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => 'parent_id', + 'options' => ['empty' => true], + 'labelText' => __d('field', 'parent_id') + ] + ]); } } diff --git a/app/templates/Standard/add-edit-view-new.php b/app/templates/Standard/add-edit-view-new.php new file mode 100644 index 000000000..d327ce81d --- /dev/null +++ b/app/templates/Standard/add-edit-view-new.php @@ -0,0 +1,227 @@ +template; +// $this->name = Models +$modelsName = $this->name; +// $tablename = models +// XXX backport to match? +$tableName = \Cake\Utility\Inflector::tableize(\Cake\Utility\Inflector::singularize($this->name)); + +// $vv_template_path will be set for plugins +$templatePath = $vv_template_path ?? ROOT . DS . "templates" . DS . $modelsName; + +// If you're looking to set a custom $vv_title, you might be able to use +// generateDisplayField() on the Table instead + +// Include subnavigation structures on add/edit/view pages +// XXX: if CFM-218 (Make fields.inc configuration only) is accepted, move the contents of fields-nav.inc into fields.inc +// When subnav exists, include on all Edit/View views and on Add views for items with a parent. +if($vv_action == 'edit' || $vv_action == 'view' || !empty($vv_bc_parent_obj) || !empty($vv_primary_link_id)) { + if(file_exists($templatePath . DS . "fields-nav.inc")) { + include($templatePath . DS . "fields-nav.inc"); + } +} + +if(file_exists($templatePath . DS . "fields-links.inc")) { + include($templatePath . DS . "fields-links.inc"); +} + +// $linkFilter is used for models that belong to a specific parent model (eg: co_id) +$linkFilter = []; + +if(!empty($vv_primary_link) && !empty($this->request->getQuery($vv_primary_link))) { + $linkFilter = [$vv_primary_link => $this->request->getQuery($vv_primary_link)]; +} + +// $flashArgs pass banner messages to the flash element container +$flashArgs = []; +if(!empty($banners)) { + // XXX this doesn't work yet because we don't include fields.inc until later + // either create a second file to include earlier, or use a function to emit + // the fields (which would be more consistent with how Views render...) + $flashArgs['vv_banners'] = $banners; +} + +// If subnavigation is present a supertitle and the subnavigation will be placed above +// the normal page title. The flash messages will be shown up there as well. +if(!empty($subnav)) { + // Include the $flashArgs for the subnavigation element + $subnav['flashArgs'] = $flashArgs; + if(!empty($topLinks) && ($modelsName == 'People' && $vv_action == 'edit')) { + // We are in Person canvas mode: pass along the top links for building the Actions menu. + $subnav['topLinks'] = $topLinks; + } + // Generate the subnavigation title and tabs + print $this->element('subnavigation', $subnav); +} +?> + +
+
+ +

+ +

+ +
+ id; + + foreach(($topLinks ?? []) as $t) { + $perm = false; + + if(!empty($t['link']['controller'])) { + // We're linking into a related model, which may or may not be in a plugin + + $linkModel = \Cake\Utility\Inflector::camelize($t['link']['controller']); + + if(!empty($t['link']['plugin'])) { + $linkModel = \Cake\Utility\Inflector::camelize($t['link']['plugin']) + . "." . $linkModel; + } + + if(isset($vv_permissions[$linkModel][ $t['link']['action'] ])) { + $perm = $vv_permissions[$linkModel][ $t['link']['action'] ]; + } + + // Inject a link to the current object ID + $t['link']['?'][\App\Lib\Util\StringUtilities::entityToForeignKey($vv_obj)] = $vv_obj->id; + } else { + $perm = $vv_permissions[ $t['link']['action'] ]; + + // We need to inject $linkFilter, but not overwrite any existing query params + if(!empty($t['link']['?'])) { + $t['link']['?'] = array_merge($t['link']['?'], $linkFilter); + } else { + $t['link']['?'] = $linkFilter; + } + } + + if($perm) { + $action_args['vv_actions'][] = [ + 'order' => $this->Menu->getMenuOrder($t['order']), + 'icon' => $this->Menu->getMenuIcon($t['icon']), + 'url' => $t['link'], + 'label' => $t['label'], + 'class' => !empty($t['class']) ? $t['class'] : '', + 'confirm' => !empty($t['confirm']) ? $t['confirm'] : [] + ]; + } + } + + // Delete + if($vv_action != 'add' && !empty($vv_obj->id) && $vv_permissions['delete']) { + $action_args['vv_actions'][] = [ + 'order' => $this->Menu->getMenuOrder('Delete'), + 'icon' => $this->Menu->getMenuIcon('Delete'), + 'url' => ['action' => 'delete', $vv_obj->id], + 'label' => __d('operation', 'delete'), + 'class' => 'deletebutton', + 'confirm' => [ + 'method' => 'post', + 'dg_title' => __d('operation', 'remove'), + 'dg_body_txt' => __d('operation', 'delete.confirm', [$vv_obj->id]), + 'dg_confirm_btn' => __d('operation', 'remove') + ] + ]; + } + + if(!empty($action_args['vv_actions'])) { + print ''; + } + ?> +
+ + + + element('flash', $flashArgs); ?> + + +request->getQuery($vv_primary_link))) { + $linkId = $this->request->getQuery($vv_primary_link); + } elseif(!empty($this->request->getData($vv_primary_link))) { + $linkId = $this->request->getData($vv_primary_link); + } elseif(!empty($vv_obj->$vv_primary_link)) { + $linkId = $vv_obj->$vv_primary_link; + } +} + +/* + * Views have a set() method that is analogous to the set() found in Controller objects. + * Using set() from your view file will add the variables to the layout and elements + * that will be rendered later. + */ + +// By default, the form will POST to the current controller +// Note we need to open the form for view so Cake will autopopulate values +print $this->Form->create($vv_obj); + +// List of records to collect +// Form body +print $this->element('form/unorderedList'); + +// Import all the hidden fields in the Form +if(!empty($hidden)) { + // Inject any hidden variables set by the included file + foreach($hidden as $attr => $v) { + print $this->Form->hidden($attr, ['value' => $v]); + } +} + +if(!empty($linkId) + && ($vv_action == 'add' || $vv_action == 'edit')) { + // We don't want/need to output these for view actions + print $this->Form->hidden($vv_primary_link, ['value' => $linkId]); +} + +// Close the Form +print $this->Form->end(); + + +/** MVEA Canvas output **/ +if($vv_action != 'add' && !empty($mveas)) { + // Pass along the $mveas and any $addMenuLinks defined in templates/.../fields-nav.inc config. + print $this->element('mveaCanvas', + [ + 'vv_mveas' => $mveas, + 'vv_add_menu_links' => !empty($addMenuLinks) ? $addMenuLinks : '', + 'vv_entity_type' => $mveasEntityType + ]); +} + +// XXX insert changelog metadata (+nav? or maybe we should have a dedicate index view that shows all records in revision order?) diff --git a/app/templates/element/banner.php b/app/templates/element/banner.php new file mode 100644 index 000000000..c0393c6c1 --- /dev/null +++ b/app/templates/element/banner.php @@ -0,0 +1,39 @@ + + + +
  • + Alert->alert($info, 'warning') ?> +
  • diff --git a/app/templates/element/form/entityID.php b/app/templates/element/form/entityID.php new file mode 100644 index 000000000..9f24868e0 --- /dev/null +++ b/app/templates/element/form/entityID.php @@ -0,0 +1,37 @@ + + +id)): ?> +
  • + id) ?> +
  • + + diff --git a/app/templates/element/form/fieldDiv.php b/app/templates/element/form/fieldDiv.php new file mode 100644 index 000000000..6248982b3 --- /dev/null +++ b/app/templates/element/form/fieldDiv.php @@ -0,0 +1,46 @@ + + +
    + element('form/nameDiv'); + + // Info Div + if(isset($vv_field_arguments['prefix'])) { + print $this->element('form/infoDivWithPrefix'); + } elseif(isset($vv_field_arguments['status'])) { + print $this->element('form/infoDivStatus'); + } else { + print $this->element('form/infoDiv'); + } + ?> +
    \ No newline at end of file diff --git a/app/templates/element/form/infoDiv.php b/app/templates/element/form/infoDiv.php new file mode 100644 index 000000000..9d1e86eb7 --- /dev/null +++ b/app/templates/element/form/infoDiv.php @@ -0,0 +1,33 @@ + +
    + Fieeld->formField(...$vv_field_arguments) ?> +
    \ No newline at end of file diff --git a/app/templates/element/form/infoDivStatus.php b/app/templates/element/form/infoDivStatus.php new file mode 100644 index 000000000..47443b023 --- /dev/null +++ b/app/templates/element/form/infoDivStatus.php @@ -0,0 +1,60 @@ +Html->link( + $link['label'], + $link['url'], + $link + ); + } else { + // Make $status the link + + $linkHtml = $this->Html->link( + $status, + $link['url'], + // Pass whatever other args are specified + $link + ); + } +} + +?> +
    + +
    \ No newline at end of file diff --git a/app/templates/element/form/infoDivWithPrefix.php b/app/templates/element/form/infoDivWithPrefix.php new file mode 100644 index 000000000..c5a80d0a7 --- /dev/null +++ b/app/templates/element/form/infoDivWithPrefix.php @@ -0,0 +1,38 @@ + +
    +
    +
    + +
    + Fieeld->formField(...$vv_field_arguments)?> +
    +
    diff --git a/app/templates/element/form/listItem.php b/app/templates/element/form/listItem.php new file mode 100644 index 000000000..2f9ef736a --- /dev/null +++ b/app/templates/element/form/listItem.php @@ -0,0 +1,48 @@ + $value) { + $this->set($key, $value); +} +$this->set('vv_field_arguments', $arguments); + +// Additional classes calculation +$classes = ''; +// XXX Even though i set the view vars above i can not access their values here/yet. +if(in_array($this->Fieeld->getFieldType($arguments['fieldName']), ['date', 'datetime', 'timestamp'])) { + $classes .= 'fields-datepicker '; +} + +?> + +
  • + element('form/fieldDiv')?> +
  • diff --git a/app/templates/element/form/nameDiv.php b/app/templates/element/form/nameDiv.php new file mode 100644 index 000000000..07598383c --- /dev/null +++ b/app/templates/element/form/nameDiv.php @@ -0,0 +1,79 @@ +name; +$fn = $fieldName; + +if(str_contains($fieldName, '.')) { + // othermodels.0.field + + $bits = explode('.', $fieldName, 3); + $mn = Inflector::classify($bits[0]); + $fn = $bits[2]; +} + +// First, try to autogenerate the field label (if we weren't given one). +$pluginDomain = (!empty($this->Fieeld->pluginName()) + ? Inflector::underscore($this->Fieeld->pluginName()) + : null); + +[$label, $desc] = $this->Fieeld->calculateLabelAndDescription($pluginDomain, $mn, $fn); +$label = $labelText ?? $label; + +?> + +
    +
    + + + Form->label($fn, $label) ?> + + + + Fieeld->isEditable() && in_array($fn, $vv_required_fields, true)) { + print $this->element('form/requiredSpan'); + } + ?> +
    + +
    + +
    \ No newline at end of file diff --git a/app/templates/element/form/notSetDiv.php b/app/templates/element/form/notSetDiv.php new file mode 100644 index 000000000..c156d03fb --- /dev/null +++ b/app/templates/element/form/notSetDiv.php @@ -0,0 +1,32 @@ + + +
    \ No newline at end of file diff --git a/app/templates/element/form/requiredSpan.php b/app/templates/element/form/requiredSpan.php new file mode 100644 index 000000000..20e2a2a50 --- /dev/null +++ b/app/templates/element/form/requiredSpan.php @@ -0,0 +1,33 @@ + + + + \ No newline at end of file diff --git a/app/templates/element/form/submit.php b/app/templates/element/form/submit.php new file mode 100644 index 000000000..a32069142 --- /dev/null +++ b/app/templates/element/form/submit.php @@ -0,0 +1,43 @@ + + +Fieeld->isEditable()): ?> +
  • +
    +
    + * +
    +
    + Form->submit($label) ?> +
    +
    +
  • + \ No newline at end of file diff --git a/app/templates/element/form/unorderedList.php b/app/templates/element/form/unorderedList.php new file mode 100644 index 000000000..d0a304382 --- /dev/null +++ b/app/templates/element/form/unorderedList.php @@ -0,0 +1,54 @@ + + \ No newline at end of file diff --git a/app/templates/element/mveaJs.php b/app/templates/element/mveaJs.php index 3ae2e54b5..7360b44e1 100644 --- a/app/templates/element/mveaJs.php +++ b/app/templates/element/mveaJs.php @@ -67,7 +67,8 @@ action: '' }, txt: JSON.parse('locales()) ?>'), - isLoading: true + isLoading: true, + title: '' } }, components: { @@ -113,32 +114,33 @@ }, created() { this.refreshComponent(); - } + }, + template: + `
    +
    +

    + {{ title }} + + + +

    +
    +
      +
    • Loading...
    • +
    + + +
    +
    +
    + ` }); // Mount the component and provide a global reference for this app instance. window.mvea = app.mount("#"); -
    -
    -
    -

    - - - - -

    -
    -
      -
    • Loading...
    • -
    - - -
    -
    -
    -
    +
    diff --git a/app/templates/element/peopleAutocomplete.php b/app/templates/element/peopleAutocomplete.php index a1d8efc22..bf7fbe1f4 100644 --- a/app/templates/element/peopleAutocomplete.php +++ b/app/templates/element/peopleAutocomplete.php @@ -104,7 +104,15 @@ console.error(res); console.log('Status Code: ', res.status) } - } + }, + template: ` + + + ` }); @@ -118,10 +126,4 @@ window. = app.mount("#-container"); -
    - - -
    +
    diff --git a/app/templates/layout/default.php b/app/templates/layout/default.php index 0138389d1..54212e4c1 100644 --- a/app/templates/layout/default.php +++ b/app/templates/layout/default.php @@ -64,13 +64,13 @@ ]) . PHP_EOL ?> - + Html->script([ 'bootstrap/bootstrap.bundle.min.js', 'jquery/jquery.min.js', 'vue/vue-3.2.31.global.prod.js', - 'vue/primevue-3.48.1.core.min.js', - 'vue/primevue-3.48.1.autocomplete.min.js' + 'vue/primevue-3.51.0.core.min.js', + 'vue/primevue-3.51.0.autocomplete.min.js' ]) . PHP_EOL ?> diff --git a/app/templates/layout/iframe.php b/app/templates/layout/iframe.php index cffdcf1a4..725e645e1 100644 --- a/app/templates/layout/iframe.php +++ b/app/templates/layout/iframe.php @@ -69,8 +69,8 @@ 'bootstrap/bootstrap.bundle.min.js', 'jquery/jquery.min.js', 'vue/vue-3.2.31.global.prod.js', - 'vue/primevue-3.48.1.core.min.js', - 'vue/primevue-3.48.1.autocomplete.min.js' + 'vue/primevue-3.51.0.core.min.js', + 'vue/primevue-3.51.0.autocomplete.min.js' ]) . PHP_EOL ?>