Added helper and CSS

This commit is contained in:
snipe
2026-05-29 17:37:12 +01:00
parent 3868e469c0
commit ade07b411b
19 changed files with 316 additions and 35 deletions
+38
View File
@@ -1856,4 +1856,42 @@ class Helper
return 'App\\Models\\'.ucwords($model);
}
/**
* Render a markdown-textarea value as HTML.
*
* Soft line breaks (single newlines) are rendered as <br> so that line
* breaks typed in the textarea are preserved in the output.
*
* When $inline is true, block-level elements are suppressed and hard
* breaks are pre-processed manually — used for the encrypted reveal span
* where block HTML cannot be placed inside a font-size-toggled <span>.
*/
public static function renderMarkdown(?string $text, bool $inline = false): string
{
if (empty($text)) {
return '';
}
if ($inline) {
// Convert newlines to CommonMark hard breaks for inline rendering
$text = preg_replace('/(?<! {2})\n/', " \n", $text);
return Str::inlineMarkdown($text, ['html_input' => 'escape']);
}
$html = trim(Str::markdown($text, [
'html_input' => 'escape',
'renderer' => ['soft_break' => "<br>\n"],
]));
// If the entire output is a single <p> block, unwrap it so the content
// renders inline-ish without the <p> adding unwanted top spacing in the
// compact detail-view layout.
if (str_starts_with($html, '<p>') && str_ends_with($html, '</p>') && substr_count($html, '<p>') === 1) {
return substr($html, 3, -4);
}
return $html;
}
}
+3 -3
View File
@@ -144,7 +144,7 @@ class AssetsTransformer
$fields_array[$field->name] = [
'field' => e($field->db_column),
'value' => e($value),
'value' => ($field->element == 'markdown-textarea' && Gate::allows('assets.view.encrypted_custom_fields')) ? Helper::renderMarkdown($value) : e($value),
'field_format' => $field->format,
'element' => $field->element,
];
@@ -158,7 +158,7 @@ class AssetsTransformer
$fields_array[$field->name] = [
'field' => e($field->db_column),
'value' => e($value),
'value' => ($field->element == 'markdown-textarea') ? Helper::renderMarkdown($value) : e($value),
'field_format' => $field->format,
'element' => $field->element,
];
@@ -274,7 +274,7 @@ class AssetsTransformer
$value = Helper::getFormattedDateObject($value, 'date', false);
}
$fields_array[$field->db_column] = e($value);
$fields_array[$field->db_column] = ($field->element == 'markdown-textarea') ? Helper::renderMarkdown($value) : e($value);
}
$array['custom_fields'] = $fields_array;
+1 -1
View File
@@ -51,7 +51,7 @@ class CustomField extends Model
*/
protected $rules = [
'name' => 'required|unique:custom_fields',
'element' => 'required|in:text,listbox,textarea,checkbox,radio',
'element' => 'required|in:text,listbox,textarea,markdown-textarea,checkbox,radio',
'field_encrypted' => 'nullable|boolean',
'auto_add_to_fieldsets' => 'boolean',
'show_in_listview' => 'boolean',
+11
View File
@@ -154,4 +154,15 @@ class CustomFieldFactory extends Factory
];
});
}
public function testMarkdownTextarea()
{
return $this->state(function () {
return [
'name' => 'Notes',
'help_text' => 'Additional notes about this asset. Markdown is supported.',
'element' => 'markdown-textarea',
];
});
}
}
+15
View File
@@ -36,6 +36,7 @@ class CustomFieldSeeder extends Seeder
CustomField::factory()->count(1)->testEncrypted()->create();
CustomField::factory()->count(1)->testCheckbox()->create();
CustomField::factory()->count(1)->testRadio()->create();
CustomField::factory()->count(1)->testMarkdownTextarea()->create();
DB::table('custom_field_custom_fieldset')->insert([
[
@@ -109,6 +110,20 @@ class CustomFieldSeeder extends Seeder
'required' => 0,
],
[
'custom_field_id' => '9',
'custom_fieldset_id' => '1',
'order' => 0,
'required' => 0,
],
[
'custom_field_id' => '9',
'custom_fieldset_id' => '2',
'order' => 0,
'required' => 0,
],
]);
}
}
+41
View File
@@ -1463,6 +1463,46 @@ caption.tableCaption {
font-size: 18px;
padding-left: 8px;
}
.markdown-field-content h1 {
font-size: 22px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h2 {
font-size: 20px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h3 {
font-size: 18px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h4 {
font-size: 16px;
font-weight: 700;
margin: 4px 0 4px;
}
.markdown-field-content h5,
.markdown-field-content h6 {
font-size: 14px;
font-weight: 700;
margin: 4px 0 4px;
}
.markdown-field-content p {
margin: 0 0 4px;
}
.markdown-field-content p:last-child {
margin-bottom: 0;
}
.markdown-field-content ul,
.markdown-field-content ol {
margin: 0 0 4px;
padding-left: 20px;
}
.markdown-field-content > *:first-child {
margin-top: 0;
}
.sidebar-toggle.btn {
border-radius: 3px;
box-shadow: none;
@@ -1565,6 +1605,7 @@ Radio toggle styles for permission settings and check/uncheck all
cursor: pointer;
}
.js-copy-link {
float: left;
color: grey;
}
.deleted {
File diff suppressed because one or more lines are too long
+41
View File
@@ -1091,6 +1091,46 @@ caption.tableCaption {
font-size: 18px;
padding-left: 8px;
}
.markdown-field-content h1 {
font-size: 22px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h2 {
font-size: 20px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h3 {
font-size: 18px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h4 {
font-size: 16px;
font-weight: 700;
margin: 4px 0 4px;
}
.markdown-field-content h5,
.markdown-field-content h6 {
font-size: 14px;
font-weight: 700;
margin: 4px 0 4px;
}
.markdown-field-content p {
margin: 0 0 4px;
}
.markdown-field-content p:last-child {
margin-bottom: 0;
}
.markdown-field-content ul,
.markdown-field-content ol {
margin: 0 0 4px;
padding-left: 20px;
}
.markdown-field-content > *:first-child {
margin-top: 0;
}
.sidebar-toggle.btn {
border-radius: 3px;
box-shadow: none;
@@ -1193,6 +1233,7 @@ Radio toggle styles for permission settings and check/uncheck all
cursor: pointer;
}
.js-copy-link {
float: left;
color: grey;
}
.deleted {
File diff suppressed because one or more lines are too long
+82
View File
@@ -22799,6 +22799,46 @@ caption.tableCaption {
font-size: 18px;
padding-left: 8px;
}
.markdown-field-content h1 {
font-size: 22px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h2 {
font-size: 20px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h3 {
font-size: 18px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h4 {
font-size: 16px;
font-weight: 700;
margin: 4px 0 4px;
}
.markdown-field-content h5,
.markdown-field-content h6 {
font-size: 14px;
font-weight: 700;
margin: 4px 0 4px;
}
.markdown-field-content p {
margin: 0 0 4px;
}
.markdown-field-content p:last-child {
margin-bottom: 0;
}
.markdown-field-content ul,
.markdown-field-content ol {
margin: 0 0 4px;
padding-left: 20px;
}
.markdown-field-content > *:first-child {
margin-top: 0;
}
.sidebar-toggle.btn {
border-radius: 3px;
box-shadow: none;
@@ -22901,6 +22941,7 @@ Radio toggle styles for permission settings and check/uncheck all
cursor: pointer;
}
.js-copy-link {
float: left;
color: grey;
}
.deleted {
@@ -24556,6 +24597,46 @@ caption.tableCaption {
font-size: 18px;
padding-left: 8px;
}
.markdown-field-content h1 {
font-size: 22px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h2 {
font-size: 20px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h3 {
font-size: 18px;
font-weight: 700;
margin: 6px 0 4px;
}
.markdown-field-content h4 {
font-size: 16px;
font-weight: 700;
margin: 4px 0 4px;
}
.markdown-field-content h5,
.markdown-field-content h6 {
font-size: 14px;
font-weight: 700;
margin: 4px 0 4px;
}
.markdown-field-content p {
margin: 0 0 4px;
}
.markdown-field-content p:last-child {
margin-bottom: 0;
}
.markdown-field-content ul,
.markdown-field-content ol {
margin: 0 0 4px;
padding-left: 20px;
}
.markdown-field-content > *:first-child {
margin-top: 0;
}
.sidebar-toggle.btn {
border-radius: 3px;
box-shadow: none;
@@ -24658,6 +24739,7 @@ Radio toggle styles for permission settings and check/uncheck all
cursor: pointer;
}
.js-copy-link {
float: left;
color: grey;
}
.deleted {
+3 -3
View File
@@ -1,9 +1,9 @@
{
"/js/dist/all.js": "/js/dist/all.js?id=b7b86fa704f44a30a4593132fd496fee",
"/css/build/overrides.css": "/css/build/overrides.css?id=c173dd71d56c1089bf560a849586d93e",
"/css/build/app.css": "/css/build/app.css?id=63ef76491d01db361ad53cf1c8c7114f",
"/css/build/overrides.css": "/css/build/overrides.css?id=cd2464a43a9df33421778c1fb81ac9dd",
"/css/build/app.css": "/css/build/app.css?id=66480c2823f94791fcb0826fd9c80322",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=ee0ed88465dd878588ed044eefb67723",
"/css/dist/all.css": "/css/dist/all.css?id=57e6bf27bcfad47e58a82b9842a7d5bd",
"/css/dist/all.css": "/css/dist/all.css?id=fba1d885ebbcf09a454a11d423ae30d8",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
+14
View File
@@ -1186,6 +1186,19 @@ caption.tableCaption {
padding-left: 8px;
}
.markdown-field-content {
h1 { font-size: 22px; font-weight: 700; margin: 6px 0 4px; }
h2 { font-size: 20px; font-weight: 700; margin: 6px 0 4px; }
h3 { font-size: 18px; font-weight: 700; margin: 6px 0 4px; }
h4 { font-size: 16px; font-weight: 700; margin: 4px 0 4px; }
h5, h6 { font-size: 14px; font-weight: 700; margin: 4px 0 4px; }
p { margin: 0 0 4px; }
p:last-child { margin-bottom: 0; }
ul, ol { margin: 0 0 4px; padding-left: 20px; }
> *:first-child { margin-top: 0; }
}
// via https://github.com/grokability/snipe-it/issues/11754
.sidebar-toggle.btn {
border-radius: 3px;
@@ -1307,6 +1320,7 @@ Radio toggle styles for permission settings and check/uncheck all
}
.js-copy-link {
float: left;
color: grey;
}
@@ -64,8 +64,10 @@ return [
'text' => 'Text Box',
'listbox' => 'List Box',
'textarea' => 'Textarea (multi-line)',
'markdown-textarea' => 'Markdown Textarea',
'checkbox' => 'Checkbox',
'radio' => 'Radio Buttons',
],
'markdown_supported' => 'Markdown is supported',
'general_help_text' => 'Custom fields store additional information not covered by the default asset fields. <a href="https://snipe-it.readme.io/docs/custom-fields#/"><i class="fa fa-external-link"></i></a>.',
];
@@ -8,42 +8,48 @@
</x-copy-to-clipboard>
{{-- Hidden span used as copy target --}}
{{-- It's tempting to break out the HTML into separate lines for this, but it results in extra spaces being added onto the end of the copied value --}}
{{-- For markdown fields, position: absolute removes it from normal flow so it can't create an anonymous block box --}}
@if (($field->field_encrypted=='1') && (Gate::allows('assets.view.encrypted_custom_fields')))
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0px;">{{ ($field->isFieldDecryptable($item->{$field->db_column_name()}) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $item->{$field->db_column_name()}) }}</span>
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0;{{ $field->element === 'markdown-textarea' ? ' position: absolute;' : '' }}">{{ ($field->isFieldDecryptable($item->{$field->db_column_name()}) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $item->{$field->db_column_name()}) }}</span>
@elseif (($field->field_encrypted=='1') && (Gate::denies('assets.view.encrypted_custom_fields')))
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0px;">{{ strtoupper(trans('admin/custom_fields/general.encrypted')) }}</span>
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0;{{ $field->element === 'markdown-textarea' ? ' position: absolute;' : '' }}">{{ strtoupper(trans('admin/custom_fields/general.encrypted')) }}</span>
@else
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0px;">{{ $item->{$field->db_column_name()} }}</span>
<span class="js-copy-{{ $field->id }} visually-hidden hidden-print" style="font-size: 0;{{ $field->element === 'markdown-textarea' ? ' position: absolute;' : '' }}">{{ $item->{$field->db_column_name()} }}</span>
@endif
@endif
@if (($field->field_encrypted=='1') && ($item->{$field->db_column_name()}!='') && (Gate::allows('assets.view.encrypted_custom_fields')))
<i class="fas fa-lock" data-tooltip="true" data-placement="top" title="{{ trans('admin/custom_fields/general.value_encrypted') }}" onclick="showHideEncValue(this)" id="text-{{ $field->id }}"></i>
<i class="fas fa-lock fa-fw pull-right" style="font-size: 16px;" data-tooltip="true" data-placement="top" title="{{ trans('admin/custom_fields/general.value_encrypted') }}" onclick="showHideEncValue(this)" id="text-{{ $field->id }}"></i>
@endif
@if ($field->isFieldDecryptable($item->{$field->db_column_name()} ))
@can('assets.view.encrypted_custom_fields')
@php
$fieldSize = strlen(Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}))
$fieldSize = strlen(\App\Helpers\Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}))
@endphp
@if ($fieldSize > 0)
<span id="text-{{ $field->id }}-to-hide">***********</span>
<span id="text-{{ $field->id }}-to-hide" style="font-size: 20px;vertical-align:middle;">***********</span>
@if (($field->format=='URL') && ($item->{$field->db_column_name()}!=''))
<span class="js-copy-{{ $field->id }} hidden-print"
id="text-{{ $field->id }}-to-show"
style="font-size: 0px;">
<a href="{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}"
target="_new">{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}</a>
</span>
style="font-size: 0;">
<a href="{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}"
target="_new">{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}</a>
</span>
@elseif (($field->format=='DATE') && ($item->{$field->db_column_name()}!=''))
<span class="js-copy-{{ $field->id }} hidden-print"
id="text-{{ $field->id }}-to-show"
style="font-size: 0px;">{{ \App\Helpers\Helper::gracefulDecrypt($field, \App\Helpers\Helper::getFormattedDateObject($item->{$field->db_column_name()}, 'date', false)) }}</span>
style="font-size: 0;">{{ \App\Helpers\Helper::gracefulDecrypt($field, \App\Helpers\Helper::getFormattedDateObject($item->{$field->db_column_name()}, 'date', false)) }}</span>
@elseif ($field->element == 'markdown-textarea')
<div class="js-copy-{{ $field->id }} hidden-print markdown-field-content"
id="text-{{ $field->id }}-to-show"
data-markdown="true"
style="display: none;">{!! Helper::renderMarkdown(Helper::gracefulDecrypt($field, $item->{$field->db_column_name()})) !!}</div>
@else
<span class="js-copy-{{ $field->id }} hidden-print"
id="text-{{ $field->id }}-to-show"
style="font-size: 0px;">{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}</span>
style="font-size: 0;">{{ Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) }}</span>
@endif
@endif
@else
@@ -57,6 +63,8 @@
<a href="{{ $item->{$field->db_column_name()} }}" target="_new">{{ $item->{$field->db_column_name()} }}</a>
@elseif (($field->format=='DATE') && ($item->{$field->db_column_name()}!=''))
{{ \App\Helpers\Helper::getFormattedDateObject($item->{$field->db_column_name()}, 'date', false) }}
@elseif (($field->element == 'markdown-textarea') && ($item->{$field->db_column_name()} != ''))
<div class="markdown-field-content">{!! Helper::renderMarkdown($item->{$field->db_column_name()}) !!}</div>
@else
{!! nl2br(e($item->{$field->db_column_name()})) !!}
@endif
@@ -63,6 +63,7 @@
'text' => trans('admin/custom_fields/general.types.text'),
'listbox' => trans('admin/custom_fields/general.types.listbox'),
'textarea' => trans('admin/custom_fields/general.types.textarea'),
'markdown-textarea' => trans('admin/custom_fields/general.types.markdown-textarea'),
'checkbox' => trans('admin/custom_fields/general.types.checkbox'),
'radio' => trans('admin/custom_fields/general.types.radio'),
]"
@@ -347,7 +348,7 @@
// and don't display encryption option for checkbox or radio
$(".field_element").change(function(){
$(this).find("option:selected").each(function(){
if (($(this).attr("value")!="text") && ($(this).attr("value")!="textarea")){
if (($(this).attr("value") != "text") && ($(this).attr("value") != "textarea") && ($(this).attr("value") != "markdown-textarea")) {
$("#field_values_text").show();
if ($(this).attr("value") == "checkbox" || $(this).attr("value") == "radio") {
$("#encryption_section").hide();
+15 -4
View File
@@ -2377,6 +2377,8 @@
// Use element id to find the text element to hide / show
var targetElement = e.id+"-to-show";
var hiddenElement = e.id+"-to-hide";
var targetEl = document.getElementById(targetElement);
var isMarkdown = targetEl && targetEl.dataset.markdown;
var audio = new Audio('{{ config('app.url') }}/sounds/lock.mp3');
if($(e).hasClass('fa-lock')) {
@if ((isset($user)) && ($user->enable_sounds))
@@ -2384,7 +2386,11 @@
@endif
$(e).removeClass('fa-lock').addClass('fa-unlock');
// Show the encrypted custom value and hide the element with asterisks
document.getElementById(targetElement).style.fontSize = "100%";
if (isMarkdown) {
targetEl.style.display = "block";
} else {
targetEl.style.fontSize = "100%";
}
document.getElementById(hiddenElement).style.display = "none";
} else {
@@ -2393,7 +2399,12 @@
@endif
$(e).removeClass('fa-unlock').addClass('fa-lock');
// ClipboardJS can't copy display:none elements so use a trick to hide the value
document.getElementById(targetElement).style.fontSize = "0px";
if (isMarkdown) {
targetEl.style.display = "none";
} else {
// ClipboardJS can't copy display:none elements so use a trick to hide the value
targetEl.style.fontSize = "0px";
}
document.getElementById(hiddenElement).style.display = "";
}
@@ -2523,8 +2534,8 @@
// This is necessary to avoid loop because value is generated dynamically
if (originalValue !== '' && originalValue !== asterisks) $element.attr('value', originalValue);
// Hide the original value and show asterisks of the same length
var asterisks = '*'.repeat(originalValue.length);
// Hide the original value and show a fixed-length asterisk placeholder
var asterisks = '*'.repeat(11);
$element.text(asterisks);
// Add click event to show original text
@@ -16,11 +16,16 @@
<label for="{{ $field->db_column_name() }}" class="col-md-3 control-label">
@if ($field->field_encrypted)
<i class="fas fa-lock" data-tooltip="true" data-placement="top" title="{{ trans('admin/custom_fields/general.value_encrypted') }}"></i>
@endif
{{ $field->name }}
</label>
<div class="col-md-7 col-sm-12">
<div class="col-md-8 col-sm-12">
@if ($field->element!='text')
@@ -36,7 +41,14 @@
@elseif ($field->element=='textarea')
<!-- Textarea -->
<textarea class="col-md-6 form-control" id="{{ $field->db_column_name() }}" name="{{ $field->db_column_name() }}"{{ ($field->pivot->required=='1') ? ' required' : '' }}>{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}</textarea>
<textarea rows="6" class="col-md-6 form-control" id="{{ $field->db_column_name() }}" name="{{ $field->db_column_name() }}"{{ ($field->pivot->required=='1') ? ' required' : '' }}>{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}</textarea>
@elseif ($field->element=='markdown-textarea')
<!-- Markdown Textarea -->
<textarea rows="6" class="col-md-6 form-control" id="{{ $field->db_column_name() }}" name="{{ $field->db_column_name() }}"{{ ($field->pivot->required=='1') ? ' required' : '' }}>{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}</textarea>
<p class="help-block">
<i class="fab fa-markdown" aria-hidden="true"></i> {{ trans('admin/custom_fields/general.markdown_supported') }}
</p>
@elseif ($field->element=='checkbox')
<!-- Checkbox -->
@@ -108,11 +120,6 @@
?>
</div>
@if ($field->field_encrypted)
<div class="col-md-1 col-sm-1 text-left">
<i class="fas fa-lock" data-tooltip="true" data-placement="top" title="{{ trans('admin/custom_fields/general.value_encrypted') }}"></i>
</div>
@endif
</div>
@endif
@@ -496,7 +496,9 @@
if (cell.is('th')) {
return cell.find('.th-inner').text()
}
return htmlData
// Convert <br> tags to newlines so that line breaks in notes and
// textarea fields survive HTML-stripping during export
return htmlData.replace(/<br\s*\/?>/gi, '\n');
}
// This allows us to override the table defaults set below using the data-dash attributes
@@ -1993,6 +1995,13 @@
return '<a href="mailto:' + row.custom_fields[field_column_plain].value + '" style="white-space: nowrap" data-tooltip="true" title="{{ trans('general.send_email') }}"><x-icon type="email" /> ' + row.custom_fields[field_column_plain].value + '</a>';
}
}
// Convert newlines to <br> for textarea fields so they render in
// the table; export will convert <br> back to \n via onCellHtmlData
if (row.custom_fields[field_column_plain].element === 'textarea') {
var val = row.custom_fields[field_column_plain].value;
return val ? val.replace(/(?:\r\n|\r|\n)/g, '<br>') : '';
}
return row.custom_fields[field_column_plain].value;
}
+2 -1
View File
@@ -381,7 +381,8 @@
@endif
<p class="help-block">
{{ trans('admin/settings/general.dashboard_message_help') }}
{!! trans('general.github_markdown') !!}</p>
<i class="fab fa-markdown" aria-hidden="true"> {!! trans('general.github_markdown') !!}
</p>
</div>
</div>
</fieldset>