fix: Uninitialised zip and missing file size error in Native Data Loads (#7081)
[openemr.git] / library / options.inc.php
blobdfe3bb1da8a4b408fcd3b06af43f07c5fbb516c5
1 <?php
3 // Copyright (C) 2007-2021 Rod Roark <rod@sunsetsystems.com>
4 // Copyright © 2010 by Andrew Moore <amoore@cpan.org>
5 // Copyright © 2010 by "Boyd Stephen Smith Jr." <bss@iguanasuicide.net>
6 // Copyright (c) 2017 - 2021 Jerry Padgett <sjpadgett@gmail.com>
7 // Copyright (c) 2021 Robert Down <robertdown@live.com>
8 // Copyright (c) 2022 David Eschelbacher <psoas@tampabay.rr.com>
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License
12 // as published by the Free Software Foundation; either version 2
13 // of the License, or (at your option) any later version.
15 // Functions for managing the lists and layouts
17 // Note: there are translation wrappers for the lists and layout labels
18 // at library/translation.inc.php. The functions are titled
19 // xl_list_label() and xl_layout_label() and are controlled by the
20 // $GLOBALS['translate_lists'] and $GLOBALS['translate_layout']
21 // flags in globals.php
23 // Documentation for layout_options.edit_options:
25 // A = Age as years or "xx month(s)"
26 // B = Gestational age as "xx week(s) y day(s)"
27 // C = Capitalize first letter of each word (text fields)
28 // D = Check for duplicates in New Patient form
29 // G = Graphable (for numeric fields in forms supporting historical data)
30 // H = Read-only field copied from static history (this is obsolete)
31 // J = Jump to Next Row
32 // K = Prepend Blank Row
33 // L = Lab Order ("ord_lab") types only (address book)
34 // M = Radio Group Master (currently for radio buttons only)
35 // m = Radio Group Member (currently for radio buttons only)
36 // N = Show in New Patient form
37 // O = Procedure Order ("ord_*") types only (address book)
38 // P = Default to previous value when current value is not yet set
39 // R = Distributor types only (address book)
40 // T = Use description as default Text
41 // DAP = Use description as placeholder
42 // U = Capitalize all letters (text fields)
43 // V = Vendor types only (address book)
44 // 0 = Read Only - the input element's "disabled" property is set
45 // 1 = Write Once (not editable when not empty) (text fields)
46 // 2 = Show descriptions instead of codes for billing code input
48 // note: isOption() returns true/false
50 // NOTE: All of the magic constants for the data types here are found in library/layout.inc.php
52 require_once("user.inc.php");
53 require_once("patient.inc.php");
54 require_once("lists.inc.php");
55 require_once(dirname(dirname(__FILE__)) . "/custom/code_types.inc.php");
57 use OpenEMR\Common\Acl\AclExtended;
58 use OpenEMR\Common\Acl\AclMain;
59 use OpenEMR\Common\Layouts\LayoutsUtils;
60 use OpenEMR\Services\EncounterService;
61 use OpenEMR\Services\FacilityService;
62 use OpenEMR\Services\PatientService;
64 $facilityService = new FacilityService();
66 $date_init = "";
67 $membership_group_number = 0;
69 // Our base Bootstrap column class, referenced here and in some other modules.
70 // Using col-lg allow us to have additional breakpoint at col-md.(992px, 768px)
71 // col-md-auto will let BS decide with col-12 always for sm devices.
72 $BS_COL_CLASS = 'col-12 col-md-auto col-lg';
74 function get_pharmacies()
76 return sqlStatement("SELECT d.id, d.name, a.line1, a.city, " .
77 "p.area_code, p.prefix, p.number FROM pharmacies AS d " .
78 "LEFT OUTER JOIN addresses AS a ON a.foreign_id = d.id " .
79 "LEFT OUTER JOIN phone_numbers AS p ON p.foreign_id = d.id " .
80 "AND p.type = 2 " .
81 "ORDER BY a.state, a.city, d.name, p.area_code, p.prefix, p.number");
84 function optionalAge($frow, $date, &$asof, $description = '')
86 $asof = '';
87 if (empty($date)) {
88 return '';
91 $edit_options = $frow['edit_options'] ?? null;
93 $date = substr($date, 0, 10);
94 if (isOption($edit_options, 'A') !== false) {
95 $format = 0;
96 } elseif (isOption($edit_options, 'B') !== false) {
97 $format = 3;
98 } else {
99 return '';
102 if (isOption($frow['form_id'], 'LBF') === false) {
103 $tmp = sqlQuery(
104 "SELECT date FROM form_encounter WHERE " .
105 "pid = ? AND encounter = ? ORDER BY id DESC LIMIT 1",
106 array($GLOBALS['pid'], $GLOBALS['encounter'])
108 if (!empty($tmp['date'])) {
109 $asof = substr($tmp['date'], 0, 10);
112 if ($description === '') {
113 $prefix = ($format ? xl('Gest age') : xl('Age')) . ' ';
114 } else {
115 $prefix = $description . ' ';
117 return $prefix . oeFormatAge($date, $asof, $format);
120 // Function to generate a drop-list.
122 function generate_select_list(
123 $tag_name,
124 $list_id,
125 $currvalue,
126 $title,
127 $empty_name = ' ',
128 $class = '',
129 $onchange = '',
130 $tag_id = '',
131 $custom_attributes = null,
132 $multiple = false, // new #10
133 $backup_list = '', // new #11
134 $ignore_default = false,
135 $include_inactive = false,
136 $tabIndex = false
138 $attributes = [];
139 $_options = [];
140 $_metadata = [];
142 $tag_name_esc = attr($tag_name);
144 $attributes['name'] = ($multiple) ? $tag_name_esc . "[]" : $tag_name_esc;
146 if ($tabIndex !== false) {
147 $attributes['tabindex'] = attr($tabIndex);
150 if ($multiple) {
151 $attributes['multiple'] = "multiple";
154 $attributes['id'] = attr($tag_name);
155 $attributes['class'] = (!empty($class)) ? "form-control " . attr($class) : "form-control";
157 if ($onchange) {
158 $attributes['onchange'] = $onchange;
161 if ($custom_attributes != null && is_array($custom_attributes)) {
162 foreach ($custom_attributes as $attr => $val) {
163 if (isset($custom_attributes [$attr])) {
164 $attributes[attr($attr)] = attr($val);
169 $attributes['title'] = attr($title);
171 $selectEmptyName = xlt($empty_name);
172 if ($empty_name) {
173 preg_match_all('/select2/m', ($class ?? ''), $matches, PREG_SET_ORDER, 0);
174 if (array_key_exists('placeholder', $attributes) && count($matches) > 0) {
175 // We have a placeholder attribute as well as a select2 class indicating there
176 // should be provide a truley empty option.
177 $_options[] = [];
178 } else {
179 $_options[] = [
180 'label' => $selectEmptyName,
181 'value' => '',
182 'isSelected' => true,
187 $got_selected = false;
189 for ($active = 1; $active == 1 || ($active == 0 && $include_inactive); --$active) {
190 $_optgroup = ($include_inactive) ? true : false;
192 // List order depends on language translation options.
193 // (Note we do not need to worry about the list order in the algorithm
194 // after the below code block since that is where searches for exceptions
195 // are done which include inactive items or items from a backup
196 // list; note these will always be shown at the bottom of the list no matter the
197 // chosen order.)
198 // This block should be migrated to the ListService but the service currently does not translate or offer a sort option.
199 $lang_id = empty($_SESSION['language_choice']) ? '1' : $_SESSION['language_choice'];
200 // sort by title
201 $order_by_sql = ($GLOBALS['gb_how_sort_list'] == '0') ? "seq, title" : "title, seq";
202 if (!$GLOBALS['translate_lists']) {
203 // do not translate
204 $lres = sqlStatement("SELECT * FROM list_options WHERE list_id = ? AND activity = ? ORDER BY $order_by_sql", [$list_id, $active]);
205 } else {
206 // do translate
207 $order_by_sql = str_replace("seq", "lo.seq", $order_by_sql);
208 $sql = "SELECT lo.option_id, lo.is_default,
209 COALESCE((SELECT ld.definition FROM lang_constants AS lc, lang_definitions AS ld
210 WHERE lc.constant_name = lo.title AND ld.cons_id = lc.cons_id AND ld.lang_id = ? AND ld.definition IS NOT NULL
211 AND ld.definition != ''
212 LIMIT 1), lo.title) AS title
213 FROM list_options AS lo
214 WHERE lo.list_id = ? AND lo.activity = ?
215 ORDER BY {$order_by_sql}";
216 $lres = sqlStatement($sql, [$lang_id, $list_id, $active]);
219 // Populate the options array with pertinent values
221 while ($lrow = sqlFetchArray($lres)) {
222 $selectedValues = explode("|", $currvalue ?? '');
223 $isSelected = false;
225 $optionValue = attr($lrow ['option_id']);
227 if (
228 (strlen($currvalue ?? '') == 0 && $lrow['is_default'] && !$ignore_default) ||
229 (strlen($currvalue ?? '') > 0 && in_array($lrow['option_id'], $selectedValues))
231 $got_selected = true;
232 $isSelected = true;
235 // Already has been translated above (if applicable), so do not need to use
236 // the xl_list_label() function here
237 $optionLabel = text($lrow ['title']);
239 $_tmp = [
240 'label' => $optionLabel,
241 'value' => $optionValue,
242 'isSelected' => $isSelected,
243 'isActive' => $include_inactive,
246 if ($_optgroup) {
247 $_tmp['optGroupOptions'] = $_tmp;
248 $_tmp['optgroupLabel'] = ($active) ? xla('Active') : xla('Inactive');
251 $_options[] = $_tmp;
253 } // end $active loop
256 To show the inactive item in the list if the value is saved to database
258 if (!$got_selected && strlen($currvalue ?? '') > 0) {
259 $_sql = "SELECT * FROM list_options WHERE list_id = ? AND activity = 0 AND option_id = ? ORDER BY seq, title";
260 $lres_inactive = sqlStatement($_sql, [$list_id, $currvalue]);
261 $lrow_inactive = sqlFetchArray($lres_inactive);
262 if (!empty($lrow_inactive['option_id'])) {
263 $optionValue = htmlspecialchars($lrow_inactive['option_id'], ENT_QUOTES);
264 $_options[] = [
265 'label' => htmlspecialchars(xl_list_label($lrow_inactive['title']), ENT_NOQUOTES),
266 'value' => $optionValue,
267 'isSelected' => true,
268 'isActive' => false,
270 $got_selected = true;
274 if (!$got_selected && strlen($currvalue ?? '') > 0 && !$multiple) {
275 $list_id = $backup_list;
276 $lrow = sqlQuery("SELECT title FROM list_options WHERE list_id = ? AND option_id = ?", [$list_id, $currvalue]);
278 $_options[] = [
279 'value' => attr($currvalue),
280 'selected' => true,
281 'label' => ($lrow > 0 && !empty($backup_list)) ? text(xl_list_label($lrow['title'])) : text($currvalue),
283 if (empty($lrow) && empty($backup_list)) {
284 $metadata['error'] = [
285 'title' => xlt('Please choose a valid selection from the list.'),
286 'text' => xlt('Fix this'),
289 } elseif (!$got_selected && strlen($currvalue ?? '') > 0 && $multiple) {
290 //if not found in main list, display all selected values that exist in backup list
291 $list_id = $backup_list;
293 $got_selected_backup = false;
294 if (!empty($backup_list)) {
295 $lres_backup = sqlStatement("SELECT * FROM list_options WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
296 while ($lrow_backup = sqlFetchArray($lres_backup)) {
297 $selectedValues = explode("|", $currvalue);
298 $optionValue = attr($lrow_backup['option_id']);
300 if (in_array($lrow_backup ['option_id'], $selectedValues)) {
301 $_options[] = [
302 'label' => text(xl_list_label($lrow_backup['title'])),
303 'value' => $optionValue,
304 'isSelected' => true,
306 $got_selected_backup = true;
311 if (!$got_selected_backup) {
312 $selectedValues = explode("|", $currvalue);
313 foreach ($selectedValues as $selectedValue) {
314 $_options[] = [
315 'label' => text($selectedValue),
316 'value' => attr($selectedValue),
317 'isSelected' => true,
321 $_metadata['error'] = [
322 'title' => xlt('Please choose a valid selection from the list.'),
323 'text' => xlt('Fix this'),
328 $_parsedOptions = [];
329 $_og = false;
330 foreach ($_options as $o) {
331 $_isOG = (array_key_exists('optGroupOptions', $o) && count($o['optGroupOptions']) > 0) ? true : false;
332 $_currOG = $o['optgroupLabel'] ?? false;
334 // Render only if the current optgroup label is not triple equal to the previous label
335 if ($_og !== $_currOG) {
336 $_parsedOptions[] = "</optgroup>";
339 // Must have an opt group and it must be different than the previous
340 if ($_isOG && $_og !== $_currOG) {
341 $_parsedOptions[] = sprintf('<optgroup label="%s">', $_currOG);
344 $_parsedOptions[] = _create_option_element($o);
346 $_og = $_currOG;
348 $optionString = implode("\n", $_parsedOptions);
350 $_parsedAttributes = [];
351 foreach ($attributes as $attr => $val) {
352 $_parsedAttributes[] = sprintf('%s="%s"', $attr, $val);
354 $attributeString = implode("\n", $_parsedAttributes);
356 $_selectString = sprintf("<select %s>%s</select>", $attributeString, $optionString);
357 $output[] = $_selectString;
359 if (array_key_exists('error', $_metadata)) {
360 $_errorString = sprintf("<span title=\"%s\">%s</span>", $_metadata['error']['title'], $metadata['error']['text']);
361 $output[] = $_errorString;
364 return implode("", $output);
367 function _create_option_element(array $o): string
369 $_valStr = (array_key_exists('value', $o)) ? "value=\"{$o['value']}\"" : "";
370 $_selStr = (array_key_exists('isSelected', $o) && $o['isSelected'] == true) ? "selected" : "";
371 $_labStr = (array_key_exists('label', $o)) ? $o['label'] : "";
372 return "<option $_valStr $_selStr>$_labStr</option>";
375 // Parsing for data type 31, static text.
376 function parse_static_text($frow, $value_allowed = true)
378 $tmp = str_replace("\r\n", "\n", $frow['description']);
379 // Translate if it does not look like HTML.
380 if (substr($tmp, 0, 1) != '<') {
381 $tmp2 = $frow['description'];
382 $tmp3 = xl_layout_label($tmp);
383 if ($tmp3 == $tmp && $tmp2 != $tmp) {
384 // No translation, try again without the CRLF substitution.
385 $tmp3 = xl_layout_label($tmp2);
387 $tmp = nl2br($tmp3);
389 $s = '';
390 if ($frow['source'] == 'D' || $frow['source'] == 'H') {
391 // Source is demographics or history. This case supports value substitution.
392 while (preg_match('/^(.*?)\{(\w+)\}(.*)$/', $tmp, $matches)) {
393 $s .= $matches[1];
394 if ($value_allowed) {
395 $tmprow = $frow;
396 $tmprow['field_id'] = $matches[2];
397 $s .= lbf_current_value($tmprow, 0, 0);
399 $tmp = $matches[3];
402 $s .= $tmp;
403 return $s;
406 function genLabResultsTextItem($name, $value, $outtype, $size, $maxlength, $disabled = '')
408 $string_maxlength = $maxlength ? ("maxlength='" . attr($maxlength) . "'") : '';
409 $s = "<td align='center'>";
410 if ($outtype == 2) {
411 $s .= text($value);
412 } else {
413 $s .= "<input type='text'";
414 if ($outtype == 0) {
415 $s .= " name='" . attr($name) . "' id='" . attr($name) . "'";
417 $s .= " size='" . attr($size) . "' $string_maxlength" .
418 " value='" . attr($value) . "'" .
419 " $under $disabled />";
421 $s .= "&nbsp;</td>";
422 return $s;
425 // $outtype = 0 for form, 1 for print, 2 for display, 3 for plain text.
426 function genLabResults($frow, $currvalue, $outtype = 0, $disabled = '')
428 $field_id = $frow['field_id'];
429 $list_id = $frow['list_id'];
430 $field_id_esc = text($field_id);
431 $under = $outtype == 1 ? "class='under'" : "";
432 $s = '';
434 $avalue = json_decode($currvalue, true);
435 if (empty($avalue)) {
436 $avalue = array();
438 // $avalue[$option_id][0] : gestation
439 // $avalue[$option_id][1] : radio button value
440 // $avalue[$option_id][2] : test value
441 // $avalue[$option_id][3] : notes
443 $maxlength = $frow['max_length'];
444 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
446 $under = $outtype == 1 ? "class='under'" : "";
448 $s .= "<table cellpadding='0' cellspacing='0'>";
449 if ($outtype < 2) {
450 $s .= "<tr>" .
451 "<td class='bold' align='center'>" . xlt('Test/Screening') . "&nbsp;</td>" .
452 "<td class='bold' align='center'>" . xlt('Gest wks') . "&nbsp;</td>" .
453 "<td class='bold' align='center'>&nbsp;" . xlt('N/A') . "&nbsp;</td>" .
454 "<td class='bold' align='center'>" . xlt('Neg/Nrml') . "</td>" .
455 "<td class='bold' align='center'>&nbsp;" . xlt('Pos/Abn') . "&nbsp;</td>" .
456 "<td class='bold' align='center'>" . xlt('Test Value') . "&nbsp;</td>" .
457 "<td class='bold' align='center'>" . xlt('Date/Notes') . "&nbsp;</td>" .
458 "</tr>";
461 $lres = sqlStatement(
462 "SELECT * FROM list_options WHERE " .
463 "list_id = ? AND activity = 1 ORDER BY seq, title",
464 array($list_id)
467 while ($lrow = sqlFetchArray($lres)) {
468 $option_id = $lrow['option_id'];
469 $option_id_esc = text($option_id);
471 if ($outtype >= 2 && empty($avalue[$option_id][1])) {
472 continue;
475 if ($outtype == 3) {
476 if (isset($avalue[$option_id][1]) && $avalue[$option_id][1] == '2') {
477 if ($s !== '') {
478 $s .= '; ';
480 $s .= text(xl_list_label($lrow['title']));
481 $s .= ':' . text($avalue[$option_id][0]);
482 $s .= ':' . text($avalue[$option_id][2]);
483 $s .= ':' . text($avalue[$option_id][3]);
485 continue;
488 $s .= "<tr>";
489 $s .= $outtype == 2 ? "<td class='bold'>" : "<td>";
490 $s .= text(xl_list_label($lrow['title'])) . "&nbsp;</td>";
492 $s .= genLabResultsTextItem(
493 "form_{$field_id_esc}[$option_id_esc][0]",
494 (isset($avalue[$option_id][0]) ? $avalue[$option_id][0] : ''),
495 $outtype,
498 $disabled,
499 $under
502 if ($outtype == 2) {
503 $tmp = isset($avalue[$option_id][1]) ? $avalue[$option_id][1] : '0';
504 $restype = ($tmp == '1') ? xl('Normal') : (($tmp == '2') ? xl('Abnormal') : xl('N/A'));
505 $s .= "<td>" . text($restype) . "&nbsp;</td>";
506 } else {
507 for ($i = 0; $i < 3; ++$i) {
508 $s .= "<td align='center'>";
509 $s .= "<input type='radio'";
510 if ($outtype == 0) {
511 $s .= " name='radio_{$field_id_esc}[$option_id_esc]'" .
512 " id='radio_{$field_id_esc}[$option_id_esc]'";
514 $s .= " value='$i' $lbfonchange";
515 if (isset($avalue[$option_id][1]) && $avalue[$option_id][1] == "$i") {
516 $s .= " checked";
518 $s .= " $disabled />";
519 $s .= "</td>";
522 $s .= genLabResultsTextItem(
523 "form_{$field_id_esc}[$option_id_esc][2]",
524 (isset($avalue[$option_id][2]) ? $avalue[$option_id][2] : ''),
525 $outtype,
528 $disabled,
529 $under
531 $s .= genLabResultsTextItem(
532 "form_{$field_id_esc}[$option_id_esc][3]",
533 (isset($avalue[$option_id][3]) ? $avalue[$option_id][3] : ''),
534 $outtype,
535 $fldlength,
536 $maxlength,
537 $disabled,
538 $under
540 $s .= "</tr>";
542 if ($outtype != 3) {
543 $s .= "</table>";
546 return $s;
549 // $frow is a row from the layout_options table.
550 // $currvalue is the current value, if any, of the associated item.
552 function generate_form_field($frow, $currvalue)
554 global $rootdir, $date_init, $ISSUE_TYPES, $code_types, $membership_group_number;
556 $currescaped = htmlspecialchars($currvalue ?? '', ENT_QUOTES);
558 $data_type = $frow['data_type'];
559 $field_id = $frow['field_id'];
560 $list_id = $frow['list_id'] ?? null;
561 $backup_list = $frow['list_backup_id'] ?? null;
562 $edit_options = $frow['edit_options'] ?? null;
563 $form_id = $frow['form_id'] ?? null;
565 // 'smallform' can be 'true' if we want a smaller form field, otherwise
566 // can be used to assign arbitrary CSS classes to data entry fields.
567 $smallform = $frow['smallform'] ?? null;
568 if ($smallform === 'true') {
569 $smallform = ' form-control-sm';
572 // escaped variables to use in html
573 $field_id_esc = htmlspecialchars($field_id, ENT_QUOTES);
574 $list_id_esc = htmlspecialchars(($list_id ?? ''), ENT_QUOTES);
576 // Added 5-09 by BM - Translate description if applicable
577 $description = (isset($frow['description']) ? htmlspecialchars(xl_layout_label($frow['description']), ENT_QUOTES) : '');
579 // Support edit option T which assigns the (possibly very long) description as
580 // the default value.
581 if (isOption($edit_options, 'T') !== false) {
582 if (strlen($currescaped) == 0) {
583 $currescaped = $description;
586 // Description used in this way is not suitable as a title.
587 $description = '';
590 // Support using the description as a placeholder
591 $placeholder = (isOption($edit_options, 'DAP') === true) ? " placeholder='{$description}' " : '';
593 // added 5-2009 by BM to allow modification of the 'empty' text title field.
594 // Can pass $frow['empty_title'] with this variable, otherwise
595 // will default to 'Unassigned'.
596 // modified 6-2009 by BM to allow complete skipping of the 'empty' text title
597 // if make $frow['empty_title'] equal to 'SKIP'
598 $showEmpty = true;
599 if (isset($frow['empty_title'])) {
600 if ($frow['empty_title'] == "SKIP") {
601 //do not display an 'empty' choice
602 $showEmpty = false;
603 $empty_title = "Unassigned";
604 } else {
605 $empty_title = $frow['empty_title'];
607 } else {
608 $empty_title = "Unassigned";
611 $disabled = isOption($edit_options, '0') === false ? '' : 'disabled';
613 $lbfchange = (
614 !empty($form_id) &&
616 strpos($form_id, 'LBF') === 0 ||
617 strpos($form_id, 'LBT') === 0 ||
618 strpos($form_id, 'DEM') === 0 ||
619 strpos($form_id, 'HIS') === 0
621 ) ? "checkSkipConditions();" : "";
622 $lbfonchange = $lbfchange ? "onchange='$lbfchange'" : "";
624 // generic single-selection list or single-selection list with search or single-selection list with comment support.
625 // These data types support backup lists.
626 if ($data_type == 1 || $data_type == 43 || $data_type == 46) {
627 if ($data_type == 46) {
628 // support for single-selection list with comment support
629 $lbfchange = "processCommentField(" . attr_js($field_id) . ");" . $lbfchange;
632 echo generate_select_list(
633 "form_$field_id",
634 $list_id,
635 $currvalue,
636 $description,
637 ($showEmpty ? $empty_title : ''),
638 (($data_type == 43) ? "select-dropdown" : $smallform),
639 $lbfchange,
641 ($disabled ? array('disabled' => 'disabled') : null),
642 false,
643 $backup_list
646 if ($data_type == 46) {
647 // support for single-selection list with comment support
648 $selectedValues = explode("|", $currvalue);
649 if (!preg_match('/^comment_/', $currvalue) || (count($selectedValues) == 1)) {
650 $display = "display:none";
651 $comment = "";
652 } else {
653 $display = "display:inline-block";
654 $comment = $selectedValues[count($selectedValues) - 1];
656 echo "<input type='text'" .
657 " name='form_text_" . attr($field_id) . "'" .
658 " id='form_text_" . attr($field_id) . "'" .
659 " size='" . attr($frow['fld_length']) . "'" .
660 " class='form-control'" .
661 $placeholder .
662 " " . ((!empty($frow['max_length'])) ? "maxlength='" . attr($frow['max_length']) . "'" : "") . " " .
663 " style='" . $display . "'" .
664 " value='" . attr($comment) . "'/>";
666 } elseif ($data_type == 2) { // simple text field
667 $fldlength = htmlspecialchars($frow['fld_length'] ?? '', ENT_QUOTES);
668 $maxlength = $frow['max_length'] ?? '';
669 $string_maxlength = "";
670 // if max_length is set to zero, then do not set a maxlength
671 if ($maxlength) {
672 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
675 echo "<input type='text'
676 class='form-control{$smallform}'
677 name='form_{$field_id_esc}'
678 id='form_{$field_id_esc}'
679 size='{$fldlength}'
680 {$string_maxlength}
681 {$placeholder}
682 title='{$description}'
683 value='{$currescaped}'";
684 $tmp = $lbfchange;
685 if (isOption($edit_options, 'C') !== false) {
686 $tmp .= "capitalizeMe(this);";
687 } elseif (isOption($edit_options, 'U') !== false) {
688 $tmp .= "this.value = this.value.toUpperCase();";
691 if ($tmp) {
692 echo " onchange='$tmp'";
695 $tmp = htmlspecialchars($GLOBALS['gbl_mask_patient_id'], ENT_QUOTES);
696 // If mask is for use at save time, treat as no mask.
697 if (strpos($tmp, '^') !== false) {
698 $tmp = '';
700 if ($field_id == 'pubpid' && strlen($tmp) > 0) {
701 echo " onkeyup='maskkeyup(this,\"$tmp\")'";
702 echo " onblur='maskblur(this,\"$tmp\")'";
705 if (isOption($edit_options, '1') !== false && strlen($currescaped) > 0) {
706 echo " readonly";
709 if ($disabled) {
710 echo ' disabled';
713 echo " />";
714 } elseif ($data_type == 3) { // long or multi-line text field
715 $textCols = htmlspecialchars($frow['fld_length'], ENT_QUOTES);
716 $textRows = htmlspecialchars($frow['fld_rows'], ENT_QUOTES);
717 echo "<textarea" .
718 " name='form_$field_id_esc'" .
719 " class='form-control$smallform'" .
720 " id='form_$field_id_esc'" .
721 " title='$description'" .
722 $placeholder .
723 " cols='$textCols'" .
724 " rows='$textRows' $lbfonchange $disabled" .
725 ">" . $currescaped . "</textarea>";
726 } elseif ($data_type == 4) { // date
727 $age_asof_date = ''; // optionalAge() sets this
728 $age_format = isOption($edit_options, 'A') === false ? 3 : 0;
729 $agestr = optionalAge($frow, $currvalue, $age_asof_date, $description);
730 if ($agestr) {
731 echo "<table class='table'><tr><td class='text'>";
734 $onchange_string = '';
735 if (!$disabled && $agestr) {
736 $onchange_string = "onchange=\"if (typeof(updateAgeString) == 'function') " .
737 "updateAgeString('$field_id','$age_asof_date', $age_format, '$description')\"";
739 if ($data_type == 4) {
740 $modtmp = isOption($edit_options, 'F') === false ? 0 : 1;
741 $datetimepickerclass = ($frow['validation'] ?? null) === 'past_date' ? '-past' : ( ($frow['validation'] ?? null) === 'future_date' ? '-future' : '' );
742 if (!$modtmp) {
743 $dateValue = oeFormatShortDate(substr($currescaped, 0, 10));
744 echo "<input type='text' size='10' class='datepicker$datetimepickerclass form-control$smallform' name='form_$field_id_esc' id='form_$field_id_esc'" . " value='" . attr($dateValue) . "'";
745 } else {
746 $dateValue = oeFormatDateTime(substr($currescaped, 0, 20), 0);
747 echo "<input type='text' size='20' class='datetimepicker$datetimepickerclass form-control$smallform' name='form_$field_id_esc' id='form_$field_id_esc'" . " value='" . attr($dateValue) . "'";
750 if (!$agestr) {
751 echo " title='$description'";
754 // help chrome users avoid autocomplete interfere with datepicker widget display
755 if ($frow['field_id'] == 'DOB') {
756 echo " autocomplete='off' $onchange_string $lbfonchange $disabled />";
757 } else {
758 echo " $onchange_string $lbfonchange $disabled />";
761 // Optional display of age or gestational age.
762 if ($agestr) {
763 echo "</td></tr><tr><td id='span_$field_id' class='text'>" . text($agestr) . "</td></tr></table>";
765 } elseif ($data_type == 10) { // provider list, local providers only
766 $ures = sqlStatement("SELECT id, fname, lname, specialty FROM users " .
767 "WHERE active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) " .
768 "AND authorized = 1 " .
769 "ORDER BY lname, fname");
770 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' $lbfonchange $disabled class='form-control$smallform'>";
771 echo "<option value=''>" . xlt($empty_title) . "</option>";
772 $got_selected = false;
773 while ($urow = sqlFetchArray($ures)) {
774 $uname = text($urow['fname'] . ' ' . $urow['lname']);
775 $optionId = attr($urow['id']);
776 echo "<option value='$optionId'";
777 if ($urow['id'] == $currvalue) {
778 echo " selected";
779 $got_selected = true;
782 echo ">$uname</option>";
785 if (!$got_selected && $currvalue) {
786 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
787 echo "</select>";
788 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
789 } else {
790 echo "</select>";
792 } elseif ($data_type == 11) { // provider list, including address book entries with an NPI number
793 $ures = sqlStatement("SELECT id, fname, lname, specialty FROM users " .
794 "WHERE active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) " .
795 "AND ( authorized = 1 OR ((username = '' OR username IS NULL) AND npi != '' )) " .
796 "ORDER BY lname, fname");
797 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' class='form-control$smallform'";
798 echo " $lbfonchange $disabled>";
799 echo "<option value=''>" . xlt('Unassigned') . "</option>";
800 $got_selected = false;
801 while ($urow = sqlFetchArray($ures)) {
802 $uname = text($urow['fname'] . ' ' . $urow['lname']);
803 $optionId = attr($urow['id']);
804 echo "<option value='$optionId'";
805 if ($urow['id'] == $currvalue) {
806 echo " selected";
807 $got_selected = true;
810 echo ">$uname</option>";
813 if (!$got_selected && $currvalue) {
814 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
815 echo "</select>";
816 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
817 } else {
818 echo "</select>";
820 } elseif ($data_type == 12) { // pharmacy list
821 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' class='form-control$smallform'";
822 echo " $lbfonchange $disabled>";
823 echo "<option value='0'></option>";
824 $pres = get_pharmacies();
825 $got_selected = false;
826 $zone = '';
827 while ($prow = sqlFetchArray($pres)) {
828 if ($zone != strtolower(trim($prow['city'] ?? ''))) {
829 if ($zone != '') {
830 echo "</optgroup>";
832 $zone = strtolower(trim($prow['city']));
833 echo "<optgroup label='" . attr($prow['city']) . "'>";
835 $key = $prow['id'];
836 $optionValue = htmlspecialchars($key, ENT_QUOTES);
837 $optionLabel = htmlspecialchars($prow['name'] . ' ' . $prow['area_code'] . '-' .
838 $prow['prefix'] . '-' . $prow['number'] . ' / ' .
839 $prow['line1'] . ' / ' . $prow['city'], ENT_NOQUOTES);
840 echo "<option value='$optionValue'";
841 if ($currvalue == $key) {
842 echo " selected";
843 $got_selected = true;
846 echo ">$optionLabel</option>";
849 if (!$got_selected && $currvalue) {
850 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
851 echo "</select>";
852 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
853 } else {
854 echo "</select>";
856 } elseif ($data_type == 13) { // squads
857 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' class='form-control$smallform'";
858 echo " $lbfonchange $disabled>";
859 echo "<option value=''>&nbsp;</option>";
860 $squads = AclExtended::aclGetSquads();
861 if ($squads) {
862 foreach ($squads as $key => $value) {
863 $optionValue = htmlspecialchars($key, ENT_QUOTES);
864 $optionLabel = htmlspecialchars($value[3], ENT_NOQUOTES);
865 echo "<option value='$optionValue'";
866 if ($currvalue == $key) {
867 echo " selected";
870 echo ">$optionLabel</option>\n";
874 echo "</select>";
875 } elseif ($data_type == 14) {
876 // Address book, preferring organization name if it exists and is not in
877 // parentheses, and excluding local users who are not providers.
878 // Supports "referred to" practitioners and facilities.
879 // Alternatively the letter L in edit_options means that abook_type
880 // must be "ord_lab", indicating types used with the procedure
881 // lab ordering system.
882 // Alternatively the letter O in edit_options means that abook_type
883 // must begin with "ord_", indicating types used with the procedure
884 // ordering system.
885 // Alternatively the letter V in edit_options means that abook_type
886 // must be "vendor", indicating the Vendor type.
887 // Alternatively the letter R in edit_options means that abook_type
888 // must be "dist", indicating the Distributor type.
890 if (isOption($edit_options, 'L') !== false) {
891 $tmp = "abook_type = 'ord_lab'";
892 } elseif (isOption($edit_options, 'O') !== false) {
893 $tmp = "abook_type LIKE 'ord\\_%'";
894 } elseif (isOption($edit_options, 'V') !== false) {
895 $tmp = "abook_type LIKE 'vendor%'";
896 } elseif (isOption($edit_options, 'R') !== false) {
897 $tmp = "abook_type LIKE 'dist'";
898 } else {
899 $tmp = "( username = '' OR authorized = 1 )";
902 $ures = sqlStatement("SELECT id, fname, lname, organization, username, npi FROM users " .
903 "WHERE active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) " .
904 "AND $tmp " .
905 "ORDER BY organization, lname, fname, npi");
906 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' class='form-control$smallform'";
907 echo " $lbfonchange $disabled>";
908 echo "<option value=''>" . htmlspecialchars(xl('Unassigned'), ENT_NOQUOTES) . "</option>";
909 while ($urow = sqlFetchArray($ures)) {
910 $uname = $urow['organization'];
911 if (empty($uname) || substr($uname, 0, 1) == '(') {
912 $uname = $urow['lname'];
913 if ($urow['fname']) {
914 $uname .= ", " . $urow['fname'];
916 if ($urow['npi']) {
917 $uname .= ": " . $urow['npi'];
921 $optionValue = htmlspecialchars($urow['id'], ENT_QUOTES);
922 $optionLabel = htmlspecialchars($uname, ENT_NOQUOTES);
923 echo "<option value='$optionValue'";
924 // Failure to translate Local and External is not an error here;
925 // they are only used as internal flags and must not be translated!
926 $title = $urow['username'] ? 'Local' : 'External';
927 $optionTitle = htmlspecialchars($title, ENT_QUOTES);
928 echo " title='$optionTitle'";
929 if ($urow['id'] == $currvalue) {
930 echo " selected";
933 echo ">$optionLabel</option>";
936 echo "</select>";
937 } elseif ($data_type == 15) { // A billing code. If description matches an existing code type then that type is used.
938 $codetype = '';
939 if (!empty($frow['description']) && isset($code_types[$frow['description']])) {
940 $codetype = $frow['description'];
942 $fldlength = attr($frow['fld_length']);
943 $maxlength = $frow['max_length'];
944 $string_maxlength = "";
945 // if max_length is set to zero, then do not set a maxlength
946 if ($maxlength) {
947 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
949 // Edit option E means allow multiple (Extra) billing codes in a field.
950 // We invent a class name for this because JavaScript needs to know.
951 $className = '';
952 if (strpos($frow['edit_options'], 'E') !== false) {
953 $className = 'EditOptionE';
956 if (isOption($edit_options, '2') !== false) {
957 // Option "2" generates a hidden input for the codes, and a matching visible field
958 // displaying their descriptions. First step is computing the description string.
959 $currdescstring = '';
960 if (!empty($currvalue)) {
961 $relcodes = explode(';', $currvalue);
962 foreach ($relcodes as $codestring) {
963 if ($codestring === '') {
964 continue;
966 if ($currdescstring !== '') {
967 $currdescstring .= '; ';
969 $currdescstring .= getCodeDescription($codestring, $codetype);
973 $currdescstring = attr($currdescstring);
975 echo "<div>"; // wrapper for myHideOrShow()
976 echo "<input type='text'" .
977 " name='form_$field_id_esc'" .
978 " id='form_related_code'" .
979 " class='" . attr($className) . "'" .
980 " size='$fldlength'" .
981 " value='$currescaped'" .
982 " style='display:none'" .
983 " $lbfonchange readonly $disabled />";
984 // Extra readonly input field for optional display of code description(s).
985 echo "<input type='text'" .
986 " name='form_$field_id_esc" . "__desc'" .
987 " size='$fldlength'" .
988 " title='$description'" .
989 " value='$currdescstring'";
990 if (!$disabled) {
991 echo " onclick='sel_related(this," . attr_js($codetype) . ")'";
994 echo "class='form-control$smallform'";
995 echo " readonly $disabled />";
996 echo "</div>";
997 } else {
998 echo "<input type='text'" .
999 " name='form_$field_id_esc'" .
1000 " id='form_related_code'" .
1001 " class='" . attr($className) . "'" .
1002 " size='$fldlength'" .
1003 " $string_maxlength" .
1004 " title='$description'" .
1005 " value='$currescaped'";
1006 if (!$disabled) {
1007 echo " onclick='sel_related(this," . attr_js($codetype) . ")'";
1010 echo "class='form-control$smallform'";
1011 echo " $lbfonchange readonly $disabled />";
1013 } elseif ($data_type == 16) { // insurance company list
1014 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' class='form-control$smallform' title='$description'>";
1015 echo "<option value='0'></option>";
1016 $insprovs = getInsuranceProviders();
1017 $got_selected = false;
1018 foreach ($insprovs as $key => $ipname) {
1019 $optionValue = htmlspecialchars($key, ENT_QUOTES);
1020 $optionLabel = htmlspecialchars($ipname, ENT_NOQUOTES);
1021 echo "<option value='$optionValue'";
1022 if ($currvalue == $key) {
1023 echo " selected";
1024 $got_selected = true;
1027 echo ">$optionLabel</option>";
1030 if (!$got_selected && $currvalue) {
1031 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
1032 echo "</select>";
1033 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
1034 } else {
1035 echo "</select>";
1037 } elseif ($data_type == 17) { // issue types
1038 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' class='form-control$smallform' title='$description'>";
1039 echo "<option value='0'></option>";
1040 $got_selected = false;
1041 foreach ($ISSUE_TYPES as $key => $value) {
1042 $optionValue = htmlspecialchars($key, ENT_QUOTES);
1043 $optionLabel = htmlspecialchars($value[1], ENT_NOQUOTES);
1044 echo "<option value='$optionValue'";
1045 if ($currvalue == $key) {
1046 echo " selected";
1047 $got_selected = true;
1050 echo ">$optionLabel</option>";
1053 if (!$got_selected && strlen($currvalue) > 0) {
1054 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
1055 echo "</select>";
1056 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
1057 } else {
1058 echo "</select>";
1060 } elseif ($data_type == 18) { // Visit categories.
1061 $cres = sqlStatement("SELECT pc_catid, pc_catname " .
1062 "FROM openemr_postcalendar_categories ORDER BY pc_catname");
1063 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' class='form-control$smallform' title='$description'" . " $lbfonchange $disabled>";
1064 echo "<option value=''>" . xlt($empty_title) . "</option>";
1065 $got_selected = false;
1066 while ($crow = sqlFetchArray($cres)) {
1067 $catid = $crow['pc_catid'];
1068 if (($catid < 9 && $catid != 5) || $catid == 11) {
1069 continue;
1072 echo "<option value='" . attr($catid) . "'";
1073 if ($catid == $currvalue) {
1074 echo " selected";
1075 $got_selected = true;
1078 echo ">" . text(xl_appt_category($crow['pc_catname'])) . "</option>";
1081 if (!$got_selected && $currvalue) {
1082 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
1083 echo "</select>";
1084 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
1085 } else {
1086 echo "</select>";
1088 } elseif ($data_type == 21) { // a set of labeled checkboxes
1089 // If no list then it's a single checkbox and its value is "Yes" or empty.
1090 if (!$list_id) {
1091 echo "<input type='checkbox' name='form_{$field_id_esc}' " .
1092 "id='form_{$field_id_esc}' value='Yes' $lbfonchange";
1093 if ($currvalue) {
1094 echo " checked";
1096 echo " $disabled />";
1097 } else {
1098 // In this special case, fld_length is the number of columns generated.
1099 $cols = max(1, $frow['fld_length']);
1100 $avalue = explode('|', $currvalue);
1101 $lres = sqlStatement("SELECT * FROM list_options " .
1102 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1103 echo "<table class='w-100' cellpadding='0' cellspacing='0' title='" . attr($description) . "'>";
1104 $tdpct = (int) (100 / $cols);
1105 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
1106 $option_id = $lrow['option_id'];
1107 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1108 // if ($count) echo "<br />";
1109 if ($count % $cols == 0) {
1110 if ($count) {
1111 echo "</tr>";
1113 echo "<tr>";
1115 echo "<td width='" . attr($tdpct) . "%' nowrap>";
1116 echo "<input type='checkbox' name='form_{$field_id_esc}[$option_id_esc]'" .
1117 "id='form_{$field_id_esc}[$option_id_esc]' class='form-check-inline' value='1' $lbfonchange";
1118 if (in_array($option_id, $avalue)) {
1119 echo " checked";
1121 // Added 5-09 by BM - Translate label if applicable
1122 echo " $disabled />" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
1123 echo "</td>";
1125 if ($count) {
1126 echo "</tr>";
1127 if ($count > $cols) {
1128 // Add some space after multiple rows of checkboxes.
1129 $cols = htmlspecialchars($cols, ENT_QUOTES);
1130 echo "<tr><td colspan='$cols' style='height:0.7rem'></td></tr>";
1133 echo "</table>";
1135 } elseif ($data_type == 22) { // a set of labeled text input fields
1136 $tmp = explode('|', $currvalue);
1137 $avalue = array();
1138 foreach ($tmp as $value) {
1139 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
1140 $avalue[$matches[1]] = $matches[2];
1144 $lres = sqlStatement("SELECT * FROM list_options " .
1145 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1146 echo "<table class='table'>";
1147 while ($lrow = sqlFetchArray($lres)) {
1148 $option_id = $lrow['option_id'];
1149 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1150 $maxlength = $frow['max_length'];
1151 $string_maxlength = "";
1152 // if max_length is set to zero, then do not set a maxlength
1153 if ($maxlength) {
1154 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
1157 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
1159 // Added 5-09 by BM - Translate label if applicable
1160 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
1161 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
1162 $optionValue = htmlspecialchars($avalue[$option_id], ENT_QUOTES);
1163 echo "<td><input type='text'" .
1164 " name='form_{$field_id_esc}[$option_id_esc]'" .
1165 " id='form_{$field_id_esc}[$option_id_esc]'" .
1166 " size='$fldlength'" .
1167 $placeholder .
1168 " class='form-control$smallform'" .
1169 " $string_maxlength" .
1170 " value='$optionValue'";
1171 echo " $lbfonchange $disabled /></td></tr>";
1174 echo "</table>";
1175 } elseif ($data_type == 23) { // a set of exam results; 3 radio buttons and a text field:
1176 $tmp = explode('|', $currvalue);
1177 $avalue = array();
1178 foreach ($tmp as $value) {
1179 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
1180 $avalue[$matches[1]] = $matches[2];
1184 $maxlength = $frow['max_length'];
1185 $string_maxlength = "";
1186 // if max_length is set to zero, then do not set a maxlength
1187 if ($maxlength) {
1188 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
1191 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
1192 $lres = sqlStatement("SELECT * FROM list_options " .
1193 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1194 echo "<table class='table'>";
1195 echo "<tr><td class='font-weight-bold'>" . htmlspecialchars(xl('Exam or Test'), ENT_NOQUOTES) .
1196 "</td><td class='font-weight-bold'>" . htmlspecialchars(xl('N/A'), ENT_NOQUOTES) .
1197 "&nbsp;</td><td class='font-weight-bold'>" .
1198 htmlspecialchars(xl('Nor'), ENT_NOQUOTES) . "&nbsp;</td>" .
1199 "<td class='font-weight-bold'>" .
1200 htmlspecialchars(xl('Abn'), ENT_NOQUOTES) . "&nbsp;</td><td class='font-weight-bold'>" .
1201 htmlspecialchars(xl('Date/Notes'), ENT_NOQUOTES) . "</td></tr>";
1202 while ($lrow = sqlFetchArray($lres)) {
1203 $option_id = $lrow['option_id'];
1204 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1205 $restype = substr(($avalue[$option_id] ?? ''), 0, 1);
1206 $resnote = substr(($avalue[$option_id] ?? ''), 2);
1208 // Added 5-09 by BM - Translate label if applicable
1209 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
1211 for ($i = 0; $i < 3; ++$i) {
1212 $inputValue = htmlspecialchars($i, ENT_QUOTES);
1213 echo "<td><input type='radio'" .
1214 " name='radio_{$field_id_esc}[$option_id_esc]'" .
1215 " id='radio_{$field_id_esc}[$option_id_esc]'" .
1216 " value='$inputValue' $lbfonchange";
1217 if ($restype === "$i") {
1218 echo " checked";
1221 echo " $disabled /></td>";
1224 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
1225 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
1226 echo "<td><input type='text'" .
1227 " name='form_{$field_id_esc}[$option_id_esc]'" .
1228 " id='form_{$field_id_esc}[$option_id_esc]'" .
1229 " size='$fldlength'" .
1230 " class='form-control'" .
1231 " $string_maxlength" .
1232 " value='$resnote' $disabled /></td>";
1233 echo "</tr>";
1236 echo "</table>";
1237 } elseif ($data_type == 24) { // the list of active allergies for the current patient
1238 // this is read-only!
1239 $query = "SELECT title, comments FROM lists WHERE " .
1240 "pid = ? AND type = 'allergy' AND enddate IS NULL " .
1241 "ORDER BY begdate";
1242 // echo "<!-- $query -->\n"; // debugging
1243 $lres = sqlStatement($query, array($GLOBALS['pid']));
1244 $count = 0;
1245 while ($lrow = sqlFetchArray($lres)) {
1246 if ($count++) {
1247 echo "<br />";
1250 echo htmlspecialchars($lrow['title'], ENT_NOQUOTES);
1251 if ($lrow['comments']) {
1252 echo ' (' . htmlspecialchars($lrow['comments'], ENT_NOQUOTES) . ')';
1255 } elseif ($data_type == 25) { // a set of labeled checkboxes, each with a text field:
1256 $tmp = explode('|', $currvalue);
1257 $avalue = array();
1258 foreach ($tmp as $value) {
1259 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
1260 $avalue[$matches[1]] = $matches[2];
1264 $maxlength = $frow['max_length'];
1265 $string_maxlength = "";
1266 // if max_length is set to zero, then do not set a maxlength
1267 if ($maxlength) {
1268 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
1271 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
1272 $lres = sqlStatement("SELECT * FROM list_options " .
1273 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1274 echo "<table class='table'>";
1275 while ($lrow = sqlFetchArray($lres)) {
1276 $option_id = $lrow['option_id'];
1277 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1278 $restype = substr($avalue[$option_id], 0, 1);
1279 $resnote = substr($avalue[$option_id], 2);
1281 // Added 5-09 by BM - Translate label if applicable
1282 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
1284 $option_id = htmlspecialchars($option_id, ENT_QUOTES);
1285 echo "<td><input type='checkbox' name='check_{$field_id_esc}[$option_id_esc]'" .
1286 " id='check_{$field_id_esc}[$option_id_esc]' class='form-check-inline' value='1' $lbfonchange";
1287 if ($restype) {
1288 echo " checked";
1291 echo " $disabled />&nbsp;</td>";
1292 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
1293 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
1294 echo "<td><input type='text'" .
1295 " name='form_{$field_id_esc}[$option_id_esc]'" .
1296 " id='form_{$field_id_esc}[$option_id_esc]'" .
1297 " size='$fldlength'" .
1298 " class='form-control$smallform' " .
1299 " $string_maxlength" .
1300 " value='$resnote' $disabled /></td>";
1301 echo "</tr>";
1304 echo "</table>";
1305 } elseif ($data_type == 26) { // single-selection list with ability to add to it
1306 echo "<div class='input-group'>";
1307 echo generate_select_list(
1308 "form_$field_id",
1309 $list_id,
1310 $currvalue,
1311 $description,
1312 ($showEmpty ? $empty_title : ''),
1313 'addtolistclass_' . $list_id . $smallform,
1314 $lbfchange,
1316 ($disabled ? array('disabled' => 'disabled') : null),
1317 false,
1318 $backup_list
1320 // show the add button if user has access to correct list
1321 $inputValue = htmlspecialchars(xl('Add'), ENT_QUOTES);
1322 $btnSize = ($smallform) ? "btn-sm" : "";
1323 $outputAddButton = "<div class='input-group-append'><input type='button' class='btn btn-secondary $btnSize mb-1 addtolist' id='addtolistid_" . $list_id_esc . "' fieldid='form_" .
1324 $field_id_esc . "' value='$inputValue' $disabled /></div>";
1325 if (AclExtended::acoExist('lists', $list_id)) {
1326 // a specific aco exist for this list, so ensure access
1327 if (AclMain::aclCheckCore('lists', $list_id)) {
1328 echo $outputAddButton;
1330 } else {
1331 // no specific aco exist for this list, so check for access to 'default' list
1332 if (AclMain::aclCheckCore('lists', 'default')) {
1333 echo $outputAddButton;
1336 echo "</div>";
1337 } elseif ($data_type == 27) { // a set of labeled radio buttons
1338 // In this special case, fld_length is the number of columns generated.
1339 $cols = max(1, $frow['fld_length']);
1340 // Support for edit option M.
1341 if (isOption($edit_options, 'M')) {
1342 ++$membership_group_number;
1345 $lres = sqlStatement("SELECT * FROM list_options " .
1346 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1347 echo "<table class='table w-100'>";
1348 $tdpct = (int) (100 / $cols);
1349 $got_selected = false;
1350 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
1351 $option_id = $lrow['option_id'];
1352 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1353 if ($count % $cols == 0) {
1354 if ($count) {
1355 echo "</tr>";
1357 echo "<tr>";
1359 echo "<td width='" . attr($tdpct) . "%' nowrap>";
1360 echo "<input type='radio' name='form_{$field_id_esc}' id='form_{$field_id_esc}[$option_id_esc]'" .
1361 " value='$option_id_esc' $lbfonchange";
1362 // Support for edit options M and m.
1363 if (isOption($edit_options, 'M')) {
1364 echo " class='form-check-inline'";
1365 echo " onclick='checkGroupMembers(this, $membership_group_number);'";
1366 } elseif (isOption($edit_options, 'm')) {
1367 echo " class='form-check-inline lbf_memgroup_$membership_group_number'";
1368 } else {
1369 echo " class='form-check-inline'";
1372 if (
1373 (strlen($currvalue) == 0 && $lrow['is_default']) ||
1374 (strlen($currvalue) > 0 && $option_id == $currvalue)
1376 echo " checked";
1377 $got_selected = true;
1379 echo " $disabled />" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
1380 echo "</td>";
1383 if ($count) {
1384 echo "</tr>";
1385 if ($count > $cols) {
1386 // Add some space after multiple rows of radio buttons.
1387 $cols = htmlspecialchars($cols, ENT_QUOTES);
1388 echo "<tr><td colspan='$cols' style='height: 0.7rem'></td></tr>";
1392 echo "</table>";
1393 if (!$got_selected && strlen($currvalue) > 0) {
1394 $fontTitle = htmlspecialchars(xl('Please choose a valid selection.'), ENT_QUOTES);
1395 $fontText = htmlspecialchars(xl('Fix this'), ENT_NOQUOTES);
1396 echo "$currescaped <span class='text-danger' title='$fontTitle'>$fontText!</span>";
1398 } elseif ($data_type == 28 || $data_type == 32) { // special case for history of lifestyle status; 3 radio buttons
1399 // and a date text field:
1400 // VicarePlus :: A selection list box for smoking status:
1401 $tmp = explode('|', $currvalue);
1402 switch (count($tmp)) {
1403 case "4":
1404 $resnote = $tmp[0];
1405 $restype = $tmp[1];
1406 $resdate = oeFormatShortDate($tmp[2]);
1407 $reslist = $tmp[3];
1408 break;
1409 case "3":
1410 $resnote = $tmp[0];
1411 $restype = $tmp[1];
1412 $resdate = oeFormatShortDate($tmp[2]);
1413 $reslist = '';
1414 break;
1415 case "2":
1416 $resnote = $tmp[0];
1417 $restype = $tmp[1];
1418 $resdate = "";
1419 $reslist = '';
1420 break;
1421 case "1":
1422 $resnote = $tmp[0];
1423 $resdate = $restype = "";
1424 $reslist = '';
1425 break;
1426 default:
1427 $restype = $resdate = $resnote = "";
1428 $reslist = '';
1429 break;
1432 $maxlength = $frow['max_length'];
1433 $string_maxlength = "";
1434 // if max_length is set to zero, then do not set a maxlength
1435 if ($maxlength) {
1436 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
1439 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
1441 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
1442 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
1443 $resdate = htmlspecialchars($resdate, ENT_QUOTES);
1444 echo "<table class='table'>";
1445 echo "<tr>";
1446 if ($data_type == 28) {
1447 // input text
1448 echo "<td><input type='text' class='form-control'" .
1449 " name='form_$field_id_esc'" .
1450 " id='form_$field_id_esc'" .
1451 " size='$fldlength'" .
1452 " class='form-control$smallform'" .
1453 " $string_maxlength" .
1454 " value='$resnote' $disabled />&nbsp;</td>";
1455 echo "<td class='font-weight-bold'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
1456 "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
1457 htmlspecialchars(xl('Status'), ENT_NOQUOTES) . ":&nbsp;&nbsp;</td>";
1458 } elseif ($data_type == 32) {
1459 // input text
1460 echo "<tr><td><input type='text'" .
1461 " name='form_text_$field_id_esc'" .
1462 " id='form_text_$field_id_esc'" .
1463 " size='$fldlength'" .
1464 " class='form-control$smallform'" .
1465 " $string_maxlength" .
1466 " value='$resnote' $disabled />&nbsp;</td></tr>";
1467 echo "<td>";
1468 //Selection list for smoking status
1469 $onchange = 'radioChange(this.options[this.selectedIndex].value)';//VicarePlus :: The javascript function for selection list.
1470 echo generate_select_list(
1471 "form_$field_id",
1472 $list_id,
1473 $reslist,
1474 $description,
1475 ($showEmpty ? $empty_title : ''),
1476 $smallform,
1477 $onchange,
1479 ($disabled ? array('disabled' => 'disabled') : null)
1481 echo "</td>";
1482 echo "<td class='font-weight-bold'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" . xlt('Status') . ":&nbsp;&nbsp;</td>";
1485 // current
1486 echo "<td class='text'><input type='radio'" .
1487 " name='radio_{$field_id_esc}'" .
1488 " id='radio_{$field_id_esc}[current]'" .
1489 " class='form-check-inline'" .
1490 " value='current" . $field_id_esc . "' $lbfonchange";
1491 if ($restype == "current" . $field_id) {
1492 echo " checked";
1495 if ($data_type == 32) {
1496 echo " onClick='smoking_statusClicked(this)'";
1499 echo " />" . xlt('Current') . "&nbsp;</td>";
1500 // quit
1501 echo "<td class='text'><input type='radio'" .
1502 " name='radio_{$field_id_esc}'" .
1503 " id='radio_{$field_id_esc}[quit]'" .
1504 " class='form-check-inline'" .
1505 " value='quit" . $field_id_esc . "' $lbfonchange";
1506 if ($restype == "quit" . $field_id) {
1507 echo " checked";
1510 if ($data_type == 32) {
1511 echo " onClick='smoking_statusClicked(this)'";
1514 echo " $disabled />" . xlt('Quit') . "&nbsp;</td>";
1515 // quit date
1516 echo "<td class='text'><input type='text' size='6' class='form-control datepicker' name='date_$field_id_esc' id='date_$field_id_esc'" .
1517 " value='$resdate'" .
1518 " title='$description'" .
1519 " $disabled />";
1520 echo "&nbsp;</td>";
1521 // never
1522 echo "<td class='text'><input type='radio'" .
1523 " name='radio_{$field_id_esc}'" .
1524 " class='form-check-inline'" .
1525 " id='radio_{$field_id_esc}[never]'" .
1526 " value='never" . $field_id_esc . "' $lbfonchange";
1527 if ($restype == "never" . $field_id) {
1528 echo " checked";
1531 if ($data_type == 32) {
1532 echo " onClick='smoking_statusClicked(this)'";
1535 echo " />" . xlt('Never') . "&nbsp;</td>";
1536 // Not Applicable
1537 echo "<td class='text'><input type='radio'" .
1538 " class='form-check-inline' " .
1539 " name='radio_{$field_id}'" .
1540 " id='radio_{$field_id}[not_applicable]'" .
1541 " value='not_applicable" . $field_id . "' $lbfonchange";
1542 if ($restype == "not_applicable" . $field_id) {
1543 echo " checked";
1546 if ($data_type == 32) {
1547 echo " onClick='smoking_statusClicked(this)'";
1550 echo " $disabled />" . xlt('N/A') . "&nbsp;</td>";
1552 //Added on 5-jun-2k14 (regarding 'Smoking Status - display SNOMED code description')
1553 echo "<td class='text'><div id='smoke_code'></div></td>";
1554 echo "</tr>";
1555 echo "</table>";
1556 } elseif ($data_type == 31) { // static text. read-only, of course.
1557 echo parse_static_text($frow);
1558 } elseif ($data_type == 34) {
1559 // $data_type == 33
1560 // Race and Ethnicity. After added support for backup lists, this is now the same as datatype 36; so have migrated it there.
1561 // $data_type == 33
1563 $arr = explode("|*|*|*|", $currvalue);
1564 echo "<div>"; // wrapper for myHideOrShow()
1565 echo "<a href='../../../library/custom_template/custom_template.php?type=form_{$field_id}&contextName=" . htmlspecialchars($list_id_esc, ENT_QUOTES) . "' class='iframe_medium text-body text-decoration-none'>";
1566 echo "<div id='form_{$field_id}_div' class='text-area' style='min-width: 133px'>" . $arr[0] . "</div>";
1567 echo "<div style='display: none'><textarea name='form_{$field_id}' id='form_{$field_id}' class='form-control$smallform' style='display: none' $lbfonchange $disabled>" . $currvalue . "</textarea></div>";
1568 echo "</a>";
1569 echo "</div>";
1570 } elseif ($data_type == 35) { //facilities drop-down list
1571 if (empty($currvalue)) {
1572 $currvalue = 0;
1575 dropdown_facility(
1576 $selected = $currvalue,
1577 $name = "form_$field_id_esc",
1578 $allow_unspecified = true,
1579 $allow_allfacilities = false,
1580 $disabled,
1581 $lbfchange,
1582 false,
1583 $smallform
1585 } elseif ($data_type == 36 || $data_type == 33) { //multiple select, supports backup list
1586 echo generate_select_list(
1587 "form_$field_id",
1588 $list_id,
1589 $currvalue,
1590 $description,
1591 $showEmpty ? $empty_title : '',
1592 $smallform,
1593 $lbfchange,
1595 null,
1596 true,
1597 $backup_list
1600 // A set of lab test results; Gestation, 3 radio buttons, test value, notes field:
1601 } elseif ($data_type == 37) {
1602 echo genLabResults($frow, $currvalue, 0, $disabled);
1603 } elseif ($data_type == 40) { // Canvas and related elements for browser-side image drawing.
1604 // Note you must invoke lbf_canvas_head() (below) to use this field type in a form.
1605 // Unlike other field types, width and height are in pixels.
1606 $canWidth = intval($frow['fld_length']);
1607 $canHeight = intval($frow['fld_rows']);
1608 if (empty($currvalue)) {
1609 if (preg_match('/\\bimage=([a-zA-Z0-9._-]*)/', $frow['description'], $matches)) {
1610 // If defined this is the filename of the default starting image.
1611 $currvalue = $GLOBALS['web_root'] . '/sites/' . $_SESSION['site_id'] . '/images/' . $matches[1];
1614 $mywidth = 50 + ($canWidth > 250 ? $canWidth : 250);
1615 $myheight = 31 + ($canHeight > 261 ? $canHeight : 261);
1616 echo "<div>"; // wrapper for myHideOrShow()
1617 echo "<div id='form_$field_id_esc' style='width:{$mywidth}px; height:{$myheight}px;'></div>";
1618 // Hidden form field exists to send updated data to the server at submit time.
1619 echo "<input type='hidden' name='form_$field_id_esc' value='' />";
1620 // Hidden image exists to support initialization of the canvas.
1621 echo "<img src='" . attr($currvalue) . "' id='form_{$field_id_esc}_img' style='display:none'>";
1622 echo "</div>";
1623 // $date_init is a misnomer but it's the place for browser-side setup logic.
1624 $date_init .= " lbfCanvasSetup('form_$field_id_esc', $canWidth, $canHeight);\n";
1625 } elseif ($data_type == 41 || $data_type == 42) {
1626 $datatype = 'patient-signature';
1627 $cpid = $GLOBALS['pid'];
1628 $cuser = $_SESSION['authUserID'];
1629 if ($data_type == 42) {
1630 $datatype = 'admin-signature';
1632 echo "<input type='hidden' id='form_$field_id_esc' name='form_$field_id_esc' value='' />\n";
1633 echo "<img class='signature' id='form_{$field_id_esc}_img' title='$description'
1634 data-pid='$cpid' data-user='$cuser' data-type='$datatype'
1635 data-action='fetch_signature' alt='Get Signature' src='" . attr($currvalue) . "'>\n";
1636 } elseif ($data_type == 44) { //multiple select facility
1637 if (empty($currvalue)) {
1638 $currvalue = 0;
1641 dropdown_facility(
1642 $selected = $currvalue,
1643 $name = "form_$field_id_esc",
1644 $allow_unspecified = false,
1645 $allow_allfacilities = false,
1646 $disabled,
1647 $lbfchange,
1648 true,
1649 $smallform
1651 } elseif ($data_type == 45) { // Multiple provider list, local providers only
1652 $ures = sqlStatement("SELECT id, fname, lname, specialty FROM users " .
1653 "WHERE active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) " .
1654 "AND authorized = 1 ORDER BY lname, fname");
1655 echo "<select name='form_$field_id_esc" . "[]'" . " id='form_$field_id_esc' title='$description' $lbfonchange $disabled class='form-control$smallform select-dropdown' style='width:100%;' multiple='multiple'>";
1656 $got_selected = false;
1657 while ($urow = sqlFetchArray($ures)) {
1658 $uname = text($urow['fname'] . ' ' . $urow['lname']);
1659 $optionId = attr($urow['id']);
1660 echo "<option value='$optionId'";
1661 $selectedValues = explode("|", $currvalue);
1663 if (in_array($optionId, $selectedValues)) {
1664 echo " selected";
1665 $got_selected = true;
1668 echo ">$uname</option>";
1671 if (!$got_selected && $currvalue) {
1672 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
1673 echo "</select>";
1674 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
1675 } else {
1676 echo "</select>";
1679 // Patient selector field.
1680 } elseif ($data_type == 51) {
1681 $fldlength = attr($frow['fld_length']);
1682 $currdescstring = '';
1683 if (!empty($currvalue)) {
1684 $currdescstring .= getPatientDescription($currvalue);
1686 $currdescstring = htmlspecialchars($currdescstring, ENT_QUOTES);
1687 echo "<div>"; // wrapper for myHideOrShow()
1688 echo "<input type='text'" .
1689 " name='form_$field_id_esc'" .
1690 " size='$fldlength'" .
1691 " value='$currescaped'" .
1692 " style='display:none'" .
1693 " $lbfonchange readonly $disabled />";
1694 // Extra readonly input field for patient description (name and pid).
1695 echo "<input type='text'" .
1696 " name='form_$field_id_esc" . "__desc'" .
1697 " size='$fldlength'" .
1698 " title='$description'" .
1699 " value='$currdescstring'";
1700 if (!$disabled) {
1701 echo " onclick='sel_patient(this, this.form.form_$field_id_esc)'";
1703 echo " readonly $disabled />";
1704 echo "</div>";
1705 // Previous Patient Names with add. Somewhat mirrors data types 44,45.
1706 } elseif ($data_type == 52) {
1707 global $pid;
1708 $pid = ($frow['blank_form'] ?? null) ? null : $pid;
1709 $patientService = new PatientService();
1710 $res = $patientService->getPatientNameHistory($pid);
1711 echo "<div class='input-group w-75'>";
1712 echo "<select name='form_$field_id_esc" . "[]'" . " id='form_$field_id_esc' title='$description' $lbfonchange $disabled class='form-control$smallform select-previous-names' multiple='multiple'>";
1713 foreach ($res as $row) {
1714 $pname = $row['formatted_name']; // esc'ed in fetch.
1715 $optionId = attr($row['id']);
1716 // all names always selected
1717 echo "<option value='$optionId'" . " selected>$pname</option>";
1719 echo "</select>";
1720 echo "<button type='button' class='btn btn-primary btn-sm' id='type_52_add' onclick='return specialtyFormDialog()'>" . xlt('Add') . "</button></div>";
1721 // Patient Encounter List Field
1722 } elseif ($data_type == 53) {
1723 global $pid;
1724 $pid = ($frow['blank_form'] ?? null) ? 0 : $pid;
1725 $encounterService = new EncounterService();
1726 $res = $encounterService->getEncountersForPatientByPid($pid);
1727 echo "<div class='input-group w-75'>";
1728 echo "<select name='form_$field_id_esc'" . " id='form_$field_id_esc' title='$description' $lbfonchange $disabled class='form-control$smallform select-encounters'>";
1729 echo "<option value=''>" . xlt("Select Encounter") . "</option>";
1730 foreach ($res as $row) {
1731 $label = text(date("Y-m-d", strtotime($row['date'])) . " " . ($row['pc_catname'] ?? ''));
1732 $optionId = attr($row['eid']);
1733 // all names always selected
1734 if ($currvalue == $row['eid']) {
1735 echo "<option value='$optionId'" . " selected>$label</option>";
1736 } else {
1737 echo "<option value='$optionId'>$label</option>";
1740 echo "</select>";
1741 } elseif ($data_type == 54) {
1742 include "templates/address_list_form.php";
1746 function generate_print_field($frow, $currvalue, $value_allowed = true)
1748 global $rootdir, $date_init, $ISSUE_TYPES;
1750 $currescaped = htmlspecialchars($currvalue, ENT_QUOTES);
1752 $data_type = $frow['data_type'];
1753 $field_id = $frow['field_id'] ?? null;
1754 $list_id = $frow['list_id'];
1755 $fld_length = $frow['fld_length'] ?? null;
1756 $backup_list = $frow['list_backup_id'] ?? null;
1758 $description = attr(xl_layout_label($frow['description'] ?? ''));
1760 // Can pass $frow['empty_title'] with this variable, otherwise
1761 // will default to 'Unassigned'.
1762 // If it is 'SKIP' then an empty text title is completely skipped.
1763 $showEmpty = true;
1764 if (isset($frow['empty_title'])) {
1765 if ($frow['empty_title'] == "SKIP") {
1766 //do not display an 'empty' choice
1767 $showEmpty = false;
1768 $empty_title = "Unassigned";
1769 } else {
1770 $empty_title = $frow['empty_title'];
1772 } else {
1773 $empty_title = "Unassigned";
1776 // generic single-selection list
1777 // Supports backup lists.
1778 // if (false && ($data_type == 1 || $data_type == 26 || $data_type == 33 || $data_type == 43 || $data_type == 46)) {
1779 // We used to show all the list options but this was undone per CV request 2017-12-07
1780 // (see alternative code below).
1781 if ($data_type == 1 || $data_type == 26 || $data_type == 33 || $data_type == 43 || $data_type == 46) {
1782 if (empty($fld_length)) {
1783 if ($list_id == 'titles') {
1784 $fld_length = 3;
1785 } else {
1786 $fld_length = 10;
1790 $tmp = '';
1791 if ($currvalue) {
1792 if ($data_type == 46) {
1793 // support for single-selection list with comment support
1794 $selectedValues = explode("|", $currvalue);
1795 $currvalue = $selectedValues[0];
1797 $lrow = sqlQuery(
1798 "SELECT title FROM list_options " .
1799 "WHERE list_id = ? AND option_id = ? AND activity = 1",
1800 array($list_id,$currvalue)
1802 // For lists Race and Ethnicity if there is no matching value in the corresponding lists check ethrace list
1803 if (empty($lrow) && $data_type == 33) {
1804 $lrow = sqlQuery(
1805 "SELECT title FROM list_options " .
1806 "WHERE list_id = ? AND option_id = ? AND activity = 1",
1807 array('ethrace', $currvalue)
1811 $tmp = xl_list_label($lrow['title']);
1812 if ($lrow == 0 && !empty($backup_list)) {
1813 // since primary list did not map, try to map to backup list
1814 $lrow = sqlQuery("SELECT title FROM list_options " .
1815 "WHERE list_id = ? AND option_id = ?", array($backup_list,$currvalue));
1816 $tmp = xl_list_label($lrow['title']);
1819 if (empty($tmp)) {
1820 $tmp = "($currvalue)";
1823 if ($data_type == 46) {
1824 // support for single-selection list with comment support
1825 $resnote = $selectedValues[1] ?? null;
1826 if (!empty($resnote)) {
1827 $tmp .= " (" . $resnote . ")";
1832 if ($tmp === '') {
1833 $tmp = '&nbsp;';
1834 } else {
1835 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1837 echo $tmp;
1838 } elseif ($data_type == 2 || $data_type == 15) { // simple text field
1839 if ($currescaped === '') {
1840 $currescaped = '&nbsp;';
1843 echo $currescaped;
1844 } elseif ($data_type == 3) { // long or multi-line text field
1845 $fldlength = htmlspecialchars($fld_length, ENT_QUOTES);
1846 $maxlength = htmlspecialchars($frow['fld_rows'], ENT_QUOTES);
1847 echo "<textarea" .
1848 " class='form-control' " .
1849 " cols='$fldlength'" .
1850 " rows='$maxlength'>" .
1851 $currescaped . "</textarea>";
1852 } elseif ($data_type == 4) { // date
1853 $age_asof_date = '';
1854 $agestr = optionalAge($frow, $currvalue, $age_asof_date, $description);
1855 if ($currvalue === '') {
1856 echo '&nbsp;';
1857 } else {
1858 $modtmp = isOption($frow['edit_options'], 'F') === false ? 0 : 1;
1859 if (!$modtmp) {
1860 echo text(oeFormatShortDate($currvalue));
1861 } else {
1862 echo text(oeFormatDateTime($currvalue));
1864 if ($agestr) {
1865 echo "&nbsp;(" . text($agestr) . ")";
1868 } elseif ($data_type == 10 || $data_type == 11) { // provider list
1869 $tmp = '';
1870 if ($currvalue) {
1871 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
1872 "WHERE id = ?", array($currvalue));
1873 $tmp = ucwords($urow['fname'] . " " . $urow['lname']);
1874 if (empty($tmp)) {
1875 $tmp = "($currvalue)";
1878 if ($tmp === '') {
1879 $tmp = '&nbsp;';
1880 } else {
1881 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1884 echo $tmp;
1885 } elseif ($data_type == 12) { // pharmacy list
1886 $tmp = '';
1887 if ($currvalue) {
1888 $pres = get_pharmacies();
1889 while ($prow = sqlFetchArray($pres)) {
1890 $key = $prow['id'];
1891 if ($currvalue == $key) {
1892 $tmp = $prow['name'] . ' ' . $prow['area_code'] . '-' .
1893 $prow['prefix'] . '-' . $prow['number'] . ' / ' .
1894 $prow['line1'] . ' / ' . $prow['city'];
1898 if (empty($tmp)) {
1899 $tmp = "($currvalue)";
1902 if ($tmp === '') {
1903 $tmp = '&nbsp;';
1904 } else {
1905 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1908 echo $tmp;
1909 } elseif ($data_type == 13) { // squads
1910 $tmp = '';
1911 if ($currvalue) {
1912 $squads = AclExtended::aclGetSquads();
1913 if ($squads) {
1914 foreach ($squads as $key => $value) {
1915 if ($currvalue == $key) {
1916 $tmp = $value[3];
1921 if (empty($tmp)) {
1922 $tmp = "($currvalue)";
1925 if ($tmp === '') {
1926 $tmp = '&nbsp;';
1927 } else {
1928 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1931 echo $tmp;
1932 } elseif ($data_type == 14) { // Address book.
1933 $tmp = '';
1934 if ($currvalue) {
1935 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
1936 "WHERE id = ?", array($currvalue));
1937 $uname = $urow['lname'];
1938 if ($urow['fname']) {
1939 $uname .= ", " . $urow['fname'];
1942 $tmp = $uname;
1943 if (empty($tmp)) {
1944 $tmp = "($currvalue)";
1947 if ($tmp === '') {
1948 $tmp = '&nbsp;';
1949 } else {
1950 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1953 echo $tmp;
1954 } elseif ($data_type == 16) { // insurance company list
1955 $tmp = '';
1956 if ($currvalue) {
1957 $insprovs = getInsuranceProviders();
1958 foreach ($insprovs as $key => $ipname) {
1959 if ($currvalue == $key) {
1960 $tmp = $ipname;
1964 if (empty($tmp)) {
1965 $tmp = "($currvalue)";
1969 if ($tmp === '') {
1970 $tmp = '&nbsp;';
1971 } else {
1972 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1975 echo $tmp;
1976 } elseif ($data_type == 17) { // issue types
1977 $tmp = '';
1978 if ($currvalue) {
1979 foreach ($ISSUE_TYPES as $key => $value) {
1980 if ($currvalue == $key) {
1981 $tmp = $value[1];
1985 if (empty($tmp)) {
1986 $tmp = "($currvalue)";
1990 if ($tmp === '') {
1991 $tmp = '&nbsp;';
1992 } else {
1993 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1996 echo $tmp;
1997 } elseif ($data_type == 18) { // Visit categories.
1998 $tmp = '';
1999 if ($currvalue) {
2000 $crow = sqlQuery(
2001 "SELECT pc_catid, pc_catname " .
2002 "FROM openemr_postcalendar_categories WHERE pc_catid = ?",
2003 array($currvalue)
2005 $tmp = xl_appt_category($crow['pc_catname']);
2006 if (empty($tmp)) {
2007 $tmp = "($currvalue)";
2011 if ($tmp === '') {
2012 $tmp = '&nbsp;';
2013 } else {
2014 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
2017 echo $tmp;
2018 } elseif ($data_type == 21) { // a single checkbox or set of labeled checkboxes
2019 if (!$list_id) {
2020 echo "<input type='checkbox'";
2021 if ($currvalue) {
2022 echo " checked";
2024 echo " />";
2025 } else {
2026 // In this special case, fld_length is the number of columns generated.
2027 $cols = max(1, $fld_length);
2028 $avalue = explode('|', $currvalue);
2029 $lres = sqlStatement("SELECT * FROM list_options " .
2030 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2031 echo "<table class='w-100' cellpadding='0' cellspacing='0'>";
2032 $tdpct = (int) (100 / $cols);
2033 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
2034 $option_id = $lrow['option_id'];
2035 if ($count % $cols == 0) {
2036 if ($count) {
2037 echo "</tr>";
2040 echo "<tr>";
2042 echo "<td width='" . attr($tdpct) . "%' nowrap>";
2043 echo "<input type='checkbox'";
2044 if (in_array($option_id, $avalue)) {
2045 echo " checked";
2047 echo ">" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
2048 echo "</td>";
2050 if ($count) {
2051 echo "</tr>";
2052 if ($count > $cols) {
2053 // Add some space after multiple rows of checkboxes.
2054 $cols = htmlspecialchars($cols, ENT_QUOTES);
2055 echo "<tr><td colspan='$cols' style='height:0.7em'></td></tr>";
2058 echo "</table>";
2060 } elseif ($data_type == 22) { // a set of labeled text input fields
2061 $tmp = explode('|', $currvalue);
2062 $avalue = array();
2063 foreach ($tmp as $value) {
2064 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2065 $avalue[$matches[1]] = $matches[2];
2069 $lres = sqlStatement("SELECT * FROM list_options " .
2070 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2071 echo "<table class='table'>";
2072 while ($lrow = sqlFetchArray($lres)) {
2073 $option_id = $lrow['option_id'];
2074 $fldlength = empty($fld_length) ? 20 : $fld_length;
2075 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2076 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
2077 $inputValue = htmlspecialchars($avalue[$option_id], ENT_QUOTES);
2078 echo "<td><input type='text'" .
2079 " class='form-control' " .
2080 " size='$fldlength'" .
2081 " value='$inputValue'" .
2082 " class='under'" .
2083 " /></td></tr>";
2086 echo "</table>";
2087 } elseif ($data_type == 23) { // a set of exam results; 3 radio buttons and a text field:
2088 $tmp = explode('|', $currvalue);
2089 $avalue = array();
2090 foreach ($tmp as $value) {
2091 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2092 $avalue[$matches[1]] = $matches[2];
2096 $fldlength = empty($fld_length) ? 20 : $fld_length;
2097 $lres = sqlStatement("SELECT * FROM list_options " .
2098 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2099 echo "<table class='table'>";
2100 echo "<tr><td><td class='font-weight-bold'>" .
2101 htmlspecialchars(xl('Exam or Test'), ENT_NOQUOTES) . "</td><td class='font-weight-bold'>" .
2102 htmlspecialchars(xl('N/A'), ENT_NOQUOTES) .
2103 "&nbsp;</td><td class='font-weight-bold'>" .
2104 htmlspecialchars(xl('Nor'), ENT_NOQUOTES) . "&nbsp;</td>" .
2105 "<td class='font-weight-bold'>" .
2106 htmlspecialchars(xl('Abn'), ENT_NOQUOTES) . "&nbsp;</td><td class='font-weight-bold'>" .
2107 htmlspecialchars(xl('Date/Notes'), ENT_NOQUOTES) . "</td></tr>";
2108 while ($lrow = sqlFetchArray($lres)) {
2109 $option_id = $lrow['option_id'];
2110 $restype = substr($avalue[$option_id], 0, 1);
2111 $resnote = substr($avalue[$option_id], 2);
2112 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2113 for ($i = 0; $i < 3; ++$i) {
2114 echo "<td><input type='radio'";
2115 if ($restype === "$i") {
2116 echo " checked";
2119 echo " /></td>";
2122 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
2123 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
2124 echo "<td><input type='text'" .
2125 " size='$fldlength'" .
2126 " value='$resnote'" .
2127 " class='under form-control' /></td>" .
2128 "</tr>";
2131 echo "</table>";
2132 } elseif ($data_type == 24) { // the list of active allergies for the current patient
2133 // this is read-only!
2134 $query = "SELECT title, comments FROM lists WHERE " .
2135 "pid = ? AND type = 'allergy' AND enddate IS NULL " .
2136 "ORDER BY begdate";
2137 $lres = sqlStatement($query, array($GLOBALS['pid']));
2138 $count = 0;
2139 while ($lrow = sqlFetchArray($lres)) {
2140 if ($count++) {
2141 echo "<br />";
2144 echo htmlspecialchars($lrow['title'], ENT_QUOTES);
2145 if ($lrow['comments']) {
2146 echo htmlspecialchars(' (' . $lrow['comments'] . ')', ENT_QUOTES);
2149 } elseif ($data_type == 25) { // a set of labeled checkboxes, each with a text field:
2150 $tmp = explode('|', $currvalue);
2151 $avalue = array();
2152 foreach ($tmp as $value) {
2153 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2154 $avalue[$matches[1]] = $matches[2];
2158 $fldlength = empty($fld_length) ? 20 : $fld_length;
2159 $lres = sqlStatement("SELECT * FROM list_options " .
2160 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2161 echo "<table class='table'>";
2162 while ($lrow = sqlFetchArray($lres)) {
2163 $option_id = $lrow['option_id'];
2164 $restype = substr($avalue[$option_id], 0, 1);
2165 $resnote = substr($avalue[$option_id], 2);
2166 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2167 echo "<td><input type='checkbox'";
2168 if ($restype) {
2169 echo " checked";
2172 echo " />&nbsp;</td>";
2173 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
2174 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
2175 echo "<td><input type='text'" .
2176 " size='$fldlength'" .
2177 " class='form-control' " .
2178 " value='$resnote'" .
2179 " class='under'" .
2180 " /></td>" .
2181 "</tr>";
2184 echo "</table>";
2185 } elseif ($data_type == 27) { // Removed: || $data_type == 1 || $data_type == 26 || $data_type == 33
2186 // a set of labeled radio buttons
2187 // In this special case, fld_length is the number of columns generated.
2189 $cols = max(1, ($frow['fld_length'] ?? null));
2190 $lres = sqlStatement("SELECT * FROM list_options " .
2191 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2192 echo "<table class='w-100' cellpadding='0' cellspacing='0'>";
2193 $tdpct = (int) (100 / $cols);
2194 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
2195 $option_id = $lrow['option_id'];
2196 if ($count % $cols == 0) {
2197 if ($count) {
2198 echo "</tr>";
2200 echo "<tr>";
2202 echo "<td width='" . attr($tdpct) . "%' nowrap>";
2203 echo "<input type='radio'";
2204 if (strlen($currvalue) > 0 && $option_id == $currvalue) {
2205 // Do not use defaults for these printable forms.
2206 echo " checked";
2208 echo ">" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
2209 echo "</td>";
2211 if ($count) {
2212 echo "</tr>";
2213 if ($count > $cols) {
2214 // Add some space after multiple rows of radio buttons.
2215 $cols = htmlspecialchars($cols, ENT_QUOTES);
2216 echo "<tr><td colspan='$cols' style='height:0.7em'></td></tr>";
2219 echo "</table>";
2221 // special case for history of lifestyle status; 3 radio buttons and a date text field:
2222 } elseif ($data_type == 28 || $data_type == 32) {
2223 $tmp = explode('|', $currvalue);
2224 switch (count($tmp)) {
2225 case "4":
2226 $resnote = $tmp[0];
2227 $restype = $tmp[1];
2228 $resdate = oeFormatShortDate($tmp[2]) ;
2229 $reslist = $tmp[3];
2230 break;
2231 case "3":
2232 $resnote = $tmp[0];
2233 $restype = $tmp[1];
2234 $resdate = oeFormatShortDate($tmp[2]);
2235 $reslist = '';
2236 break;
2237 case "2":
2238 $resnote = $tmp[0];
2239 $restype = $tmp[1];
2240 $resdate = "";
2241 $reslist = '';
2242 break;
2243 case "1":
2244 $resnote = $tmp[0];
2245 $resdate = $restype = "";
2246 $reslist = '';
2247 break;
2248 default:
2249 $restype = $resdate = $resnote = "";
2250 $reslist = '';
2251 break;
2254 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
2255 echo "<table class='table'>";
2256 echo "<tr>";
2257 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
2258 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
2259 $resdate = htmlspecialchars($resdate, ENT_QUOTES);
2260 if ($data_type == 28) {
2261 echo "<td><input type='text'" .
2262 " size='$fldlength'" .
2263 " class='under'" .
2264 " value='$resnote' /></td>";
2265 echo "<td class='font-weight-bold'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
2266 "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
2267 htmlspecialchars(xl('Status'), ENT_NOQUOTES) . ":&nbsp;</td>";
2268 } elseif ($data_type == 32) {
2269 echo "<tr><td><input type='text'" .
2270 " size='$fldlength'" .
2271 " class='under form-control'" .
2272 " value='$resnote' /></td></tr>";
2273 $fldlength = 30;
2274 $smoking_status_title = generate_display_field(array('data_type' => '1','list_id' => $list_id), $reslist);
2275 echo "<td><input type='text'" .
2276 " size='$fldlength'" .
2277 " class='under form-control'" .
2278 " value='$smoking_status_title' /></td>";
2279 echo "<td class='font-weight-bold'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" . htmlspecialchars(xl('Status'), ENT_NOQUOTES) . ":&nbsp;&nbsp;</td>";
2282 echo "<td><input type='radio' class='form-check-inline'";
2283 if ($restype == "current" . $field_id) {
2284 echo " checked";
2287 echo "/>" . htmlspecialchars(xl('Current'), ENT_NOQUOTES) . "&nbsp;</td>";
2289 echo "<td><input type='radio' class='form-check-inline'";
2290 if ($restype == "current" . $field_id) {
2291 echo " checked";
2294 echo "/>" . htmlspecialchars(xl('Quit'), ENT_NOQUOTES) . "&nbsp;</td>";
2296 echo "<td><input type='text' size='6'" .
2297 " value='$resdate'" .
2298 " class='under form-control'" .
2299 " /></td>";
2301 echo "<td><input type='radio' class='form-check-inline'";
2302 if ($restype == "current" . $field_id) {
2303 echo " checked";
2306 echo " />" . htmlspecialchars(xl('Never'), ENT_NOQUOTES) . "</td>";
2308 echo "<td><input type='radio' class='form-check-inline'";
2309 if ($restype == "not_applicable" . $field_id) {
2310 echo " checked";
2313 echo " />" . htmlspecialchars(xl('N/A'), ENT_NOQUOTES) . "&nbsp;</td>";
2314 echo "</tr>";
2315 echo "</table>";
2316 } elseif ($data_type == 31) { // static text. read-only, of course.
2317 echo parse_static_text($frow, $value_allowed);
2318 } elseif ($data_type == 34) {
2319 echo "<a href='../../../library/custom_template/custom_template.php?type=form_{$field_id}&contextName=" . htmlspecialchars($list_id_esc, ENT_QUOTES) . "' class='iframe_medium text-body text-decoration-none'>";
2320 echo "<div id='form_{$field_id}_div' class='text-area'></div>";
2321 echo "<div style='display: none'><textarea name='form_{$field_id}' class='form-control' id='form_{$field_id}' style='display: none'></textarea></div>";
2322 echo "</a>";
2324 // Facilities. Changed 2017-12-15 to not show the choices.
2325 } elseif ($data_type == 35) {
2326 $urow = sqlQuery(
2327 "SELECT id, name FROM facility WHERE id = ?",
2328 array($currvalue)
2330 echo empty($urow['id']) ? '&nbsp;' : text($urow['name']);
2331 } elseif ($data_type == 36) { //Multi-select. Supports backup lists.
2332 if (empty($fld_length)) {
2333 if ($list_id == 'titles') {
2334 $fld_length = 3;
2335 } else {
2336 $fld_length = 10;
2340 $tmp = '';
2342 $values_array = explode("|", $currvalue);
2344 $i = 0;
2345 foreach ($values_array as $value) {
2346 if ($value) {
2347 $lrow = sqlQuery("SELECT title FROM list_options " .
2348 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($list_id,$value));
2349 $tmp = xl_list_label($lrow['title']);
2350 if ($lrow == 0 && !empty($backup_list)) {
2351 // since primary list did not map, try to map to backup list
2352 $lrow = sqlQuery("SELECT title FROM list_options " .
2353 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list,$currvalue));
2354 $tmp = xl_list_label($lrow['title']);
2357 if (empty($tmp)) {
2358 $tmp = "($value)";
2362 if ($tmp === '') {
2363 $tmp = '&nbsp;';
2364 } else {
2365 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
2368 if ($i != 0 && $tmp != '&nbsp;') {
2369 echo ",";
2372 echo $tmp;
2373 $i++;
2376 // A set of lab test results; Gestation, 3 radio buttons, test value, notes field:
2377 } elseif ($data_type == 37) {
2378 echo genLabResults($frow, $currvalue, 1, $disabled);
2379 } elseif ($data_type == 40) { // Image from canvas drawing
2380 if (empty($currvalue)) {
2381 if (preg_match('/\\bimage=([a-zA-Z0-9._-]*)/', $frow['description'], $matches)) {
2382 $currvalue = $GLOBALS['web_root'] . '/sites/' . $_SESSION['site_id'] . '/images/' . $matches[1];
2385 if ($currvalue) {
2386 echo "<img src='" . attr($currvalue) . "'>";
2388 } elseif ($data_type == 41 || $data_type == 42) {
2389 if ($currvalue) {
2390 echo "<img class='w-auto' style='height: 70px;' src='" . attr($currvalue) . "'>";
2392 } elseif ($data_type == 44 || $data_type == 45) {
2393 $tmp = '';
2395 $values_array = explode("|", $currvalue);
2397 $i = 0;
2398 foreach ($values_array as $value) {
2399 if ($value) {
2400 if ($data_type == 44) {
2401 $lrow = sqlQuery("SELECT name as name FROM facility WHERE id = ?", array($value));
2403 if ($data_type == 45) {
2404 $lrow = sqlQuery("SELECT CONCAT(fname,' ',lname) as name FROM users WHERE id = ?", array($value));
2406 $tmp = $lrow['name'];
2409 if ($tmp === '') {
2410 $tmp = '&nbsp;';
2411 } else {
2412 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
2415 if ($i != 0 && $tmp != '&nbsp;') {
2416 echo ",";
2419 echo $tmp;
2420 $i++;
2423 // Patient selector field.
2424 } elseif ($data_type == 51) {
2425 if (!empty($currvalue)) {
2426 $tmp = text(getPatientDescription($currvalue));
2427 } else {
2428 echo '&nbsp;';
2434 * @param $list_id
2435 * @param bool $translate
2436 * @return array
2438 * Generate a key-value array containing each row of the specified list,
2439 * with the option ID as the index, and the title as the element
2441 * Pass in the list_id to specify this list.
2443 * Use the translate flag to run the title element through the translator
2445 function generate_list_map($list_id, $translate = false)
2447 $result = sqlStatement("SELECT option_id, title FROM list_options WHERE list_id = ?", [$list_id]);
2448 $map = [];
2449 while ($row = sqlFetchArray($result)) {
2450 if ($translate === true) {
2451 $title = xl_list_label($row['title']);
2452 } else {
2453 $title = $row['title'];
2455 $map[$row['option_id']] = $title;
2458 return $map;
2461 function generate_display_field($frow, $currvalue)
2463 global $ISSUE_TYPES, $facilityService;
2465 $data_type = $frow['data_type'];
2466 $field_id = isset($frow['field_id']) ? $frow['field_id'] : null;
2467 $list_id = $frow['list_id'];
2468 $backup_list = isset($frow['list_backup_id']) ? $frow['list_backup_id'] : null;
2469 $show_unchecked_arr = array();
2470 getLayoutProperties($frow['form_id'] ?? null, $show_unchecked_arr, 'grp_unchecked', "1");
2471 $show_unchecked = strval($show_unchecked_arr['']['grp_unchecked'] ?? null) == "0" ? false : true;
2473 $s = '';
2475 // generic selection list or the generic selection list with add on the fly
2476 // feature
2477 if ($data_type == 1 || $data_type == 26 || $data_type == 43 || $data_type == 46) {
2478 if ($data_type == 46) {
2479 // support for single-selection list with comment support
2480 $selectedValues = explode("|", $currvalue);
2481 $currvalue = $selectedValues[0];
2484 $lrow = sqlQuery("SELECT title FROM list_options " .
2485 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($list_id,$currvalue));
2486 $s = htmlspecialchars(xl_list_label($lrow['title'] ?? ''), ENT_NOQUOTES);
2487 //if there is no matching value in the corresponding lists check backup list
2488 // only supported in data types 1,26,43,46
2489 if ($lrow == 0 && !empty($backup_list) && ($data_type == 1 || $data_type == 26 || $data_type == 43 || $data_type == 46)) {
2490 $lrow = sqlQuery("SELECT title FROM list_options " .
2491 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list,$currvalue));
2492 $s = htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
2495 // If match is not found in main and backup lists, return the key with exclamation mark
2496 if ($s == '') {
2497 $s = nl2br(text(xl_list_label($currvalue))) .
2498 '<span> <i class="fa fas fa-exclamation-circle ml-1"></i></span>';
2501 if ($data_type == 46) {
2502 // support for single-selection list with comment support
2503 $resnote = $selectedValues[1] ?? null;
2504 if (!empty($resnote)) {
2505 $s .= " (" . text($resnote) . ")";
2508 } elseif ($data_type == 2) { // simple text field
2509 $s = nl2br(htmlspecialchars($currvalue, ENT_NOQUOTES));
2510 } elseif ($data_type == 3) { // long or multi-line text field
2511 $s = nl2br(htmlspecialchars($currvalue, ENT_NOQUOTES));
2512 } elseif ($data_type == 4) { // date
2513 $asof = ''; //not used here, but set to prevent a php warning when call optionalAge
2514 $s = '';
2515 $description = (isset($frow['description']) ? htmlspecialchars(xl_layout_label($frow['description']), ENT_QUOTES) : '');
2516 $age_asof_date = '';
2517 $agestr = optionalAge($frow, $currvalue, $age_asof_date, $description);
2518 if ($currvalue === '') {
2519 $s .= '&nbsp;';
2520 } else {
2521 $modtmp = isOption($frow['edit_options'], 'F') === false ? 0 : 1;
2522 if (!$modtmp) {
2523 $s .= text(oeFormatShortDate($currvalue));
2524 } else {
2525 $s .= text(oeFormatDateTime($currvalue));
2527 if ($agestr) {
2528 $s .= "&nbsp;(" . text($agestr) . ")";
2531 } elseif ($data_type == 10 || $data_type == 11) { // provider
2532 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
2533 "WHERE id = ?", array($currvalue));
2534 $s = text(ucwords(($urow['fname'] ?? '') . " " . ($urow['lname'] ?? '')));
2535 } elseif ($data_type == 12) { // pharmacy list
2536 $pres = get_pharmacies();
2537 while ($prow = sqlFetchArray($pres)) {
2538 $key = $prow['id'];
2539 if ($currvalue == $key) {
2540 $s .= htmlspecialchars($prow['name'] . ' ' . $prow['area_code'] . '-' .
2541 $prow['prefix'] . '-' . $prow['number'] . ' / ' .
2542 $prow['line1'] . ' / ' . $prow['city'], ENT_NOQUOTES);
2545 } elseif ($data_type == 13) { // squads
2546 $squads = AclExtended::aclGetSquads();
2547 if ($squads) {
2548 foreach ($squads as $key => $value) {
2549 if ($currvalue == $key) {
2550 $s .= htmlspecialchars($value[3], ENT_NOQUOTES);
2554 } elseif ($data_type == 14) { // address book
2555 $urow = sqlQuery("SELECT fname, lname, specialty, organization FROM users " .
2556 "WHERE id = ?", array($currvalue));
2557 //ViSolve: To display the Organization Name if it exist. Else it will display the user name.
2558 if (!empty($urow['organization'])) {
2559 $uname = $urow['organization'];
2560 } else {
2561 $uname = $urow['lname'] ?? '';
2562 if (!empty($urow['fname'])) {
2563 $uname .= ", " . $urow['fname'];
2567 $s = htmlspecialchars($uname, ENT_NOQUOTES);
2568 } elseif ($data_type == 15) { // billing code
2569 $s = '';
2570 if (!empty($currvalue)) {
2571 $relcodes = explode(';', $currvalue);
2572 foreach ($relcodes as $codestring) {
2573 if ($codestring === '') {
2574 continue;
2576 $tmp = lookup_code_descriptions($codestring);
2577 if ($s !== '') {
2578 $s .= '; ';
2580 if (!empty($tmp)) {
2581 $s .= text($tmp);
2582 } else {
2583 $s .= text($codestring) . ' (' . xlt('not found') . ')';
2587 } elseif ($data_type == 16) { // insurance company list
2588 $insprovs = getInsuranceProviders();
2589 foreach ($insprovs as $key => $ipname) {
2590 if ($currvalue == $key) {
2591 $s .= htmlspecialchars($ipname, ENT_NOQUOTES);
2594 } elseif ($data_type == 17) { // issue types
2595 foreach ($ISSUE_TYPES as $key => $value) {
2596 if ($currvalue == $key) {
2597 $s .= htmlspecialchars($value[1], ENT_NOQUOTES);
2600 } elseif ($data_type == 18) { // visit category
2601 $crow = sqlQuery(
2602 "SELECT pc_catid, pc_catname " .
2603 "FROM openemr_postcalendar_categories WHERE pc_catid = ?",
2604 array($currvalue)
2606 $s = htmlspecialchars($crow['pc_catname'], ENT_NOQUOTES);
2607 } elseif ($data_type == 21) { // a single checkbox or set of labeled checkboxes
2608 if (!$list_id) {
2609 $s .= $currvalue ? '&#9745;' : '&#9744;';
2610 } else {
2611 // In this special case, fld_length is the number of columns generated.
2612 $cols = max(1, $frow['fld_length']);
2613 $avalue = explode('|', $currvalue);
2614 $lres = sqlStatement("SELECT * FROM list_options " .
2615 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2616 $s .= "<table cellspacing='0' cellpadding='0'>";
2617 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
2618 $option_id = $lrow['option_id'];
2619 $option_id_esc = text($option_id);
2620 if ($count % $cols == 0) {
2621 if ($count) {
2622 $s .= "</tr>";
2624 $s .= "<tr>";
2626 $checked = in_array($option_id, $avalue);
2627 if (!$show_unchecked && $checked) {
2628 $s .= "<td nowrap>";
2629 $s .= text(xl_list_label($lrow['title'])) . '&nbsp;&nbsp;';
2630 $s .= "</td>";
2631 } elseif ($show_unchecked) {
2632 $s .= "<td nowrap>";
2633 $s .= $checked ? '&#9745;' : '&#9744;';
2634 $s .= '&nbsp;' . text(xl_list_label($lrow['title'])) . '&nbsp;&nbsp;';
2635 $s .= "</td>";
2638 if ($count) {
2639 $s .= "</tr>";
2641 $s .= "</table>";
2643 } elseif ($data_type == 22) { // a set of labeled text input fields
2644 $tmp = explode('|', $currvalue);
2645 $avalue = array();
2646 foreach ($tmp as $value) {
2647 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2648 $avalue[$matches[1]] = $matches[2];
2652 $lres = sqlStatement("SELECT * FROM list_options " .
2653 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2654 $s .= "<table class='table'>";
2655 while ($lrow = sqlFetchArray($lres)) {
2656 $option_id = $lrow['option_id'];
2657 if (empty($avalue[$option_id])) {
2658 continue;
2661 // Added 5-09 by BM - Translate label if applicable
2662 $s .= "<tr><td class='font-weight-bold align-top'>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . ":&nbsp;</td>";
2664 $s .= "<td class='text align-top'>" . htmlspecialchars($avalue[$option_id], ENT_NOQUOTES) . "</td></tr>";
2667 $s .= "</table>";
2668 } elseif ($data_type == 23) { // a set of exam results; 3 radio buttons and a text field:
2669 $tmp = explode('|', $currvalue);
2670 $avalue = array();
2671 foreach ($tmp as $value) {
2672 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2673 $avalue[$matches[1]] = $matches[2];
2677 $lres = sqlStatement("SELECT * FROM list_options " .
2678 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2679 $s .= "<table class='table'>";
2680 while ($lrow = sqlFetchArray($lres)) {
2681 $option_id = $lrow['option_id'];
2682 $restype = substr(($avalue[$option_id] ?? ''), 0, 1);
2683 $resnote = substr(($avalue[$option_id] ?? ''), 2);
2684 if (empty($restype) && empty($resnote)) {
2685 continue;
2688 // Added 5-09 by BM - Translate label if applicable
2689 $s .= "<tr><td class='font-weight-bold align-top'>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2691 $restype = ($restype == '1') ? xl('Normal') : (($restype == '2') ? xl('Abnormal') : xl('N/A'));
2692 // $s .= "<td class='text align-top'>$restype</td></tr>";
2693 // $s .= "<td class='text align-top'>$resnote</td></tr>";
2694 $s .= "<td class='text align-top'>" . htmlspecialchars($restype, ENT_NOQUOTES) . "&nbsp;</td>";
2695 $s .= "<td class='text align-top'>" . htmlspecialchars($resnote, ENT_NOQUOTES) . "</td>";
2696 $s .= "</tr>";
2699 $s .= "</table>";
2700 } elseif ($data_type == 24) { // the list of active allergies for the current patient
2701 $query = "SELECT title, comments FROM lists WHERE " .
2702 "pid = ? AND type = 'allergy' AND enddate IS NULL " .
2703 "ORDER BY begdate";
2704 // echo "<!-- $query -->\n"; // debugging
2705 $lres = sqlStatement($query, array($GLOBALS['pid']));
2706 $count = 0;
2707 while ($lrow = sqlFetchArray($lres)) {
2708 if ($count++) {
2709 $s .= "<br />";
2712 $s .= htmlspecialchars($lrow['title'], ENT_NOQUOTES);
2713 if ($lrow['comments']) {
2714 $s .= ' (' . htmlspecialchars($lrow['comments'], ENT_NOQUOTES) . ')';
2717 } elseif ($data_type == 25) { // a set of labeled checkboxes, each with a text field:
2718 $tmp = explode('|', $currvalue);
2719 $avalue = array();
2720 foreach ($tmp as $value) {
2721 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2722 $avalue[$matches[1]] = $matches[2];
2726 $lres = sqlStatement("SELECT * FROM list_options " .
2727 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2728 $s .= "<table class='table'>";
2729 while ($lrow = sqlFetchArray($lres)) {
2730 $option_id = $lrow['option_id'];
2731 $restype = substr($avalue[$option_id], 0, 1);
2732 $resnote = substr($avalue[$option_id], 2);
2733 if (empty($restype) && empty($resnote)) {
2734 continue;
2737 // Added 5-09 by BM - Translate label if applicable
2738 $s .= "<tr><td class='font-weight-bold align-top'>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2740 $restype = $restype ? xl('Yes') : xl('No');
2741 $s .= "<td class='text align-top'>" . htmlspecialchars($restype, ENT_NOQUOTES) . "&nbsp;</td>";
2742 $s .= "<td class='text align-top'>" . htmlspecialchars($resnote, ENT_NOQUOTES) . "</td>";
2743 $s .= "</tr>";
2746 $s .= "</table>";
2747 } elseif ($data_type == 27) { // a set of labeled radio buttons
2748 // In this special case, fld_length is the number of columns generated.
2749 $cols = max(1, $frow['fld_length']);
2750 $lres = sqlStatement("SELECT * FROM list_options " .
2751 "WHERE list_id = ? ORDER BY seq, title", array($list_id));
2752 $s .= "<table cellspacing='0' cellpadding='0'>";
2753 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
2754 $option_id = $lrow['option_id'];
2755 $option_id_esc = text($option_id);
2756 if ($count % $cols == 0) {
2757 if ($count) {
2758 $s .= "</tr>";
2760 $s .= "<tr>";
2762 $checked = ((strlen($currvalue) == 0 && $lrow['is_default']) ||
2763 (strlen($currvalue) > 0 && $option_id == $currvalue));
2764 if (!$show_unchecked && $checked) {
2765 $s .= "<td nowrap>";
2766 $s .= text(xl_list_label($lrow['title'])) . '&nbsp;&nbsp;';
2767 $s .= "</td>";
2768 } elseif ($show_unchecked) {
2769 $s .= "<td nowrap>";
2770 $s .= $checked ? '&#9745;' : '&#9744;';
2771 $s .= '&nbsp;' . text(xl_list_label($lrow['title'])) . '&nbsp;&nbsp;';
2772 $s .= "</td>";
2775 if ($count) {
2776 $s .= "</tr>";
2778 $s .= "</table>";
2779 } elseif ($data_type == 28 || $data_type == 32) { // special case for history of lifestyle status; 3 radio buttons
2780 // and a date text field:
2781 // VicarePlus :: A selection list for smoking status.
2782 $tmp = explode('|', $currvalue);
2783 switch (count($tmp)) {
2784 case "4":
2785 $resnote = $tmp[0];
2786 $restype = $tmp[1];
2787 $resdate = oeFormatShortDate($tmp[2]);
2788 $reslist = $tmp[3];
2789 break;
2790 case "3":
2791 $resnote = $tmp[0];
2792 $restype = $tmp[1];
2793 $resdate = oeFormatShortDate($tmp[2]);
2794 $reslist = '';
2795 break;
2796 case "2":
2797 $resnote = $tmp[0];
2798 $restype = $tmp[1];
2799 $resdate = "";
2800 $reslist = '';
2801 break;
2802 case "1":
2803 $resnote = $tmp[0];
2804 $resdate = $restype = "";
2805 $reslist = '';
2806 break;
2807 default:
2808 $restype = $resdate = $resnote = "";
2809 $reslist = '';
2810 break;
2813 $s .= "<table class='table'>";
2815 $s .= "<tr>";
2816 $res = "";
2817 if ($restype == "current" . $field_id) {
2818 $res = xl('Current');
2821 if ($restype == "quit" . $field_id) {
2822 $res = xl('Quit');
2825 if ($restype == "never" . $field_id) {
2826 $res = xl('Never');
2829 if ($restype == "not_applicable" . $field_id) {
2830 $res = xl('N/A');
2833 // $s .= "<td class='text align-top'>$restype</td></tr>";
2834 // $s .= "<td class='text align-top'>$resnote</td></tr>";
2835 if ($data_type == 28) {
2836 if (!empty($resnote)) {
2837 $s .= "<td class='text align-top'>" . htmlspecialchars($resnote, ENT_NOQUOTES) . "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>";
2839 } elseif ($data_type == 32) { //VicarePlus :: Tobacco field has a listbox, text box, date field and 3 radio buttons.
2840 // changes on 5-jun-2k14 (regarding 'Smoking Status - display SNOMED code description')
2841 $smoke_codes = getSmokeCodes();
2842 if (!empty($reslist)) {
2843 if ($smoke_codes[$reslist] != "") {
2844 $code_desc = "( " . $smoke_codes[$reslist] . " )";
2847 $s .= "<td class='text align-top'>" . generate_display_field(array('data_type' => '1','list_id' => $list_id), $reslist) . "&nbsp;" . text($code_desc) . "&nbsp;&nbsp;&nbsp;&nbsp;</td>";
2850 if (!empty($resnote)) {
2851 $s .= "<td class='text align-top'>" . htmlspecialchars($resnote, ENT_NOQUOTES) . "&nbsp;&nbsp;</td>";
2855 if (!empty($res)) {
2856 $s .= "<td class='text align-top'><strong>" . htmlspecialchars(xl('Status'), ENT_NOQUOTES) . "</strong>:&nbsp;" . htmlspecialchars($res, ENT_NOQUOTES) . "&nbsp;</td>";
2859 if ($restype == "quit" . $field_id) {
2860 $s .= "<td class='text align-top'>" . htmlspecialchars($resdate, ENT_NOQUOTES) . "&nbsp;</td>";
2863 $s .= "</tr>";
2864 $s .= "</table>";
2865 } elseif ($data_type == 31) { // static text. read-only, of course.
2866 $s .= parse_static_text($frow);
2867 } elseif ($data_type == 34) {
2868 $arr = explode("|*|*|*|", $currvalue);
2869 for ($i = 0; $i < sizeof($arr); $i++) {
2870 $s .= $arr[$i];
2872 } elseif ($data_type == 35) { // facility
2873 $urow = $facilityService->getById($currvalue);
2874 $s = htmlspecialchars($urow['name'] ?? '', ENT_NOQUOTES);
2875 } elseif ($data_type == 36 || $data_type == 33) { // Multi select. Supports backup lists
2876 $values_array = explode("|", $currvalue);
2877 $i = 0;
2878 foreach ($values_array as $value) {
2879 $lrow = sqlQuery("SELECT title FROM list_options " .
2880 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($list_id,$value));
2881 if ($lrow == 0 && !empty($backup_list)) {
2882 //use back up list
2883 $lrow = sqlQuery("SELECT title FROM list_options " .
2884 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list,$value));
2887 $title = $lrow['title'] ?? '';
2888 if ($i > 0) {
2889 $s = $s . ", " . text(xl_list_label($title));
2890 } else {
2891 $s = text(xl_list_label($title));
2894 $i++;
2897 // A set of lab test results; Gestation, 3 radio buttons, test value, notes field:
2898 } elseif ($data_type == 37) {
2899 $s .= genLabResults($frow, $currvalue, 2, '');
2900 } elseif ($data_type == 40) { // Image from canvas drawing
2901 if (empty($currvalue)) {
2902 if (preg_match('/\\bimage=([a-zA-Z0-9._-]*)/', $frow['description'], $matches)) {
2903 $currvalue = $GLOBALS['web_root'] . '/sites/' . $_SESSION['site_id'] . '/images/' . $matches[1];
2906 if ($currvalue) {
2907 $s .= "<img src='" . attr($currvalue) . "'>";
2909 } elseif ($data_type == 41 || $data_type == 42) {
2910 if ($currvalue) {
2911 $s .= "<img class='w-auto' style='height: 70px;' src='" . attr($currvalue) . "'>";
2913 } elseif ($data_type == 44 || $data_type == 45) { // Multiple select facility and provider
2914 $values_array = explode("|", $currvalue);
2915 $i = 0;
2916 foreach ($values_array as $value) {
2917 if ($data_type == 44) {
2918 $lrow = sqlQuery("SELECT name as name FROM facility WHERE id = ?", array($value));
2920 if ($data_type == 45) {
2921 $lrow = sqlQuery("SELECT CONCAT(fname,' ',lname) as name FROM users WHERE id = ?", array($value));
2923 if ($i > 0) {
2924 $s = $s . ", " . htmlspecialchars($lrow['name'], ENT_NOQUOTES);
2925 } else {
2926 $s = text($lrow['name'] ?? '');
2928 $i++;
2931 // Patient selector field.
2932 } elseif ($data_type == 51) {
2933 if (!empty($currvalue)) {
2934 $s .= text(getPatientDescription($currvalue));
2936 } elseif ($data_type == 52) {
2937 global $pid;
2938 $patientService = new PatientService();
2939 $rows = $patientService->getPatientNameHistory($pid);
2940 $i = 0;
2941 foreach ($rows as $row) {
2942 // name escaped in fetch
2943 if ($i > 0) {
2944 $s .= ", " . $row['formatted_name'];
2945 } else {
2946 $s = $row['formatted_name'] ?? '';
2948 $i++;
2950 // now that we've concatenated everything, let's escape it.
2951 $s = text($s);
2952 } elseif ($data_type == 53) {
2953 $service = new EncounterService();
2954 if (!empty($currvalue)) {
2955 $encounterResult = $service->getEncounterById($currvalue);
2956 if (!empty($encounterResult) && $encounterResult->hasData()) {
2957 $encounter = reset($encounterResult->getData());
2958 $s = text($encounter['date'] ?? '');
2961 } elseif ($data_type == 54) {
2962 include "templates/address_list_display.php";
2965 return $s;
2968 // Generate plain text versions of selected LBF field types.
2969 // Currently used by interface/patient_file/download_template.php and interface/main/finder/dynamic_finder_ajax.php.
2970 // More field types might need to be supported here in the future.
2972 function generate_plaintext_field($frow, $currvalue)
2974 global $ISSUE_TYPES;
2976 $data_type = $frow['data_type'];
2977 $field_id = isset($frow['field_id']) ? $frow['field_id'] : null;
2978 $list_id = $frow['list_id'];
2979 $backup_list = $frow['backup_list'] ?? null;
2980 $edit_options = $frow['edit_options'] ?? null;
2981 $s = '';
2983 // generic selection list or the generic selection list with add on the fly
2984 // feature, or radio buttons
2985 // Supports backup lists (for datatypes 1,26,43)
2986 if ($data_type == 1 || $data_type == 26 || $data_type == 27 || $data_type == 43 || $data_type == 46) {
2987 if ($data_type == 46) {
2988 // support for single-selection list with comment support
2989 $selectedValues = explode("|", $currvalue);
2990 $currvalue = $selectedValues[0];
2993 $lrow = sqlQuery(
2994 "SELECT title FROM list_options " .
2995 "WHERE list_id = ? AND option_id = ? AND activity = 1",
2996 array($list_id, $currvalue)
2998 $s = xl_list_label($lrow['title'] ?? '');
2999 //if there is no matching value in the corresponding lists check backup list
3000 // only supported in data types 1,26,43
3001 if ($lrow == 0 && !empty($backup_list) && ($data_type == 1 || $data_type == 26 || $data_type == 43 || $data_type == 46)) {
3002 $lrow = sqlQuery("SELECT title FROM list_options " .
3003 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list, $currvalue));
3004 $s = xl_list_label($lrow['title']);
3007 if ($data_type == 46) {
3008 // support for single-selection list with comment support
3009 $resnote = $selectedValues[1] ?? null;
3010 if (!empty($resnote)) {
3011 $s .= " (" . $resnote . ")";
3014 } elseif ($data_type == 2 || $data_type == 3 || $data_type == 15) { // simple or long text field
3015 $s = $currvalue;
3016 } elseif ($data_type == 4) { // date
3017 $modtmp = isOption($edit_options, 'F') === false ? 0 : 1;
3018 if (!$modtmp) {
3019 $s = text(oeFormatShortDate($currvalue));
3020 } else {
3021 $s = text(oeFormatDateTime($currvalue));
3023 $description = (isset($frow['description']) ? htmlspecialchars(xl_layout_label($frow['description']), ENT_QUOTES) : '');
3024 $age_asof_date = '';
3025 // Optional display of age or gestational age.
3026 $tmp = optionalAge($frow, $currvalue, $age_asof_date, $description);
3027 if ($tmp) {
3028 $s .= ' ' . $tmp;
3030 } elseif ($data_type == 10 || $data_type == 11) { // provider
3031 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
3032 "WHERE id = ?", array($currvalue));
3033 $s = ucwords($urow['fname'] . " " . $urow['lname']);
3034 } elseif ($data_type == 12) { // pharmacy list
3035 $pres = get_pharmacies();
3036 while ($prow = sqlFetchArray($pres)) {
3037 $key = $prow['id'];
3038 if ($currvalue == $key) {
3039 $s .= $prow['name'] . ' ' . $prow['area_code'] . '-' .
3040 $prow['prefix'] . '-' . $prow['number'] . ' / ' .
3041 $prow['line1'] . ' / ' . $prow['city'];
3044 } elseif ($data_type == 14) { // address book
3045 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
3046 "WHERE id = ?", array($currvalue));
3047 $uname = $urow['lname'];
3048 if ($urow['fname']) {
3049 $uname .= ", " . $urow['fname'];
3052 $s = $uname;
3053 } elseif ($data_type == 16) { // insurance company list
3054 $insprovs = getInsuranceProviders();
3055 foreach ($insprovs as $key => $ipname) {
3056 if ($currvalue == $key) {
3057 $s .= $ipname;
3060 } elseif ($data_type == 17) { // issue type
3061 foreach ($ISSUE_TYPES as $key => $value) {
3062 if ($currvalue == $key) {
3063 $s .= $value[1];
3066 } elseif ($data_type == 18) { // visit category
3067 $crow = sqlQuery(
3068 "SELECT pc_catid, pc_catname " .
3069 "FROM openemr_postcalendar_categories WHERE pc_catid = ?",
3070 array($currvalue)
3072 $s = $crow['pc_catname'];
3073 } elseif ($data_type == 21) { // a set of labeled checkboxes
3074 if (!$list_id) {
3075 $s .= $currvalue ? xlt('Yes') : xlt('No');
3076 } else {
3077 $avalue = explode('|', $currvalue);
3078 $lres = sqlStatement("SELECT * FROM list_options " .
3079 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
3080 $count = 0;
3081 while ($lrow = sqlFetchArray($lres)) {
3082 $option_id = $lrow['option_id'];
3083 if (in_array($option_id, $avalue)) {
3084 if ($count++) {
3085 $s .= "; ";
3087 $s .= xl_list_label($lrow['title']);
3091 } elseif ($data_type == 22) { // a set of labeled text input fields
3092 $tmp = explode('|', $currvalue);
3093 $avalue = array();
3094 foreach ($tmp as $value) {
3095 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
3096 $avalue[$matches[1]] = $matches[2];
3100 $lres = sqlStatement("SELECT * FROM list_options " .
3101 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
3102 while ($lrow = sqlFetchArray($lres)) {
3103 $option_id = $lrow['option_id'];
3104 if (empty($avalue[$option_id])) {
3105 continue;
3108 if ($s !== '') {
3109 $s .= '; ';
3112 $s .= xl_list_label($lrow['title']) . ': ';
3113 $s .= $avalue[$option_id];
3115 } elseif ($data_type == 23) { // A set of exam results; 3 radio buttons and a text field.
3116 // This shows abnormal results only.
3117 $tmp = explode('|', $currvalue);
3118 $avalue = array();
3119 foreach ($tmp as $value) {
3120 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
3121 $avalue[$matches[1]] = $matches[2];
3125 $lres = sqlStatement("SELECT * FROM list_options " .
3126 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
3127 while ($lrow = sqlFetchArray($lres)) {
3128 $option_id = $lrow['option_id'];
3129 $restype = substr($avalue[$option_id], 0, 1);
3130 $resnote = substr($avalue[$option_id], 2);
3131 if (empty($restype) && empty($resnote)) {
3132 continue;
3135 if ($restype != '2') {
3136 continue; // show abnormal results only
3139 if ($s !== '') {
3140 $s .= '; ';
3143 $s .= xl_list_label($lrow['title']);
3144 if (!empty($resnote)) {
3145 $s .= ': ' . $resnote;
3148 } elseif ($data_type == 24) { // the list of active allergies for the current patient
3149 $query = "SELECT title, comments FROM lists WHERE " .
3150 "pid = ? AND type = 'allergy' AND enddate IS NULL " .
3151 "ORDER BY begdate";
3152 $lres = sqlStatement($query, array($GLOBALS['pid']));
3153 $count = 0;
3154 while ($lrow = sqlFetchArray($lres)) {
3155 if ($count++) {
3156 $s .= "; ";
3159 $s .= $lrow['title'];
3160 if ($lrow['comments']) {
3161 $s .= ' (' . $lrow['comments'] . ')';
3164 } elseif ($data_type == 25) { // a set of labeled checkboxes, each with a text field:
3165 $tmp = explode('|', $currvalue);
3166 $avalue = array();
3167 foreach ($tmp as $value) {
3168 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
3169 $avalue[$matches[1]] = $matches[2];
3173 $lres = sqlStatement("SELECT * FROM list_options " .
3174 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
3175 while ($lrow = sqlFetchArray($lres)) {
3176 $option_id = $lrow['option_id'];
3177 $restype = substr($avalue[$option_id], 0, 1);
3178 $resnote = substr($avalue[$option_id], 2);
3179 if (empty($restype) && empty($resnote)) {
3180 continue;
3183 if ($s !== '') {
3184 $s .= '; ';
3187 $s .= xl_list_label($lrow['title']);
3188 $restype = $restype ? xl('Yes') : xl('No');
3189 $s .= $restype;
3190 if ($resnote) {
3191 $s .= ' ' . $resnote;
3194 } elseif ($data_type == 28 || $data_type == 32) { // special case for history of lifestyle status; 3 radio buttons and a date text field:
3195 // VicarePlus :: A selection list for smoking status.
3196 $tmp = explode('|', $currvalue);
3197 $resnote = count($tmp) > 0 ? $tmp[0] : '';
3198 $restype = count($tmp) > 1 ? $tmp[1] : '';
3199 $resdate = count($tmp) > 2 ? oeFormatShortDate($tmp[2]) : '';
3200 $reslist = count($tmp) > 3 ? $tmp[3] : '';
3201 $res = "";
3202 if ($restype == "current" . $field_id) {
3203 $res = xl('Current');
3206 if ($restype == "quit" . $field_id) {
3207 $res = xl('Quit');
3210 if ($restype == "never" . $field_id) {
3211 $res = xl('Never');
3214 if ($restype == "not_applicable" . $field_id) {
3215 $res = xl('N/A');
3218 if ($data_type == 28) {
3219 if (!empty($resnote)) {
3220 $s .= $resnote;
3222 } elseif ($data_type == 32) { // Tobacco field has a listbox, text box, date field and 3 radio buttons.
3223 if (!empty($reslist)) {
3224 $s .= generate_plaintext_field(array('data_type' => '1','list_id' => $list_id), $reslist);
3227 if (!empty($resnote)) {
3228 $s .= ' ' . $resnote;
3232 if (!empty($res)) {
3233 if ($s !== '') {
3234 $s .= ' ';
3237 $s .= xl('Status') . ' ' . $res;
3240 if ($restype == "quit" . $field_id) {
3241 if ($s !== '') {
3242 $s .= ' ';
3245 $s .= $resdate;
3247 } elseif ($data_type == 35) { // Facility, so facility can be listed in plain-text, as in patient finder column
3248 $facilityService = new FacilityService();
3249 $facility = $facilityService->getById($currvalue);
3250 $s = $facility['name'];
3251 } elseif ($data_type == 36 || $data_type == 33) { // Multi select. Supports backup lists
3252 $values_array = explode("|", $currvalue);
3254 $i = 0;
3255 foreach ($values_array as $value) {
3256 $lrow = sqlQuery("SELECT title FROM list_options " .
3257 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($list_id,$value));
3259 if ($lrow == 0 && !empty($backup_list)) {
3260 //use back up list
3261 $lrow = sqlQuery("SELECT title FROM list_options " .
3262 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list,$value));
3265 if ($i > 0) {
3266 $s = $s . ", " . xl_list_label($lrow['title']);
3267 } else {
3268 $s = xl_list_label($lrow['title']);
3271 $i++;
3274 // A set of lab test results; Gestation, 3 radio buttons, test value, notes field:
3275 } elseif ($data_type == 37) {
3276 $s .= genLabResults($frow, $currvalue, 3, '');
3277 } elseif ($data_type == 44 || $data_type == 45) {
3278 $values_array = explode("|", $currvalue);
3280 $i = 0;
3281 foreach ($values_array as $value) {
3282 if ($data_type == 44) {
3283 $lrow = sqlQuery("SELECT name as name FROM facility WHERE id = ?", array($value));
3285 if ($data_type == 45) {
3286 $lrow = sqlQuery("SELECT CONCAT(fname,' ',lname) as name FROM users WHERE id = ?", array($value));
3289 if ($i > 0) {
3290 $s = $s . ", " . $lrow['name'];
3291 } else {
3292 $s = $lrow['name'];
3295 $i++;
3298 // Patient selector field.
3299 } elseif ($data_type == 51) {
3300 if (!empty($currvalue)) {
3301 $s .= getPatientDescription($currvalue);
3305 return $s;
3308 $CPR = 4; // cells per row of generic data
3309 $last_group = '';
3310 $cell_count = 0;
3311 $item_count = 0;
3313 function disp_end_cell()
3315 global $item_count, $cell_count;
3316 if ($item_count > 0) {
3317 echo "</td>";
3318 $item_count = 0;
3322 function disp_end_row()
3324 global $cell_count, $CPR;
3325 disp_end_cell();
3326 if ($cell_count > 0) {
3327 for (; $cell_count < $CPR; ++$cell_count) {
3328 echo "<td></td>";
3331 echo "</tr>\n";
3332 $cell_count = 0;
3336 function disp_end_group()
3338 global $last_group;
3339 if (strlen($last_group) > 0) {
3340 disp_end_row();
3344 // Bootstrapped versions of disp_end_* functions:
3346 function bs_disp_end_cell()
3348 global $item_count;
3349 if ($item_count > 0) {
3350 echo "</div>"; // end BS column
3351 $item_count = 0;
3355 function bs_disp_end_row()
3357 global $cell_count, $CPR, $BS_COL_CLASS;
3358 bs_disp_end_cell();
3359 if ($cell_count > 0 && $cell_count < $CPR) {
3360 // Create a cell occupying the remaining bootstrap columns.
3361 // BS columns will be less than 12 if $CPR is not 2, 3, 4, 6 or 12.
3362 $bs_cols_remaining = ($CPR - $cell_count) * intval(12 / $CPR);
3363 echo "<div class='$BS_COL_CLASS-$bs_cols_remaining'></div>";
3365 if ($cell_count > 0) {
3366 echo "</div><!-- End BS row -->\n";
3367 $cell_count = 0;
3371 function bs_disp_end_group()
3373 global $last_group;
3374 if (strlen($last_group) > 0) {
3375 bs_disp_end_row();
3381 function getPatientDescription($pid)
3383 $prow = sqlQuery("SELECT lname, fname FROM patient_data WHERE pid = ?", array($pid));
3384 if ($prow) {
3385 return $prow['lname'] . ", " . $prow['fname'] . " ($pid)";
3387 return xl('Unknown') . " ($pid)";
3390 // Accumulate action conditions into a JSON expression for the browser side.
3391 function accumActionConditions(&$frow, &$condition_str)
3393 $field_id = $frow['field_id'];
3394 $conditions = empty($frow['conditions']) ? array() : unserialize($frow['conditions'], ['allowed_classes' => false]);
3395 $action = 'skip';
3396 foreach ($conditions as $key => $condition) {
3397 if ($key === 'action') {
3398 // If specified this should be the first array item.
3399 if ($condition) {
3400 $action = $condition;
3402 continue;
3404 if (empty($condition['id'])) {
3405 continue;
3407 $andor = empty($condition['andor']) ? '' : $condition['andor'];
3408 if ($condition_str) {
3409 $condition_str .= ",\n";
3411 $condition_str .= "{" .
3412 "target:" . js_escape($field_id) . ", " .
3413 "action:" . js_escape($action) . ", " .
3414 "id:" . js_escape($condition['id']) . ", " .
3415 "itemid:" . js_escape($condition['itemid']) . ", " .
3416 "operator:" . js_escape($condition['operator']) . ", " .
3417 "value:" . js_escape($condition['value']) . ", ";
3418 if ($frow['data_type'] == 15 && strpos($frow['edit_options'], '2') !== false) {
3419 // For billing codes handle requirement to display its description.
3420 $tmp = explode('=', $action, 2);
3421 if (!empty($tmp[1])) {
3422 $condition_str .= "valdesc:" . js_escape(getCodeDescription($tmp[1])) . ", ";
3425 $condition_str .= "andor:" . js_escape($andor) . "}";
3429 function getCodeDescription($codestring, $defaulttype = 'ICD10')
3431 if ($codestring === '') {
3432 return '';
3434 list($ctype, $code) = explode(':', $codestring);
3435 if (empty($code)) {
3436 $code = $ctype;
3437 $ctype = $defaulttype;
3439 $desc = lookup_code_descriptions("$ctype:$code");
3440 if (!empty($desc)) {
3441 return $desc;
3442 } else {
3443 return $codestring;
3447 // This checks if the given field with the given value should have an action applied.
3448 // Originally the only action was skip, but now you can also set the field to a
3449 // specified value, or "skip and otherwise set a value".
3450 // It somewhat mirrors the checkSkipConditions function in options.js.php.
3451 // If you use this for multiple layouts in the same script, you should
3452 // clear $sk_layout_items before each layout.
3453 function isSkipped(&$frow, $currvalue)
3455 global $sk_layout_items;
3457 // Accumulate an array of the encountered fields and their values.
3458 // It is assumed that fields appear before they are tested by another field.
3459 // TBD: Bad assumption?
3460 $field_id = $frow['field_id'];
3461 if (!is_array($sk_layout_items)) {
3462 $sk_layout_items = array();
3464 $sk_layout_items[$field_id] = array('row' => $frow, 'value' => $currvalue);
3466 if (empty($frow['conditions'])) {
3467 return false;
3470 $skiprows = unserialize($frow['conditions'], ['allowed_classes' => false]);
3471 $prevandor = '';
3472 $prevcond = false;
3473 $datatype = $frow['data_type'];
3474 $action = 'skip'; // default action if none specified
3476 foreach ($skiprows as $key => $skiprow) {
3477 // id referenced field id
3478 // itemid referenced array key if applicable
3479 // operator "eq", "ne", "se" or "ns"
3480 // value if eq or ne, some string to compare with
3481 // andor "and", "or" or empty
3483 if ($key === 'action') {
3484 // Action value is a string. It can be "skip", or "value=" or "hsval=" followed by a value.
3485 $action = $skiprow;
3486 continue;
3489 if (empty($skiprow['id'])) {
3490 continue;
3493 $id = $skiprow['id'];
3494 if (!isset($sk_layout_items[$id])) {
3495 error_log("Function isSkipped() cannot find skip source field '" . errorLogEscape($id) . "'.");
3496 continue;
3498 $itemid = $skiprow['itemid'];
3499 $operator = $skiprow['operator'];
3500 $skipval = $skiprow['value'];
3501 $srcvalue = $sk_layout_items[$id]['value'];
3502 $src_datatype = $sk_layout_items[$id]['row']['data_type'];
3503 $src_list_id = $sk_layout_items[$id]['row']['list_id'];
3505 // Some data types use itemid and we have to dig for their value.
3506 if ($src_datatype == 21 && $src_list_id) { // array of checkboxes
3507 $tmp = explode('|', $srcvalue);
3508 $srcvalue = in_array($itemid, $tmp);
3509 } elseif ($src_datatype == 22 || $src_datatype == 23 || $src_datatype == 25) {
3510 $tmp = explode('|', $srcvalue);
3511 $srcvalue = '';
3512 foreach ($tmp as $tmp2) {
3513 if (strpos($tmp2, "$itemid:") === 0) {
3514 if ($datatype == 22) {
3515 $srcvalue = substr($tmp2, strlen($itemid) + 1);
3516 } else {
3517 $srcvalue = substr($tmp2, strlen($itemid) + 1, 1);
3523 // Compute the result of the test for this condition row.
3524 // PHP's looseness with variable type conversion helps us here.
3525 $condition = false;
3526 if ($operator == 'eq') {
3527 $condition = $srcvalue == $skipval;
3528 } elseif ($operator == 'ne') {
3529 $condition = $srcvalue != $skipval;
3530 } elseif ($operator == 'se') {
3531 $condition = $srcvalue == true;
3532 } elseif ($operator == 'ns') {
3533 $condition = $srcvalue != true;
3534 } else {
3535 error_log("Unknown skip operator '" . errorLogEscape($operator) . "' for field '" . errorLogEscape($field_id) . "'.");
3538 // Logic to accumulate multiple conditions for the same target.
3539 if ($prevandor == 'and') {
3540 $condition = $condition && $prevcond;
3541 } elseif ($prevandor == 'or') {
3542 $condition = $condition || $prevcond;
3544 $prevandor = $skiprow['andor'];
3545 $prevcond = $condition;
3548 if (substr($action, 0, 6) == 'hsval=') {
3549 return $prevcond ? 'skip' : ('value=' . substr($action, 6));
3551 return $prevcond ? $action : '';
3554 // Load array of names of the given layout and its groups.
3555 function getLayoutProperties($formtype, &$grparr, $sel = "grp_title", $limit = null)
3557 if ($sel != '*' && strpos($sel, 'grp_group_id') === false) {
3558 $sel = "grp_group_id, $sel";
3560 $gres = sqlStatement("SELECT $sel FROM layout_group_properties WHERE grp_form_id = ? " .
3561 " ORDER BY grp_group_id " .
3562 ($limit ? "LIMIT " . escape_limit($limit) : ""), array($formtype));
3563 while ($grow = sqlFetchArray($gres)) {
3564 // TBD: Remove this after grp_init_open column is implemented.
3565 if ($sel == '*' && !isset($grow['grp_init_open'])) {
3566 $tmprow = sqlQuery(
3567 "SELECT form_id FROM layout_options " .
3568 "WHERE form_id = ? AND group_id LIKE ? AND uor > 0 AND edit_options LIKE '%I%' " .
3569 "LIMIT 1",
3570 array($formtype, $grow['grp_group_id'] . '%')
3572 $grow['grp_init_open'] = !empty($tmprow['form_id']);
3574 $grparr[$grow['grp_group_id']] = $grow;
3578 function display_layout_rows($formtype, $result1, $result2 = '')
3580 global $item_count, $cell_count, $last_group, $CPR;
3582 if ('HIS' == $formtype) {
3583 $formtype .= '%'; // TBD: DEM also?
3585 $pres = sqlStatement(
3586 "SELECT grp_form_id, grp_seq, grp_title " .
3587 "FROM layout_group_properties " .
3588 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
3589 "ORDER BY grp_seq, grp_title, grp_form_id",
3590 array("$formtype")
3592 while ($prow = sqlFetchArray($pres)) {
3593 $formtype = $prow['grp_form_id'];
3594 $last_group = '';
3595 $cell_count = 0;
3596 $item_count = 0;
3598 $grparr = array();
3599 getLayoutProperties($formtype, $grparr, '*');
3601 $TOPCPR = empty($grparr['']['grp_columns']) ? 4 : $grparr['']['grp_columns'];
3603 $fres = sqlStatement("SELECT * FROM layout_options " .
3604 "WHERE form_id = ? AND uor > 0 " .
3605 "ORDER BY group_id, seq", array($formtype));
3607 while ($frow = sqlFetchArray($fres)) {
3608 $this_group = $frow['group_id'];
3609 $titlecols = $frow['titlecols'];
3610 $datacols = $frow['datacols'];
3611 $data_type = $frow['data_type'];
3612 $field_id = $frow['field_id'];
3613 $list_id = $frow['list_id'];
3614 $currvalue = '';
3615 $jump_new_row = isOption($frow['edit_options'], 'J');
3616 $prepend_blank_row = isOption($frow['edit_options'], 'K');
3617 $portal_exclude = (!empty($_SESSION["patient_portal_onsite_two"]) && isOption($frow['edit_options'], 'EP')) ?? null;
3618 $span_col_row = isOption($frow['edit_options'], 'SP');
3620 if (!empty($portal_exclude)) {
3621 continue;
3624 $CPR = empty($grparr[$this_group]['grp_columns']) ? $TOPCPR : $grparr[$this_group]['grp_columns'];
3626 if ($formtype == 'DEM') {
3627 if (strpos($field_id, 'em_') === 0) {
3628 // Skip employer related fields, if it's disabled.
3629 if ($GLOBALS['omit_employers']) {
3630 continue;
3633 $tmp = substr($field_id, 3);
3634 if (isset($result2[$tmp])) {
3635 $currvalue = $result2[$tmp];
3637 } else {
3638 if (isset($result1[$field_id])) {
3639 $currvalue = $result1[$field_id];
3642 } else {
3643 if (isset($result1[$field_id])) {
3644 $currvalue = $result1[$field_id];
3648 // Handle a data category (group) change.
3649 if (strcmp($this_group, $last_group) != 0) {
3650 $group_name = $grparr[$this_group]['grp_title'];
3651 // totally skip generating the employer category, if it's disabled.
3652 if ($group_name === 'Employer' && $GLOBALS['omit_employers']) {
3653 continue;
3656 disp_end_group();
3657 $last_group = $this_group;
3660 // filter out all the empty field data from the patient report.
3661 if (!empty($currvalue) && !($currvalue == '0000-00-00 00:00:00')) {
3662 // Handle starting of a new row.
3663 if (($titlecols > 0 && $cell_count >= $CPR) || $cell_count == 0 || $prepend_blank_row || $jump_new_row) {
3664 disp_end_row();
3665 if ($prepend_blank_row) {
3666 echo "<tr><td class='label' colspan='" . ($CPR + 1) . "'>&nbsp;</td></tr>\n";
3668 echo "<tr>";
3669 if ($group_name) {
3670 echo "<td class='groupname'>";
3671 echo text(xl_layout_label($group_name));
3672 $group_name = '';
3673 } else {
3674 echo "<td class='align-top'>&nbsp;";
3677 echo "</td>";
3680 if ($item_count == 0 && $titlecols == 0) {
3681 $titlecols = 1;
3684 // Handle starting of a new label cell.
3685 if ($titlecols > 0 || $span_col_row) {
3686 disp_end_cell();
3687 $titlecols = $span_col_row ? 0 : $titlecols;
3688 $titlecols_esc = htmlspecialchars($titlecols, ENT_QUOTES);
3689 if (!$span_col_row) {
3690 echo "<td class='label_custom' colspan='$titlecols_esc' ";
3691 echo ">";
3693 $cell_count += $titlecols;
3696 ++$item_count;
3698 // Prevent title write if span entire row.
3699 if (!$span_col_row) {
3700 // Added 5-09 by BM - Translate label if applicable
3701 if ($frow['title']) {
3702 $tmp = xl_layout_label($frow['title']);
3703 echo text($tmp);
3704 // Append colon only if label does not end with punctuation.
3705 if (strpos('?!.,:-=', substr($tmp, -1, 1)) === false) {
3706 echo ':';
3708 } else {
3709 echo "&nbsp;";
3712 // Handle starting of a new data cell.
3713 if ($datacols > 0) {
3714 disp_end_cell();
3715 $datacols = $span_col_row ? $CPR : $datacols;
3716 $datacols_esc = htmlspecialchars($datacols, ENT_QUOTES);
3717 echo "<td class='text data' colspan='$datacols_esc'";
3718 echo ">";
3719 $cell_count += $datacols;
3722 ++$item_count;
3723 echo generate_display_field($frow, $currvalue);
3726 disp_end_group();
3727 } // End this layout, there may be more in the case of history.
3730 // This generates the tabs for a form.
3732 function display_layout_tabs($formtype, $result1, $result2 = '')
3734 global $item_count, $cell_count, $last_group, $CPR;
3736 if ('HIS' == $formtype) {
3737 $formtype .= '%'; // TBD: DEM also?
3739 $pres = sqlStatement(
3740 "SELECT grp_form_id, grp_seq, grp_title " .
3741 "FROM layout_group_properties " .
3742 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
3743 "ORDER BY grp_seq, grp_title, grp_form_id",
3744 array("$formtype")
3746 $first = true;
3747 while ($prow = sqlFetchArray($pres)) {
3748 $formtype = $prow['grp_form_id'];
3749 $last_group = '';
3750 $cell_count = 0;
3751 $item_count = 0;
3753 $grparr = array();
3754 getLayoutProperties($formtype, $grparr);
3756 $fres = sqlStatement("SELECT distinct group_id FROM layout_options " .
3757 "WHERE form_id = ? AND uor > 0 " .
3758 "ORDER BY group_id", array($formtype));
3760 $prev_group = '';
3761 while ($frow = sqlFetchArray($fres)) {
3762 $this_group = $frow['group_id'];
3763 if (substr($prev_group, 0, 1) === substr($this_group, 0, 1)) {
3764 // Skip sub-groups, they will not start a new tab.
3765 continue;
3767 $prev_group = $this_group;
3768 $group_name = $grparr[$this_group]['grp_title'];
3769 if ($group_name === 'Employer' && $GLOBALS['omit_employers']) {
3770 continue;
3773 <li <?php echo $first ? 'class="current"' : '' ?>>
3774 <a href="#" id="header_tab_<?php echo attr($group_name); ?>">
3775 <?php echo text(xl_layout_label($group_name)); ?></a>
3776 </li>
3777 <?php
3778 $first = false;
3780 } // End this layout, there may be more in the case of history.
3783 // This generates the tab contents of the display version of a form.
3785 function display_layout_tabs_data($formtype, $result1, $result2 = '')
3787 global $item_count, $cell_count, $last_group, $CPR;
3789 if ('HIS' == $formtype) {
3790 $formtype .= '%'; // TBD: DEM also?
3792 $pres = sqlStatement(
3793 "SELECT grp_form_id, grp_seq, grp_title " .
3794 "FROM layout_group_properties " .
3795 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
3796 "ORDER BY grp_seq, grp_title, grp_form_id",
3797 array("$formtype")
3799 $first = true;
3801 // This loops once per layout. Only Patient History can have multiple layouts.
3802 while ($prow = sqlFetchArray($pres)) {
3803 $formtype = $prow['grp_form_id'];
3804 $last_group = '';
3805 $cell_count = 0;
3806 $item_count = 0;
3808 $grparr = array();
3809 getLayoutProperties($formtype, $grparr, '*');
3811 $TOPCPR = empty($grparr['']['grp_columns']) ? 4 : $grparr['']['grp_columns'];
3813 // By selecting distinct group_id from layout_options we avoid empty groups.
3814 $fres = sqlStatement("SELECT distinct group_id FROM layout_options " .
3815 "WHERE form_id = ? AND uor > 0 " .
3816 "ORDER BY group_id", array($formtype));
3818 $prev_group = '';
3820 // This loops once per group within a given layout.
3821 while ($frow = sqlFetchArray($fres)) {
3822 $this_group = isset($frow['group_id']) ? $frow['group_id'] : "" ;
3824 if ($grparr[$this_group]['grp_title'] === 'Employer' && $GLOBALS['omit_employers']) {
3825 continue;
3827 $CPR = empty($grparr[$this_group]['grp_columns']) ? $TOPCPR : $grparr[$this_group]['grp_columns'];
3828 $subtitle = empty($grparr[$this_group]['grp_subtitle']) ? '' : xl_layout_label($grparr[$this_group]['grp_subtitle']);
3830 $group_fields_query = sqlStatement(
3831 "SELECT * FROM layout_options " .
3832 "WHERE form_id = ? AND uor > 0 AND group_id = ? " .
3833 "ORDER BY seq",
3834 array($formtype, $this_group)
3837 if (substr($this_group, 0, 1) !== substr($prev_group, 0, 1)) {
3838 // Each new top level group gets its own tab div.
3839 if (!$first) {
3840 echo "</div>\n";
3842 echo "<div class='tab" . ($first ? ' current' : '') . "'>\n";
3844 echo "<table border='0' cellpadding='0'>\n";
3846 // This loops once per field within a given group.
3847 while ($group_fields = sqlFetchArray($group_fields_query)) {
3848 $titlecols = $group_fields['titlecols'];
3849 $datacols = $group_fields['datacols'];
3850 $data_type = $group_fields['data_type'];
3851 $field_id = $group_fields['field_id'];
3852 $list_id = $group_fields['list_id'];
3853 $currvalue = '';
3854 $edit_options = $group_fields['edit_options'];
3855 $jump_new_row = isOption($edit_options, 'J');
3856 $prepend_blank_row = isOption($edit_options, 'K');
3857 $span_col_row = isOption($edit_options, 'SP');
3859 if ($formtype == 'DEM') {
3860 if (strpos($field_id, 'em_') === 0) {
3861 // Skip employer related fields, if it's disabled.
3862 if ($GLOBALS['omit_employers']) {
3863 continue;
3866 $tmp = substr($field_id, 3);
3867 if (isset($result2[$tmp])) {
3868 $currvalue = $result2[$tmp];
3870 } else {
3871 if (isset($result1[$field_id])) {
3872 $currvalue = $result1[$field_id];
3875 } else {
3876 if (isset($result1[$field_id])) {
3877 $currvalue = $result1[$field_id];
3881 // Skip this field if action conditions call for that.
3882 // Note this also accumulates info for subsequent skip tests.
3883 $skip_this_field = isSkipped($group_fields, $currvalue) == 'skip';
3885 // Skip this field if its do-not-print option is set.
3886 if (isOption($edit_options, 'X') !== false) {
3887 $skip_this_field = true;
3890 // Handle a data category (group) change.
3891 if (strcmp($this_group, $last_group) != 0) {
3892 $group_name = $grparr[$this_group]['grp_title'];
3893 // totally skip generating the employer category, if it's disabled.
3894 if ($group_name === 'Employer' && $GLOBALS['omit_employers']) {
3895 continue;
3897 $last_group = $this_group;
3900 // Handle starting of a new row.
3901 if (($titlecols > 0 && $cell_count >= $CPR) || $cell_count == 0 || $prepend_blank_row || $jump_new_row) {
3902 disp_end_row();
3903 if ($subtitle) {
3904 // Group subtitle exists and is not displayed yet.
3905 echo "<tr><td class='label' style='background-color: var(--gray300); padding: 4px' colspan='$CPR'>" . text($subtitle) . "</td></tr>\n";
3906 echo "<tr><td class='label' style='height: 5px' colspan='$CPR'></td></tr>\n";
3907 $subtitle = '';
3909 if ($prepend_blank_row) {
3910 echo "<tr><td class='label' style='font-size:25%' colspan='$CPR'>&nbsp;</td></tr>\n";
3912 echo "<tr>";
3915 if ($item_count == 0 && $titlecols == 0) {
3916 $titlecols = 1;
3919 // Handle starting of a new label cell.
3920 if ($titlecols > 0 || $span_col_row) {
3921 disp_end_cell();
3922 $titlecols = $span_col_row ? 0 : $titlecols;
3923 $titlecols_esc = htmlspecialchars($titlecols, ENT_QUOTES);
3924 $field_id_label = 'label_' . $group_fields['field_id'];
3925 if (!$span_col_row) {
3926 echo "<td class='label_custom' colspan='$titlecols_esc' id='" . attr($field_id_label) . "'";
3927 echo ">";
3929 $cell_count += $titlecols;
3932 ++$item_count;
3934 if ($datacols == 0) {
3935 // Data will be in the same cell, so prevent wrapping to a new line.
3936 echo "<span class='text-nowrap mr-2'>";
3939 $field_id_label = 'label_' . $group_fields['field_id'];
3940 if (!$span_col_row) {
3941 echo "<span id='" . attr($field_id_label) . "'>";
3942 if ($skip_this_field) {
3943 // No label because skipping
3944 } elseif ($group_fields['title']) {
3945 $tmp = xl_layout_label($group_fields['title']);
3946 echo text($tmp);
3947 // Append colon only if label does not end with punctuation.
3948 if (!str_contains('?!.,:-=', $tmp[strlen($tmp) - 1])) {
3949 echo ':';
3951 } else {
3952 echo "&nbsp;";
3954 echo "</span>";
3957 // Handle starting of a new data cell.
3958 if ($datacols > 0) {
3959 disp_end_cell();
3960 $datacols = $span_col_row ? $CPR : $datacols;
3961 $datacols_esc = htmlspecialchars($datacols, ENT_QUOTES);
3962 $field_id = 'text_' . $group_fields['field_id'];
3963 echo "<td class='text data' colspan='$datacols_esc' id='" . attr($field_id) . "' data-value='" . attr($currvalue) . "'";
3964 if (!$skip_this_field && $data_type == 3) {
3965 // Textarea gets a light grey border.
3966 echo " style='border: 1px solid var(--gray400)'";
3968 echo ">";
3969 $cell_count += $datacols;
3970 } else {
3971 $field_id = 'text_' . $group_fields['field_id'];
3972 echo "<span id='" . attr($field_id) . "' style='display: none'>" . text($currvalue) . "</span>";
3975 ++$item_count;
3976 if (!$skip_this_field) {
3977 if ($item_count > 1) {
3978 echo "&nbsp;";
3980 echo generate_display_field($group_fields, $currvalue);
3982 if ($datacols == 0) {
3983 // End nowrap
3984 echo "</span> "; // space to allow wrap between spans
3986 } // end field
3988 disp_end_row();
3990 // End table for the group.
3991 echo "</table>\n";
3993 $prev_group = $this_group;
3994 $first = false;
3995 } // End this group.
3996 } // End this layout, there may be more in the case of history.
3998 if (!$first) {
3999 echo "</div>\n";
4003 // This generates the tab contents of the data entry version of a form.
4005 function display_layout_tabs_data_editable($formtype, $result1, $result2 = '')
4007 global $item_count, $cell_count, $last_group, $CPR, $condition_str, $BS_COL_CLASS;
4009 if ('HIS' == $formtype) {
4010 $formtype .= '%'; // TBD: DEM also?
4012 $pres = sqlStatement(
4013 "SELECT grp_form_id, grp_seq, grp_title " .
4014 "FROM layout_group_properties " .
4015 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
4016 "ORDER BY grp_seq, grp_title, grp_form_id",
4017 array("$formtype")
4019 $first = true;
4020 $condition_str = '';
4022 // This loops once per layout. Only Patient History can have multiple layouts.
4023 while ($prow = sqlFetchArray($pres)) {
4024 $formtype = $prow['grp_form_id'];
4025 $last_group = '';
4026 $cell_count = 0;
4027 $item_count = 0;
4029 $grparr = array();
4030 getLayoutProperties($formtype, $grparr, '*');
4032 $TOPCPR = empty($grparr['']['grp_columns']) ? 4 : $grparr['']['grp_columns'];
4034 // Check the children of each top-level group to see if any of them are initially open.
4035 // If not, make the first such child initially open.
4036 foreach ($grparr as $tmprow1) {
4037 if (strlen($tmprow1['grp_group_id']) == 1) {
4038 $got_init_open = false;
4039 $keyfirst = false;
4040 foreach ($grparr as $key2 => $tmprow2) {
4041 if (substr($tmprow2['grp_group_id'], 0, 1) == $tmprow1['grp_group_id'] && strlen($tmprow2['grp_group_id']) == 2) {
4042 if (!$keyfirst) {
4043 $keyfirst = $key2;
4045 if ($tmprow2['grp_init_open']) {
4046 $got_init_open = true;
4050 if (!$got_init_open && $keyfirst) {
4051 $grparr[$keyfirst]['grp_init_open'] = 1;
4056 // Variables $gs_* are context for the group set in the current tab.
4057 $gs_display_style = 'block';
4058 // This string is the active group levels representing the current display state.
4059 // Each leading substring represents an instance of nesting.
4060 // As each new group is encountered, groups will be closed and opened as needed
4061 // until the display state matches the new group.
4062 $gs_group_levels = '';
4064 // By selecting distinct group_id from layout_options we avoid empty groups.
4065 $fres = sqlStatement("SELECT distinct group_id FROM layout_options " .
4066 "WHERE form_id = ? AND uor > 0 " .
4067 "ORDER BY group_id", array($formtype));
4069 // This loops once per group within a given layout.
4070 while ($frow = sqlFetchArray($fres)) {
4071 $this_group = $frow['group_id'];
4072 $group_name = $grparr[$this_group]['grp_title'];
4073 $group_name_esc = text($group_name);
4075 if ($grparr[$this_group]['grp_title'] === 'Employer' && $GLOBALS['omit_employers']) {
4076 continue;
4078 $CPR = empty($grparr[$this_group]['grp_columns']) ? $TOPCPR : $grparr[$this_group]['grp_columns'];
4079 $subtitle = empty($grparr[$this_group]['grp_subtitle']) ? '' : xl_layout_label($grparr[$this_group]['grp_subtitle']);
4081 $group_fields_query = sqlStatement("SELECT * FROM layout_options " .
4082 "WHERE form_id = ? AND uor > 0 AND group_id = ? " .
4083 "ORDER BY seq", array($formtype, $this_group));
4085 $gs_this_levels = $this_group;
4086 // Compute $gs_i as the number of initial matching levels.
4087 $gs_i = 0;
4088 $tmp = min(strlen($gs_this_levels), strlen($gs_group_levels));
4089 while ($gs_i < $tmp && $gs_this_levels[$gs_i] == $gs_group_levels[$gs_i]) {
4090 ++$gs_i;
4093 // Close any groups that we are done with.
4094 while (strlen($gs_group_levels) > $gs_i) {
4095 $gs_group_name = $grparr[$gs_group_levels]['grp_title'];
4096 if (strlen($gs_group_levels) > 1) {
4097 // No div for an empty sub-group name.
4098 if (strlen($gs_group_name)) {
4099 echo "</div>\n";
4101 } else {
4102 // This is the top group level so ending this tab and will start a new one.
4103 echo "</div>\n";
4105 $gs_group_levels = substr($gs_group_levels, 0, -1); // remove last character
4108 // If there are any new groups, open them.
4109 while ($gs_i < strlen($gs_this_levels)) {
4110 $gs_group_levels .= $gs_this_levels[$gs_i++];
4111 $gs_group_name = $grparr[substr($gs_group_levels, 0, $gs_i)]['grp_title'];
4112 $gs_init_open = $grparr[substr($gs_group_levels, 0, $gs_i)]['grp_init_open'];
4113 // Compute a short unique identifier for this group.
4114 $gs_group_seq = "grp-$formtype-$gs_group_levels";
4115 if ($gs_i <= 1) {
4116 // Top level group so new tab.
4117 echo "<div class='tab" . ($first ? ' current' : '') . "' id='tab_$group_name_esc'>\n";
4118 } else {
4119 // Not a new tab so start the group inline.
4120 // If group name is blank, no checkbox or div.
4121 if (strlen($gs_group_name)) {
4122 echo "<br /><span class='bold'><input type='checkbox' name='form_cb_" .
4123 attr($gs_group_seq) . "' value='1' " .
4124 "onclick='return divclick(this," . attr_js('div_' . $gs_group_seq) . ");'";
4125 $gs_display_style = $gs_init_open ? 'block' : 'none';
4126 if ($gs_display_style == 'block') {
4127 echo " checked";
4129 echo " /><b>" . text(xl_layout_label($gs_group_name)) . "</b></span>\n";
4130 echo "<div id='div_" . attr($gs_group_seq) .
4131 "' class='section' style='display:" . attr($gs_display_style) . ";'>\n";
4136 // Each group or subgroup has its own separate container.
4137 $gs_group_table_active = true;
4138 echo "<div class='container-fluid lbfdata'>\n";
4139 if ($subtitle) {
4140 // There is a group subtitle so show it.
4141 $bs_cols = $CPR * intval(12 / $CPR);
4142 echo "<div class='row mb-2'>";
4143 echo "<div class='$BS_COL_CLASS-$bs_cols' style='color:#0000ff'>" . text($subtitle) . "</div>";
4144 echo "</div>\n";
4147 // This loops once per field within a given group.
4148 while ($group_fields = sqlFetchArray($group_fields_query)) {
4149 $titlecols = $group_fields['titlecols'];
4150 $datacols = $group_fields['datacols'];
4151 $data_type = $group_fields['data_type'];
4152 $field_id = $group_fields['field_id'];
4153 $list_id = $group_fields['list_id'];
4154 $backup_list = $group_fields['list_backup_id'];
4155 $currvalue = '';
4156 $action = 'skip';
4157 $jump_new_row = isOption($group_fields['edit_options'], 'J');
4158 $prepend_blank_row = isOption($group_fields['edit_options'], 'K');
4159 $span_col_row = isOption($group_fields['edit_options'], 'SP');
4161 // Accumulate action conditions into a JSON expression for the browser side.
4162 accumActionConditions($group_fields, $condition_str);
4164 if ($formtype == 'DEM') {
4165 if (strpos($field_id, 'em_') === 0) {
4166 // Skip employer related fields, if it's disabled.
4167 if ($GLOBALS['omit_employers']) {
4168 continue;
4171 $tmp = substr($field_id, 3);
4172 if (isset($result2[$tmp])) {
4173 $currvalue = $result2[$tmp];
4175 } else {
4176 if (isset($result1[$field_id])) {
4177 $currvalue = $result1[$field_id];
4180 } else {
4181 if (isset($result1[$field_id])) {
4182 $currvalue = $result1[$field_id];
4186 // Handle a data category (group) change.
4187 if (strcmp($this_group, $last_group) != 0) {
4188 // totally skip generating the employer category, if it's disabled.
4189 if ($group_name === 'Employer' && $GLOBALS['omit_employers']) {
4190 continue;
4193 $last_group = $this_group;
4196 // Handle starting of a new row.
4197 if (($titlecols > 0 && $cell_count >= $CPR) || $cell_count == 0 || $prepend_blank_row || $jump_new_row) {
4198 bs_disp_end_row();
4199 $bs_cols = $CPR * intval(12 / $CPR);
4200 if ($subtitle) {
4201 // Group subtitle exists and is not displayed yet.
4202 echo "<div class='form-row mb-2'>";
4203 echo "<div class='$BS_COL_CLASS-$bs_cols p-2 label' style='background-color: var(--gray300)'>" . text($subtitle) . "</div>";
4204 echo "</div>\n";
4205 $subtitle = '';
4207 if ($prepend_blank_row) {
4208 echo "<div class='form-row'>";
4209 echo "<div class='$BS_COL_CLASS-$bs_cols label' style='font-size: 25%'>&nbsp;</div>";
4210 echo "</div>\n";
4212 echo "<div class='form-row'>";
4215 if ($item_count == 0 && $titlecols == 0) {
4216 $titlecols = 1;
4219 // Handle starting of a new label cell.
4220 if ($titlecols > 0 || $span_col_row) {
4221 bs_disp_end_cell();
4222 $titlecols = $span_col_row ? 0 : $titlecols;
4223 $bs_cols = $titlecols * intval(12 / $CPR);
4224 echo "<div class='$BS_COL_CLASS-$bs_cols pt-1 label_custom' ";
4225 echo "id='label_id_" . attr($field_id) . "'";
4226 echo ">";
4227 $cell_count += $titlecols;
4230 // $item_count is the number of title and data items in the current cell.
4231 ++$item_count;
4233 if ($datacols == 0) {
4234 // Data will be in the same cell, so prevent wrapping to a new line.
4235 echo "<span class='text-nowrap mr-2'>";
4238 if (!$span_col_row) {
4239 if ($group_fields['title']) {
4240 $tmp = xl_layout_label($group_fields['title']);
4241 echo text($tmp);
4242 // Append colon only if label does not end with punctuation.
4243 if (strpos('?!.,:-=', substr($tmp, -1, 1)) === false) {
4244 echo ':';
4246 } else {
4247 echo "&nbsp;";
4251 // Handle starting of a new data cell.
4252 if ($datacols > 0) {
4253 bs_disp_end_cell();
4254 $field_id = 'text_' . $group_fields['field_id'];
4255 $datacols = $span_col_row ? $CPR : $datacols;
4256 $bs_cols = $datacols * intval(12 / $CPR);
4257 echo "<div class='$BS_COL_CLASS-$bs_cols'";
4258 echo " id='value_id_" . attr($field_id) . "'";
4259 echo ">";
4260 $cell_count += $datacols;
4263 ++$item_count;
4264 if ($item_count > 1) {
4265 echo "&nbsp;";
4267 // 'smallform' can be used to add arbitrary CSS classes. Note the leading space.
4268 $group_fields['smallform'] = ' form-control-sm mb-1 mw-100';
4269 echo generate_form_field($group_fields, $currvalue);
4270 if ($datacols == 0) {
4271 // End nowrap
4272 echo "</span> "; // space to allow wrap between spans
4274 } // End of fields for this group.
4276 bs_disp_end_row(); // TBD: Does this belong here?
4277 echo "</div>\n"; // end container-fluid
4278 $first = false;
4279 } // End this group.
4281 // Close any groups still open.
4282 while (strlen($gs_group_levels) > 0) {
4283 $gs_group_name = $grparr[$gs_group_levels]['grp_title'];
4284 if (strlen($gs_group_levels) > 1) {
4285 // No div for an empty sub-group name.
4286 if (strlen($gs_group_name)) {
4287 echo "</div>\n";
4289 } else {
4290 // This is the top group level so ending this tab and will start a new one.
4291 echo "</div>\n";
4293 $gs_group_levels = substr($gs_group_levels, 0, -1); // remove last character
4295 } // End this layout, there may be more in the case of history.
4298 // From the currently posted HTML form, this gets the value of the
4299 // field corresponding to the provided layout_options table row.
4301 function get_layout_form_value($frow, $prefix = 'form_')
4303 $maxlength = empty($frow['max_length']) ? 0 : intval($frow['max_length']);
4304 $data_type = $frow['data_type'];
4305 $field_id = $frow['field_id'];
4306 $value = '';
4307 if (isset($_POST["$prefix$field_id"])) {
4308 if ($data_type == 4) {
4309 $modtmp = isOption($frow['edit_options'], 'F') === false ? 0 : 1;
4310 if (!$modtmp) {
4311 $value = DateToYYYYMMDD($_POST["$prefix$field_id"]);
4312 } else {
4313 $value = DateTimeToYYYYMMDDHHMMSS($_POST["$prefix$field_id"]);
4315 } elseif ($data_type == 21) {
4316 if (!$frow['list_id']) {
4317 if (!empty($_POST["form_$field_id"])) {
4318 $value = xlt('Yes');
4320 } else {
4321 // $_POST["$prefix$field_id"] is an array of checkboxes and its keys
4322 // must be concatenated into a |-separated string.
4323 foreach ($_POST["$prefix$field_id"] as $key => $val) {
4324 if (strlen($value)) {
4325 $value .= '|';
4327 $value .= $key;
4330 } elseif ($data_type == 22) {
4331 // $_POST["$prefix$field_id"] is an array of text fields to be imploded
4332 // into "key:value|key:value|...".
4333 foreach ($_POST["$prefix$field_id"] as $key => $val) {
4334 $val = str_replace('|', ' ', $val);
4335 if (strlen($value)) {
4336 $value .= '|';
4339 $value .= "$key:$val";
4341 } elseif ($data_type == 23) {
4342 // $_POST["$prefix$field_id"] is an array of text fields with companion
4343 // radio buttons to be imploded into "key:n:notes|key:n:notes|...".
4344 foreach ($_POST["$prefix$field_id"] as $key => $val) {
4345 $restype = $_POST["radio_{$field_id}"][$key] ?? null;
4346 if (empty($restype)) {
4347 $restype = '0';
4350 $val = str_replace('|', ' ', $val);
4351 if (strlen($value)) {
4352 $value .= '|';
4355 $value .= "$key:$restype:$val";
4357 } elseif ($data_type == 25) {
4358 // $_POST["$prefix$field_id"] is an array of text fields with companion
4359 // checkboxes to be imploded into "key:n:notes|key:n:notes|...".
4360 foreach ($_POST["$prefix$field_id"] as $key => $val) {
4361 $restype = empty($_POST["check_{$field_id}"][$key]) ? '0' : '1';
4362 $val = str_replace('|', ' ', $val);
4363 if (strlen($value)) {
4364 $value .= '|';
4367 $value .= "$key:$restype:$val";
4369 } elseif ($data_type == 28 || $data_type == 32) {
4370 // $_POST["$prefix$field_id"] is an date text fields with companion
4371 // radio buttons to be imploded into "notes|type|date".
4372 $restype = $_POST["radio_{$field_id}"] ?? '';
4373 if (empty($restype)) {
4374 $restype = '0';
4377 $resdate = DateToYYYYMMDD(str_replace('|', ' ', $_POST["date_$field_id"]));
4378 $resnote = str_replace('|', ' ', $_POST["$prefix$field_id"]);
4379 if ($data_type == 32) {
4380 //VicarePlus :: Smoking status data is imploded into "note|type|date|list".
4381 $reslist = str_replace('|', ' ', $_POST["$prefix$field_id"]);
4382 $res_text_note = str_replace('|', ' ', $_POST["{$prefix}text_$field_id"]);
4383 $value = "$res_text_note|$restype|$resdate|$reslist";
4384 } else {
4385 $value = "$resnote|$restype|$resdate";
4387 } elseif ($data_type == 37) {
4388 // $_POST["form_$field_id"] is an array of arrays of 3 text fields with companion
4389 // radio button set to be encoded as json.
4390 $tmparr = array();
4391 foreach ($_POST["form_$field_id"] as $key => $valarr) {
4392 // Each $key here is a list item ID. $valarr has 3 text field values keyed on 0, 2 and 3.
4393 $tmparr[$key][0] = $valarr['0'];
4394 $tmparr[$key][1] = $_POST["radio_{$field_id}"][$key];
4395 $tmparr[$key][2] = $valarr['2'];
4396 $tmparr[$key][3] = $valarr['3'];
4398 $value .= json_encode($tmparr);
4399 } elseif ($data_type == 36 || $data_type == 44 || $data_type == 45 || $data_type == 33) {
4400 $value_array = $_POST["form_$field_id"];
4401 $i = 0;
4402 foreach ($value_array as $key => $valueofkey) {
4403 if ($i == 0) {
4404 $value = $valueofkey;
4405 } else {
4406 $value = $value . "|" . $valueofkey;
4409 $i++;
4411 } elseif ($data_type == 46) {
4412 $reslist = trim($_POST["$prefix$field_id"]);
4413 if (preg_match('/^comment_/', $reslist)) {
4414 $res_comment = str_replace('|', ' ', $_POST["{$prefix}text_$field_id"]);
4415 $value = $reslist . "|" . $res_comment;
4416 } else {
4417 $value = $_POST["$prefix$field_id"];
4419 } elseif ($data_type == 52) {
4420 $value_array = $_POST["form_$field_id"];
4421 $i = 0;
4422 foreach ($value_array as $key => $valueofkey) {
4423 if ($i == 0) {
4424 $value = $valueofkey;
4425 } else {
4426 $value = $value . "|" . $valueofkey;
4429 $i++;
4431 } else {
4432 $value = $_POST["$prefix$field_id"];
4436 // Better to die than to silently truncate data!
4437 if ($maxlength && $maxlength != 0 && mb_strlen(trim($value)) > $maxlength && !$frow['list_id']) {
4438 die(htmlspecialchars(xl('ERROR: Field') . " '$field_id' " . xl('is too long'), ENT_NOQUOTES) .
4439 ":<br />&nbsp;<br />" . htmlspecialchars($value, ENT_NOQUOTES));
4442 if (is_string($value)) {
4443 return trim($value);
4444 } else {
4445 return $value;
4449 // Generate JavaScript validation logic for the required fields.
4451 function generate_layout_validation($form_id)
4453 if ('HIS' == $form_id) {
4454 $form_id .= '%'; // TBD: DEM also?
4456 $pres = sqlStatement(
4457 "SELECT grp_form_id, grp_seq, grp_title " .
4458 "FROM layout_group_properties " .
4459 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
4460 "ORDER BY grp_seq, grp_title, grp_form_id",
4461 array("$form_id")
4463 while ($prow = sqlFetchArray($pres)) {
4464 $form_id = $prow['grp_form_id'];
4466 $fres = sqlStatement("SELECT * FROM layout_options " .
4467 "WHERE form_id = ? AND uor > 0 AND field_id != '' " .
4468 "ORDER BY group_id, seq", array($form_id));
4470 while ($frow = sqlFetchArray($fres)) {
4471 $data_type = $frow['data_type'];
4472 $field_id = $frow['field_id'];
4473 $fldtitle = $frow['title'];
4474 if (!$fldtitle) {
4475 $fldtitle = $frow['description'];
4478 $fldname = attr("form_$field_id");
4480 if ($data_type == 40) {
4481 $fldid = "form_" . $field_id;
4482 // Move canvas image data to its hidden form field so the server will get it.
4483 echo
4484 " var canfld = f[" . js_escape($fldid) . "];\n" .
4485 " if (canfld) canfld.value = lbfCanvasGetData(" . js_escape($fldid) . ");\n";
4486 continue;
4488 if ($data_type == 41 || $data_type == 42) {
4489 $fldid = "form_" . $field_id;
4490 // Move canvas image data to its hidden form field so the server will get it.
4491 echo " lbfSetSignature(" . js_escape($fldid) . ");\n";
4492 continue;
4494 if ($frow['uor'] < 2) {
4495 continue;
4498 echo " if (f.$fldname && !f.$fldname.disabled) {\n";
4499 switch ($data_type) {
4500 case 1:
4501 case 11:
4502 case 12:
4503 case 13:
4504 case 14:
4505 case 26:
4506 echo
4507 " if (f.$fldname.selectedIndex <= 0) {\n" .
4508 " alert(" . xlj('Please choose a value for') . " + " .
4509 "\":\\n\" + " . js_escape(xl_layout_label($fldtitle)) . ");\n" .
4510 " if (f.$fldname.focus) f.$fldname.focus();\n" .
4511 " return false;\n" .
4512 " }\n";
4513 break;
4514 case 33:
4515 echo
4516 " if (f.$fldname.selectedIndex <= 0) {\n" .
4517 " if (f.$fldname.focus) f.$fldname.focus();\n" .
4518 " errMsgs[errMsgs.length] = " . js_escape(xl_layout_label($fldtitle)) . "; \n" .
4519 " }\n";
4520 break;
4521 case 27: // radio buttons
4522 echo
4523 " var i = 0;\n" .
4524 " for (; i < f.$fldname.length; ++i) if (f.{$fldname}[i].checked) break;\n" .
4525 " if (i >= f.$fldname.length) {\n" .
4526 " alert(" . xlj('Please choose a value for') . " + " .
4527 "\":\\n\" + " . js_escape(xl_layout_label($fldtitle)) . ");\n" .
4528 " return false;\n" .
4529 " }\n";
4530 break;
4531 case 2:
4532 case 3:
4533 case 4:
4534 case 15:
4535 echo
4536 " if (trimlen(f.$fldname.value) == 0) {\n" .
4537 " if (f.$fldname.focus) f.$fldname.focus();\n" .
4538 " $('#" . $fldname . "').parents('div.tab').each( function(){ var tabHeader = $('#header_' + $(this).attr('id') ); tabHeader.css('color','var(--danger)'); } ); " .
4539 " $('#" . $fldname . "').attr('style','background: var(--danger)'); \n" .
4540 " errMsgs[errMsgs.length] = " . js_escape(xl_layout_label($fldtitle)) . "; \n" .
4541 " } else { " .
4542 " $('#" . $fldname . "').attr('style',''); " .
4543 " $('#" . $fldname . "').parents('div.tab').each( function(){ var tabHeader = $('#header_' + $(this).attr('id') ); tabHeader.css('color',''); } ); " .
4544 " } \n";
4545 break;
4546 case 36: // multi select
4547 echo
4548 " var multi_select=f['$fldname" . "[]']; \n " .
4549 " var multi_choice_made=false; \n" .
4550 " for (var options_index=0; options_index < multi_select.length; options_index++) { " .
4551 " multi_choice_made=multi_choice_made || multi_select.options[options_index].selected; \n" .
4552 " } \n" .
4553 " if(!multi_choice_made)
4554 errMsgs[errMsgs.length] = " . js_escape(xl_layout_label($fldtitle)) . "; \n" .
4556 break;
4558 echo " }\n";
4560 } // End this layout, there may be more in the case of history.
4564 * DROPDOWN FOR FACILITIES
4566 * build a dropdown with all facilities
4568 * @param string $selected - name of the currently selected facility
4569 * use '0' for "unspecified facility"
4570 * use '' for "All facilities" (the default)
4571 * @param string $name - the name/id for select form (defaults to "form_facility")
4572 * @param boolean $allow_unspecified - include an option for "unspecified" facility
4573 * defaults to true
4574 * @return void - just echo the html encoded string
4576 * Note: This should become a data-type at some point, according to Brady
4578 function dropdown_facility(
4579 $selected = '',
4580 $name = 'form_facility',
4581 $allow_unspecified = true,
4582 $allow_allfacilities = true,
4583 $disabled = '',
4584 $onchange = '',
4585 $multiple = false,
4586 $class = ''
4588 global $facilityService;
4590 $have_selected = false;
4591 $fres = $facilityService->getAllFacility();
4592 $id = $name;
4594 if ($multiple) {
4595 $name = $name . "[]";
4597 echo " <select class='form-control$class";
4598 if ($multiple) {
4599 echo " select-dropdown";
4601 echo "' name='" . attr($name) . "' id='" . attr($id) . "'";
4602 if ($onchange) {
4603 echo " onchange='$onchange'";
4606 if ($multiple) {
4607 echo " multiple='multiple'";
4610 echo " $disabled>\n";
4612 if ($allow_allfacilities) {
4613 $option_value = '';
4614 $option_selected_attr = '';
4615 if ($selected == '') {
4616 $option_selected_attr = ' selected="selected"';
4617 $have_selected = true;
4620 $option_content = '-- ' . xl('All Facilities') . ' --';
4621 echo " <option value='" . attr($option_value) . "' $option_selected_attr>" . text($option_content) . "</option>\n";
4622 } elseif ($allow_unspecified) {
4623 $option_value = '0';
4624 $option_selected_attr = '';
4625 if ($selected == '0') {
4626 $option_selected_attr = ' selected="selected"';
4627 $have_selected = true;
4630 $option_content = '-- ' . xl('Unspecified') . ' --';
4631 echo " <option value='" . attr($option_value) . "' $option_selected_attr>" . text($option_content) . "</option>\n";
4634 foreach ($fres as $frow) {
4635 $facility_id = $frow['id'];
4636 $option_value = $facility_id;
4637 $option_selected_attr = '';
4638 if ($multiple) {
4639 $selectedValues = explode("|", $selected);
4641 if (in_array($facility_id, $selectedValues)) {
4642 $option_selected_attr = ' selected="selected"';
4643 $have_selected = true;
4645 } else {
4646 if ($selected == $facility_id) {
4647 $option_selected_attr = ' selected="selected"';
4648 $have_selected = true;
4652 $option_content = $frow['name'];
4653 echo " <option value='" . attr($option_value) . "' $option_selected_attr>" . text($option_content) . "</option>\n";
4656 if ($allow_unspecified && $allow_allfacilities) {
4657 $option_value = '0';
4658 $option_selected_attr = '';
4659 if ($selected == '0') {
4660 $option_selected_attr = ' selected="selected"';
4661 $have_selected = true;
4664 $option_content = '-- ' . xl('Unspecified') . ' --';
4665 echo " <option value='" . attr($option_value) . "' $option_selected_attr>" . text($option_content) . "</option>\n";
4668 if (!$have_selected && !$multiple) {
4669 $option_value = $selected;
4670 $option_label = '(' . xl('Do not change') . ')';
4671 $option_content = xl('Missing or Invalid');
4672 echo " <option value='" . attr($option_value) . "' label='" . attr($option_label) . "' selected='selected'>" . text($option_content) . "</option>\n";
4675 echo " </select>\n";
4679 * Expand Collapse Widget
4680 * This forms the header and functionality component of the widget. The information that is displayed
4681 * then follows this function followed by a closing div tag
4683 * @var $title is the title of the section (already translated)
4684 * @var $label is identifier used in the tag id's and sql columns
4685 * @var $buttonLabel is the button label text (already translated)
4686 * @var $buttonLink is the button link information
4687 * @var $buttonClass is any additional needed class elements for the button tag
4688 * @var $linkMethod is the button link method ('javascript' vs 'html')
4689 * @var $bodyClass is to set class(es) of the body
4690 * @var $auth is a flag to decide whether to show the button
4691 * @var $fixedWidth is to flag whether width is fixed
4692 * @var $forceExpandAlways is a flag to force the widget to always be expanded
4694 * @todo Convert to a modern layout
4696 function expand_collapse_widget($title, $label, $buttonLabel, $buttonLink, $buttonClass, $linkMethod, $bodyClass, $auth, $fixedWidth, $forceExpandAlways = false)
4698 if ($fixedWidth) {
4699 echo "<div class='section-header'>";
4700 } else {
4701 echo "<div class='section-header-dynamic'>";
4704 echo "<table><tr>";
4705 if ($auth) {
4706 // show button, since authorized
4707 // first prepare class string
4708 if ($buttonClass) {
4709 $class_string = "btn btn-primary btn-sm " . $buttonClass;
4710 } else {
4711 $class_string = "btn btn-primary btn-sm";
4714 // next, create the link
4715 if ($linkMethod == "javascript") {
4716 echo "<td><a class='" . attr($class_string) . "' href='javascript:;' onclick='" . $buttonLink . "'";
4717 } else {
4718 echo "<td><a class='" . attr($class_string) . "' href='" . $buttonLink . "'";
4719 if (!isset($_SESSION['patient_portal_onsite_two'])) {
4720 // prevent an error from occuring when calling the function from the patient portal
4721 echo " onclick='top.restoreSession()'";
4725 echo "><span>" .
4726 text($buttonLabel) . "</span></a></td>";
4729 if ($forceExpandAlways) {
4730 // Special case to force the widget to always be expanded
4731 echo "<td><span class='text font-weight-bold'>" . text($title) . "</span>";
4732 $indicatorTag = "style='display: none'";
4735 $indicatorTag = isset($indicatorTag) ? $indicatorTag : "";
4736 echo "<td><a " . $indicatorTag . " href='javascript:;' class='small' onclick='toggleIndicator(this," .
4737 attr_js($label . "_ps_expand") . ")'><span class='text font-weight-bold'>";
4738 echo text($title) . "</span>";
4740 if (isset($_SESSION['patient_portal_onsite_two'])) {
4741 // collapse all entries in the patient portal
4742 $text = xl('expand');
4743 } elseif (getUserSetting($label . "_ps_expand")) {
4744 $text = xl('collapse');
4745 } else {
4746 $text = xl('expand');
4749 echo " (<span class='indicator'>" . text($text) .
4750 "</span>)</a></td>";
4751 echo "</tr></table>";
4752 echo "</div>";
4753 if ($forceExpandAlways) {
4754 // Special case to force the widget to always be expanded
4755 $styling = "";
4756 } elseif (isset($_SESSION['patient_portal_onsite_two'])) {
4757 // collapse all entries in the patient portal
4758 $styling = "style='display: none'";
4759 } elseif (getUserSetting($label . "_ps_expand")) {
4760 $styling = "";
4761 } else {
4762 $styling = "style='display: none'";
4765 if ($bodyClass) {
4766 $styling .= " class='" . attr($bodyClass) . "'";
4769 //next, create the first div tag to hold the information
4770 // note the code that calls this function will then place the ending div tag after the data
4771 echo "<div id='" . attr($label) . "_ps_expand' " . $styling . ">";
4774 //billing_facility fuction will give the dropdown list which contain billing faciliies.
4775 function billing_facility($name, $select)
4777 global $facilityService;
4779 $fres = $facilityService->getAllBillingLocations();
4780 echo " <select id='" . htmlspecialchars($name, ENT_QUOTES) . "' class='form-control' name='" . htmlspecialchars($name, ENT_QUOTES) . "'>";
4781 foreach ($fres as $facrow) {
4782 $selected = ( $facrow['id'] == $select ) ? 'selected="selected"' : '' ;
4783 echo "<option value=" . htmlspecialchars($facrow['id'], ENT_QUOTES) . " $selected>" . htmlspecialchars($facrow['name'], ENT_QUOTES) . "</option>";
4786 echo "</select>";
4789 // Generic function to get the translated title value for a particular list option.
4791 function getListItemTitle($list, $option)
4793 return LayoutsUtils::getListItemTitle($list, $option);
4796 //function to get the translated title value in Patient Transactions
4797 function getLayoutTitle($list, $option)
4799 $row = sqlQuery("SELECT grp_title FROM layout_group_properties " .
4800 "WHERE grp_mapping = ? AND grp_form_id = ? ", array($list, $option));
4802 if (empty($row['grp_title'])) {
4803 return $option;
4805 return xl_list_label($row['grp_title']);
4807 //Added on 5-jun-2k14 (regarding get the smoking code descriptions)
4808 function getSmokeCodes()
4810 $smoking_codes_arr = array();
4811 $smoking_codes = sqlStatement("SELECT option_id,codes FROM list_options WHERE list_id='smoking_status' AND activity = 1");
4812 while ($codes_row = sqlFetchArray($smoking_codes)) {
4813 $smoking_codes_arr[$codes_row['option_id']] = $codes_row['codes'];
4816 return $smoking_codes_arr;
4819 // Get the current value for a layout based form field.
4820 // Depending on options this might come from lbf_data, patient_data,
4821 // form_encounter, shared_attributes or elsewhere.
4822 // Returns FALSE if the field ID is invalid (layout error).
4824 function lbf_current_value($frow, $formid, $encounter)
4826 global $pid;
4827 $formname = $frow['form_id'];
4828 $field_id = $frow['field_id'];
4829 $source = $frow['source'];
4830 $currvalue = '';
4831 $deffname = $formname . '_default_' . $field_id;
4832 if ($source == 'D' || $source == 'H') {
4833 // Get from patient_data, employer_data or history_data.
4834 if ($source == 'H') {
4835 $table = 'history_data';
4836 $orderby = 'ORDER BY date DESC LIMIT 1';
4837 } elseif (strpos($field_id, 'em_') === 0) {
4838 $field_id = substr($field_id, 3);
4839 $table = 'employer_data';
4840 $orderby = 'ORDER BY date DESC LIMIT 1';
4841 } else {
4842 $table = 'patient_data';
4843 $orderby = '';
4846 // It is an error if the field does not exist, but don't crash.
4847 $tmp = sqlQuery("SHOW COLUMNS FROM " . escape_table_name($table) . " WHERE Field = ?", array($field_id));
4848 if (empty($tmp)) {
4849 return '*?*';
4852 $pdrow = sqlQuery("SELECT `$field_id` AS field_value FROM " . escape_table_name($table) . " WHERE pid = ? $orderby", array($pid));
4853 if (isset($pdrow)) {
4854 $currvalue = $pdrow['field_value'];
4856 } elseif ($source == 'E') {
4857 $sarow = false;
4858 if ($encounter) {
4859 // Get value from shared_attributes of the current encounter.
4860 $sarow = sqlQuery(
4861 "SELECT field_value FROM shared_attributes WHERE " .
4862 "pid = ? AND encounter = ? AND field_id = ?",
4863 array($pid, $encounter, $field_id)
4865 if (!empty($sarow)) {
4866 $currvalue = $sarow['field_value'];
4868 } elseif ($formid) {
4869 // Get from shared_attributes of the encounter that this form is linked to.
4870 // Note the importance of having an index on forms.form_id.
4871 $sarow = sqlQuery(
4872 "SELECT sa.field_value " .
4873 "FROM forms AS f, shared_attributes AS sa WHERE " .
4874 "f.form_id = ? AND f.formdir = ? AND f.deleted = 0 AND " .
4875 "sa.pid = f.pid AND sa.encounter = f.encounter AND sa.field_id = ?",
4876 array($formid, $formname, $field_id)
4878 if (!empty($sarow)) {
4879 $currvalue = $sarow['field_value'];
4881 } else {
4882 // New form and encounter not available, this should not happen.
4884 if (empty($sarow) && !$formid) {
4885 // New form, see if there is a custom default from a plugin.
4886 if (function_exists($deffname)) {
4887 $currvalue = call_user_func($deffname);
4890 } elseif ($source == 'V') {
4891 if ($encounter) {
4892 // Get value from the current encounter's form_encounter.
4893 $ferow = sqlQuery(
4894 "SELECT * FROM form_encounter WHERE " .
4895 "pid = ? AND encounter = ?",
4896 array($pid, $encounter)
4898 if (isset($ferow[$field_id])) {
4899 $currvalue = $ferow[$field_id];
4901 } elseif ($formid) {
4902 // Get value from the form_encounter that this form is linked to.
4903 $ferow = sqlQuery(
4904 "SELECT fe.* " .
4905 "FROM forms AS f, form_encounter AS fe WHERE " .
4906 "f.form_id = ? AND f.formdir = ? AND f.deleted = 0 AND " .
4907 "fe.pid = f.pid AND fe.encounter = f.encounter",
4908 array($formid, $formname)
4910 if (isset($ferow[$field_id])) {
4911 $currvalue = $ferow[$field_id];
4913 } else {
4914 // New form and encounter not available, this should not happen.
4916 } elseif ($formid) {
4917 // This is a normal form field.
4918 $ldrow = sqlQuery("SELECT field_value FROM lbf_data WHERE " .
4919 "form_id = ? AND field_id = ?", array($formid, $field_id));
4920 if (!empty($ldrow)) {
4921 $currvalue = $ldrow['field_value'];
4923 } else {
4924 // New form, see if there is a custom default from a plugin.
4925 if (function_exists($deffname)) {
4926 $currvalue = call_user_func($deffname);
4930 return $currvalue;
4933 function signer_head()
4935 return <<<EOD
4936 <link href="{$GLOBALS['web_root']}/portal/sign/css/signer_modal.css?v={$GLOBALS['v_js_includes']}" rel="stylesheet"/>
4937 <script src="{$GLOBALS['web_root']}/portal/sign/assets/signature_pad.umd.js?v={$GLOBALS['v_js_includes']}"></script>
4938 <script src="{$GLOBALS['web_root']}/portal/sign/assets/signer_api.js?v={$GLOBALS['v_js_includes']}"></script>
4939 EOD;
4942 // This returns stuff that needs to go into the <head> section of a caller using
4943 // the drawable image field type in a form.
4944 // A TRUE argument makes the widget controls smaller.
4946 function lbf_canvas_head($small = true)
4948 $s = <<<EOD
4949 <link href="{$GLOBALS['assets_static_relative']}/literallycanvas/css/literallycanvas.css" rel="stylesheet" />
4950 <script src="{$GLOBALS['assets_static_relative']}/react/build/react-with-addons.min.js"></script>
4951 <script src="{$GLOBALS['assets_static_relative']}/react/build/react-dom.min.js"></script>
4952 <script src="{$GLOBALS['assets_static_relative']}/literallycanvas/js/literallycanvas.min.js"></script>
4953 EOD;
4954 if ($small) {
4955 $s .= <<<EOD
4956 <style>
4957 /* Custom LiterallyCanvas styling.
4958 * This makes the widget 25% less tall and adjusts some other things accordingly.
4960 .literally {
4961 min-height: 100%;
4962 min-width: 300px; /* Was 400, unspecified */
4964 .literally .lc-picker .toolbar-button {
4965 width: 20px;
4966 height: 20px;
4967 line-height: 20px; /* Was 26, 26, 26 */
4969 .literally .color-well {
4970 font-size: 8px;
4971 width: 49px; /* Was 10, 60 */
4973 .literally .color-well-color-container {
4974 width: 21px;
4975 height: 21px; /* Was 28, 28 */
4977 .literally .lc-picker {
4978 width: 50px; /* Was 61 */
4980 .literally .lc-drawing.with-gui {
4981 left: 50px; /* Was 61 */
4983 .literally .lc-options {
4984 left: 50px; /* Was 61 */
4986 .literally .color-picker-popup {
4987 left: 49px;
4988 bottom: 0px; /* Was 60, 31 */
4990 </style>
4991 EOD;
4994 return $s;
4998 * Test if modifier($test) is in array of options for data type.
5000 * @param json array $options ["G","P","T"], ["G"] or could be legacy string with form "GPT", "G", "012"
5001 * @param string $test
5002 * @return boolean
5004 function isOption($options, string $test): bool
5006 if (empty($options) || !isset($test) || $options == "null") {
5007 return false; // why bother?
5009 if (strpos($options, ',') === false) { // not json array of modifiers.
5010 // could be string of char's or single element of json ["RO"] or "TP" or "P" e.t.c.
5011 json_decode($options, true); // test if options json. json_last_error() will return JSON_ERROR_SYNTAX if not.
5012 // if of form ["RO"] (single modifier) means not legacy so continue on.
5013 if (is_string($options) && (json_last_error() !== JSON_ERROR_NONE)) { // nope, it's string.
5014 $t = str_split(trim($options)); // very good chance it's legacy modifier string.
5015 $options = json_encode($t); // make it json array to convert from legacy to new modifier json schema.
5019 $options = json_decode($options, true); // all should now be json
5021 return is_array($options) && in_array($test, $options, true); // finally the truth!