Increased contrast in navbar hover in dark theme (#6747)
[openemr.git] / library / options.inc.php
blobc7b5abd1420311d5ecd29932c9cda8b3ee6d310b
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\Services\EncounterService;
60 use OpenEMR\Services\FacilityService;
61 use OpenEMR\Services\PatientService;
63 $facilityService = new FacilityService();
65 $date_init = "";
66 $membership_group_number = 0;
68 // Our base Bootstrap column class, referenced here and in some other modules.
69 // Using col-lg allow us to have additional breakpoint at col-md.(992px, 768px)
70 // col-md-auto will let BS decide with col-12 always for sm devices.
71 $BS_COL_CLASS = 'col-12 col-md-auto col-lg';
73 function get_pharmacies()
75 return sqlStatement("SELECT d.id, d.name, a.line1, a.city, " .
76 "p.area_code, p.prefix, p.number FROM pharmacies AS d " .
77 "LEFT OUTER JOIN addresses AS a ON a.foreign_id = d.id " .
78 "LEFT OUTER JOIN phone_numbers AS p ON p.foreign_id = d.id " .
79 "AND p.type = 2 " .
80 "ORDER BY a.state, a.city, d.name, p.area_code, p.prefix, p.number");
83 function optionalAge($frow, $date, &$asof, $description = '')
85 $asof = '';
86 if (empty($date)) {
87 return '';
90 $edit_options = $frow['edit_options'] ?? null;
92 $date = substr($date, 0, 10);
93 if (isOption($edit_options, 'A') !== false) {
94 $format = 0;
95 } elseif (isOption($edit_options, 'B') !== false) {
96 $format = 3;
97 } else {
98 return '';
101 if (isOption($frow['form_id'], 'LBF') === false) {
102 $tmp = sqlQuery(
103 "SELECT date FROM form_encounter WHERE " .
104 "pid = ? AND encounter = ? ORDER BY id DESC LIMIT 1",
105 array($GLOBALS['pid'], $GLOBALS['encounter'])
107 if (!empty($tmp['date'])) {
108 $asof = substr($tmp['date'], 0, 10);
111 if ($description === '') {
112 $prefix = ($format ? xl('Gest age') : xl('Age')) . ' ';
113 } else {
114 $prefix = $description . ' ';
116 return $prefix . oeFormatAge($date, $asof, $format);
119 // Function to generate a drop-list.
121 function generate_select_list(
122 $tag_name,
123 $list_id,
124 $currvalue,
125 $title,
126 $empty_name = ' ',
127 $class = '',
128 $onchange = '',
129 $tag_id = '',
130 $custom_attributes = null,
131 $multiple = false, // new #10
132 $backup_list = '', // new #11
133 $ignore_default = false,
134 $include_inactive = false,
135 $tabIndex = false
137 $attributes = [];
138 $_options = [];
139 $_metadata = [];
141 $tag_name_esc = attr($tag_name);
143 $attributes['name'] = ($multiple) ? $tag_name_esc . "[]" : $tag_name_esc;
145 if ($tabIndex !== false) {
146 $attributes['tabindex'] = attr($tabIndex);
149 if ($multiple) {
150 $attributes['multiple'] = "multiple";
153 $attributes['id'] = attr($tag_name);
154 $attributes['class'] = (!empty($class)) ? "form-control " . attr($class) : "form-control";
156 if ($onchange) {
157 $attributes['onchange'] = $onchange;
160 if ($custom_attributes != null && is_array($custom_attributes)) {
161 foreach ($custom_attributes as $attr => $val) {
162 if (isset($custom_attributes [$attr])) {
163 $attributes[attr($attr)] = attr($val);
168 $attributes['title'] = attr($title);
170 $selectEmptyName = xlt($empty_name);
171 if ($empty_name) {
172 preg_match_all('/select2/m', $class, $matches, PREG_SET_ORDER, 0);
173 if (array_key_exists('placeholder', $attributes) && count($matches) > 0) {
174 // We have a placeholder attribute as well as a select2 class indicating there
175 // should be provide a truley empty option.
176 $_options[] = [];
177 } else {
178 $_options[] = [
179 'label' => $selectEmptyName,
180 'value' => '',
181 'isSelected' => true,
186 $got_selected = false;
188 for ($active = 1; $active == 1 || ($active == 0 && $include_inactive); --$active) {
189 $_optgroup = ($include_inactive) ? true : false;
191 // List order depends on language translation options.
192 // (Note we do not need to worry about the list order in the algorithm
193 // after the below code block since that is where searches for exceptions
194 // are done which include inactive items or items from a backup
195 // list; note these will always be shown at the bottom of the list no matter the
196 // chosen order.)
197 // This block should be migrated to the ListService but the service currently does not translate or offer a sort option.
198 $lang_id = empty($_SESSION['language_choice']) ? '1' : $_SESSION['language_choice'];
199 // sort by title
200 $order_by_sql = ($GLOBALS['gb_how_sort_list'] == '0') ? "seq, title" : "title, seq";
201 if (!$GLOBALS['translate_lists']) {
202 // do not translate
203 $lres = sqlStatement("SELECT * FROM list_options WHERE list_id = ? AND activity = ? ORDER BY $order_by_sql", [$list_id, $active]);
204 } else {
205 // do translate
206 $order_by_sql = str_replace("seq", "lo.seq", $order_by_sql);
207 $sql = "SELECT lo.option_id, lo.is_default,
208 COALESCE((SELECT ld.definition FROM lang_constants AS lc, lang_definitions AS ld
209 WHERE lc.constant_name = lo.title AND ld.cons_id = lc.cons_id AND ld.lang_id = ? AND ld.definition IS NOT NULL
210 AND ld.definition != ''
211 LIMIT 1), lo.title) AS title
212 FROM list_options AS lo
213 WHERE lo.list_id = ? AND lo.activity = ?
214 ORDER BY {$order_by_sql}";
215 $lres = sqlStatement($sql, [$lang_id, $list_id, $active]);
218 // Populate the options array with pertinent values
220 while ($lrow = sqlFetchArray($lres)) {
221 $selectedValues = explode("|", $currvalue ?? '');
222 $isSelected = false;
224 $optionValue = attr($lrow ['option_id']);
226 if (
227 (strlen($currvalue ?? '') == 0 && $lrow['is_default'] && !$ignore_default) ||
228 (strlen($currvalue ?? '') > 0 && in_array($lrow['option_id'], $selectedValues))
230 $got_selected = true;
231 $isSelected = true;
234 // Already has been translated above (if applicable), so do not need to use
235 // the xl_list_label() function here
236 $optionLabel = text($lrow ['title']);
238 $_tmp = [
239 'label' => $optionLabel,
240 'value' => $optionValue,
241 'isSelected' => $isSelected,
242 'isActive' => $include_inactive,
245 if ($_optgroup) {
246 $_tmp['optGroupOptions'] = $_tmp;
247 $_tmp['optgroupLabel'] = ($active) ? xla('Active') : xla('Inactive');
250 $_options[] = $_tmp;
252 } // end $active loop
255 To show the inactive item in the list if the value is saved to database
257 if (!$got_selected && strlen($currvalue ?? '') > 0) {
258 $_sql = "SELECT * FROM list_options WHERE list_id = ? AND activity = 0 AND option_id = ? ORDER BY seq, title";
259 $lres_inactive = sqlStatement($_sql, [$list_id, $currvalue]);
260 $lrow_inactive = sqlFetchArray($lres_inactive);
261 if (!empty($lrow_inactive['option_id'])) {
262 $optionValue = htmlspecialchars($lrow_inactive['option_id'], ENT_QUOTES);
263 $_options[] = [
264 'label' => htmlspecialchars(xl_list_label($lrow_inactive['title']), ENT_NOQUOTES),
265 'value' => $optionValue,
266 'isSelected' => true,
267 'isActive' => false,
269 $got_selected = true;
273 if (!$got_selected && strlen($currvalue ?? '') > 0 && !$multiple) {
274 $list_id = $backup_list;
275 $lrow = sqlQuery("SELECT title FROM list_options WHERE list_id = ? AND option_id = ?", [$list_id, $currvalue]);
277 $_options[] = [
278 'value' => attr($currvalue),
279 'selected' => true,
280 'label' => ($lrow > 0 && !empty($backup_list)) ? text(xl_list_label($lrow['title'])) : text($currvalue),
282 if (empty($lrow) && empty($backup_list)) {
283 $metadata['error'] = [
284 'title' => xlt('Please choose a valid selection from the list.'),
285 'text' => xlt('Fix this'),
288 } elseif (!$got_selected && strlen($currvalue ?? '') > 0 && $multiple) {
289 //if not found in main list, display all selected values that exist in backup list
290 $list_id = $backup_list;
292 $got_selected_backup = false;
293 if (!empty($backup_list)) {
294 $lres_backup = sqlStatement("SELECT * FROM list_options WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
295 while ($lrow_backup = sqlFetchArray($lres_backup)) {
296 $selectedValues = explode("|", $currvalue);
297 $optionValue = attr($lrow_backup['option_id']);
299 if (in_array($lrow_backup ['option_id'], $selectedValues)) {
300 $_options[] = [
301 'label' => text(xl_list_label($lrow_backup['title'])),
302 'value' => $optionValue,
303 'isSelected' => true,
305 $got_selected_backup = true;
310 if (!$got_selected_backup) {
311 $selectedValues = explode("|", $currvalue);
312 foreach ($selectedValues as $selectedValue) {
313 $_options[] = [
314 'label' => text($selectedValue),
315 'value' => attr($selectedValue),
316 'isSelected' => true,
320 $_metadata['error'] = [
321 'title' => xlt('Please choose a valid selection from the list.'),
322 'text' => xlt('Fix this'),
327 $_parsedOptions = [];
328 $_og = false;
329 foreach ($_options as $o) {
330 $_isOG = (array_key_exists('optGroupOptions', $o) && count($o['optGroupOptions']) > 0) ? true : false;
331 $_currOG = $o['optgroupLabel'] ?? false;
333 // Render only if the current optgroup label is not triple equal to the previous label
334 if ($_og !== $_currOG) {
335 $_parsedOptions[] = "</optgroup>";
338 // Must have an opt group and it must be different than the previous
339 if ($_isOG && $_og !== $_currOG) {
340 $_parsedOptions[] = sprintf('<optgroup label="%s">', $_currOG);
343 $_parsedOptions[] = _create_option_element($o);
345 $_og = $_currOG;
347 $optionString = implode("\n", $_parsedOptions);
349 $_parsedAttributes = [];
350 foreach ($attributes as $attr => $val) {
351 $_parsedAttributes[] = sprintf('%s="%s"', $attr, $val);
353 $attributeString = implode("\n", $_parsedAttributes);
355 $_selectString = sprintf("<select %s>%s</select>", $attributeString, $optionString);
356 $output[] = $_selectString;
358 if (array_key_exists('error', $_metadata)) {
359 $_errorString = sprintf("<span title=\"%s\">%s</span>", $_metadata['error']['title'], $metadata['error']['text']);
360 $output[] = $_errorString;
363 return implode("", $output);
366 function _create_option_element(array $o): string
368 $_valStr = (array_key_exists('value', $o)) ? "value=\"{$o['value']}\"" : "";
369 $_selStr = (array_key_exists('isSelected', $o) && $o['isSelected'] == true) ? "selected" : "";
370 $_labStr = (array_key_exists('label', $o)) ? $o['label'] : "";
371 return "<option $_valStr $_selStr>$_labStr</option>";
374 // Parsing for data type 31, static text.
375 function parse_static_text($frow, $value_allowed = true)
377 $tmp = str_replace("\r\n", "\n", $frow['description']);
378 // Translate if it does not look like HTML.
379 if (substr($tmp, 0, 1) != '<') {
380 $tmp2 = $frow['description'];
381 $tmp3 = xl_layout_label($tmp);
382 if ($tmp3 == $tmp && $tmp2 != $tmp) {
383 // No translation, try again without the CRLF substitution.
384 $tmp3 = xl_layout_label($tmp2);
386 $tmp = nl2br($tmp3);
388 $s = '';
389 if ($frow['source'] == 'D' || $frow['source'] == 'H') {
390 // Source is demographics or history. This case supports value substitution.
391 while (preg_match('/^(.*?)\{(\w+)\}(.*)$/', $tmp, $matches)) {
392 $s .= $matches[1];
393 if ($value_allowed) {
394 $tmprow = $frow;
395 $tmprow['field_id'] = $matches[2];
396 $s .= lbf_current_value($tmprow, 0, 0);
398 $tmp = $matches[3];
401 $s .= $tmp;
402 return $s;
405 function genLabResultsTextItem($name, $value, $outtype, $size, $maxlength, $disabled = '')
407 $string_maxlength = $maxlength ? ("maxlength='" . attr($maxlength) . "'") : '';
408 $s = "<td align='center'>";
409 if ($outtype == 2) {
410 $s .= text($value);
411 } else {
412 $s .= "<input type='text'";
413 if ($outtype == 0) {
414 $s .= " name='" . attr($name) . "' id='" . attr($name) . "'";
416 $s .= " size='" . attr($size) . "' $string_maxlength" .
417 " value='" . attr($value) . "'" .
418 " $under $disabled />";
420 $s .= "&nbsp;</td>";
421 return $s;
424 // $outtype = 0 for form, 1 for print, 2 for display, 3 for plain text.
425 function genLabResults($frow, $currvalue, $outtype = 0, $disabled = '')
427 $field_id = $frow['field_id'];
428 $list_id = $frow['list_id'];
429 $field_id_esc = text($field_id);
430 $under = $outtype == 1 ? "class='under'" : "";
431 $s = '';
433 $avalue = json_decode($currvalue, true);
434 if (empty($avalue)) {
435 $avalue = array();
437 // $avalue[$option_id][0] : gestation
438 // $avalue[$option_id][1] : radio button value
439 // $avalue[$option_id][2] : test value
440 // $avalue[$option_id][3] : notes
442 $maxlength = $frow['max_length'];
443 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
445 $under = $outtype == 1 ? "class='under'" : "";
447 $s .= "<table cellpadding='0' cellspacing='0'>";
448 if ($outtype < 2) {
449 $s .= "<tr>" .
450 "<td class='bold' align='center'>" . xlt('Test/Screening') . "&nbsp;</td>" .
451 "<td class='bold' align='center'>" . xlt('Gest wks') . "&nbsp;</td>" .
452 "<td class='bold' align='center'>&nbsp;" . xlt('N/A') . "&nbsp;</td>" .
453 "<td class='bold' align='center'>" . xlt('Neg/Nrml') . "</td>" .
454 "<td class='bold' align='center'>&nbsp;" . xlt('Pos/Abn') . "&nbsp;</td>" .
455 "<td class='bold' align='center'>" . xlt('Test Value') . "&nbsp;</td>" .
456 "<td class='bold' align='center'>" . xlt('Date/Notes') . "&nbsp;</td>" .
457 "</tr>";
460 $lres = sqlStatement(
461 "SELECT * FROM list_options WHERE " .
462 "list_id = ? AND activity = 1 ORDER BY seq, title",
463 array($list_id)
466 while ($lrow = sqlFetchArray($lres)) {
467 $option_id = $lrow['option_id'];
468 $option_id_esc = text($option_id);
470 if ($outtype >= 2 && empty($avalue[$option_id][1])) {
471 continue;
474 if ($outtype == 3) {
475 if (isset($avalue[$option_id][1]) && $avalue[$option_id][1] == '2') {
476 if ($s !== '') {
477 $s .= '; ';
479 $s .= text(xl_list_label($lrow['title']));
480 $s .= ':' . text($avalue[$option_id][0]);
481 $s .= ':' . text($avalue[$option_id][2]);
482 $s .= ':' . text($avalue[$option_id][3]);
484 continue;
487 $s .= "<tr>";
488 $s .= $outtype == 2 ? "<td class='bold'>" : "<td>";
489 $s .= text(xl_list_label($lrow['title'])) . "&nbsp;</td>";
491 $s .= genLabResultsTextItem(
492 "form_{$field_id_esc}[$option_id_esc][0]",
493 (isset($avalue[$option_id][0]) ? $avalue[$option_id][0] : ''),
494 $outtype,
497 $disabled,
498 $under
501 if ($outtype == 2) {
502 $tmp = isset($avalue[$option_id][1]) ? $avalue[$option_id][1] : '0';
503 $restype = ($tmp == '1') ? xl('Normal') : (($tmp == '2') ? xl('Abnormal') : xl('N/A'));
504 $s .= "<td>" . text($restype) . "&nbsp;</td>";
505 } else {
506 for ($i = 0; $i < 3; ++$i) {
507 $s .= "<td align='center'>";
508 $s .= "<input type='radio'";
509 if ($outtype == 0) {
510 $s .= " name='radio_{$field_id_esc}[$option_id_esc]'" .
511 " id='radio_{$field_id_esc}[$option_id_esc]'";
513 $s .= " value='$i' $lbfonchange";
514 if (isset($avalue[$option_id][1]) && $avalue[$option_id][1] == "$i") {
515 $s .= " checked";
517 $s .= " $disabled />";
518 $s .= "</td>";
521 $s .= genLabResultsTextItem(
522 "form_{$field_id_esc}[$option_id_esc][2]",
523 (isset($avalue[$option_id][2]) ? $avalue[$option_id][2] : ''),
524 $outtype,
527 $disabled,
528 $under
530 $s .= genLabResultsTextItem(
531 "form_{$field_id_esc}[$option_id_esc][3]",
532 (isset($avalue[$option_id][3]) ? $avalue[$option_id][3] : ''),
533 $outtype,
534 $fldlength,
535 $maxlength,
536 $disabled,
537 $under
539 $s .= "</tr>";
541 if ($outtype != 3) {
542 $s .= "</table>";
545 return $s;
548 // $frow is a row from the layout_options table.
549 // $currvalue is the current value, if any, of the associated item.
551 function generate_form_field($frow, $currvalue)
553 global $rootdir, $date_init, $ISSUE_TYPES, $code_types, $membership_group_number;
555 $currescaped = htmlspecialchars($currvalue ?? '', ENT_QUOTES);
557 $data_type = $frow['data_type'];
558 $field_id = $frow['field_id'];
559 $list_id = $frow['list_id'] ?? null;
560 $backup_list = $frow['list_backup_id'] ?? null;
561 $edit_options = $frow['edit_options'] ?? null;
562 $form_id = $frow['form_id'] ?? null;
564 // 'smallform' can be 'true' if we want a smaller form field, otherwise
565 // can be used to assign arbitrary CSS classes to data entry fields.
566 $smallform = $frow['smallform'] ?? null;
567 if ($smallform === 'true') {
568 $smallform = ' form-control-sm';
571 // escaped variables to use in html
572 $field_id_esc = htmlspecialchars($field_id, ENT_QUOTES);
573 $list_id_esc = htmlspecialchars(($list_id ?? ''), ENT_QUOTES);
575 // Added 5-09 by BM - Translate description if applicable
576 $description = (isset($frow['description']) ? htmlspecialchars(xl_layout_label($frow['description']), ENT_QUOTES) : '');
578 // Support edit option T which assigns the (possibly very long) description as
579 // the default value.
580 if (isOption($edit_options, 'T') !== false) {
581 if (strlen($currescaped) == 0) {
582 $currescaped = $description;
585 // Description used in this way is not suitable as a title.
586 $description = '';
589 // Support using the description as a placeholder
590 $placeholder = (isOption($edit_options, 'DAP') === true) ? " placeholder='{$description}' " : '';
592 // added 5-2009 by BM to allow modification of the 'empty' text title field.
593 // Can pass $frow['empty_title'] with this variable, otherwise
594 // will default to 'Unassigned'.
595 // modified 6-2009 by BM to allow complete skipping of the 'empty' text title
596 // if make $frow['empty_title'] equal to 'SKIP'
597 $showEmpty = true;
598 if (isset($frow['empty_title'])) {
599 if ($frow['empty_title'] == "SKIP") {
600 //do not display an 'empty' choice
601 $showEmpty = false;
602 $empty_title = "Unassigned";
603 } else {
604 $empty_title = $frow['empty_title'];
606 } else {
607 $empty_title = "Unassigned";
610 $disabled = isOption($edit_options, '0') === false ? '' : 'disabled';
612 $lbfchange = (
613 !empty($form_id) &&
615 strpos($form_id, 'LBF') === 0 ||
616 strpos($form_id, 'LBT') === 0 ||
617 strpos($form_id, 'DEM') === 0 ||
618 strpos($form_id, 'HIS') === 0
620 ) ? "checkSkipConditions();" : "";
621 $lbfonchange = $lbfchange ? "onchange='$lbfchange'" : "";
623 // generic single-selection list or single-selection list with search or single-selection list with comment support.
624 // These data types support backup lists.
625 if ($data_type == 1 || $data_type == 43 || $data_type == 46) {
626 if ($data_type == 46) {
627 // support for single-selection list with comment support
628 $lbfchange = "processCommentField(" . attr_js($field_id) . ");" . $lbfchange;
631 echo generate_select_list(
632 "form_$field_id",
633 $list_id,
634 $currvalue,
635 $description,
636 ($showEmpty ? $empty_title : ''),
637 (($data_type == 43) ? "select-dropdown" : $smallform),
638 $lbfchange,
640 ($disabled ? array('disabled' => 'disabled') : null),
641 false,
642 $backup_list
645 if ($data_type == 46) {
646 // support for single-selection list with comment support
647 $selectedValues = explode("|", $currvalue);
648 if (!preg_match('/^comment_/', $currvalue) || (count($selectedValues) == 1)) {
649 $display = "display:none";
650 $comment = "";
651 } else {
652 $display = "display:inline-block";
653 $comment = $selectedValues[count($selectedValues) - 1];
655 echo "<input type='text'" .
656 " name='form_text_" . attr($field_id) . "'" .
657 " id='form_text_" . attr($field_id) . "'" .
658 " size='" . attr($frow['fld_length']) . "'" .
659 " class='form-control'" .
660 $placeholder .
661 " " . ((!empty($frow['max_length'])) ? "maxlength='" . attr($frow['max_length']) . "'" : "") . " " .
662 " style='" . $display . "'" .
663 " value='" . attr($comment) . "'/>";
665 } elseif ($data_type == 2) { // simple text field
666 $fldlength = htmlspecialchars($frow['fld_length'] ?? '', ENT_QUOTES);
667 $maxlength = $frow['max_length'] ?? '';
668 $string_maxlength = "";
669 // if max_length is set to zero, then do not set a maxlength
670 if ($maxlength) {
671 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
674 echo "<input type='text'
675 class='form-control{$smallform}'
676 name='form_{$field_id_esc}'
677 id='form_{$field_id_esc}'
678 size='{$fldlength}'
679 {$string_maxlength}
680 {$placeholder}
681 title='{$description}'
682 value='{$currescaped}'";
683 $tmp = $lbfchange;
684 if (isOption($edit_options, 'C') !== false) {
685 $tmp .= "capitalizeMe(this);";
686 } elseif (isOption($edit_options, 'U') !== false) {
687 $tmp .= "this.value = this.value.toUpperCase();";
690 if ($tmp) {
691 echo " onchange='$tmp'";
694 $tmp = htmlspecialchars($GLOBALS['gbl_mask_patient_id'], ENT_QUOTES);
695 // If mask is for use at save time, treat as no mask.
696 if (strpos($tmp, '^') !== false) {
697 $tmp = '';
699 if ($field_id == 'pubpid' && strlen($tmp) > 0) {
700 echo " onkeyup='maskkeyup(this,\"$tmp\")'";
701 echo " onblur='maskblur(this,\"$tmp\")'";
704 if (isOption($edit_options, '1') !== false && strlen($currescaped) > 0) {
705 echo " readonly";
708 if ($disabled) {
709 echo ' disabled';
712 echo " />";
713 } elseif ($data_type == 3) { // long or multi-line text field
714 $textCols = htmlspecialchars($frow['fld_length'], ENT_QUOTES);
715 $textRows = htmlspecialchars($frow['fld_rows'], ENT_QUOTES);
716 echo "<textarea" .
717 " name='form_$field_id_esc'" .
718 " class='form-control$smallform'" .
719 " id='form_$field_id_esc'" .
720 " title='$description'" .
721 $placeholder .
722 " cols='$textCols'" .
723 " rows='$textRows' $lbfonchange $disabled" .
724 ">" . $currescaped . "</textarea>";
725 } elseif ($data_type == 4) { // date
726 $age_asof_date = ''; // optionalAge() sets this
727 $age_format = isOption($edit_options, 'A') === false ? 3 : 0;
728 $agestr = optionalAge($frow, $currvalue, $age_asof_date, $description);
729 if ($agestr) {
730 echo "<table class='table'><tr><td class='text'>";
733 $onchange_string = '';
734 if (!$disabled && $agestr) {
735 $onchange_string = "onchange=\"if (typeof(updateAgeString) == 'function') " .
736 "updateAgeString('$field_id','$age_asof_date', $age_format, '$description')\"";
738 if ($data_type == 4) {
739 $modtmp = isOption($edit_options, 'F') === false ? 0 : 1;
740 $datetimepickerclass = ($frow['validation'] ?? null) === 'past_date' ? '-past' : ( ($frow['validation'] ?? null) === 'future_date' ? '-future' : '' );
741 if (!$modtmp) {
742 $dateValue = oeFormatShortDate(substr($currescaped, 0, 10));
743 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) . "'";
744 } else {
745 $dateValue = oeFormatDateTime(substr($currescaped, 0, 20), 0);
746 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) . "'";
749 if (!$agestr) {
750 echo " title='$description'";
753 // help chrome users avoid autocomplete interfere with datepicker widget display
754 if ($frow['field_id'] == 'DOB') {
755 echo " autocomplete='off' $onchange_string $lbfonchange $disabled />";
756 } else {
757 echo " $onchange_string $lbfonchange $disabled />";
760 // Optional display of age or gestational age.
761 if ($agestr) {
762 echo "</td></tr><tr><td id='span_$field_id' class='text'>" . text($agestr) . "</td></tr></table>";
764 } elseif ($data_type == 10) { // provider list, local providers only
765 $ures = sqlStatement("SELECT id, fname, lname, specialty FROM users " .
766 "WHERE active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) " .
767 "AND authorized = 1 " .
768 "ORDER BY lname, fname");
769 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' $lbfonchange $disabled class='form-control$smallform'>";
770 echo "<option value=''>" . xlt($empty_title) . "</option>";
771 $got_selected = false;
772 while ($urow = sqlFetchArray($ures)) {
773 $uname = text($urow['fname'] . ' ' . $urow['lname']);
774 $optionId = attr($urow['id']);
775 echo "<option value='$optionId'";
776 if ($urow['id'] == $currvalue) {
777 echo " selected";
778 $got_selected = true;
781 echo ">$uname</option>";
784 if (!$got_selected && $currvalue) {
785 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
786 echo "</select>";
787 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
788 } else {
789 echo "</select>";
791 } elseif ($data_type == 11) { // provider list, including address book entries with an NPI number
792 $ures = sqlStatement("SELECT id, fname, lname, specialty FROM users " .
793 "WHERE active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) " .
794 "AND ( authorized = 1 OR ((username = '' OR username IS NULL) AND npi != '' )) " .
795 "ORDER BY lname, fname");
796 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' class='form-control$smallform'";
797 echo " $lbfonchange $disabled>";
798 echo "<option value=''>" . xlt('Unassigned') . "</option>";
799 $got_selected = false;
800 while ($urow = sqlFetchArray($ures)) {
801 $uname = text($urow['fname'] . ' ' . $urow['lname']);
802 $optionId = attr($urow['id']);
803 echo "<option value='$optionId'";
804 if ($urow['id'] == $currvalue) {
805 echo " selected";
806 $got_selected = true;
809 echo ">$uname</option>";
812 if (!$got_selected && $currvalue) {
813 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
814 echo "</select>";
815 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
816 } else {
817 echo "</select>";
819 } elseif ($data_type == 12) { // pharmacy list
820 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' class='form-control$smallform'";
821 echo " $lbfonchange $disabled>";
822 echo "<option value='0'></option>";
823 $pres = get_pharmacies();
824 $got_selected = false;
825 $zone = '';
826 while ($prow = sqlFetchArray($pres)) {
827 if ($zone != strtolower(trim($prow['city'] ?? ''))) {
828 if ($zone != '') {
829 echo "</optgroup>";
831 $zone = strtolower(trim($prow['city']));
832 echo "<optgroup label='" . attr($prow['city']) . "'>";
834 $key = $prow['id'];
835 $optionValue = htmlspecialchars($key, ENT_QUOTES);
836 $optionLabel = htmlspecialchars($prow['name'] . ' ' . $prow['area_code'] . '-' .
837 $prow['prefix'] . '-' . $prow['number'] . ' / ' .
838 $prow['line1'] . ' / ' . $prow['city'], ENT_NOQUOTES);
839 echo "<option value='$optionValue'";
840 if ($currvalue == $key) {
841 echo " selected";
842 $got_selected = true;
845 echo ">$optionLabel</option>";
848 if (!$got_selected && $currvalue) {
849 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
850 echo "</select>";
851 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
852 } else {
853 echo "</select>";
855 } elseif ($data_type == 13) { // squads
856 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' class='form-control$smallform'";
857 echo " $lbfonchange $disabled>";
858 echo "<option value=''>&nbsp;</option>";
859 $squads = AclExtended::aclGetSquads();
860 if ($squads) {
861 foreach ($squads as $key => $value) {
862 $optionValue = htmlspecialchars($key, ENT_QUOTES);
863 $optionLabel = htmlspecialchars($value[3], ENT_NOQUOTES);
864 echo "<option value='$optionValue'";
865 if ($currvalue == $key) {
866 echo " selected";
869 echo ">$optionLabel</option>\n";
873 echo "</select>";
874 } elseif ($data_type == 14) {
875 // Address book, preferring organization name if it exists and is not in
876 // parentheses, and excluding local users who are not providers.
877 // Supports "referred to" practitioners and facilities.
878 // Alternatively the letter L in edit_options means that abook_type
879 // must be "ord_lab", indicating types used with the procedure
880 // lab ordering system.
881 // Alternatively the letter O in edit_options means that abook_type
882 // must begin with "ord_", indicating types used with the procedure
883 // ordering system.
884 // Alternatively the letter V in edit_options means that abook_type
885 // must be "vendor", indicating the Vendor type.
886 // Alternatively the letter R in edit_options means that abook_type
887 // must be "dist", indicating the Distributor type.
889 if (isOption($edit_options, 'L') !== false) {
890 $tmp = "abook_type = 'ord_lab'";
891 } elseif (isOption($edit_options, 'O') !== false) {
892 $tmp = "abook_type LIKE 'ord\\_%'";
893 } elseif (isOption($edit_options, 'V') !== false) {
894 $tmp = "abook_type LIKE 'vendor%'";
895 } elseif (isOption($edit_options, 'R') !== false) {
896 $tmp = "abook_type LIKE 'dist'";
897 } else {
898 $tmp = "( username = '' OR authorized = 1 )";
901 $ures = sqlStatement("SELECT id, fname, lname, organization, username, npi FROM users " .
902 "WHERE active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) " .
903 "AND $tmp " .
904 "ORDER BY organization, lname, fname, npi");
905 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' title='$description' class='form-control$smallform'";
906 echo " $lbfonchange $disabled>";
907 echo "<option value=''>" . htmlspecialchars(xl('Unassigned'), ENT_NOQUOTES) . "</option>";
908 while ($urow = sqlFetchArray($ures)) {
909 $uname = $urow['organization'];
910 if (empty($uname) || substr($uname, 0, 1) == '(') {
911 $uname = $urow['lname'];
912 if ($urow['fname']) {
913 $uname .= ", " . $urow['fname'];
915 if ($urow['npi']) {
916 $uname .= ": " . $urow['npi'];
920 $optionValue = htmlspecialchars($urow['id'], ENT_QUOTES);
921 $optionLabel = htmlspecialchars($uname, ENT_NOQUOTES);
922 echo "<option value='$optionValue'";
923 // Failure to translate Local and External is not an error here;
924 // they are only used as internal flags and must not be translated!
925 $title = $urow['username'] ? 'Local' : 'External';
926 $optionTitle = htmlspecialchars($title, ENT_QUOTES);
927 echo " title='$optionTitle'";
928 if ($urow['id'] == $currvalue) {
929 echo " selected";
932 echo ">$optionLabel</option>";
935 echo "</select>";
936 } elseif ($data_type == 15) { // A billing code. If description matches an existing code type then that type is used.
937 $codetype = '';
938 if (!empty($frow['description']) && isset($code_types[$frow['description']])) {
939 $codetype = $frow['description'];
941 $fldlength = attr($frow['fld_length']);
942 $maxlength = $frow['max_length'];
943 $string_maxlength = "";
944 // if max_length is set to zero, then do not set a maxlength
945 if ($maxlength) {
946 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
948 // Edit option E means allow multiple (Extra) billing codes in a field.
949 // We invent a class name for this because JavaScript needs to know.
950 $className = '';
951 if (strpos($frow['edit_options'], 'E') !== false) {
952 $className = 'EditOptionE';
955 if (isOption($edit_options, '2') !== false) {
956 // Option "2" generates a hidden input for the codes, and a matching visible field
957 // displaying their descriptions. First step is computing the description string.
958 $currdescstring = '';
959 if (!empty($currvalue)) {
960 $relcodes = explode(';', $currvalue);
961 foreach ($relcodes as $codestring) {
962 if ($codestring === '') {
963 continue;
965 if ($currdescstring !== '') {
966 $currdescstring .= '; ';
968 $currdescstring .= getCodeDescription($codestring, $codetype);
972 $currdescstring = attr($currdescstring);
974 echo "<div>"; // wrapper for myHideOrShow()
975 echo "<input type='text'" .
976 " name='form_$field_id_esc'" .
977 " id='form_related_code'" .
978 " class='" . attr($className) . "'" .
979 " size='$fldlength'" .
980 " value='$currescaped'" .
981 " style='display:none'" .
982 " $lbfonchange readonly $disabled />";
983 // Extra readonly input field for optional display of code description(s).
984 echo "<input type='text'" .
985 " name='form_$field_id_esc" . "__desc'" .
986 " size='$fldlength'" .
987 " title='$description'" .
988 " value='$currdescstring'";
989 if (!$disabled) {
990 echo " onclick='sel_related(this," . attr_js($codetype) . ")'";
993 echo "class='form-control$smallform'";
994 echo " readonly $disabled />";
995 echo "</div>";
996 } else {
997 echo "<input type='text'" .
998 " name='form_$field_id_esc'" .
999 " id='form_related_code'" .
1000 " class='" . attr($className) . "'" .
1001 " size='$fldlength'" .
1002 " $string_maxlength" .
1003 " title='$description'" .
1004 " value='$currescaped'";
1005 if (!$disabled) {
1006 echo " onclick='sel_related(this," . attr_js($codetype) . ")'";
1009 echo "class='form-control$smallform'";
1010 echo " $lbfonchange readonly $disabled />";
1012 } elseif ($data_type == 16) { // insurance company list
1013 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' class='form-control$smallform' title='$description'>";
1014 echo "<option value='0'></option>";
1015 $insprovs = getInsuranceProviders();
1016 $got_selected = false;
1017 foreach ($insprovs as $key => $ipname) {
1018 $optionValue = htmlspecialchars($key, ENT_QUOTES);
1019 $optionLabel = htmlspecialchars($ipname, ENT_NOQUOTES);
1020 echo "<option value='$optionValue'";
1021 if ($currvalue == $key) {
1022 echo " selected";
1023 $got_selected = true;
1026 echo ">$optionLabel</option>";
1029 if (!$got_selected && $currvalue) {
1030 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
1031 echo "</select>";
1032 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
1033 } else {
1034 echo "</select>";
1036 } elseif ($data_type == 17) { // issue types
1037 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' class='form-control$smallform' title='$description'>";
1038 echo "<option value='0'></option>";
1039 $got_selected = false;
1040 foreach ($ISSUE_TYPES as $key => $value) {
1041 $optionValue = htmlspecialchars($key, ENT_QUOTES);
1042 $optionLabel = htmlspecialchars($value[1], ENT_NOQUOTES);
1043 echo "<option value='$optionValue'";
1044 if ($currvalue == $key) {
1045 echo " selected";
1046 $got_selected = true;
1049 echo ">$optionLabel</option>";
1052 if (!$got_selected && strlen($currvalue) > 0) {
1053 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
1054 echo "</select>";
1055 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
1056 } else {
1057 echo "</select>";
1059 } elseif ($data_type == 18) { // Visit categories.
1060 $cres = sqlStatement("SELECT pc_catid, pc_catname " .
1061 "FROM openemr_postcalendar_categories ORDER BY pc_catname");
1062 echo "<select name='form_$field_id_esc' id='form_$field_id_esc' class='form-control$smallform' title='$description'" . " $lbfonchange $disabled>";
1063 echo "<option value=''>" . xlt($empty_title) . "</option>";
1064 $got_selected = false;
1065 while ($crow = sqlFetchArray($cres)) {
1066 $catid = $crow['pc_catid'];
1067 if (($catid < 9 && $catid != 5) || $catid == 11) {
1068 continue;
1071 echo "<option value='" . attr($catid) . "'";
1072 if ($catid == $currvalue) {
1073 echo " selected";
1074 $got_selected = true;
1077 echo ">" . text(xl_appt_category($crow['pc_catname'])) . "</option>";
1080 if (!$got_selected && $currvalue) {
1081 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
1082 echo "</select>";
1083 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
1084 } else {
1085 echo "</select>";
1087 } elseif ($data_type == 21) { // a set of labeled checkboxes
1088 // If no list then it's a single checkbox and its value is "Yes" or empty.
1089 if (!$list_id) {
1090 echo "<input type='checkbox' name='form_{$field_id_esc}' " .
1091 "id='form_{$field_id_esc}' value='Yes' $lbfonchange";
1092 if ($currvalue) {
1093 echo " checked";
1095 echo " $disabled />";
1096 } else {
1097 // In this special case, fld_length is the number of columns generated.
1098 $cols = max(1, $frow['fld_length']);
1099 $avalue = explode('|', $currvalue);
1100 $lres = sqlStatement("SELECT * FROM list_options " .
1101 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1102 echo "<table class='w-100' cellpadding='0' cellspacing='0' title='" . attr($description) . "'>";
1103 $tdpct = (int) (100 / $cols);
1104 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
1105 $option_id = $lrow['option_id'];
1106 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1107 // if ($count) echo "<br />";
1108 if ($count % $cols == 0) {
1109 if ($count) {
1110 echo "</tr>";
1112 echo "<tr>";
1114 echo "<td width='" . attr($tdpct) . "%' nowrap>";
1115 echo "<input type='checkbox' name='form_{$field_id_esc}[$option_id_esc]'" .
1116 "id='form_{$field_id_esc}[$option_id_esc]' class='form-check-inline' value='1' $lbfonchange";
1117 if (in_array($option_id, $avalue)) {
1118 echo " checked";
1120 // Added 5-09 by BM - Translate label if applicable
1121 echo " $disabled />" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
1122 echo "</td>";
1124 if ($count) {
1125 echo "</tr>";
1126 if ($count > $cols) {
1127 // Add some space after multiple rows of checkboxes.
1128 $cols = htmlspecialchars($cols, ENT_QUOTES);
1129 echo "<tr><td colspan='$cols' style='height:0.7rem'></td></tr>";
1132 echo "</table>";
1134 } elseif ($data_type == 22) { // a set of labeled text input fields
1135 $tmp = explode('|', $currvalue);
1136 $avalue = array();
1137 foreach ($tmp as $value) {
1138 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
1139 $avalue[$matches[1]] = $matches[2];
1143 $lres = sqlStatement("SELECT * FROM list_options " .
1144 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1145 echo "<table class='table'>";
1146 while ($lrow = sqlFetchArray($lres)) {
1147 $option_id = $lrow['option_id'];
1148 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1149 $maxlength = $frow['max_length'];
1150 $string_maxlength = "";
1151 // if max_length is set to zero, then do not set a maxlength
1152 if ($maxlength) {
1153 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
1156 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
1158 // Added 5-09 by BM - Translate label if applicable
1159 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
1160 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
1161 $optionValue = htmlspecialchars($avalue[$option_id], ENT_QUOTES);
1162 echo "<td><input type='text'" .
1163 " name='form_{$field_id_esc}[$option_id_esc]'" .
1164 " id='form_{$field_id_esc}[$option_id_esc]'" .
1165 " size='$fldlength'" .
1166 $placeholder .
1167 " class='form-control$smallform'" .
1168 " $string_maxlength" .
1169 " value='$optionValue'";
1170 echo " $lbfonchange $disabled /></td></tr>";
1173 echo "</table>";
1174 } elseif ($data_type == 23) { // a set of exam results; 3 radio buttons and a text field:
1175 $tmp = explode('|', $currvalue);
1176 $avalue = array();
1177 foreach ($tmp as $value) {
1178 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
1179 $avalue[$matches[1]] = $matches[2];
1183 $maxlength = $frow['max_length'];
1184 $string_maxlength = "";
1185 // if max_length is set to zero, then do not set a maxlength
1186 if ($maxlength) {
1187 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
1190 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
1191 $lres = sqlStatement("SELECT * FROM list_options " .
1192 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1193 echo "<table class='table'>";
1194 echo "<tr><td class='font-weight-bold'>" . htmlspecialchars(xl('Exam or Test'), ENT_NOQUOTES) .
1195 "</td><td class='font-weight-bold'>" . htmlspecialchars(xl('N/A'), ENT_NOQUOTES) .
1196 "&nbsp;</td><td class='font-weight-bold'>" .
1197 htmlspecialchars(xl('Nor'), ENT_NOQUOTES) . "&nbsp;</td>" .
1198 "<td class='font-weight-bold'>" .
1199 htmlspecialchars(xl('Abn'), ENT_NOQUOTES) . "&nbsp;</td><td class='font-weight-bold'>" .
1200 htmlspecialchars(xl('Date/Notes'), ENT_NOQUOTES) . "</td></tr>";
1201 while ($lrow = sqlFetchArray($lres)) {
1202 $option_id = $lrow['option_id'];
1203 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1204 $restype = substr(($avalue[$option_id] ?? ''), 0, 1);
1205 $resnote = substr(($avalue[$option_id] ?? ''), 2);
1207 // Added 5-09 by BM - Translate label if applicable
1208 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
1210 for ($i = 0; $i < 3; ++$i) {
1211 $inputValue = htmlspecialchars($i, ENT_QUOTES);
1212 echo "<td><input type='radio'" .
1213 " name='radio_{$field_id_esc}[$option_id_esc]'" .
1214 " id='radio_{$field_id_esc}[$option_id_esc]'" .
1215 " value='$inputValue' $lbfonchange";
1216 if ($restype === "$i") {
1217 echo " checked";
1220 echo " $disabled /></td>";
1223 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
1224 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
1225 echo "<td><input type='text'" .
1226 " name='form_{$field_id_esc}[$option_id_esc]'" .
1227 " id='form_{$field_id_esc}[$option_id_esc]'" .
1228 " size='$fldlength'" .
1229 " class='form-control'" .
1230 " $string_maxlength" .
1231 " value='$resnote' $disabled /></td>";
1232 echo "</tr>";
1235 echo "</table>";
1236 } elseif ($data_type == 24) { // the list of active allergies for the current patient
1237 // this is read-only!
1238 $query = "SELECT title, comments FROM lists WHERE " .
1239 "pid = ? AND type = 'allergy' AND enddate IS NULL " .
1240 "ORDER BY begdate";
1241 // echo "<!-- $query -->\n"; // debugging
1242 $lres = sqlStatement($query, array($GLOBALS['pid']));
1243 $count = 0;
1244 while ($lrow = sqlFetchArray($lres)) {
1245 if ($count++) {
1246 echo "<br />";
1249 echo htmlspecialchars($lrow['title'], ENT_NOQUOTES);
1250 if ($lrow['comments']) {
1251 echo ' (' . htmlspecialchars($lrow['comments'], ENT_NOQUOTES) . ')';
1254 } elseif ($data_type == 25) { // a set of labeled checkboxes, each with a text field:
1255 $tmp = explode('|', $currvalue);
1256 $avalue = array();
1257 foreach ($tmp as $value) {
1258 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
1259 $avalue[$matches[1]] = $matches[2];
1263 $maxlength = $frow['max_length'];
1264 $string_maxlength = "";
1265 // if max_length is set to zero, then do not set a maxlength
1266 if ($maxlength) {
1267 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
1270 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
1271 $lres = sqlStatement("SELECT * FROM list_options " .
1272 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1273 echo "<table class='table'>";
1274 while ($lrow = sqlFetchArray($lres)) {
1275 $option_id = $lrow['option_id'];
1276 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1277 $restype = substr($avalue[$option_id], 0, 1);
1278 $resnote = substr($avalue[$option_id], 2);
1280 // Added 5-09 by BM - Translate label if applicable
1281 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
1283 $option_id = htmlspecialchars($option_id, ENT_QUOTES);
1284 echo "<td><input type='checkbox' name='check_{$field_id_esc}[$option_id_esc]'" .
1285 " id='check_{$field_id_esc}[$option_id_esc]' class='form-check-inline' value='1' $lbfonchange";
1286 if ($restype) {
1287 echo " checked";
1290 echo " $disabled />&nbsp;</td>";
1291 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
1292 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
1293 echo "<td><input type='text'" .
1294 " name='form_{$field_id_esc}[$option_id_esc]'" .
1295 " id='form_{$field_id_esc}[$option_id_esc]'" .
1296 " size='$fldlength'" .
1297 " class='form-control$smallform' " .
1298 " $string_maxlength" .
1299 " value='$resnote' $disabled /></td>";
1300 echo "</tr>";
1303 echo "</table>";
1304 } elseif ($data_type == 26) { // single-selection list with ability to add to it
1305 echo "<div class='input-group'>";
1306 echo generate_select_list(
1307 "form_$field_id",
1308 $list_id,
1309 $currvalue,
1310 $description,
1311 ($showEmpty ? $empty_title : ''),
1312 'addtolistclass_' . $list_id . $smallform,
1313 $lbfchange,
1315 ($disabled ? array('disabled' => 'disabled') : null),
1316 false,
1317 $backup_list
1319 // show the add button if user has access to correct list
1320 $inputValue = htmlspecialchars(xl('Add'), ENT_QUOTES);
1321 $btnSize = ($smallform) ? "btn-sm" : "";
1322 $outputAddButton = "<div class='input-group-append'><input type='button' class='btn btn-secondary $btnSize mb-1 addtolist' id='addtolistid_" . $list_id_esc . "' fieldid='form_" .
1323 $field_id_esc . "' value='$inputValue' $disabled /></div>";
1324 if (AclExtended::acoExist('lists', $list_id)) {
1325 // a specific aco exist for this list, so ensure access
1326 if (AclMain::aclCheckCore('lists', $list_id)) {
1327 echo $outputAddButton;
1329 } else {
1330 // no specific aco exist for this list, so check for access to 'default' list
1331 if (AclMain::aclCheckCore('lists', 'default')) {
1332 echo $outputAddButton;
1335 echo "</div>";
1336 } elseif ($data_type == 27) { // a set of labeled radio buttons
1337 // In this special case, fld_length is the number of columns generated.
1338 $cols = max(1, $frow['fld_length']);
1339 // Support for edit option M.
1340 if (isOption($edit_options, 'M')) {
1341 ++$membership_group_number;
1344 $lres = sqlStatement("SELECT * FROM list_options " .
1345 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
1346 echo "<table class='table w-100'>";
1347 $tdpct = (int) (100 / $cols);
1348 $got_selected = false;
1349 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
1350 $option_id = $lrow['option_id'];
1351 $option_id_esc = htmlspecialchars($option_id, ENT_QUOTES);
1352 if ($count % $cols == 0) {
1353 if ($count) {
1354 echo "</tr>";
1356 echo "<tr>";
1358 echo "<td width='" . attr($tdpct) . "%' nowrap>";
1359 echo "<input type='radio' name='form_{$field_id_esc}' id='form_{$field_id_esc}[$option_id_esc]'" .
1360 " value='$option_id_esc' $lbfonchange";
1361 // Support for edit options M and m.
1362 if (isOption($edit_options, 'M')) {
1363 echo " class='form-check-inline'";
1364 echo " onclick='checkGroupMembers(this, $membership_group_number);'";
1365 } elseif (isOption($edit_options, 'm')) {
1366 echo " class='form-check-inline lbf_memgroup_$membership_group_number'";
1367 } else {
1368 echo " class='form-check-inline'";
1371 if (
1372 (strlen($currvalue) == 0 && $lrow['is_default']) ||
1373 (strlen($currvalue) > 0 && $option_id == $currvalue)
1375 echo " checked";
1376 $got_selected = true;
1378 echo " $disabled />" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
1379 echo "</td>";
1382 if ($count) {
1383 echo "</tr>";
1384 if ($count > $cols) {
1385 // Add some space after multiple rows of radio buttons.
1386 $cols = htmlspecialchars($cols, ENT_QUOTES);
1387 echo "<tr><td colspan='$cols' style='height: 0.7rem'></td></tr>";
1391 echo "</table>";
1392 if (!$got_selected && strlen($currvalue) > 0) {
1393 $fontTitle = htmlspecialchars(xl('Please choose a valid selection.'), ENT_QUOTES);
1394 $fontText = htmlspecialchars(xl('Fix this'), ENT_NOQUOTES);
1395 echo "$currescaped <span class='text-danger' title='$fontTitle'>$fontText!</span>";
1397 } elseif ($data_type == 28 || $data_type == 32) { // special case for history of lifestyle status; 3 radio buttons
1398 // and a date text field:
1399 // VicarePlus :: A selection list box for smoking status:
1400 $tmp = explode('|', $currvalue);
1401 switch (count($tmp)) {
1402 case "4":
1403 $resnote = $tmp[0];
1404 $restype = $tmp[1];
1405 $resdate = oeFormatShortDate($tmp[2]);
1406 $reslist = $tmp[3];
1407 break;
1408 case "3":
1409 $resnote = $tmp[0];
1410 $restype = $tmp[1];
1411 $resdate = oeFormatShortDate($tmp[2]);
1412 $reslist = '';
1413 break;
1414 case "2":
1415 $resnote = $tmp[0];
1416 $restype = $tmp[1];
1417 $resdate = "";
1418 $reslist = '';
1419 break;
1420 case "1":
1421 $resnote = $tmp[0];
1422 $resdate = $restype = "";
1423 $reslist = '';
1424 break;
1425 default:
1426 $restype = $resdate = $resnote = "";
1427 $reslist = '';
1428 break;
1431 $maxlength = $frow['max_length'];
1432 $string_maxlength = "";
1433 // if max_length is set to zero, then do not set a maxlength
1434 if ($maxlength) {
1435 $string_maxlength = "maxlength='" . attr($maxlength) . "'";
1438 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
1440 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
1441 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
1442 $resdate = htmlspecialchars($resdate, ENT_QUOTES);
1443 echo "<table class='table'>";
1444 echo "<tr>";
1445 if ($data_type == 28) {
1446 // input text
1447 echo "<td><input type='text' class='form-control'" .
1448 " name='form_$field_id_esc'" .
1449 " id='form_$field_id_esc'" .
1450 " size='$fldlength'" .
1451 " class='form-control$smallform'" .
1452 " $string_maxlength" .
1453 " value='$resnote' $disabled />&nbsp;</td>";
1454 echo "<td class='font-weight-bold'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
1455 "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
1456 htmlspecialchars(xl('Status'), ENT_NOQUOTES) . ":&nbsp;&nbsp;</td>";
1457 } elseif ($data_type == 32) {
1458 // input text
1459 echo "<tr><td><input type='text'" .
1460 " name='form_text_$field_id_esc'" .
1461 " id='form_text_$field_id_esc'" .
1462 " size='$fldlength'" .
1463 " class='form-control$smallform'" .
1464 " $string_maxlength" .
1465 " value='$resnote' $disabled />&nbsp;</td></tr>";
1466 echo "<td>";
1467 //Selection list for smoking status
1468 $onchange = 'radioChange(this.options[this.selectedIndex].value)';//VicarePlus :: The javascript function for selection list.
1469 echo generate_select_list(
1470 "form_$field_id",
1471 $list_id,
1472 $reslist,
1473 $description,
1474 ($showEmpty ? $empty_title : ''),
1475 $smallform,
1476 $onchange,
1478 ($disabled ? array('disabled' => 'disabled') : null)
1480 echo "</td>";
1481 echo "<td class='font-weight-bold'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" . xlt('Status') . ":&nbsp;&nbsp;</td>";
1484 // current
1485 echo "<td class='text'><input type='radio'" .
1486 " name='radio_{$field_id_esc}'" .
1487 " id='radio_{$field_id_esc}[current]'" .
1488 " class='form-check-inline'" .
1489 " value='current" . $field_id_esc . "' $lbfonchange";
1490 if ($restype == "current" . $field_id) {
1491 echo " checked";
1494 if ($data_type == 32) {
1495 echo " onClick='smoking_statusClicked(this)'";
1498 echo " />" . xlt('Current') . "&nbsp;</td>";
1499 // quit
1500 echo "<td class='text'><input type='radio'" .
1501 " name='radio_{$field_id_esc}'" .
1502 " id='radio_{$field_id_esc}[quit]'" .
1503 " class='form-check-inline'" .
1504 " value='quit" . $field_id_esc . "' $lbfonchange";
1505 if ($restype == "quit" . $field_id) {
1506 echo " checked";
1509 if ($data_type == 32) {
1510 echo " onClick='smoking_statusClicked(this)'";
1513 echo " $disabled />" . xlt('Quit') . "&nbsp;</td>";
1514 // quit date
1515 echo "<td class='text'><input type='text' size='6' class='form-control datepicker' name='date_$field_id_esc' id='date_$field_id_esc'" .
1516 " value='$resdate'" .
1517 " title='$description'" .
1518 " $disabled />";
1519 echo "&nbsp;</td>";
1520 // never
1521 echo "<td class='text'><input type='radio'" .
1522 " name='radio_{$field_id_esc}'" .
1523 " class='form-check-inline'" .
1524 " id='radio_{$field_id_esc}[never]'" .
1525 " value='never" . $field_id_esc . "' $lbfonchange";
1526 if ($restype == "never" . $field_id) {
1527 echo " checked";
1530 if ($data_type == 32) {
1531 echo " onClick='smoking_statusClicked(this)'";
1534 echo " />" . xlt('Never') . "&nbsp;</td>";
1535 // Not Applicable
1536 echo "<td class='text'><input type='radio'" .
1537 " class='form-check-inline' " .
1538 " name='radio_{$field_id}'" .
1539 " id='radio_{$field_id}[not_applicable]'" .
1540 " value='not_applicable" . $field_id . "' $lbfonchange";
1541 if ($restype == "not_applicable" . $field_id) {
1542 echo " checked";
1545 if ($data_type == 32) {
1546 echo " onClick='smoking_statusClicked(this)'";
1549 echo " $disabled />" . xlt('N/A') . "&nbsp;</td>";
1551 //Added on 5-jun-2k14 (regarding 'Smoking Status - display SNOMED code description')
1552 echo "<td class='text'><div id='smoke_code'></div></td>";
1553 echo "</tr>";
1554 echo "</table>";
1555 } elseif ($data_type == 31) { // static text. read-only, of course.
1556 echo parse_static_text($frow);
1557 } elseif ($data_type == 34) {
1558 // $data_type == 33
1559 // Race and Ethnicity. After added support for backup lists, this is now the same as datatype 36; so have migrated it there.
1560 // $data_type == 33
1562 $arr = explode("|*|*|*|", $currvalue);
1563 echo "<div>"; // wrapper for myHideOrShow()
1564 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'>";
1565 echo "<div id='form_{$field_id}_div' class='text-area' style='min-width: 133px'>" . $arr[0] . "</div>";
1566 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>";
1567 echo "</a>";
1568 echo "</div>";
1569 } elseif ($data_type == 35) { //facilities drop-down list
1570 if (empty($currvalue)) {
1571 $currvalue = 0;
1574 dropdown_facility(
1575 $selected = $currvalue,
1576 $name = "form_$field_id_esc",
1577 $allow_unspecified = true,
1578 $allow_allfacilities = false,
1579 $disabled,
1580 $lbfchange,
1581 false,
1582 $smallform
1584 } elseif ($data_type == 36 || $data_type == 33) { //multiple select, supports backup list
1585 echo generate_select_list(
1586 "form_$field_id",
1587 $list_id,
1588 $currvalue,
1589 $description,
1590 $showEmpty ? $empty_title : '',
1591 $smallform,
1592 $lbfchange,
1594 null,
1595 true,
1596 $backup_list
1599 // A set of lab test results; Gestation, 3 radio buttons, test value, notes field:
1600 } elseif ($data_type == 37) {
1601 echo genLabResults($frow, $currvalue, 0, $disabled);
1602 } elseif ($data_type == 40) { // Canvas and related elements for browser-side image drawing.
1603 // Note you must invoke lbf_canvas_head() (below) to use this field type in a form.
1604 // Unlike other field types, width and height are in pixels.
1605 $canWidth = intval($frow['fld_length']);
1606 $canHeight = intval($frow['fld_rows']);
1607 if (empty($currvalue)) {
1608 if (preg_match('/\\bimage=([a-zA-Z0-9._-]*)/', $frow['description'], $matches)) {
1609 // If defined this is the filename of the default starting image.
1610 $currvalue = $GLOBALS['web_root'] . '/sites/' . $_SESSION['site_id'] . '/images/' . $matches[1];
1613 $mywidth = 50 + ($canWidth > 250 ? $canWidth : 250);
1614 $myheight = 31 + ($canHeight > 261 ? $canHeight : 261);
1615 echo "<div>"; // wrapper for myHideOrShow()
1616 echo "<div id='form_$field_id_esc' style='width:{$mywidth}px; height:{$myheight}px;'></div>";
1617 // Hidden form field exists to send updated data to the server at submit time.
1618 echo "<input type='hidden' name='form_$field_id_esc' value='' />";
1619 // Hidden image exists to support initialization of the canvas.
1620 echo "<img src='" . attr($currvalue) . "' id='form_{$field_id_esc}_img' style='display:none'>";
1621 echo "</div>";
1622 // $date_init is a misnomer but it's the place for browser-side setup logic.
1623 $date_init .= " lbfCanvasSetup('form_$field_id_esc', $canWidth, $canHeight);\n";
1624 } elseif ($data_type == 41 || $data_type == 42) {
1625 $datatype = 'patient-signature';
1626 $cpid = $GLOBALS['pid'];
1627 $cuser = $_SESSION['authUserID'];
1628 if ($data_type == 42) {
1629 $datatype = 'admin-signature';
1631 echo "<input type='hidden' id='form_$field_id_esc' name='form_$field_id_esc' value='' />\n";
1632 echo "<img class='signature' id='form_{$field_id_esc}_img' title='$description'
1633 data-pid='$cpid' data-user='$cuser' data-type='$datatype'
1634 data-action='fetch_signature' alt='Get Signature' src='" . attr($currvalue) . "'>\n";
1635 } elseif ($data_type == 44) { //multiple select facility
1636 if (empty($currvalue)) {
1637 $currvalue = 0;
1640 dropdown_facility(
1641 $selected = $currvalue,
1642 $name = "form_$field_id_esc",
1643 $allow_unspecified = false,
1644 $allow_allfacilities = false,
1645 $disabled,
1646 $lbfchange,
1647 true,
1648 $smallform
1650 } elseif ($data_type == 45) { // Multiple provider list, local providers only
1651 $ures = sqlStatement("SELECT id, fname, lname, specialty FROM users " .
1652 "WHERE active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) " .
1653 "AND authorized = 1 ORDER BY lname, fname");
1654 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'>";
1655 $got_selected = false;
1656 while ($urow = sqlFetchArray($ures)) {
1657 $uname = text($urow['fname'] . ' ' . $urow['lname']);
1658 $optionId = attr($urow['id']);
1659 echo "<option value='$optionId'";
1660 $selectedValues = explode("|", $currvalue);
1662 if (in_array($optionId, $selectedValues)) {
1663 echo " selected";
1664 $got_selected = true;
1667 echo ">$uname</option>";
1670 if (!$got_selected && $currvalue) {
1671 echo "<option value='" . attr($currvalue) . "' selected>* " . text($currvalue) . " *</option>";
1672 echo "</select>";
1673 echo " <span class='text-danger' title='" . xla('Please choose a valid selection from the list.') . "'>" . xlt('Fix this') . "!</span>";
1674 } else {
1675 echo "</select>";
1678 // Patient selector field.
1679 } elseif ($data_type == 51) {
1680 $fldlength = attr($frow['fld_length']);
1681 $currdescstring = '';
1682 if (!empty($currvalue)) {
1683 $currdescstring .= getPatientDescription($currvalue);
1685 $currdescstring = htmlspecialchars($currdescstring, ENT_QUOTES);
1686 echo "<div>"; // wrapper for myHideOrShow()
1687 echo "<input type='text'" .
1688 " name='form_$field_id_esc'" .
1689 " size='$fldlength'" .
1690 " value='$currescaped'" .
1691 " style='display:none'" .
1692 " $lbfonchange readonly $disabled />";
1693 // Extra readonly input field for patient description (name and pid).
1694 echo "<input type='text'" .
1695 " name='form_$field_id_esc" . "__desc'" .
1696 " size='$fldlength'" .
1697 " title='$description'" .
1698 " value='$currdescstring'";
1699 if (!$disabled) {
1700 echo " onclick='sel_patient(this, this.form.form_$field_id_esc)'";
1702 echo " readonly $disabled />";
1703 echo "</div>";
1704 // Previous Patient Names with add. Somewhat mirrors data types 44,45.
1705 } elseif ($data_type == 52) {
1706 global $pid;
1707 $pid = ($frow['blank_form'] ?? null) ? 0 : $pid;
1708 $patientService = new PatientService();
1709 $res = $patientService->getPatientNameHistory($pid);
1710 echo "<div class='input-group w-75'>";
1711 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'>";
1712 foreach ($res as $row) {
1713 $pname = $row['formatted_name']; // esc'ed in fetch.
1714 $optionId = attr($row['id']);
1715 // all names always selected
1716 echo "<option value='$optionId'" . " selected>$pname</option>";
1718 echo "</select>";
1719 echo "<button type='button' class='btn btn-primary btn-sm' id='type_52_add' onclick='return specialtyFormDialog()'>" . xlt('Add') . "</button></div>";
1720 // Patient Encounter List Field
1721 } elseif ($data_type == 53) {
1722 global $pid;
1723 $pid = ($frow['blank_form'] ?? null) ? 0 : $pid;
1724 $encounterService = new EncounterService();
1725 $res = $encounterService->getEncountersForPatientByPid($pid);
1726 echo "<div class='input-group w-75'>";
1727 echo "<select name='form_$field_id_esc'" . " id='form_$field_id_esc' title='$description' $lbfonchange $disabled class='form-control$smallform select-encounters'>";
1728 echo "<option value=''>" . xlt("Select Encounter") . "</option>";
1729 foreach ($res as $row) {
1730 $label = text(date("Y-m-d", strtotime($row['date'])) . " " . ($row['pc_catname'] ?? ''));
1731 $optionId = attr($row['eid']);
1732 // all names always selected
1733 if ($currvalue == $row['eid']) {
1734 echo "<option value='$optionId'" . " selected>$label</option>";
1735 } else {
1736 echo "<option value='$optionId'>$label</option>";
1739 echo "</select>";
1740 } elseif ($data_type == 54) {
1741 include "templates/address_list_form.php";
1745 function generate_print_field($frow, $currvalue, $value_allowed = true)
1747 global $rootdir, $date_init, $ISSUE_TYPES;
1749 $currescaped = htmlspecialchars($currvalue, ENT_QUOTES);
1751 $data_type = $frow['data_type'];
1752 $field_id = $frow['field_id'] ?? null;
1753 $list_id = $frow['list_id'];
1754 $fld_length = $frow['fld_length'] ?? null;
1755 $backup_list = $frow['list_backup_id'] ?? null;
1757 $description = attr(xl_layout_label($frow['description'] ?? ''));
1759 // Can pass $frow['empty_title'] with this variable, otherwise
1760 // will default to 'Unassigned'.
1761 // If it is 'SKIP' then an empty text title is completely skipped.
1762 $showEmpty = true;
1763 if (isset($frow['empty_title'])) {
1764 if ($frow['empty_title'] == "SKIP") {
1765 //do not display an 'empty' choice
1766 $showEmpty = false;
1767 $empty_title = "Unassigned";
1768 } else {
1769 $empty_title = $frow['empty_title'];
1771 } else {
1772 $empty_title = "Unassigned";
1775 // generic single-selection list
1776 // Supports backup lists.
1777 // if (false && ($data_type == 1 || $data_type == 26 || $data_type == 33 || $data_type == 43 || $data_type == 46)) {
1778 // We used to show all the list options but this was undone per CV request 2017-12-07
1779 // (see alternative code below).
1780 if ($data_type == 1 || $data_type == 26 || $data_type == 33 || $data_type == 43 || $data_type == 46) {
1781 if (empty($fld_length)) {
1782 if ($list_id == 'titles') {
1783 $fld_length = 3;
1784 } else {
1785 $fld_length = 10;
1789 $tmp = '';
1790 if ($currvalue) {
1791 if ($data_type == 46) {
1792 // support for single-selection list with comment support
1793 $selectedValues = explode("|", $currvalue);
1794 $currvalue = $selectedValues[0];
1796 $lrow = sqlQuery(
1797 "SELECT title FROM list_options " .
1798 "WHERE list_id = ? AND option_id = ? AND activity = 1",
1799 array($list_id,$currvalue)
1801 // For lists Race and Ethnicity if there is no matching value in the corresponding lists check ethrace list
1802 if (empty($lrow) && $data_type == 33) {
1803 $lrow = sqlQuery(
1804 "SELECT title FROM list_options " .
1805 "WHERE list_id = ? AND option_id = ? AND activity = 1",
1806 array('ethrace', $currvalue)
1810 $tmp = xl_list_label($lrow['title']);
1811 if ($lrow == 0 && !empty($backup_list)) {
1812 // since primary list did not map, try to map to backup list
1813 $lrow = sqlQuery("SELECT title FROM list_options " .
1814 "WHERE list_id = ? AND option_id = ?", array($backup_list,$currvalue));
1815 $tmp = xl_list_label($lrow['title']);
1818 if (empty($tmp)) {
1819 $tmp = "($currvalue)";
1822 if ($data_type == 46) {
1823 // support for single-selection list with comment support
1824 $resnote = $selectedValues[1] ?? null;
1825 if (!empty($resnote)) {
1826 $tmp .= " (" . $resnote . ")";
1831 if ($tmp === '') {
1832 $tmp = '&nbsp;';
1833 } else {
1834 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1836 echo $tmp;
1837 } elseif ($data_type == 2 || $data_type == 15) { // simple text field
1838 if ($currescaped === '') {
1839 $currescaped = '&nbsp;';
1842 echo $currescaped;
1843 } elseif ($data_type == 3) { // long or multi-line text field
1844 $fldlength = htmlspecialchars($fld_length, ENT_QUOTES);
1845 $maxlength = htmlspecialchars($frow['fld_rows'], ENT_QUOTES);
1846 echo "<textarea" .
1847 " class='form-control' " .
1848 " cols='$fldlength'" .
1849 " rows='$maxlength'>" .
1850 $currescaped . "</textarea>";
1851 } elseif ($data_type == 4) { // date
1852 $age_asof_date = '';
1853 $agestr = optionalAge($frow, $currvalue, $age_asof_date, $description);
1854 if ($currvalue === '') {
1855 echo '&nbsp;';
1856 } else {
1857 $modtmp = isOption($frow['edit_options'], 'F') === false ? 0 : 1;
1858 if (!$modtmp) {
1859 echo text(oeFormatShortDate($currvalue));
1860 } else {
1861 echo text(oeFormatDateTime($currvalue));
1863 if ($agestr) {
1864 echo "&nbsp;(" . text($agestr) . ")";
1867 } elseif ($data_type == 10 || $data_type == 11) { // provider list
1868 $tmp = '';
1869 if ($currvalue) {
1870 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
1871 "WHERE id = ?", array($currvalue));
1872 $tmp = ucwords($urow['fname'] . " " . $urow['lname']);
1873 if (empty($tmp)) {
1874 $tmp = "($currvalue)";
1877 if ($tmp === '') {
1878 $tmp = '&nbsp;';
1879 } else {
1880 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1883 echo $tmp;
1884 } elseif ($data_type == 12) { // pharmacy list
1885 $tmp = '';
1886 if ($currvalue) {
1887 $pres = get_pharmacies();
1888 while ($prow = sqlFetchArray($pres)) {
1889 $key = $prow['id'];
1890 if ($currvalue == $key) {
1891 $tmp = $prow['name'] . ' ' . $prow['area_code'] . '-' .
1892 $prow['prefix'] . '-' . $prow['number'] . ' / ' .
1893 $prow['line1'] . ' / ' . $prow['city'];
1897 if (empty($tmp)) {
1898 $tmp = "($currvalue)";
1901 if ($tmp === '') {
1902 $tmp = '&nbsp;';
1903 } else {
1904 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1907 echo $tmp;
1908 } elseif ($data_type == 13) { // squads
1909 $tmp = '';
1910 if ($currvalue) {
1911 $squads = AclExtended::aclGetSquads();
1912 if ($squads) {
1913 foreach ($squads as $key => $value) {
1914 if ($currvalue == $key) {
1915 $tmp = $value[3];
1920 if (empty($tmp)) {
1921 $tmp = "($currvalue)";
1924 if ($tmp === '') {
1925 $tmp = '&nbsp;';
1926 } else {
1927 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1930 echo $tmp;
1931 } elseif ($data_type == 14) { // Address book.
1932 $tmp = '';
1933 if ($currvalue) {
1934 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
1935 "WHERE id = ?", array($currvalue));
1936 $uname = $urow['lname'];
1937 if ($urow['fname']) {
1938 $uname .= ", " . $urow['fname'];
1941 $tmp = $uname;
1942 if (empty($tmp)) {
1943 $tmp = "($currvalue)";
1946 if ($tmp === '') {
1947 $tmp = '&nbsp;';
1948 } else {
1949 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1952 echo $tmp;
1953 } elseif ($data_type == 16) { // insurance company list
1954 $tmp = '';
1955 if ($currvalue) {
1956 $insprovs = getInsuranceProviders();
1957 foreach ($insprovs as $key => $ipname) {
1958 if ($currvalue == $key) {
1959 $tmp = $ipname;
1963 if (empty($tmp)) {
1964 $tmp = "($currvalue)";
1968 if ($tmp === '') {
1969 $tmp = '&nbsp;';
1970 } else {
1971 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1974 echo $tmp;
1975 } elseif ($data_type == 17) { // issue types
1976 $tmp = '';
1977 if ($currvalue) {
1978 foreach ($ISSUE_TYPES as $key => $value) {
1979 if ($currvalue == $key) {
1980 $tmp = $value[1];
1984 if (empty($tmp)) {
1985 $tmp = "($currvalue)";
1989 if ($tmp === '') {
1990 $tmp = '&nbsp;';
1991 } else {
1992 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
1995 echo $tmp;
1996 } elseif ($data_type == 18) { // Visit categories.
1997 $tmp = '';
1998 if ($currvalue) {
1999 $crow = sqlQuery(
2000 "SELECT pc_catid, pc_catname " .
2001 "FROM openemr_postcalendar_categories WHERE pc_catid = ?",
2002 array($currvalue)
2004 $tmp = xl_appt_category($crow['pc_catname']);
2005 if (empty($tmp)) {
2006 $tmp = "($currvalue)";
2010 if ($tmp === '') {
2011 $tmp = '&nbsp;';
2012 } else {
2013 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
2016 echo $tmp;
2017 } elseif ($data_type == 21) { // a single checkbox or set of labeled checkboxes
2018 if (!$list_id) {
2019 echo "<input type='checkbox'";
2020 if ($currvalue) {
2021 echo " checked";
2023 echo " />";
2024 } else {
2025 // In this special case, fld_length is the number of columns generated.
2026 $cols = max(1, $fld_length);
2027 $avalue = explode('|', $currvalue);
2028 $lres = sqlStatement("SELECT * FROM list_options " .
2029 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2030 echo "<table class='w-100' cellpadding='0' cellspacing='0'>";
2031 $tdpct = (int) (100 / $cols);
2032 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
2033 $option_id = $lrow['option_id'];
2034 if ($count % $cols == 0) {
2035 if ($count) {
2036 echo "</tr>";
2039 echo "<tr>";
2041 echo "<td width='" . attr($tdpct) . "%' nowrap>";
2042 echo "<input type='checkbox'";
2043 if (in_array($option_id, $avalue)) {
2044 echo " checked";
2046 echo ">" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
2047 echo "</td>";
2049 if ($count) {
2050 echo "</tr>";
2051 if ($count > $cols) {
2052 // Add some space after multiple rows of checkboxes.
2053 $cols = htmlspecialchars($cols, ENT_QUOTES);
2054 echo "<tr><td colspan='$cols' style='height:0.7em'></td></tr>";
2057 echo "</table>";
2059 } elseif ($data_type == 22) { // a set of labeled text input fields
2060 $tmp = explode('|', $currvalue);
2061 $avalue = array();
2062 foreach ($tmp as $value) {
2063 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2064 $avalue[$matches[1]] = $matches[2];
2068 $lres = sqlStatement("SELECT * FROM list_options " .
2069 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2070 echo "<table class='table'>";
2071 while ($lrow = sqlFetchArray($lres)) {
2072 $option_id = $lrow['option_id'];
2073 $fldlength = empty($fld_length) ? 20 : $fld_length;
2074 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2075 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
2076 $inputValue = htmlspecialchars($avalue[$option_id], ENT_QUOTES);
2077 echo "<td><input type='text'" .
2078 " class='form-control' " .
2079 " size='$fldlength'" .
2080 " value='$inputValue'" .
2081 " class='under'" .
2082 " /></td></tr>";
2085 echo "</table>";
2086 } elseif ($data_type == 23) { // a set of exam results; 3 radio buttons and a text field:
2087 $tmp = explode('|', $currvalue);
2088 $avalue = array();
2089 foreach ($tmp as $value) {
2090 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2091 $avalue[$matches[1]] = $matches[2];
2095 $fldlength = empty($fld_length) ? 20 : $fld_length;
2096 $lres = sqlStatement("SELECT * FROM list_options " .
2097 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2098 echo "<table class='table'>";
2099 echo "<tr><td><td class='font-weight-bold'>" .
2100 htmlspecialchars(xl('Exam or Test'), ENT_NOQUOTES) . "</td><td class='font-weight-bold'>" .
2101 htmlspecialchars(xl('N/A'), ENT_NOQUOTES) .
2102 "&nbsp;</td><td class='font-weight-bold'>" .
2103 htmlspecialchars(xl('Nor'), ENT_NOQUOTES) . "&nbsp;</td>" .
2104 "<td class='font-weight-bold'>" .
2105 htmlspecialchars(xl('Abn'), ENT_NOQUOTES) . "&nbsp;</td><td class='font-weight-bold'>" .
2106 htmlspecialchars(xl('Date/Notes'), ENT_NOQUOTES) . "</td></tr>";
2107 while ($lrow = sqlFetchArray($lres)) {
2108 $option_id = $lrow['option_id'];
2109 $restype = substr($avalue[$option_id], 0, 1);
2110 $resnote = substr($avalue[$option_id], 2);
2111 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2112 for ($i = 0; $i < 3; ++$i) {
2113 echo "<td><input type='radio'";
2114 if ($restype === "$i") {
2115 echo " checked";
2118 echo " /></td>";
2121 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
2122 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
2123 echo "<td><input type='text'" .
2124 " size='$fldlength'" .
2125 " value='$resnote'" .
2126 " class='under form-control' /></td>" .
2127 "</tr>";
2130 echo "</table>";
2131 } elseif ($data_type == 24) { // the list of active allergies for the current patient
2132 // this is read-only!
2133 $query = "SELECT title, comments FROM lists WHERE " .
2134 "pid = ? AND type = 'allergy' AND enddate IS NULL " .
2135 "ORDER BY begdate";
2136 $lres = sqlStatement($query, array($GLOBALS['pid']));
2137 $count = 0;
2138 while ($lrow = sqlFetchArray($lres)) {
2139 if ($count++) {
2140 echo "<br />";
2143 echo htmlspecialchars($lrow['title'], ENT_QUOTES);
2144 if ($lrow['comments']) {
2145 echo htmlspecialchars(' (' . $lrow['comments'] . ')', ENT_QUOTES);
2148 } elseif ($data_type == 25) { // a set of labeled checkboxes, each with a text field:
2149 $tmp = explode('|', $currvalue);
2150 $avalue = array();
2151 foreach ($tmp as $value) {
2152 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2153 $avalue[$matches[1]] = $matches[2];
2157 $fldlength = empty($fld_length) ? 20 : $fld_length;
2158 $lres = sqlStatement("SELECT * FROM list_options " .
2159 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2160 echo "<table class='table'>";
2161 while ($lrow = sqlFetchArray($lres)) {
2162 $option_id = $lrow['option_id'];
2163 $restype = substr($avalue[$option_id], 0, 1);
2164 $resnote = substr($avalue[$option_id], 2);
2165 echo "<tr><td>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2166 echo "<td><input type='checkbox'";
2167 if ($restype) {
2168 echo " checked";
2171 echo " />&nbsp;</td>";
2172 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
2173 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
2174 echo "<td><input type='text'" .
2175 " size='$fldlength'" .
2176 " class='form-control' " .
2177 " value='$resnote'" .
2178 " class='under'" .
2179 " /></td>" .
2180 "</tr>";
2183 echo "</table>";
2184 } elseif ($data_type == 27) { // Removed: || $data_type == 1 || $data_type == 26 || $data_type == 33
2185 // a set of labeled radio buttons
2186 // In this special case, fld_length is the number of columns generated.
2188 $cols = max(1, ($frow['fld_length'] ?? null));
2189 $lres = sqlStatement("SELECT * FROM list_options " .
2190 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2191 echo "<table class='w-100' cellpadding='0' cellspacing='0'>";
2192 $tdpct = (int) (100 / $cols);
2193 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
2194 $option_id = $lrow['option_id'];
2195 if ($count % $cols == 0) {
2196 if ($count) {
2197 echo "</tr>";
2199 echo "<tr>";
2201 echo "<td width='" . attr($tdpct) . "%' nowrap>";
2202 echo "<input type='radio'";
2203 if (strlen($currvalue) > 0 && $option_id == $currvalue) {
2204 // Do not use defaults for these printable forms.
2205 echo " checked";
2207 echo ">" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
2208 echo "</td>";
2210 if ($count) {
2211 echo "</tr>";
2212 if ($count > $cols) {
2213 // Add some space after multiple rows of radio buttons.
2214 $cols = htmlspecialchars($cols, ENT_QUOTES);
2215 echo "<tr><td colspan='$cols' style='height:0.7em'></td></tr>";
2218 echo "</table>";
2220 // special case for history of lifestyle status; 3 radio buttons and a date text field:
2221 } elseif ($data_type == 28 || $data_type == 32) {
2222 $tmp = explode('|', $currvalue);
2223 switch (count($tmp)) {
2224 case "4":
2225 $resnote = $tmp[0];
2226 $restype = $tmp[1];
2227 $resdate = oeFormatShortDate($tmp[2]) ;
2228 $reslist = $tmp[3];
2229 break;
2230 case "3":
2231 $resnote = $tmp[0];
2232 $restype = $tmp[1];
2233 $resdate = oeFormatShortDate($tmp[2]);
2234 $reslist = '';
2235 break;
2236 case "2":
2237 $resnote = $tmp[0];
2238 $restype = $tmp[1];
2239 $resdate = "";
2240 $reslist = '';
2241 break;
2242 case "1":
2243 $resnote = $tmp[0];
2244 $resdate = $restype = "";
2245 $reslist = '';
2246 break;
2247 default:
2248 $restype = $resdate = $resnote = "";
2249 $reslist = '';
2250 break;
2253 $fldlength = empty($frow['fld_length']) ? 20 : $frow['fld_length'];
2254 echo "<table class='table'>";
2255 echo "<tr>";
2256 $fldlength = htmlspecialchars($fldlength, ENT_QUOTES);
2257 $resnote = htmlspecialchars($resnote, ENT_QUOTES);
2258 $resdate = htmlspecialchars($resdate, ENT_QUOTES);
2259 if ($data_type == 28) {
2260 echo "<td><input type='text'" .
2261 " size='$fldlength'" .
2262 " class='under'" .
2263 " value='$resnote' /></td>";
2264 echo "<td class='font-weight-bold'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
2265 "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
2266 htmlspecialchars(xl('Status'), ENT_NOQUOTES) . ":&nbsp;</td>";
2267 } elseif ($data_type == 32) {
2268 echo "<tr><td><input type='text'" .
2269 " size='$fldlength'" .
2270 " class='under form-control'" .
2271 " value='$resnote' /></td></tr>";
2272 $fldlength = 30;
2273 $smoking_status_title = generate_display_field(array('data_type' => '1','list_id' => $list_id), $reslist);
2274 echo "<td><input type='text'" .
2275 " size='$fldlength'" .
2276 " class='under form-control'" .
2277 " value='$smoking_status_title' /></td>";
2278 echo "<td class='font-weight-bold'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" . htmlspecialchars(xl('Status'), ENT_NOQUOTES) . ":&nbsp;&nbsp;</td>";
2281 echo "<td><input type='radio' class='form-check-inline'";
2282 if ($restype == "current" . $field_id) {
2283 echo " checked";
2286 echo "/>" . htmlspecialchars(xl('Current'), ENT_NOQUOTES) . "&nbsp;</td>";
2288 echo "<td><input type='radio' class='form-check-inline'";
2289 if ($restype == "current" . $field_id) {
2290 echo " checked";
2293 echo "/>" . htmlspecialchars(xl('Quit'), ENT_NOQUOTES) . "&nbsp;</td>";
2295 echo "<td><input type='text' size='6'" .
2296 " value='$resdate'" .
2297 " class='under form-control'" .
2298 " /></td>";
2300 echo "<td><input type='radio' class='form-check-inline'";
2301 if ($restype == "current" . $field_id) {
2302 echo " checked";
2305 echo " />" . htmlspecialchars(xl('Never'), ENT_NOQUOTES) . "</td>";
2307 echo "<td><input type='radio' class='form-check-inline'";
2308 if ($restype == "not_applicable" . $field_id) {
2309 echo " checked";
2312 echo " />" . htmlspecialchars(xl('N/A'), ENT_NOQUOTES) . "&nbsp;</td>";
2313 echo "</tr>";
2314 echo "</table>";
2315 } elseif ($data_type == 31) { // static text. read-only, of course.
2316 echo parse_static_text($frow, $value_allowed);
2317 } elseif ($data_type == 34) {
2318 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'>";
2319 echo "<div id='form_{$field_id}_div' class='text-area'></div>";
2320 echo "<div style='display: none'><textarea name='form_{$field_id}' class='form-control' id='form_{$field_id}' style='display: none'></textarea></div>";
2321 echo "</a>";
2323 // Facilities. Changed 2017-12-15 to not show the choices.
2324 } elseif ($data_type == 35) {
2325 $urow = sqlQuery(
2326 "SELECT id, name FROM facility WHERE id = ?",
2327 array($currvalue)
2329 echo empty($urow['id']) ? '&nbsp;' : text($urow['name']);
2330 } elseif ($data_type == 36) { //Multi-select. Supports backup lists.
2331 if (empty($fld_length)) {
2332 if ($list_id == 'titles') {
2333 $fld_length = 3;
2334 } else {
2335 $fld_length = 10;
2339 $tmp = '';
2341 $values_array = explode("|", $currvalue);
2343 $i = 0;
2344 foreach ($values_array as $value) {
2345 if ($value) {
2346 $lrow = sqlQuery("SELECT title FROM list_options " .
2347 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($list_id,$value));
2348 $tmp = xl_list_label($lrow['title']);
2349 if ($lrow == 0 && !empty($backup_list)) {
2350 // since primary list did not map, try to map to backup list
2351 $lrow = sqlQuery("SELECT title FROM list_options " .
2352 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list,$currvalue));
2353 $tmp = xl_list_label($lrow['title']);
2356 if (empty($tmp)) {
2357 $tmp = "($value)";
2361 if ($tmp === '') {
2362 $tmp = '&nbsp;';
2363 } else {
2364 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
2367 if ($i != 0 && $tmp != '&nbsp;') {
2368 echo ",";
2371 echo $tmp;
2372 $i++;
2375 // A set of lab test results; Gestation, 3 radio buttons, test value, notes field:
2376 } elseif ($data_type == 37) {
2377 echo genLabResults($frow, $currvalue, 1, $disabled);
2378 } elseif ($data_type == 40) { // Image from canvas drawing
2379 if (empty($currvalue)) {
2380 if (preg_match('/\\bimage=([a-zA-Z0-9._-]*)/', $frow['description'], $matches)) {
2381 $currvalue = $GLOBALS['web_root'] . '/sites/' . $_SESSION['site_id'] . '/images/' . $matches[1];
2384 if ($currvalue) {
2385 echo "<img src='" . attr($currvalue) . "'>";
2387 } elseif ($data_type == 41 || $data_type == 42) {
2388 if ($currvalue) {
2389 echo "<img class='w-auto' style='height: 70px;' src='" . attr($currvalue) . "'>";
2391 } elseif ($data_type == 44 || $data_type == 45) {
2392 $tmp = '';
2394 $values_array = explode("|", $currvalue);
2396 $i = 0;
2397 foreach ($values_array as $value) {
2398 if ($value) {
2399 if ($data_type == 44) {
2400 $lrow = sqlQuery("SELECT name as name FROM facility WHERE id = ?", array($value));
2402 if ($data_type == 45) {
2403 $lrow = sqlQuery("SELECT CONCAT(fname,' ',lname) as name FROM users WHERE id = ?", array($value));
2405 $tmp = $lrow['name'];
2408 if ($tmp === '') {
2409 $tmp = '&nbsp;';
2410 } else {
2411 $tmp = htmlspecialchars($tmp, ENT_QUOTES);
2414 if ($i != 0 && $tmp != '&nbsp;') {
2415 echo ",";
2418 echo $tmp;
2419 $i++;
2422 // Patient selector field.
2423 } elseif ($data_type == 51) {
2424 if (!empty($currvalue)) {
2425 $tmp = text(getPatientDescription($currvalue));
2426 } else {
2427 echo '&nbsp;';
2433 * @param $list_id
2434 * @param bool $translate
2435 * @return array
2437 * Generate a key-value array containing each row of the specified list,
2438 * with the option ID as the index, and the title as the element
2440 * Pass in the list_id to specify this list.
2442 * Use the translate flag to run the title element through the translator
2444 function generate_list_map($list_id, $translate = false)
2446 $result = sqlStatement("SELECT option_id, title FROM list_options WHERE list_id = ?", [$list_id]);
2447 $map = [];
2448 while ($row = sqlFetchArray($result)) {
2449 if ($translate === true) {
2450 $title = xl_list_label($row['title']);
2451 } else {
2452 $title = $row['title'];
2454 $map[$row['option_id']] = $title;
2457 return $map;
2460 function generate_display_field($frow, $currvalue)
2462 global $ISSUE_TYPES, $facilityService;
2464 $data_type = $frow['data_type'];
2465 $field_id = isset($frow['field_id']) ? $frow['field_id'] : null;
2466 $list_id = $frow['list_id'];
2467 $backup_list = isset($frow['list_backup_id']) ? $frow['list_backup_id'] : null;
2468 $show_unchecked_arr = array();
2469 getLayoutProperties($frow['form_id'] ?? null, $show_unchecked_arr, 'grp_unchecked', "1");
2470 $show_unchecked = strval($show_unchecked_arr['']['grp_unchecked'] ?? null) == "0" ? false : true;
2472 $s = '';
2474 // generic selection list or the generic selection list with add on the fly
2475 // feature
2476 if ($data_type == 1 || $data_type == 26 || $data_type == 43 || $data_type == 46) {
2477 if ($data_type == 46) {
2478 // support for single-selection list with comment support
2479 $selectedValues = explode("|", $currvalue);
2480 $currvalue = $selectedValues[0];
2483 $lrow = sqlQuery("SELECT title FROM list_options " .
2484 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($list_id,$currvalue));
2485 $s = htmlspecialchars(xl_list_label($lrow['title'] ?? ''), ENT_NOQUOTES);
2486 //if there is no matching value in the corresponding lists check backup list
2487 // only supported in data types 1,26,43,46
2488 if ($lrow == 0 && !empty($backup_list) && ($data_type == 1 || $data_type == 26 || $$data_type == 43 || $data_type == 46)) {
2489 $lrow = sqlQuery("SELECT title FROM list_options " .
2490 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list,$currvalue));
2491 $s = htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES);
2494 // If match is not found in main and backup lists, return the key with exclamation mark
2495 if ($s == '') {
2496 $s = nl2br(text(xl_list_label($currvalue))) .
2497 '<span> <i class="fa fas fa-exclamation-circle ml-1"></i></span>';
2500 if ($data_type == 46) {
2501 // support for single-selection list with comment support
2502 $resnote = $selectedValues[1] ?? null;
2503 if (!empty($resnote)) {
2504 $s .= " (" . text($resnote) . ")";
2507 } elseif ($data_type == 2) { // simple text field
2508 $s = nl2br(htmlspecialchars($currvalue, ENT_NOQUOTES));
2509 } elseif ($data_type == 3) { // long or multi-line text field
2510 $s = nl2br(htmlspecialchars($currvalue, ENT_NOQUOTES));
2511 } elseif ($data_type == 4) { // date
2512 $asof = ''; //not used here, but set to prevent a php warning when call optionalAge
2513 $s = '';
2514 $description = (isset($frow['description']) ? htmlspecialchars(xl_layout_label($frow['description']), ENT_QUOTES) : '');
2515 $age_asof_date = '';
2516 $agestr = optionalAge($frow, $currvalue, $age_asof_date, $description);
2517 if ($currvalue === '') {
2518 $s .= '&nbsp;';
2519 } else {
2520 $modtmp = isOption($frow['edit_options'], 'F') === false ? 0 : 1;
2521 if (!$modtmp) {
2522 $s .= text(oeFormatShortDate($currvalue));
2523 } else {
2524 $s .= text(oeFormatDateTime($currvalue));
2526 if ($agestr) {
2527 $s .= "&nbsp;(" . text($agestr) . ")";
2530 } elseif ($data_type == 10 || $data_type == 11) { // provider
2531 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
2532 "WHERE id = ?", array($currvalue));
2533 $s = text(ucwords(($urow['fname'] ?? '') . " " . ($urow['lname'] ?? '')));
2534 } elseif ($data_type == 12) { // pharmacy list
2535 $pres = get_pharmacies();
2536 while ($prow = sqlFetchArray($pres)) {
2537 $key = $prow['id'];
2538 if ($currvalue == $key) {
2539 $s .= htmlspecialchars($prow['name'] . ' ' . $prow['area_code'] . '-' .
2540 $prow['prefix'] . '-' . $prow['number'] . ' / ' .
2541 $prow['line1'] . ' / ' . $prow['city'], ENT_NOQUOTES);
2544 } elseif ($data_type == 13) { // squads
2545 $squads = AclExtended::aclGetSquads();
2546 if ($squads) {
2547 foreach ($squads as $key => $value) {
2548 if ($currvalue == $key) {
2549 $s .= htmlspecialchars($value[3], ENT_NOQUOTES);
2553 } elseif ($data_type == 14) { // address book
2554 $urow = sqlQuery("SELECT fname, lname, specialty, organization FROM users " .
2555 "WHERE id = ?", array($currvalue));
2556 //ViSolve: To display the Organization Name if it exist. Else it will display the user name.
2557 if (!empty($urow['organization'])) {
2558 $uname = $urow['organization'];
2559 } else {
2560 $uname = $urow['lname'] ?? '';
2561 if (!empty($urow['fname'])) {
2562 $uname .= ", " . $urow['fname'];
2566 $s = htmlspecialchars($uname, ENT_NOQUOTES);
2567 } elseif ($data_type == 15) { // billing code
2568 $s = '';
2569 if (!empty($currvalue)) {
2570 $relcodes = explode(';', $currvalue);
2571 foreach ($relcodes as $codestring) {
2572 if ($codestring === '') {
2573 continue;
2575 $tmp = lookup_code_descriptions($codestring);
2576 if ($s !== '') {
2577 $s .= '; ';
2579 if (!empty($tmp)) {
2580 $s .= text($tmp);
2581 } else {
2582 $s .= text($codestring) . ' (' . xlt('not found') . ')';
2586 } elseif ($data_type == 16) { // insurance company list
2587 $insprovs = getInsuranceProviders();
2588 foreach ($insprovs as $key => $ipname) {
2589 if ($currvalue == $key) {
2590 $s .= htmlspecialchars($ipname, ENT_NOQUOTES);
2593 } elseif ($data_type == 17) { // issue types
2594 foreach ($ISSUE_TYPES as $key => $value) {
2595 if ($currvalue == $key) {
2596 $s .= htmlspecialchars($value[1], ENT_NOQUOTES);
2599 } elseif ($data_type == 18) { // visit category
2600 $crow = sqlQuery(
2601 "SELECT pc_catid, pc_catname " .
2602 "FROM openemr_postcalendar_categories WHERE pc_catid = ?",
2603 array($currvalue)
2605 $s = htmlspecialchars($crow['pc_catname'], ENT_NOQUOTES);
2606 } elseif ($data_type == 21) { // a single checkbox or set of labeled checkboxes
2607 if (!$list_id) {
2608 $s .= $currvalue ? '&#9745;' : '&#9744;';
2609 } else {
2610 // In this special case, fld_length is the number of columns generated.
2611 $cols = max(1, $frow['fld_length']);
2612 $avalue = explode('|', $currvalue);
2613 $lres = sqlStatement("SELECT * FROM list_options " .
2614 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2615 $s .= "<table cellspacing='0' cellpadding='0'>";
2616 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
2617 $option_id = $lrow['option_id'];
2618 $option_id_esc = text($option_id);
2619 if ($count % $cols == 0) {
2620 if ($count) {
2621 $s .= "</tr>";
2623 $s .= "<tr>";
2625 $checked = in_array($option_id, $avalue);
2626 if (!$show_unchecked && $checked) {
2627 $s .= "<td nowrap>";
2628 $s .= text(xl_list_label($lrow['title'])) . '&nbsp;&nbsp;';
2629 $s .= "</td>";
2630 } elseif ($show_unchecked) {
2631 $s .= "<td nowrap>";
2632 $s .= $checked ? '&#9745;' : '&#9744;';
2633 $s .= '&nbsp;' . text(xl_list_label($lrow['title'])) . '&nbsp;&nbsp;';
2634 $s .= "</td>";
2637 if ($count) {
2638 $s .= "</tr>";
2640 $s .= "</table>";
2642 } elseif ($data_type == 22) { // a set of labeled text input fields
2643 $tmp = explode('|', $currvalue);
2644 $avalue = array();
2645 foreach ($tmp as $value) {
2646 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2647 $avalue[$matches[1]] = $matches[2];
2651 $lres = sqlStatement("SELECT * FROM list_options " .
2652 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2653 $s .= "<table class='table'>";
2654 while ($lrow = sqlFetchArray($lres)) {
2655 $option_id = $lrow['option_id'];
2656 if (empty($avalue[$option_id])) {
2657 continue;
2660 // Added 5-09 by BM - Translate label if applicable
2661 $s .= "<tr><td class='font-weight-bold align-top'>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . ":&nbsp;</td>";
2663 $s .= "<td class='text align-top'>" . htmlspecialchars($avalue[$option_id], ENT_NOQUOTES) . "</td></tr>";
2666 $s .= "</table>";
2667 } elseif ($data_type == 23) { // a set of exam results; 3 radio buttons and a text field:
2668 $tmp = explode('|', $currvalue);
2669 $avalue = array();
2670 foreach ($tmp as $value) {
2671 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2672 $avalue[$matches[1]] = $matches[2];
2676 $lres = sqlStatement("SELECT * FROM list_options " .
2677 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2678 $s .= "<table class='table'>";
2679 while ($lrow = sqlFetchArray($lres)) {
2680 $option_id = $lrow['option_id'];
2681 $restype = substr(($avalue[$option_id] ?? ''), 0, 1);
2682 $resnote = substr(($avalue[$option_id] ?? ''), 2);
2683 if (empty($restype) && empty($resnote)) {
2684 continue;
2687 // Added 5-09 by BM - Translate label if applicable
2688 $s .= "<tr><td class='font-weight-bold align-top'>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2690 $restype = ($restype == '1') ? xl('Normal') : (($restype == '2') ? xl('Abnormal') : xl('N/A'));
2691 // $s .= "<td class='text align-top'>$restype</td></tr>";
2692 // $s .= "<td class='text align-top'>$resnote</td></tr>";
2693 $s .= "<td class='text align-top'>" . htmlspecialchars($restype, ENT_NOQUOTES) . "&nbsp;</td>";
2694 $s .= "<td class='text align-top'>" . htmlspecialchars($resnote, ENT_NOQUOTES) . "</td>";
2695 $s .= "</tr>";
2698 $s .= "</table>";
2699 } elseif ($data_type == 24) { // the list of active allergies for the current patient
2700 $query = "SELECT title, comments FROM lists WHERE " .
2701 "pid = ? AND type = 'allergy' AND enddate IS NULL " .
2702 "ORDER BY begdate";
2703 // echo "<!-- $query -->\n"; // debugging
2704 $lres = sqlStatement($query, array($GLOBALS['pid']));
2705 $count = 0;
2706 while ($lrow = sqlFetchArray($lres)) {
2707 if ($count++) {
2708 $s .= "<br />";
2711 $s .= htmlspecialchars($lrow['title'], ENT_NOQUOTES);
2712 if ($lrow['comments']) {
2713 $s .= ' (' . htmlspecialchars($lrow['comments'], ENT_NOQUOTES) . ')';
2716 } elseif ($data_type == 25) { // a set of labeled checkboxes, each with a text field:
2717 $tmp = explode('|', $currvalue);
2718 $avalue = array();
2719 foreach ($tmp as $value) {
2720 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
2721 $avalue[$matches[1]] = $matches[2];
2725 $lres = sqlStatement("SELECT * FROM list_options " .
2726 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
2727 $s .= "<table class='table'>";
2728 while ($lrow = sqlFetchArray($lres)) {
2729 $option_id = $lrow['option_id'];
2730 $restype = substr($avalue[$option_id], 0, 1);
2731 $resnote = substr($avalue[$option_id], 2);
2732 if (empty($restype) && empty($resnote)) {
2733 continue;
2736 // Added 5-09 by BM - Translate label if applicable
2737 $s .= "<tr><td class='font-weight-bold align-top'>" . htmlspecialchars(xl_list_label($lrow['title']), ENT_NOQUOTES) . "&nbsp;</td>";
2739 $restype = $restype ? xl('Yes') : xl('No');
2740 $s .= "<td class='text align-top'>" . htmlspecialchars($restype, ENT_NOQUOTES) . "&nbsp;</td>";
2741 $s .= "<td class='text align-top'>" . htmlspecialchars($resnote, ENT_NOQUOTES) . "</td>";
2742 $s .= "</tr>";
2745 $s .= "</table>";
2746 } elseif ($data_type == 27) { // a set of labeled radio buttons
2747 // In this special case, fld_length is the number of columns generated.
2748 $cols = max(1, $frow['fld_length']);
2749 $lres = sqlStatement("SELECT * FROM list_options " .
2750 "WHERE list_id = ? ORDER BY seq, title", array($list_id));
2751 $s .= "<table cellspacing='0' cellpadding='0'>";
2752 for ($count = 0; $lrow = sqlFetchArray($lres); ++$count) {
2753 $option_id = $lrow['option_id'];
2754 $option_id_esc = text($option_id);
2755 if ($count % $cols == 0) {
2756 if ($count) {
2757 $s .= "</tr>";
2759 $s .= "<tr>";
2761 $checked = ((strlen($currvalue) == 0 && $lrow['is_default']) ||
2762 (strlen($currvalue) > 0 && $option_id == $currvalue));
2763 if (!$show_unchecked && $checked) {
2764 $s .= "<td nowrap>";
2765 $s .= text(xl_list_label($lrow['title'])) . '&nbsp;&nbsp;';
2766 $s .= "</td>";
2767 } elseif ($show_unchecked) {
2768 $s .= "<td nowrap>";
2769 $s .= $checked ? '&#9745;' : '&#9744;';
2770 $s .= '&nbsp;' . text(xl_list_label($lrow['title'])) . '&nbsp;&nbsp;';
2771 $s .= "</td>";
2774 if ($count) {
2775 $s .= "</tr>";
2777 $s .= "</table>";
2778 } elseif ($data_type == 28 || $data_type == 32) { // special case for history of lifestyle status; 3 radio buttons
2779 // and a date text field:
2780 // VicarePlus :: A selection list for smoking status.
2781 $tmp = explode('|', $currvalue);
2782 switch (count($tmp)) {
2783 case "4":
2784 $resnote = $tmp[0];
2785 $restype = $tmp[1];
2786 $resdate = oeFormatShortDate($tmp[2]);
2787 $reslist = $tmp[3];
2788 break;
2789 case "3":
2790 $resnote = $tmp[0];
2791 $restype = $tmp[1];
2792 $resdate = oeFormatShortDate($tmp[2]);
2793 $reslist = '';
2794 break;
2795 case "2":
2796 $resnote = $tmp[0];
2797 $restype = $tmp[1];
2798 $resdate = "";
2799 $reslist = '';
2800 break;
2801 case "1":
2802 $resnote = $tmp[0];
2803 $resdate = $restype = "";
2804 $reslist = '';
2805 break;
2806 default:
2807 $restype = $resdate = $resnote = "";
2808 $reslist = '';
2809 break;
2812 $s .= "<table class='table'>";
2814 $s .= "<tr>";
2815 $res = "";
2816 if ($restype == "current" . $field_id) {
2817 $res = xl('Current');
2820 if ($restype == "quit" . $field_id) {
2821 $res = xl('Quit');
2824 if ($restype == "never" . $field_id) {
2825 $res = xl('Never');
2828 if ($restype == "not_applicable" . $field_id) {
2829 $res = xl('N/A');
2832 // $s .= "<td class='text align-top'>$restype</td></tr>";
2833 // $s .= "<td class='text align-top'>$resnote</td></tr>";
2834 if ($data_type == 28) {
2835 if (!empty($resnote)) {
2836 $s .= "<td class='text align-top'>" . htmlspecialchars($resnote, ENT_NOQUOTES) . "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>";
2838 } elseif ($data_type == 32) { //VicarePlus :: Tobacco field has a listbox, text box, date field and 3 radio buttons.
2839 // changes on 5-jun-2k14 (regarding 'Smoking Status - display SNOMED code description')
2840 $smoke_codes = getSmokeCodes();
2841 if (!empty($reslist)) {
2842 if ($smoke_codes[$reslist] != "") {
2843 $code_desc = "( " . $smoke_codes[$reslist] . " )";
2846 $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>";
2849 if (!empty($resnote)) {
2850 $s .= "<td class='text align-top'>" . htmlspecialchars($resnote, ENT_NOQUOTES) . "&nbsp;&nbsp;</td>";
2854 if (!empty($res)) {
2855 $s .= "<td class='text align-top'><strong>" . htmlspecialchars(xl('Status'), ENT_NOQUOTES) . "</strong>:&nbsp;" . htmlspecialchars($res, ENT_NOQUOTES) . "&nbsp;</td>";
2858 if ($restype == "quit" . $field_id) {
2859 $s .= "<td class='text align-top'>" . htmlspecialchars($resdate, ENT_NOQUOTES) . "&nbsp;</td>";
2862 $s .= "</tr>";
2863 $s .= "</table>";
2864 } elseif ($data_type == 31) { // static text. read-only, of course.
2865 $s .= parse_static_text($frow);
2866 } elseif ($data_type == 34) {
2867 $arr = explode("|*|*|*|", $currvalue);
2868 for ($i = 0; $i < sizeof($arr); $i++) {
2869 $s .= $arr[$i];
2871 } elseif ($data_type == 35) { // facility
2872 $urow = $facilityService->getById($currvalue);
2873 $s = htmlspecialchars($urow['name'] ?? '', ENT_NOQUOTES);
2874 } elseif ($data_type == 36 || $data_type == 33) { // Multi select. Supports backup lists
2875 $values_array = explode("|", $currvalue);
2876 $i = 0;
2877 foreach ($values_array as $value) {
2878 $lrow = sqlQuery("SELECT title FROM list_options " .
2879 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($list_id,$value));
2880 if ($lrow == 0 && !empty($backup_list)) {
2881 //use back up list
2882 $lrow = sqlQuery("SELECT title FROM list_options " .
2883 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list,$value));
2886 $title = $lrow['title'] ?? '';
2887 if ($i > 0) {
2888 $s = $s . ", " . text(xl_list_label($title));
2889 } else {
2890 $s = text(xl_list_label($title));
2893 $i++;
2896 // A set of lab test results; Gestation, 3 radio buttons, test value, notes field:
2897 } elseif ($data_type == 37) {
2898 $s .= genLabResults($frow, $currvalue, 2, '');
2899 } elseif ($data_type == 40) { // Image from canvas drawing
2900 if (empty($currvalue)) {
2901 if (preg_match('/\\bimage=([a-zA-Z0-9._-]*)/', $frow['description'], $matches)) {
2902 $currvalue = $GLOBALS['web_root'] . '/sites/' . $_SESSION['site_id'] . '/images/' . $matches[1];
2905 if ($currvalue) {
2906 $s .= "<img src='" . attr($currvalue) . "'>";
2908 } elseif ($data_type == 41 || $data_type == 42) {
2909 if ($currvalue) {
2910 $s .= "<img class='w-auto' style='height: 70px;' src='" . attr($currvalue) . "'>";
2912 } elseif ($data_type == 44 || $data_type == 45) { // Multiple select facility and provider
2913 $values_array = explode("|", $currvalue);
2914 $i = 0;
2915 foreach ($values_array as $value) {
2916 if ($data_type == 44) {
2917 $lrow = sqlQuery("SELECT name as name FROM facility WHERE id = ?", array($value));
2919 if ($data_type == 45) {
2920 $lrow = sqlQuery("SELECT CONCAT(fname,' ',lname) as name FROM users WHERE id = ?", array($value));
2922 if ($i > 0) {
2923 $s = $s . ", " . htmlspecialchars($lrow['name'], ENT_NOQUOTES);
2924 } else {
2925 $s = text($lrow['name'] ?? '');
2927 $i++;
2930 // Patient selector field.
2931 } elseif ($data_type == 51) {
2932 if (!empty($currvalue)) {
2933 $s .= text(getPatientDescription($currvalue));
2935 } elseif ($data_type == 52) {
2936 global $pid;
2937 $patientService = new PatientService();
2938 $rows = $patientService->getPatientNameHistory($pid);
2939 $i = 0;
2940 foreach ($rows as $row) {
2941 // name escaped in fetch
2942 if ($i > 0) {
2943 $s .= ", " . $row['formatted_name'];
2944 } else {
2945 $s = $row['formatted_name'] ?? '';
2947 $i++;
2949 // now that we've concatenated everything, let's escape it.
2950 $s = text($s);
2951 } elseif ($data_type == 53) {
2952 $service = new EncounterService();
2953 if (!empty($currvalue)) {
2954 $encounterResult = $service->getEncounterById($currvalue);
2955 if (!empty($encounterResult) && $encounterResult->hasData()) {
2956 $encounter = reset($encounterResult->getData());
2957 $s = text($encounter['date'] ?? '');
2960 } elseif ($data_type == 54) {
2961 include "templates/address_list_display.php";
2964 return $s;
2967 // Generate plain text versions of selected LBF field types.
2968 // Currently used by interface/patient_file/download_template.php and interface/main/finder/dynamic_finder_ajax.php.
2969 // More field types might need to be supported here in the future.
2971 function generate_plaintext_field($frow, $currvalue)
2973 global $ISSUE_TYPES;
2975 $data_type = $frow['data_type'];
2976 $field_id = isset($frow['field_id']) ? $frow['field_id'] : null;
2977 $list_id = $frow['list_id'];
2978 $backup_list = $frow['backup_list'] ?? null;
2979 $edit_options = $frow['edit_options'] ?? null;
2980 $s = '';
2982 // generic selection list or the generic selection list with add on the fly
2983 // feature, or radio buttons
2984 // Supports backup lists (for datatypes 1,26,43)
2985 if ($data_type == 1 || $data_type == 26 || $data_type == 27 || $data_type == 43 || $data_type == 46) {
2986 if ($data_type == 46) {
2987 // support for single-selection list with comment support
2988 $selectedValues = explode("|", $currvalue);
2989 $currvalue = $selectedValues[0];
2992 $lrow = sqlQuery(
2993 "SELECT title FROM list_options " .
2994 "WHERE list_id = ? AND option_id = ? AND activity = 1",
2995 array($list_id, $currvalue)
2997 $s = xl_list_label($lrow['title'] ?? '');
2998 //if there is no matching value in the corresponding lists check backup list
2999 // only supported in data types 1,26,43
3000 if ($lrow == 0 && !empty($backup_list) && ($data_type == 1 || $data_type == 26 || $data_type == 43 || $data_type == 46)) {
3001 $lrow = sqlQuery("SELECT title FROM list_options " .
3002 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list, $currvalue));
3003 $s = xl_list_label($lrow['title']);
3006 if ($data_type == 46) {
3007 // support for single-selection list with comment support
3008 $resnote = $selectedValues[1] ?? null;
3009 if (!empty($resnote)) {
3010 $s .= " (" . $resnote . ")";
3013 } elseif ($data_type == 2 || $data_type == 3 || $data_type == 15) { // simple or long text field
3014 $s = $currvalue;
3015 } elseif ($data_type == 4) { // date
3016 $modtmp = isOption($edit_options, 'F') === false ? 0 : 1;
3017 if (!$modtmp) {
3018 $s = text(oeFormatShortDate($currvalue));
3019 } else {
3020 $s = text(oeFormatDateTime($currvalue));
3022 $description = (isset($frow['description']) ? htmlspecialchars(xl_layout_label($frow['description']), ENT_QUOTES) : '');
3023 $age_asof_date = '';
3024 // Optional display of age or gestational age.
3025 $tmp = optionalAge($frow, $currvalue, $age_asof_date, $description);
3026 if ($tmp) {
3027 $s .= ' ' . $tmp;
3029 } elseif ($data_type == 10 || $data_type == 11) { // provider
3030 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
3031 "WHERE id = ?", array($currvalue));
3032 $s = ucwords($urow['fname'] . " " . $urow['lname']);
3033 } elseif ($data_type == 12) { // pharmacy list
3034 $pres = get_pharmacies();
3035 while ($prow = sqlFetchArray($pres)) {
3036 $key = $prow['id'];
3037 if ($currvalue == $key) {
3038 $s .= $prow['name'] . ' ' . $prow['area_code'] . '-' .
3039 $prow['prefix'] . '-' . $prow['number'] . ' / ' .
3040 $prow['line1'] . ' / ' . $prow['city'];
3043 } elseif ($data_type == 14) { // address book
3044 $urow = sqlQuery("SELECT fname, lname, specialty FROM users " .
3045 "WHERE id = ?", array($currvalue));
3046 $uname = $urow['lname'];
3047 if ($urow['fname']) {
3048 $uname .= ", " . $urow['fname'];
3051 $s = $uname;
3052 } elseif ($data_type == 16) { // insurance company list
3053 $insprovs = getInsuranceProviders();
3054 foreach ($insprovs as $key => $ipname) {
3055 if ($currvalue == $key) {
3056 $s .= $ipname;
3059 } elseif ($data_type == 17) { // issue type
3060 foreach ($ISSUE_TYPES as $key => $value) {
3061 if ($currvalue == $key) {
3062 $s .= $value[1];
3065 } elseif ($data_type == 18) { // visit category
3066 $crow = sqlQuery(
3067 "SELECT pc_catid, pc_catname " .
3068 "FROM openemr_postcalendar_categories WHERE pc_catid = ?",
3069 array($currvalue)
3071 $s = $crow['pc_catname'];
3072 } elseif ($data_type == 21) { // a set of labeled checkboxes
3073 if (!$list_id) {
3074 $s .= $currvalue ? xlt('Yes') : xlt('No');
3075 } else {
3076 $avalue = explode('|', $currvalue);
3077 $lres = sqlStatement("SELECT * FROM list_options " .
3078 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
3079 $count = 0;
3080 while ($lrow = sqlFetchArray($lres)) {
3081 $option_id = $lrow['option_id'];
3082 if (in_array($option_id, $avalue)) {
3083 if ($count++) {
3084 $s .= "; ";
3086 $s .= xl_list_label($lrow['title']);
3090 } elseif ($data_type == 22) { // a set of labeled text input fields
3091 $tmp = explode('|', $currvalue);
3092 $avalue = array();
3093 foreach ($tmp as $value) {
3094 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
3095 $avalue[$matches[1]] = $matches[2];
3099 $lres = sqlStatement("SELECT * FROM list_options " .
3100 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
3101 while ($lrow = sqlFetchArray($lres)) {
3102 $option_id = $lrow['option_id'];
3103 if (empty($avalue[$option_id])) {
3104 continue;
3107 if ($s !== '') {
3108 $s .= '; ';
3111 $s .= xl_list_label($lrow['title']) . ': ';
3112 $s .= $avalue[$option_id];
3114 } elseif ($data_type == 23) { // A set of exam results; 3 radio buttons and a text field.
3115 // This shows abnormal results only.
3116 $tmp = explode('|', $currvalue);
3117 $avalue = array();
3118 foreach ($tmp as $value) {
3119 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
3120 $avalue[$matches[1]] = $matches[2];
3124 $lres = sqlStatement("SELECT * FROM list_options " .
3125 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
3126 while ($lrow = sqlFetchArray($lres)) {
3127 $option_id = $lrow['option_id'];
3128 $restype = substr($avalue[$option_id], 0, 1);
3129 $resnote = substr($avalue[$option_id], 2);
3130 if (empty($restype) && empty($resnote)) {
3131 continue;
3134 if ($restype != '2') {
3135 continue; // show abnormal results only
3138 if ($s !== '') {
3139 $s .= '; ';
3142 $s .= xl_list_label($lrow['title']);
3143 if (!empty($resnote)) {
3144 $s .= ': ' . $resnote;
3147 } elseif ($data_type == 24) { // the list of active allergies for the current patient
3148 $query = "SELECT title, comments FROM lists WHERE " .
3149 "pid = ? AND type = 'allergy' AND enddate IS NULL " .
3150 "ORDER BY begdate";
3151 $lres = sqlStatement($query, array($GLOBALS['pid']));
3152 $count = 0;
3153 while ($lrow = sqlFetchArray($lres)) {
3154 if ($count++) {
3155 $s .= "; ";
3158 $s .= $lrow['title'];
3159 if ($lrow['comments']) {
3160 $s .= ' (' . $lrow['comments'] . ')';
3163 } elseif ($data_type == 25) { // a set of labeled checkboxes, each with a text field:
3164 $tmp = explode('|', $currvalue);
3165 $avalue = array();
3166 foreach ($tmp as $value) {
3167 if (preg_match('/^([^:]+):(.*)$/', $value, $matches)) {
3168 $avalue[$matches[1]] = $matches[2];
3172 $lres = sqlStatement("SELECT * FROM list_options " .
3173 "WHERE list_id = ? AND activity = 1 ORDER BY seq, title", array($list_id));
3174 while ($lrow = sqlFetchArray($lres)) {
3175 $option_id = $lrow['option_id'];
3176 $restype = substr($avalue[$option_id], 0, 1);
3177 $resnote = substr($avalue[$option_id], 2);
3178 if (empty($restype) && empty($resnote)) {
3179 continue;
3182 if ($s !== '') {
3183 $s .= '; ';
3186 $s .= xl_list_label($lrow['title']);
3187 $restype = $restype ? xl('Yes') : xl('No');
3188 $s .= $restype;
3189 if ($resnote) {
3190 $s .= ' ' . $resnote;
3193 } elseif ($data_type == 28 || $data_type == 32) { // special case for history of lifestyle status; 3 radio buttons and a date text field:
3194 // VicarePlus :: A selection list for smoking status.
3195 $tmp = explode('|', $currvalue);
3196 $resnote = count($tmp) > 0 ? $tmp[0] : '';
3197 $restype = count($tmp) > 1 ? $tmp[1] : '';
3198 $resdate = count($tmp) > 2 ? oeFormatShortDate($tmp[2]) : '';
3199 $reslist = count($tmp) > 3 ? $tmp[3] : '';
3200 $res = "";
3201 if ($restype == "current" . $field_id) {
3202 $res = xl('Current');
3205 if ($restype == "quit" . $field_id) {
3206 $res = xl('Quit');
3209 if ($restype == "never" . $field_id) {
3210 $res = xl('Never');
3213 if ($restype == "not_applicable" . $field_id) {
3214 $res = xl('N/A');
3217 if ($data_type == 28) {
3218 if (!empty($resnote)) {
3219 $s .= $resnote;
3221 } elseif ($data_type == 32) { // Tobacco field has a listbox, text box, date field and 3 radio buttons.
3222 if (!empty($reslist)) {
3223 $s .= generate_plaintext_field(array('data_type' => '1','list_id' => $list_id), $reslist);
3226 if (!empty($resnote)) {
3227 $s .= ' ' . $resnote;
3231 if (!empty($res)) {
3232 if ($s !== '') {
3233 $s .= ' ';
3236 $s .= xl('Status') . ' ' . $res;
3239 if ($restype == "quit" . $field_id) {
3240 if ($s !== '') {
3241 $s .= ' ';
3244 $s .= $resdate;
3246 } elseif ($data_type == 35) { // Facility, so facility can be listed in plain-text, as in patient finder column
3247 $facilityService = new FacilityService();
3248 $facility = $facilityService->getById($currvalue);
3249 $s = $facility['name'];
3250 } elseif ($data_type == 36 || $data_type == 33) { // Multi select. Supports backup lists
3251 $values_array = explode("|", $currvalue);
3253 $i = 0;
3254 foreach ($values_array as $value) {
3255 $lrow = sqlQuery("SELECT title FROM list_options " .
3256 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($list_id,$value));
3258 if ($lrow == 0 && !empty($backup_list)) {
3259 //use back up list
3260 $lrow = sqlQuery("SELECT title FROM list_options " .
3261 "WHERE list_id = ? AND option_id = ? AND activity = 1", array($backup_list,$value));
3264 if ($i > 0) {
3265 $s = $s . ", " . xl_list_label($lrow['title']);
3266 } else {
3267 $s = xl_list_label($lrow['title']);
3270 $i++;
3273 // A set of lab test results; Gestation, 3 radio buttons, test value, notes field:
3274 } elseif ($data_type == 37) {
3275 $s .= genLabResults($frow, $currvalue, 3, '');
3276 } elseif ($data_type == 44 || $data_type == 45) {
3277 $values_array = explode("|", $currvalue);
3279 $i = 0;
3280 foreach ($values_array as $value) {
3281 if ($data_type == 44) {
3282 $lrow = sqlQuery("SELECT name as name FROM facility WHERE id = ?", array($value));
3284 if ($data_type == 45) {
3285 $lrow = sqlQuery("SELECT CONCAT(fname,' ',lname) as name FROM users WHERE id = ?", array($value));
3288 if ($i > 0) {
3289 $s = $s . ", " . $lrow['name'];
3290 } else {
3291 $s = $lrow['name'];
3294 $i++;
3297 // Patient selector field.
3298 } elseif ($data_type == 51) {
3299 if (!empty($currvalue)) {
3300 $s .= getPatientDescription($currvalue);
3304 return $s;
3307 $CPR = 4; // cells per row of generic data
3308 $last_group = '';
3309 $cell_count = 0;
3310 $item_count = 0;
3312 function disp_end_cell()
3314 global $item_count, $cell_count;
3315 if ($item_count > 0) {
3316 echo "</td>";
3317 $item_count = 0;
3321 function disp_end_row()
3323 global $cell_count, $CPR;
3324 disp_end_cell();
3325 if ($cell_count > 0) {
3326 for (; $cell_count < $CPR; ++$cell_count) {
3327 echo "<td></td>";
3330 echo "</tr>\n";
3331 $cell_count = 0;
3335 function disp_end_group()
3337 global $last_group;
3338 if (strlen($last_group) > 0) {
3339 disp_end_row();
3343 // Bootstrapped versions of disp_end_* functions:
3345 function bs_disp_end_cell()
3347 global $item_count;
3348 if ($item_count > 0) {
3349 echo "</div>"; // end BS column
3350 $item_count = 0;
3354 function bs_disp_end_row()
3356 global $cell_count, $CPR, $BS_COL_CLASS;
3357 bs_disp_end_cell();
3358 if ($cell_count > 0 && $cell_count < $CPR) {
3359 // Create a cell occupying the remaining bootstrap columns.
3360 // BS columns will be less than 12 if $CPR is not 2, 3, 4, 6 or 12.
3361 $bs_cols_remaining = ($CPR - $cell_count) * intval(12 / $CPR);
3362 echo "<div class='$BS_COL_CLASS-$bs_cols_remaining'></div>";
3364 if ($cell_count > 0) {
3365 echo "</div><!-- End BS row -->\n";
3366 $cell_count = 0;
3370 function bs_disp_end_group()
3372 global $last_group;
3373 if (strlen($last_group) > 0) {
3374 bs_disp_end_row();
3380 function getPatientDescription($pid)
3382 $prow = sqlQuery("SELECT lname, fname FROM patient_data WHERE pid = ?", array($pid));
3383 if ($prow) {
3384 return $prow['lname'] . ", " . $prow['fname'] . " ($pid)";
3386 return xl('Unknown') . " ($pid)";
3389 // Accumulate action conditions into a JSON expression for the browser side.
3390 function accumActionConditions(&$frow, &$condition_str)
3392 $field_id = $frow['field_id'];
3393 $conditions = empty($frow['conditions']) ? array() : unserialize($frow['conditions'], ['allowed_classes' => false]);
3394 $action = 'skip';
3395 foreach ($conditions as $key => $condition) {
3396 if ($key === 'action') {
3397 // If specified this should be the first array item.
3398 if ($condition) {
3399 $action = $condition;
3401 continue;
3403 if (empty($condition['id'])) {
3404 continue;
3406 $andor = empty($condition['andor']) ? '' : $condition['andor'];
3407 if ($condition_str) {
3408 $condition_str .= ",\n";
3410 $condition_str .= "{" .
3411 "target:" . js_escape($field_id) . ", " .
3412 "action:" . js_escape($action) . ", " .
3413 "id:" . js_escape($condition['id']) . ", " .
3414 "itemid:" . js_escape($condition['itemid']) . ", " .
3415 "operator:" . js_escape($condition['operator']) . ", " .
3416 "value:" . js_escape($condition['value']) . ", ";
3417 if ($frow['data_type'] == 15 && strpos($frow['edit_options'], '2') !== false) {
3418 // For billing codes handle requirement to display its description.
3419 $tmp = explode('=', $action, 2);
3420 if (!empty($tmp[1])) {
3421 $condition_str .= "valdesc:" . js_escape(getCodeDescription($tmp[1])) . ", ";
3424 $condition_str .= "andor:" . js_escape($andor) . "}";
3428 function getCodeDescription($codestring, $defaulttype = 'ICD10')
3430 if ($codestring === '') {
3431 return '';
3433 list($ctype, $code) = explode(':', $codestring);
3434 if (empty($code)) {
3435 $code = $ctype;
3436 $ctype = $defaulttype;
3438 $desc = lookup_code_descriptions("$ctype:$code");
3439 if (!empty($desc)) {
3440 return $desc;
3441 } else {
3442 return $codestring;
3446 // This checks if the given field with the given value should have an action applied.
3447 // Originally the only action was skip, but now you can also set the field to a
3448 // specified value, or "skip and otherwise set a value".
3449 // It somewhat mirrors the checkSkipConditions function in options.js.php.
3450 // If you use this for multiple layouts in the same script, you should
3451 // clear $sk_layout_items before each layout.
3452 function isSkipped(&$frow, $currvalue)
3454 global $sk_layout_items;
3456 // Accumulate an array of the encountered fields and their values.
3457 // It is assumed that fields appear before they are tested by another field.
3458 // TBD: Bad assumption?
3459 $field_id = $frow['field_id'];
3460 if (!is_array($sk_layout_items)) {
3461 $sk_layout_items = array();
3463 $sk_layout_items[$field_id] = array('row' => $frow, 'value' => $currvalue);
3465 if (empty($frow['conditions'])) {
3466 return false;
3469 $skiprows = unserialize($frow['conditions'], ['allowed_classes' => false]);
3470 $prevandor = '';
3471 $prevcond = false;
3472 $datatype = $frow['data_type'];
3473 $action = 'skip'; // default action if none specified
3475 foreach ($skiprows as $key => $skiprow) {
3476 // id referenced field id
3477 // itemid referenced array key if applicable
3478 // operator "eq", "ne", "se" or "ns"
3479 // value if eq or ne, some string to compare with
3480 // andor "and", "or" or empty
3482 if ($key === 'action') {
3483 // Action value is a string. It can be "skip", or "value=" or "hsval=" followed by a value.
3484 $action = $skiprow;
3485 continue;
3488 if (empty($skiprow['id'])) {
3489 continue;
3492 $id = $skiprow['id'];
3493 if (!isset($sk_layout_items[$id])) {
3494 error_log("Function isSkipped() cannot find skip source field '" . errorLogEscape($id) . "'.");
3495 continue;
3497 $itemid = $skiprow['itemid'];
3498 $operator = $skiprow['operator'];
3499 $skipval = $skiprow['value'];
3500 $srcvalue = $sk_layout_items[$id]['value'];
3501 $src_datatype = $sk_layout_items[$id]['row']['data_type'];
3502 $src_list_id = $sk_layout_items[$id]['row']['list_id'];
3504 // Some data types use itemid and we have to dig for their value.
3505 if ($src_datatype == 21 && $src_list_id) { // array of checkboxes
3506 $tmp = explode('|', $srcvalue);
3507 $srcvalue = in_array($itemid, $tmp);
3508 } elseif ($src_datatype == 22 || $src_datatype == 23 || $src_datatype == 25) {
3509 $tmp = explode('|', $srcvalue);
3510 $srcvalue = '';
3511 foreach ($tmp as $tmp2) {
3512 if (strpos($tmp2, "$itemid:") === 0) {
3513 if ($datatype == 22) {
3514 $srcvalue = substr($tmp2, strlen($itemid) + 1);
3515 } else {
3516 $srcvalue = substr($tmp2, strlen($itemid) + 1, 1);
3522 // Compute the result of the test for this condition row.
3523 // PHP's looseness with variable type conversion helps us here.
3524 $condition = false;
3525 if ($operator == 'eq') {
3526 $condition = $srcvalue == $skipval;
3527 } elseif ($operator == 'ne') {
3528 $condition = $srcvalue != $skipval;
3529 } elseif ($operator == 'se') {
3530 $condition = $srcvalue == true;
3531 } elseif ($operator == 'ns') {
3532 $condition = $srcvalue != true;
3533 } else {
3534 error_log("Unknown skip operator '" . errorLogEscape($operator) . "' for field '" . errorLogEscape($field_id) . "'.");
3537 // Logic to accumulate multiple conditions for the same target.
3538 if ($prevandor == 'and') {
3539 $condition = $condition && $prevcond;
3540 } elseif ($prevandor == 'or') {
3541 $condition = $condition || $prevcond;
3543 $prevandor = $skiprow['andor'];
3544 $prevcond = $condition;
3547 if (substr($action, 0, 6) == 'hsval=') {
3548 return $prevcond ? 'skip' : ('value=' . substr($action, 6));
3550 return $prevcond ? $action : '';
3553 // Load array of names of the given layout and its groups.
3554 function getLayoutProperties($formtype, &$grparr, $sel = "grp_title", $limit = null)
3556 if ($sel != '*' && strpos($sel, 'grp_group_id') === false) {
3557 $sel = "grp_group_id, $sel";
3559 $gres = sqlStatement("SELECT $sel FROM layout_group_properties WHERE grp_form_id = ? " .
3560 " ORDER BY grp_group_id " .
3561 ($limit ? "LIMIT " . escape_limit($limit) : ""), array($formtype));
3562 while ($grow = sqlFetchArray($gres)) {
3563 // TBD: Remove this after grp_init_open column is implemented.
3564 if ($sel == '*' && !isset($grow['grp_init_open'])) {
3565 $tmprow = sqlQuery(
3566 "SELECT form_id FROM layout_options " .
3567 "WHERE form_id = ? AND group_id LIKE ? AND uor > 0 AND edit_options LIKE '%I%' " .
3568 "LIMIT 1",
3569 array($formtype, $grow['grp_group_id'] . '%')
3571 $grow['grp_init_open'] = !empty($tmprow['form_id']);
3573 $grparr[$grow['grp_group_id']] = $grow;
3577 function display_layout_rows($formtype, $result1, $result2 = '')
3579 global $item_count, $cell_count, $last_group, $CPR;
3581 if ('HIS' == $formtype) {
3582 $formtype .= '%'; // TBD: DEM also?
3584 $pres = sqlStatement(
3585 "SELECT grp_form_id, grp_seq, grp_title " .
3586 "FROM layout_group_properties " .
3587 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
3588 "ORDER BY grp_seq, grp_title, grp_form_id",
3589 array("$formtype")
3591 while ($prow = sqlFetchArray($pres)) {
3592 $formtype = $prow['grp_form_id'];
3593 $last_group = '';
3594 $cell_count = 0;
3595 $item_count = 0;
3597 $grparr = array();
3598 getLayoutProperties($formtype, $grparr, '*');
3600 $TOPCPR = empty($grparr['']['grp_columns']) ? 4 : $grparr['']['grp_columns'];
3602 $fres = sqlStatement("SELECT * FROM layout_options " .
3603 "WHERE form_id = ? AND uor > 0 " .
3604 "ORDER BY group_id, seq", array($formtype));
3606 while ($frow = sqlFetchArray($fres)) {
3607 $this_group = $frow['group_id'];
3608 $titlecols = $frow['titlecols'];
3609 $datacols = $frow['datacols'];
3610 $data_type = $frow['data_type'];
3611 $field_id = $frow['field_id'];
3612 $list_id = $frow['list_id'];
3613 $currvalue = '';
3614 $jump_new_row = isOption($frow['edit_options'], 'J');
3615 $prepend_blank_row = isOption($frow['edit_options'], 'K');
3616 $portal_exclude = (!empty($_SESSION["patient_portal_onsite_two"]) && isOption($frow['edit_options'], 'EP')) ?? null;
3617 $span_col_row = isOption($frow['edit_options'], 'SP');
3619 if (!empty($portal_exclude)) {
3620 continue;
3623 $CPR = empty($grparr[$this_group]['grp_columns']) ? $TOPCPR : $grparr[$this_group]['grp_columns'];
3625 if ($formtype == 'DEM') {
3626 if (strpos($field_id, 'em_') === 0) {
3627 // Skip employer related fields, if it's disabled.
3628 if ($GLOBALS['omit_employers']) {
3629 continue;
3632 $tmp = substr($field_id, 3);
3633 if (isset($result2[$tmp])) {
3634 $currvalue = $result2[$tmp];
3636 } else {
3637 if (isset($result1[$field_id])) {
3638 $currvalue = $result1[$field_id];
3641 } else {
3642 if (isset($result1[$field_id])) {
3643 $currvalue = $result1[$field_id];
3647 // Handle a data category (group) change.
3648 if (strcmp($this_group, $last_group) != 0) {
3649 $group_name = $grparr[$this_group]['grp_title'];
3650 // totally skip generating the employer category, if it's disabled.
3651 if ($group_name === 'Employer' && $GLOBALS['omit_employers']) {
3652 continue;
3655 disp_end_group();
3656 $last_group = $this_group;
3659 // filter out all the empty field data from the patient report.
3660 if (!empty($currvalue) && !($currvalue == '0000-00-00 00:00:00')) {
3661 // Handle starting of a new row.
3662 if (($titlecols > 0 && $cell_count >= $CPR) || $cell_count == 0 || $prepend_blank_row || $jump_new_row) {
3663 disp_end_row();
3664 if ($prepend_blank_row) {
3665 echo "<tr><td class='label' colspan='" . ($CPR + 1) . "'>&nbsp;</td></tr>\n";
3667 echo "<tr>";
3668 if ($group_name) {
3669 echo "<td class='groupname'>";
3670 echo text(xl_layout_label($group_name));
3671 $group_name = '';
3672 } else {
3673 echo "<td class='align-top'>&nbsp;";
3676 echo "</td>";
3679 if ($item_count == 0 && $titlecols == 0) {
3680 $titlecols = 1;
3683 // Handle starting of a new label cell.
3684 if ($titlecols > 0 || $span_col_row) {
3685 disp_end_cell();
3686 $titlecols = $span_col_row ? 0 : $titlecols;
3687 $titlecols_esc = htmlspecialchars($titlecols, ENT_QUOTES);
3688 if (!$span_col_row) {
3689 echo "<td class='label_custom' colspan='$titlecols_esc' ";
3690 echo ">";
3692 $cell_count += $titlecols;
3695 ++$item_count;
3697 // Prevent title write if span entire row.
3698 if (!$span_col_row) {
3699 // Added 5-09 by BM - Translate label if applicable
3700 if ($frow['title']) {
3701 $tmp = xl_layout_label($frow['title']);
3702 echo text($tmp);
3703 // Append colon only if label does not end with punctuation.
3704 if (strpos('?!.,:-=', substr($tmp, -1, 1)) === false) {
3705 echo ':';
3707 } else {
3708 echo "&nbsp;";
3711 // Handle starting of a new data cell.
3712 if ($datacols > 0) {
3713 disp_end_cell();
3714 $datacols = $span_col_row ? $CPR : $datacols;
3715 $datacols_esc = htmlspecialchars($datacols, ENT_QUOTES);
3716 echo "<td class='text data' colspan='$datacols_esc'";
3717 echo ">";
3718 $cell_count += $datacols;
3721 ++$item_count;
3722 echo generate_display_field($frow, $currvalue);
3725 disp_end_group();
3726 } // End this layout, there may be more in the case of history.
3729 // This generates the tabs for a form.
3731 function display_layout_tabs($formtype, $result1, $result2 = '')
3733 global $item_count, $cell_count, $last_group, $CPR;
3735 if ('HIS' == $formtype) {
3736 $formtype .= '%'; // TBD: DEM also?
3738 $pres = sqlStatement(
3739 "SELECT grp_form_id, grp_seq, grp_title " .
3740 "FROM layout_group_properties " .
3741 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
3742 "ORDER BY grp_seq, grp_title, grp_form_id",
3743 array("$formtype")
3745 $first = true;
3746 while ($prow = sqlFetchArray($pres)) {
3747 $formtype = $prow['grp_form_id'];
3748 $last_group = '';
3749 $cell_count = 0;
3750 $item_count = 0;
3752 $grparr = array();
3753 getLayoutProperties($formtype, $grparr);
3755 $fres = sqlStatement("SELECT distinct group_id FROM layout_options " .
3756 "WHERE form_id = ? AND uor > 0 " .
3757 "ORDER BY group_id", array($formtype));
3759 $prev_group = '';
3760 while ($frow = sqlFetchArray($fres)) {
3761 $this_group = $frow['group_id'];
3762 if (substr($prev_group, 0, 1) === substr($this_group, 0, 1)) {
3763 // Skip sub-groups, they will not start a new tab.
3764 continue;
3766 $prev_group = $this_group;
3767 $group_name = $grparr[$this_group]['grp_title'];
3768 if ($group_name === 'Employer' && $GLOBALS['omit_employers']) {
3769 continue;
3772 <li <?php echo $first ? 'class="current"' : '' ?>>
3773 <a href="#" id="header_tab_<?php echo attr($group_name); ?>">
3774 <?php echo text(xl_layout_label($group_name)); ?></a>
3775 </li>
3776 <?php
3777 $first = false;
3779 } // End this layout, there may be more in the case of history.
3782 // This generates the tab contents of the display version of a form.
3784 function display_layout_tabs_data($formtype, $result1, $result2 = '')
3786 global $item_count, $cell_count, $last_group, $CPR;
3788 if ('HIS' == $formtype) {
3789 $formtype .= '%'; // TBD: DEM also?
3791 $pres = sqlStatement(
3792 "SELECT grp_form_id, grp_seq, grp_title " .
3793 "FROM layout_group_properties " .
3794 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
3795 "ORDER BY grp_seq, grp_title, grp_form_id",
3796 array("$formtype")
3798 $first = true;
3800 // This loops once per layout. Only Patient History can have multiple layouts.
3801 while ($prow = sqlFetchArray($pres)) {
3802 $formtype = $prow['grp_form_id'];
3803 $last_group = '';
3804 $cell_count = 0;
3805 $item_count = 0;
3807 $grparr = array();
3808 getLayoutProperties($formtype, $grparr, '*');
3810 $TOPCPR = empty($grparr['']['grp_columns']) ? 4 : $grparr['']['grp_columns'];
3812 // By selecting distinct group_id from layout_options we avoid empty groups.
3813 $fres = sqlStatement("SELECT distinct group_id FROM layout_options " .
3814 "WHERE form_id = ? AND uor > 0 " .
3815 "ORDER BY group_id", array($formtype));
3817 $prev_group = '';
3819 // This loops once per group within a given layout.
3820 while ($frow = sqlFetchArray($fres)) {
3821 $this_group = isset($frow['group_id']) ? $frow['group_id'] : "" ;
3823 if ($grparr[$this_group]['grp_title'] === 'Employer' && $GLOBALS['omit_employers']) {
3824 continue;
3826 $CPR = empty($grparr[$this_group]['grp_columns']) ? $TOPCPR : $grparr[$this_group]['grp_columns'];
3827 $subtitle = empty($grparr[$this_group]['grp_subtitle']) ? '' : xl_layout_label($grparr[$this_group]['grp_subtitle']);
3829 $group_fields_query = sqlStatement(
3830 "SELECT * FROM layout_options " .
3831 "WHERE form_id = ? AND uor > 0 AND group_id = ? " .
3832 "ORDER BY seq",
3833 array($formtype, $this_group)
3836 if (substr($this_group, 0, 1) !== substr($prev_group, 0, 1)) {
3837 // Each new top level group gets its own tab div.
3838 if (!$first) {
3839 echo "</div>\n";
3841 echo "<div class='tab" . ($first ? ' current' : '') . "'>\n";
3843 echo "<table border='0' cellpadding='0'>\n";
3845 // This loops once per field within a given group.
3846 while ($group_fields = sqlFetchArray($group_fields_query)) {
3847 $titlecols = $group_fields['titlecols'];
3848 $datacols = $group_fields['datacols'];
3849 $data_type = $group_fields['data_type'];
3850 $field_id = $group_fields['field_id'];
3851 $list_id = $group_fields['list_id'];
3852 $currvalue = '';
3853 $edit_options = $group_fields['edit_options'];
3854 $jump_new_row = isOption($edit_options, 'J');
3855 $prepend_blank_row = isOption($edit_options, 'K');
3856 $span_col_row = isOption($edit_options, 'SP');
3858 if ($formtype == 'DEM') {
3859 if (strpos($field_id, 'em_') === 0) {
3860 // Skip employer related fields, if it's disabled.
3861 if ($GLOBALS['omit_employers']) {
3862 continue;
3865 $tmp = substr($field_id, 3);
3866 if (isset($result2[$tmp])) {
3867 $currvalue = $result2[$tmp];
3869 } else {
3870 if (isset($result1[$field_id])) {
3871 $currvalue = $result1[$field_id];
3874 } else {
3875 if (isset($result1[$field_id])) {
3876 $currvalue = $result1[$field_id];
3880 // Skip this field if action conditions call for that.
3881 // Note this also accumulates info for subsequent skip tests.
3882 $skip_this_field = isSkipped($group_fields, $currvalue) == 'skip';
3884 // Skip this field if its do-not-print option is set.
3885 if (isOption($edit_options, 'X') !== false) {
3886 $skip_this_field = true;
3889 // Handle a data category (group) change.
3890 if (strcmp($this_group, $last_group) != 0) {
3891 $group_name = $grparr[$this_group]['grp_title'];
3892 // totally skip generating the employer category, if it's disabled.
3893 if ($group_name === 'Employer' && $GLOBALS['omit_employers']) {
3894 continue;
3896 $last_group = $this_group;
3899 // Handle starting of a new row.
3900 if (($titlecols > 0 && $cell_count >= $CPR) || $cell_count == 0 || $prepend_blank_row || $jump_new_row) {
3901 disp_end_row();
3902 if ($subtitle) {
3903 // Group subtitle exists and is not displayed yet.
3904 echo "<tr><td class='label' style='background-color: var(--gray300); padding: 4px' colspan='$CPR'>" . text($subtitle) . "</td></tr>\n";
3905 echo "<tr><td class='label' style='height: 5px' colspan='$CPR'></td></tr>\n";
3906 $subtitle = '';
3908 if ($prepend_blank_row) {
3909 echo "<tr><td class='label' style='font-size:25%' colspan='$CPR'>&nbsp;</td></tr>\n";
3911 echo "<tr>";
3914 if ($item_count == 0 && $titlecols == 0) {
3915 $titlecols = 1;
3918 // Handle starting of a new label cell.
3919 if ($titlecols > 0 || $span_col_row) {
3920 disp_end_cell();
3921 $titlecols = $span_col_row ? 0 : $titlecols;
3922 $titlecols_esc = htmlspecialchars($titlecols, ENT_QUOTES);
3923 $field_id_label = 'label_' . $group_fields['field_id'];
3924 if (!$span_col_row) {
3925 echo "<td class='label_custom' colspan='$titlecols_esc' id='" . attr($field_id_label) . "'";
3926 echo ">";
3928 $cell_count += $titlecols;
3931 ++$item_count;
3933 if ($datacols == 0) {
3934 // Data will be in the same cell, so prevent wrapping to a new line.
3935 echo "<span class='text-nowrap mr-2'>";
3938 $field_id_label = 'label_' . $group_fields['field_id'];
3939 if (!$span_col_row) {
3940 echo "<span id='" . attr($field_id_label) . "'>";
3941 if ($skip_this_field) {
3942 // No label because skipping
3943 } elseif ($group_fields['title']) {
3944 $tmp = xl_layout_label($group_fields['title']);
3945 echo text($tmp);
3946 // Append colon only if label does not end with punctuation.
3947 if (!str_contains('?!.,:-=', $tmp[strlen($tmp) - 1])) {
3948 echo ':';
3950 } else {
3951 echo "&nbsp;";
3953 echo "</span>";
3956 // Handle starting of a new data cell.
3957 if ($datacols > 0) {
3958 disp_end_cell();
3959 $datacols = $span_col_row ? $CPR : $datacols;
3960 $datacols_esc = htmlspecialchars($datacols, ENT_QUOTES);
3961 $field_id = 'text_' . $group_fields['field_id'];
3962 echo "<td class='text data' colspan='$datacols_esc' id='" . attr($field_id) . "' data-value='" . attr($currvalue) . "'";
3963 if (!$skip_this_field && $data_type == 3) {
3964 // Textarea gets a light grey border.
3965 echo " style='border: 1px solid var(--gray400)'";
3967 echo ">";
3968 $cell_count += $datacols;
3969 } else {
3970 $field_id = 'text_' . $group_fields['field_id'];
3971 echo "<span id='" . attr($field_id) . "' style='display: none'>" . text($currvalue) . "</span>";
3974 ++$item_count;
3975 if (!$skip_this_field) {
3976 if ($item_count > 1) {
3977 echo "&nbsp;";
3979 echo generate_display_field($group_fields, $currvalue);
3981 if ($datacols == 0) {
3982 // End nowrap
3983 echo "</span> "; // space to allow wrap between spans
3985 } // end field
3987 disp_end_row();
3989 // End table for the group.
3990 echo "</table>\n";
3992 $prev_group = $this_group;
3993 $first = false;
3994 } // End this group.
3995 } // End this layout, there may be more in the case of history.
3997 if (!$first) {
3998 echo "</div>\n";
4002 // This generates the tab contents of the data entry version of a form.
4004 function display_layout_tabs_data_editable($formtype, $result1, $result2 = '')
4006 global $item_count, $cell_count, $last_group, $CPR, $condition_str, $BS_COL_CLASS;
4008 if ('HIS' == $formtype) {
4009 $formtype .= '%'; // TBD: DEM also?
4011 $pres = sqlStatement(
4012 "SELECT grp_form_id, grp_seq, grp_title " .
4013 "FROM layout_group_properties " .
4014 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
4015 "ORDER BY grp_seq, grp_title, grp_form_id",
4016 array("$formtype")
4018 $first = true;
4019 $condition_str = '';
4021 // This loops once per layout. Only Patient History can have multiple layouts.
4022 while ($prow = sqlFetchArray($pres)) {
4023 $formtype = $prow['grp_form_id'];
4024 $last_group = '';
4025 $cell_count = 0;
4026 $item_count = 0;
4028 $grparr = array();
4029 getLayoutProperties($formtype, $grparr, '*');
4031 $TOPCPR = empty($grparr['']['grp_columns']) ? 4 : $grparr['']['grp_columns'];
4033 // Check the children of each top-level group to see if any of them are initially open.
4034 // If not, make the first such child initially open.
4035 foreach ($grparr as $tmprow1) {
4036 if (strlen($tmprow1['grp_group_id']) == 1) {
4037 $got_init_open = false;
4038 $keyfirst = false;
4039 foreach ($grparr as $key2 => $tmprow2) {
4040 if (substr($tmprow2['grp_group_id'], 0, 1) == $tmprow1['grp_group_id'] && strlen($tmprow2['grp_group_id']) == 2) {
4041 if (!$keyfirst) {
4042 $keyfirst = $key2;
4044 if ($tmprow2['grp_init_open']) {
4045 $got_init_open = true;
4049 if (!$got_init_open && $keyfirst) {
4050 $grparr[$keyfirst]['grp_init_open'] = 1;
4055 // Variables $gs_* are context for the group set in the current tab.
4056 $gs_display_style = 'block';
4057 // This string is the active group levels representing the current display state.
4058 // Each leading substring represents an instance of nesting.
4059 // As each new group is encountered, groups will be closed and opened as needed
4060 // until the display state matches the new group.
4061 $gs_group_levels = '';
4063 // By selecting distinct group_id from layout_options we avoid empty groups.
4064 $fres = sqlStatement("SELECT distinct group_id FROM layout_options " .
4065 "WHERE form_id = ? AND uor > 0 " .
4066 "ORDER BY group_id", array($formtype));
4068 // This loops once per group within a given layout.
4069 while ($frow = sqlFetchArray($fres)) {
4070 $this_group = $frow['group_id'];
4071 $group_name = $grparr[$this_group]['grp_title'];
4072 $group_name_esc = text($group_name);
4074 if ($grparr[$this_group]['grp_title'] === 'Employer' && $GLOBALS['omit_employers']) {
4075 continue;
4077 $CPR = empty($grparr[$this_group]['grp_columns']) ? $TOPCPR : $grparr[$this_group]['grp_columns'];
4078 $subtitle = empty($grparr[$this_group]['grp_subtitle']) ? '' : xl_layout_label($grparr[$this_group]['grp_subtitle']);
4080 $group_fields_query = sqlStatement("SELECT * FROM layout_options " .
4081 "WHERE form_id = ? AND uor > 0 AND group_id = ? " .
4082 "ORDER BY seq", array($formtype, $this_group));
4084 $gs_this_levels = $this_group;
4085 // Compute $gs_i as the number of initial matching levels.
4086 $gs_i = 0;
4087 $tmp = min(strlen($gs_this_levels), strlen($gs_group_levels));
4088 while ($gs_i < $tmp && $gs_this_levels[$gs_i] == $gs_group_levels[$gs_i]) {
4089 ++$gs_i;
4092 // Close any groups that we are done with.
4093 while (strlen($gs_group_levels) > $gs_i) {
4094 $gs_group_name = $grparr[$gs_group_levels]['grp_title'];
4095 if (strlen($gs_group_levels) > 1) {
4096 // No div for an empty sub-group name.
4097 if (strlen($gs_group_name)) {
4098 echo "</div>\n";
4100 } else {
4101 // This is the top group level so ending this tab and will start a new one.
4102 echo "</div>\n";
4104 $gs_group_levels = substr($gs_group_levels, 0, -1); // remove last character
4107 // If there are any new groups, open them.
4108 while ($gs_i < strlen($gs_this_levels)) {
4109 $gs_group_levels .= $gs_this_levels[$gs_i++];
4110 $gs_group_name = $grparr[substr($gs_group_levels, 0, $gs_i)]['grp_title'];
4111 $gs_init_open = $grparr[substr($gs_group_levels, 0, $gs_i)]['grp_init_open'];
4112 // Compute a short unique identifier for this group.
4113 $gs_group_seq = "grp-$formtype-$gs_group_levels";
4114 if ($gs_i <= 1) {
4115 // Top level group so new tab.
4116 echo "<div class='tab" . ($first ? ' current' : '') . "' id='tab_$group_name_esc'>\n";
4117 } else {
4118 // Not a new tab so start the group inline.
4119 // If group name is blank, no checkbox or div.
4120 if (strlen($gs_group_name)) {
4121 echo "<br /><span class='bold'><input type='checkbox' name='form_cb_" .
4122 attr($gs_group_seq) . "' value='1' " .
4123 "onclick='return divclick(this," . attr_js('div_' . $gs_group_seq) . ");'";
4124 $gs_display_style = $gs_init_open ? 'block' : 'none';
4125 if ($gs_display_style == 'block') {
4126 echo " checked";
4128 echo " /><b>" . text(xl_layout_label($gs_group_name)) . "</b></span>\n";
4129 echo "<div id='div_" . attr($gs_group_seq) .
4130 "' class='section' style='display:" . attr($gs_display_style) . ";'>\n";
4135 // Each group or subgroup has its own separate container.
4136 $gs_group_table_active = true;
4137 echo "<div class='container-fluid lbfdata'>\n";
4138 if ($subtitle) {
4139 // There is a group subtitle so show it.
4140 $bs_cols = $CPR * intval(12 / $CPR);
4141 echo "<div class='row mb-2'>";
4142 echo "<div class='$BS_COL_CLASS-$bs_cols' style='color:#0000ff'>" . text($subtitle) . "</div>";
4143 echo "</div>\n";
4146 // This loops once per field within a given group.
4147 while ($group_fields = sqlFetchArray($group_fields_query)) {
4148 $titlecols = $group_fields['titlecols'];
4149 $datacols = $group_fields['datacols'];
4150 $data_type = $group_fields['data_type'];
4151 $field_id = $group_fields['field_id'];
4152 $list_id = $group_fields['list_id'];
4153 $backup_list = $group_fields['list_backup_id'];
4154 $currvalue = '';
4155 $action = 'skip';
4156 $jump_new_row = isOption($group_fields['edit_options'], 'J');
4157 $prepend_blank_row = isOption($group_fields['edit_options'], 'K');
4158 $span_col_row = isOption($group_fields['edit_options'], 'SP');
4160 // Accumulate action conditions into a JSON expression for the browser side.
4161 accumActionConditions($group_fields, $condition_str);
4163 if ($formtype == 'DEM') {
4164 if (strpos($field_id, 'em_') === 0) {
4165 // Skip employer related fields, if it's disabled.
4166 if ($GLOBALS['omit_employers']) {
4167 continue;
4170 $tmp = substr($field_id, 3);
4171 if (isset($result2[$tmp])) {
4172 $currvalue = $result2[$tmp];
4174 } else {
4175 if (isset($result1[$field_id])) {
4176 $currvalue = $result1[$field_id];
4179 } else {
4180 if (isset($result1[$field_id])) {
4181 $currvalue = $result1[$field_id];
4185 // Handle a data category (group) change.
4186 if (strcmp($this_group, $last_group) != 0) {
4187 // totally skip generating the employer category, if it's disabled.
4188 if ($group_name === 'Employer' && $GLOBALS['omit_employers']) {
4189 continue;
4192 $last_group = $this_group;
4195 // Handle starting of a new row.
4196 if (($titlecols > 0 && $cell_count >= $CPR) || $cell_count == 0 || $prepend_blank_row || $jump_new_row) {
4197 bs_disp_end_row();
4198 $bs_cols = $CPR * intval(12 / $CPR);
4199 if ($subtitle) {
4200 // Group subtitle exists and is not displayed yet.
4201 echo "<div class='form-row mb-2'>";
4202 echo "<div class='$BS_COL_CLASS-$bs_cols p-2 label' style='background-color: var(--gray300)'>" . text($subtitle) . "</div>";
4203 echo "</div>\n";
4204 $subtitle = '';
4206 if ($prepend_blank_row) {
4207 echo "<div class='form-row'>";
4208 echo "<div class='$BS_COL_CLASS-$bs_cols label' style='font-size: 25%'>&nbsp;</div>";
4209 echo "</div>\n";
4211 echo "<div class='form-row'>";
4214 if ($item_count == 0 && $titlecols == 0) {
4215 $titlecols = 1;
4218 // Handle starting of a new label cell.
4219 if ($titlecols > 0 || $span_col_row) {
4220 bs_disp_end_cell();
4221 $titlecols = $span_col_row ? 0 : $titlecols;
4222 $bs_cols = $titlecols * intval(12 / $CPR);
4223 echo "<div class='$BS_COL_CLASS-$bs_cols pt-1 label_custom' ";
4224 echo "id='label_id_" . attr($field_id) . "'";
4225 echo ">";
4226 $cell_count += $titlecols;
4229 // $item_count is the number of title and data items in the current cell.
4230 ++$item_count;
4232 if ($datacols == 0) {
4233 // Data will be in the same cell, so prevent wrapping to a new line.
4234 echo "<span class='text-nowrap mr-2'>";
4237 if (!$span_col_row) {
4238 if ($group_fields['title']) {
4239 $tmp = xl_layout_label($group_fields['title']);
4240 echo text($tmp);
4241 // Append colon only if label does not end with punctuation.
4242 if (strpos('?!.,:-=', substr($tmp, -1, 1)) === false) {
4243 echo ':';
4245 } else {
4246 echo "&nbsp;";
4250 // Handle starting of a new data cell.
4251 if ($datacols > 0) {
4252 bs_disp_end_cell();
4253 $field_id = 'text_' . $group_fields['field_id'];
4254 $datacols = $span_col_row ? $CPR : $datacols;
4255 $bs_cols = $datacols * intval(12 / $CPR);
4256 echo "<div class='$BS_COL_CLASS-$bs_cols'";
4257 echo " id='value_id_" . attr($field_id) . "'";
4258 echo ">";
4259 $cell_count += $datacols;
4262 ++$item_count;
4263 if ($item_count > 1) {
4264 echo "&nbsp;";
4266 // 'smallform' can be used to add arbitrary CSS classes. Note the leading space.
4267 $group_fields['smallform'] = ' form-control-sm mb-1 mw-100';
4268 echo generate_form_field($group_fields, $currvalue);
4269 if ($datacols == 0) {
4270 // End nowrap
4271 echo "</span> "; // space to allow wrap between spans
4273 } // End of fields for this group.
4275 bs_disp_end_row(); // TBD: Does this belong here?
4276 echo "</div>\n"; // end container-fluid
4277 $first = false;
4278 } // End this group.
4280 // Close any groups still open.
4281 while (strlen($gs_group_levels) > 0) {
4282 $gs_group_name = $grparr[$gs_group_levels]['grp_title'];
4283 if (strlen($gs_group_levels) > 1) {
4284 // No div for an empty sub-group name.
4285 if (strlen($gs_group_name)) {
4286 echo "</div>\n";
4288 } else {
4289 // This is the top group level so ending this tab and will start a new one.
4290 echo "</div>\n";
4292 $gs_group_levels = substr($gs_group_levels, 0, -1); // remove last character
4294 } // End this layout, there may be more in the case of history.
4297 // From the currently posted HTML form, this gets the value of the
4298 // field corresponding to the provided layout_options table row.
4300 function get_layout_form_value($frow, $prefix = 'form_')
4302 $maxlength = empty($frow['max_length']) ? 0 : intval($frow['max_length']);
4303 $data_type = $frow['data_type'];
4304 $field_id = $frow['field_id'];
4305 $value = '';
4306 if (isset($_POST["$prefix$field_id"])) {
4307 if ($data_type == 4) {
4308 $modtmp = isOption($frow['edit_options'], 'F') === false ? 0 : 1;
4309 if (!$modtmp) {
4310 $value = DateToYYYYMMDD($_POST["$prefix$field_id"]);
4311 } else {
4312 $value = DateTimeToYYYYMMDDHHMMSS($_POST["$prefix$field_id"]);
4314 } elseif ($data_type == 21) {
4315 if (!$frow['list_id']) {
4316 if (!empty($_POST["form_$field_id"])) {
4317 $value = xlt('Yes');
4319 } else {
4320 // $_POST["$prefix$field_id"] is an array of checkboxes and its keys
4321 // must be concatenated into a |-separated string.
4322 foreach ($_POST["$prefix$field_id"] as $key => $val) {
4323 if (strlen($value)) {
4324 $value .= '|';
4326 $value .= $key;
4329 } elseif ($data_type == 22) {
4330 // $_POST["$prefix$field_id"] is an array of text fields to be imploded
4331 // into "key:value|key:value|...".
4332 foreach ($_POST["$prefix$field_id"] as $key => $val) {
4333 $val = str_replace('|', ' ', $val);
4334 if (strlen($value)) {
4335 $value .= '|';
4338 $value .= "$key:$val";
4340 } elseif ($data_type == 23) {
4341 // $_POST["$prefix$field_id"] is an array of text fields with companion
4342 // radio buttons to be imploded into "key:n:notes|key:n:notes|...".
4343 foreach ($_POST["$prefix$field_id"] as $key => $val) {
4344 $restype = $_POST["radio_{$field_id}"][$key] ?? null;
4345 if (empty($restype)) {
4346 $restype = '0';
4349 $val = str_replace('|', ' ', $val);
4350 if (strlen($value)) {
4351 $value .= '|';
4354 $value .= "$key:$restype:$val";
4356 } elseif ($data_type == 25) {
4357 // $_POST["$prefix$field_id"] is an array of text fields with companion
4358 // checkboxes to be imploded into "key:n:notes|key:n:notes|...".
4359 foreach ($_POST["$prefix$field_id"] as $key => $val) {
4360 $restype = empty($_POST["check_{$field_id}"][$key]) ? '0' : '1';
4361 $val = str_replace('|', ' ', $val);
4362 if (strlen($value)) {
4363 $value .= '|';
4366 $value .= "$key:$restype:$val";
4368 } elseif ($data_type == 28 || $data_type == 32) {
4369 // $_POST["$prefix$field_id"] is an date text fields with companion
4370 // radio buttons to be imploded into "notes|type|date".
4371 $restype = $_POST["radio_{$field_id}"] ?? '';
4372 if (empty($restype)) {
4373 $restype = '0';
4376 $resdate = DateToYYYYMMDD(str_replace('|', ' ', $_POST["date_$field_id"]));
4377 $resnote = str_replace('|', ' ', $_POST["$prefix$field_id"]);
4378 if ($data_type == 32) {
4379 //VicarePlus :: Smoking status data is imploded into "note|type|date|list".
4380 $reslist = str_replace('|', ' ', $_POST["$prefix$field_id"]);
4381 $res_text_note = str_replace('|', ' ', $_POST["{$prefix}text_$field_id"]);
4382 $value = "$res_text_note|$restype|$resdate|$reslist";
4383 } else {
4384 $value = "$resnote|$restype|$resdate";
4386 } elseif ($data_type == 37) {
4387 // $_POST["form_$field_id"] is an array of arrays of 3 text fields with companion
4388 // radio button set to be encoded as json.
4389 $tmparr = array();
4390 foreach ($_POST["form_$field_id"] as $key => $valarr) {
4391 // Each $key here is a list item ID. $valarr has 3 text field values keyed on 0, 2 and 3.
4392 $tmparr[$key][0] = $valarr['0'];
4393 $tmparr[$key][1] = $_POST["radio_{$field_id}"][$key];
4394 $tmparr[$key][2] = $valarr['2'];
4395 $tmparr[$key][3] = $valarr['3'];
4397 $value .= json_encode($tmparr);
4398 } elseif ($data_type == 36 || $data_type == 44 || $data_type == 45 || $data_type == 33) {
4399 $value_array = $_POST["form_$field_id"];
4400 $i = 0;
4401 foreach ($value_array as $key => $valueofkey) {
4402 if ($i == 0) {
4403 $value = $valueofkey;
4404 } else {
4405 $value = $value . "|" . $valueofkey;
4408 $i++;
4410 } elseif ($data_type == 46) {
4411 $reslist = trim($_POST["$prefix$field_id"]);
4412 if (preg_match('/^comment_/', $reslist)) {
4413 $res_comment = str_replace('|', ' ', $_POST["{$prefix}text_$field_id"]);
4414 $value = $reslist . "|" . $res_comment;
4415 } else {
4416 $value = $_POST["$prefix$field_id"];
4418 } elseif ($data_type == 52) {
4419 $value_array = $_POST["form_$field_id"];
4420 $i = 0;
4421 foreach ($value_array as $key => $valueofkey) {
4422 if ($i == 0) {
4423 $value = $valueofkey;
4424 } else {
4425 $value = $value . "|" . $valueofkey;
4428 $i++;
4430 } else {
4431 $value = $_POST["$prefix$field_id"];
4435 // Better to die than to silently truncate data!
4436 if ($maxlength && $maxlength != 0 && mb_strlen(trim($value)) > $maxlength && !$frow['list_id']) {
4437 die(htmlspecialchars(xl('ERROR: Field') . " '$field_id' " . xl('is too long'), ENT_NOQUOTES) .
4438 ":<br />&nbsp;<br />" . htmlspecialchars($value, ENT_NOQUOTES));
4441 if (is_string($value)) {
4442 return trim($value);
4443 } else {
4444 return $value;
4448 // Generate JavaScript validation logic for the required fields.
4450 function generate_layout_validation($form_id)
4452 if ('HIS' == $form_id) {
4453 $form_id .= '%'; // TBD: DEM also?
4455 $pres = sqlStatement(
4456 "SELECT grp_form_id, grp_seq, grp_title " .
4457 "FROM layout_group_properties " .
4458 "WHERE grp_form_id LIKE ? AND grp_group_id = '' " .
4459 "ORDER BY grp_seq, grp_title, grp_form_id",
4460 array("$form_id")
4462 while ($prow = sqlFetchArray($pres)) {
4463 $form_id = $prow['grp_form_id'];
4465 $fres = sqlStatement("SELECT * FROM layout_options " .
4466 "WHERE form_id = ? AND uor > 0 AND field_id != '' " .
4467 "ORDER BY group_id, seq", array($form_id));
4469 while ($frow = sqlFetchArray($fres)) {
4470 $data_type = $frow['data_type'];
4471 $field_id = $frow['field_id'];
4472 $fldtitle = $frow['title'];
4473 if (!$fldtitle) {
4474 $fldtitle = $frow['description'];
4477 $fldname = attr("form_$field_id");
4479 if ($data_type == 40) {
4480 $fldid = "form_" . $field_id;
4481 // Move canvas image data to its hidden form field so the server will get it.
4482 echo
4483 " var canfld = f[" . js_escape($fldid) . "];\n" .
4484 " if (canfld) canfld.value = lbfCanvasGetData(" . js_escape($fldid) . ");\n";
4485 continue;
4487 if ($data_type == 41 || $data_type == 42) {
4488 $fldid = "form_" . $field_id;
4489 // Move canvas image data to its hidden form field so the server will get it.
4490 echo " lbfSetSignature(" . js_escape($fldid) . ");\n";
4491 continue;
4493 if ($frow['uor'] < 2) {
4494 continue;
4497 echo " if (f.$fldname && !f.$fldname.disabled) {\n";
4498 switch ($data_type) {
4499 case 1:
4500 case 11:
4501 case 12:
4502 case 13:
4503 case 14:
4504 case 26:
4505 echo
4506 " if (f.$fldname.selectedIndex <= 0) {\n" .
4507 " alert(" . xlj('Please choose a value for') . " + " .
4508 "\":\\n\" + " . js_escape(xl_layout_label($fldtitle)) . ");\n" .
4509 " if (f.$fldname.focus) f.$fldname.focus();\n" .
4510 " return false;\n" .
4511 " }\n";
4512 break;
4513 case 33:
4514 echo
4515 " if (f.$fldname.selectedIndex <= 0) {\n" .
4516 " if (f.$fldname.focus) f.$fldname.focus();\n" .
4517 " errMsgs[errMsgs.length] = " . js_escape(xl_layout_label($fldtitle)) . "; \n" .
4518 " }\n";
4519 break;
4520 case 27: // radio buttons
4521 echo
4522 " var i = 0;\n" .
4523 " for (; i < f.$fldname.length; ++i) if (f.{$fldname}[i].checked) break;\n" .
4524 " if (i >= f.$fldname.length) {\n" .
4525 " alert(" . xlj('Please choose a value for') . " + " .
4526 "\":\\n\" + " . js_escape(xl_layout_label($fldtitle)) . ");\n" .
4527 " return false;\n" .
4528 " }\n";
4529 break;
4530 case 2:
4531 case 3:
4532 case 4:
4533 case 15:
4534 echo
4535 " if (trimlen(f.$fldname.value) == 0) {\n" .
4536 " if (f.$fldname.focus) f.$fldname.focus();\n" .
4537 " $('#" . $fldname . "').parents('div.tab').each( function(){ var tabHeader = $('#header_' + $(this).attr('id') ); tabHeader.css('color','var(--danger)'); } ); " .
4538 " $('#" . $fldname . "').attr('style','background: var(--danger)'); \n" .
4539 " errMsgs[errMsgs.length] = " . js_escape(xl_layout_label($fldtitle)) . "; \n" .
4540 " } else { " .
4541 " $('#" . $fldname . "').attr('style',''); " .
4542 " $('#" . $fldname . "').parents('div.tab').each( function(){ var tabHeader = $('#header_' + $(this).attr('id') ); tabHeader.css('color',''); } ); " .
4543 " } \n";
4544 break;
4545 case 36: // multi select
4546 echo
4547 " var multi_select=f['$fldname" . "[]']; \n " .
4548 " var multi_choice_made=false; \n" .
4549 " for (var options_index=0; options_index < multi_select.length; options_index++) { " .
4550 " multi_choice_made=multi_choice_made || multi_select.options[options_index].selected; \n" .
4551 " } \n" .
4552 " if(!multi_choice_made)
4553 errMsgs[errMsgs.length] = " . js_escape(xl_layout_label($fldtitle)) . "; \n" .
4555 break;
4557 echo " }\n";
4559 } // End this layout, there may be more in the case of history.
4563 * DROPDOWN FOR FACILITIES
4565 * build a dropdown with all facilities
4567 * @param string $selected - name of the currently selected facility
4568 * use '0' for "unspecified facility"
4569 * use '' for "All facilities" (the default)
4570 * @param string $name - the name/id for select form (defaults to "form_facility")
4571 * @param boolean $allow_unspecified - include an option for "unspecified" facility
4572 * defaults to true
4573 * @return void - just echo the html encoded string
4575 * Note: This should become a data-type at some point, according to Brady
4577 function dropdown_facility(
4578 $selected = '',
4579 $name = 'form_facility',
4580 $allow_unspecified = true,
4581 $allow_allfacilities = true,
4582 $disabled = '',
4583 $onchange = '',
4584 $multiple = false,
4585 $class = ''
4587 global $facilityService;
4589 $have_selected = false;
4590 $fres = $facilityService->getAllFacility();
4591 $id = $name;
4593 if ($multiple) {
4594 $name = $name . "[]";
4596 echo " <select class='form-control$class";
4597 if ($multiple) {
4598 echo " select-dropdown";
4600 echo "' name='" . attr($name) . "' id='" . attr($id) . "'";
4601 if ($onchange) {
4602 echo " onchange='$onchange'";
4605 if ($multiple) {
4606 echo " multiple='multiple'";
4609 echo " $disabled>\n";
4611 if ($allow_allfacilities) {
4612 $option_value = '';
4613 $option_selected_attr = '';
4614 if ($selected == '') {
4615 $option_selected_attr = ' selected="selected"';
4616 $have_selected = true;
4619 $option_content = '-- ' . xl('All Facilities') . ' --';
4620 echo " <option value='" . attr($option_value) . "' $option_selected_attr>" . text($option_content) . "</option>\n";
4621 } elseif ($allow_unspecified) {
4622 $option_value = '0';
4623 $option_selected_attr = '';
4624 if ($selected == '0') {
4625 $option_selected_attr = ' selected="selected"';
4626 $have_selected = true;
4629 $option_content = '-- ' . xl('Unspecified') . ' --';
4630 echo " <option value='" . attr($option_value) . "' $option_selected_attr>" . text($option_content) . "</option>\n";
4633 foreach ($fres as $frow) {
4634 $facility_id = $frow['id'];
4635 $option_value = $facility_id;
4636 $option_selected_attr = '';
4637 if ($multiple) {
4638 $selectedValues = explode("|", $selected);
4640 if (in_array($facility_id, $selectedValues)) {
4641 $option_selected_attr = ' selected="selected"';
4642 $have_selected = true;
4644 } else {
4645 if ($selected == $facility_id) {
4646 $option_selected_attr = ' selected="selected"';
4647 $have_selected = true;
4651 $option_content = $frow['name'];
4652 echo " <option value='" . attr($option_value) . "' $option_selected_attr>" . text($option_content) . "</option>\n";
4655 if ($allow_unspecified && $allow_allfacilities) {
4656 $option_value = '0';
4657 $option_selected_attr = '';
4658 if ($selected == '0') {
4659 $option_selected_attr = ' selected="selected"';
4660 $have_selected = true;
4663 $option_content = '-- ' . xl('Unspecified') . ' --';
4664 echo " <option value='" . attr($option_value) . "' $option_selected_attr>" . text($option_content) . "</option>\n";
4667 if (!$have_selected && !$multiple) {
4668 $option_value = $selected;
4669 $option_label = '(' . xl('Do not change') . ')';
4670 $option_content = xl('Missing or Invalid');
4671 echo " <option value='" . attr($option_value) . "' label='" . attr($option_label) . "' selected='selected'>" . text($option_content) . "</option>\n";
4674 echo " </select>\n";
4678 * Expand Collapse Widget
4679 * This forms the header and functionality component of the widget. The information that is displayed
4680 * then follows this function followed by a closing div tag
4682 * @var $title is the title of the section (already translated)
4683 * @var $label is identifier used in the tag id's and sql columns
4684 * @var $buttonLabel is the button label text (already translated)
4685 * @var $buttonLink is the button link information
4686 * @var $buttonClass is any additional needed class elements for the button tag
4687 * @var $linkMethod is the button link method ('javascript' vs 'html')
4688 * @var $bodyClass is to set class(es) of the body
4689 * @var $auth is a flag to decide whether to show the button
4690 * @var $fixedWidth is to flag whether width is fixed
4691 * @var $forceExpandAlways is a flag to force the widget to always be expanded
4693 * @todo Convert to a modern layout
4695 function expand_collapse_widget($title, $label, $buttonLabel, $buttonLink, $buttonClass, $linkMethod, $bodyClass, $auth, $fixedWidth, $forceExpandAlways = false)
4697 if ($fixedWidth) {
4698 echo "<div class='section-header'>";
4699 } else {
4700 echo "<div class='section-header-dynamic'>";
4703 echo "<table><tr>";
4704 if ($auth) {
4705 // show button, since authorized
4706 // first prepare class string
4707 if ($buttonClass) {
4708 $class_string = "btn btn-primary btn-sm " . $buttonClass;
4709 } else {
4710 $class_string = "btn btn-primary btn-sm";
4713 // next, create the link
4714 if ($linkMethod == "javascript") {
4715 echo "<td><a class='" . attr($class_string) . "' href='javascript:;' onclick='" . $buttonLink . "'";
4716 } else {
4717 echo "<td><a class='" . attr($class_string) . "' href='" . $buttonLink . "'";
4718 if (!isset($_SESSION['patient_portal_onsite_two'])) {
4719 // prevent an error from occuring when calling the function from the patient portal
4720 echo " onclick='top.restoreSession()'";
4724 echo "><span>" .
4725 text($buttonLabel) . "</span></a></td>";
4728 if ($forceExpandAlways) {
4729 // Special case to force the widget to always be expanded
4730 echo "<td><span class='text font-weight-bold'>" . text($title) . "</span>";
4731 $indicatorTag = "style='display: none'";
4734 $indicatorTag = isset($indicatorTag) ? $indicatorTag : "";
4735 echo "<td><a " . $indicatorTag . " href='javascript:;' class='small' onclick='toggleIndicator(this," .
4736 attr_js($label . "_ps_expand") . ")'><span class='text font-weight-bold'>";
4737 echo text($title) . "</span>";
4739 if (isset($_SESSION['patient_portal_onsite_two'])) {
4740 // collapse all entries in the patient portal
4741 $text = xl('expand');
4742 } elseif (getUserSetting($label . "_ps_expand")) {
4743 $text = xl('collapse');
4744 } else {
4745 $text = xl('expand');
4748 echo " (<span class='indicator'>" . text($text) .
4749 "</span>)</a></td>";
4750 echo "</tr></table>";
4751 echo "</div>";
4752 if ($forceExpandAlways) {
4753 // Special case to force the widget to always be expanded
4754 $styling = "";
4755 } elseif (isset($_SESSION['patient_portal_onsite_two'])) {
4756 // collapse all entries in the patient portal
4757 $styling = "style='display: none'";
4758 } elseif (getUserSetting($label . "_ps_expand")) {
4759 $styling = "";
4760 } else {
4761 $styling = "style='display: none'";
4764 if ($bodyClass) {
4765 $styling .= " class='" . attr($bodyClass) . "'";
4768 //next, create the first div tag to hold the information
4769 // note the code that calls this function will then place the ending div tag after the data
4770 echo "<div id='" . attr($label) . "_ps_expand' " . $styling . ">";
4773 //billing_facility fuction will give the dropdown list which contain billing faciliies.
4774 function billing_facility($name, $select)
4776 global $facilityService;
4778 $fres = $facilityService->getAllBillingLocations();
4779 echo " <select id='" . htmlspecialchars($name, ENT_QUOTES) . "' class='form-control' name='" . htmlspecialchars($name, ENT_QUOTES) . "'>";
4780 foreach ($fres as $facrow) {
4781 $selected = ( $facrow['id'] == $select ) ? 'selected="selected"' : '' ;
4782 echo "<option value=" . htmlspecialchars($facrow['id'], ENT_QUOTES) . " $selected>" . htmlspecialchars($facrow['name'], ENT_QUOTES) . "</option>";
4785 echo "</select>";
4788 // Generic function to get the translated title value for a particular list option.
4790 function getListItemTitle($list, $option)
4792 $row = sqlQuery("SELECT title FROM list_options WHERE " .
4793 "list_id = ? AND option_id = ? AND activity = 1", array($list, $option));
4794 if (empty($row['title'])) {
4795 return $option;
4798 return xl_list_label($row['title']);
4801 //function to get the translated title value in Patient Transactions
4802 function getLayoutTitle($list, $option)
4804 $row = sqlQuery("SELECT grp_title FROM layout_group_properties " .
4805 "WHERE grp_mapping = ? AND grp_form_id = ? ", array($list, $option));
4807 if (empty($row['grp_title'])) {
4808 return $option;
4810 return xl_list_label($row['grp_title']);
4812 //Added on 5-jun-2k14 (regarding get the smoking code descriptions)
4813 function getSmokeCodes()
4815 $smoking_codes_arr = array();
4816 $smoking_codes = sqlStatement("SELECT option_id,codes FROM list_options WHERE list_id='smoking_status' AND activity = 1");
4817 while ($codes_row = sqlFetchArray($smoking_codes)) {
4818 $smoking_codes_arr[$codes_row['option_id']] = $codes_row['codes'];
4821 return $smoking_codes_arr;
4824 // Get the current value for a layout based form field.
4825 // Depending on options this might come from lbf_data, patient_data,
4826 // form_encounter, shared_attributes or elsewhere.
4827 // Returns FALSE if the field ID is invalid (layout error).
4829 function lbf_current_value($frow, $formid, $encounter)
4831 global $pid;
4832 $formname = $frow['form_id'];
4833 $field_id = $frow['field_id'];
4834 $source = $frow['source'];
4835 $currvalue = '';
4836 $deffname = $formname . '_default_' . $field_id;
4837 if ($source == 'D' || $source == 'H') {
4838 // Get from patient_data, employer_data or history_data.
4839 if ($source == 'H') {
4840 $table = 'history_data';
4841 $orderby = 'ORDER BY date DESC LIMIT 1';
4842 } elseif (strpos($field_id, 'em_') === 0) {
4843 $field_id = substr($field_id, 3);
4844 $table = 'employer_data';
4845 $orderby = 'ORDER BY date DESC LIMIT 1';
4846 } else {
4847 $table = 'patient_data';
4848 $orderby = '';
4851 // It is an error if the field does not exist, but don't crash.
4852 $tmp = sqlQuery("SHOW COLUMNS FROM " . escape_table_name($table) . " WHERE Field = ?", array($field_id));
4853 if (empty($tmp)) {
4854 return '*?*';
4857 $pdrow = sqlQuery("SELECT `$field_id` AS field_value FROM " . escape_table_name($table) . " WHERE pid = ? $orderby", array($pid));
4858 if (isset($pdrow)) {
4859 $currvalue = $pdrow['field_value'];
4861 } elseif ($source == 'E') {
4862 $sarow = false;
4863 if ($encounter) {
4864 // Get value from shared_attributes of the current encounter.
4865 $sarow = sqlQuery(
4866 "SELECT field_value FROM shared_attributes WHERE " .
4867 "pid = ? AND encounter = ? AND field_id = ?",
4868 array($pid, $encounter, $field_id)
4870 if (!empty($sarow)) {
4871 $currvalue = $sarow['field_value'];
4873 } elseif ($formid) {
4874 // Get from shared_attributes of the encounter that this form is linked to.
4875 // Note the importance of having an index on forms.form_id.
4876 $sarow = sqlQuery(
4877 "SELECT sa.field_value " .
4878 "FROM forms AS f, shared_attributes AS sa WHERE " .
4879 "f.form_id = ? AND f.formdir = ? AND f.deleted = 0 AND " .
4880 "sa.pid = f.pid AND sa.encounter = f.encounter AND sa.field_id = ?",
4881 array($formid, $formname, $field_id)
4883 if (!empty($sarow)) {
4884 $currvalue = $sarow['field_value'];
4886 } else {
4887 // New form and encounter not available, this should not happen.
4889 if (empty($sarow) && !$formid) {
4890 // New form, see if there is a custom default from a plugin.
4891 if (function_exists($deffname)) {
4892 $currvalue = call_user_func($deffname);
4895 } elseif ($source == 'V') {
4896 if ($encounter) {
4897 // Get value from the current encounter's form_encounter.
4898 $ferow = sqlQuery(
4899 "SELECT * FROM form_encounter WHERE " .
4900 "pid = ? AND encounter = ?",
4901 array($pid, $encounter)
4903 if (isset($ferow[$field_id])) {
4904 $currvalue = $ferow[$field_id];
4906 } elseif ($formid) {
4907 // Get value from the form_encounter that this form is linked to.
4908 $ferow = sqlQuery(
4909 "SELECT fe.* " .
4910 "FROM forms AS f, form_encounter AS fe WHERE " .
4911 "f.form_id = ? AND f.formdir = ? AND f.deleted = 0 AND " .
4912 "fe.pid = f.pid AND fe.encounter = f.encounter",
4913 array($formid, $formname)
4915 if (isset($ferow[$field_id])) {
4916 $currvalue = $ferow[$field_id];
4918 } else {
4919 // New form and encounter not available, this should not happen.
4921 } elseif ($formid) {
4922 // This is a normal form field.
4923 $ldrow = sqlQuery("SELECT field_value FROM lbf_data WHERE " .
4924 "form_id = ? AND field_id = ?", array($formid, $field_id));
4925 if (!empty($ldrow)) {
4926 $currvalue = $ldrow['field_value'];
4928 } else {
4929 // New form, see if there is a custom default from a plugin.
4930 if (function_exists($deffname)) {
4931 $currvalue = call_user_func($deffname);
4935 return $currvalue;
4938 function signer_head()
4940 return <<<EOD
4941 <link href="{$GLOBALS['web_root']}/portal/sign/css/signer_modal.css?v={$GLOBALS['v_js_includes']}" rel="stylesheet"/>
4942 <script src="{$GLOBALS['web_root']}/portal/sign/assets/signature_pad.umd.js?v={$GLOBALS['v_js_includes']}"></script>
4943 <script src="{$GLOBALS['web_root']}/portal/sign/assets/signer_api.js?v={$GLOBALS['v_js_includes']}"></script>
4944 EOD;
4947 // This returns stuff that needs to go into the <head> section of a caller using
4948 // the drawable image field type in a form.
4949 // A TRUE argument makes the widget controls smaller.
4951 function lbf_canvas_head($small = true)
4953 $s = <<<EOD
4954 <link href="{$GLOBALS['assets_static_relative']}/literallycanvas/css/literallycanvas.css" rel="stylesheet" />
4955 <script src="{$GLOBALS['assets_static_relative']}/react/build/react-with-addons.min.js"></script>
4956 <script src="{$GLOBALS['assets_static_relative']}/react/build/react-dom.min.js"></script>
4957 <script src="{$GLOBALS['assets_static_relative']}/literallycanvas/js/literallycanvas.min.js"></script>
4958 EOD;
4959 if ($small) {
4960 $s .= <<<EOD
4961 <style>
4962 /* Custom LiterallyCanvas styling.
4963 * This makes the widget 25% less tall and adjusts some other things accordingly.
4965 .literally {
4966 min-height: 100%;
4967 min-width: 300px; /* Was 400, unspecified */
4969 .literally .lc-picker .toolbar-button {
4970 width: 20px;
4971 height: 20px;
4972 line-height: 20px; /* Was 26, 26, 26 */
4974 .literally .color-well {
4975 font-size: 8px;
4976 width: 49px; /* Was 10, 60 */
4978 .literally .color-well-color-container {
4979 width: 21px;
4980 height: 21px; /* Was 28, 28 */
4982 .literally .lc-picker {
4983 width: 50px; /* Was 61 */
4985 .literally .lc-drawing.with-gui {
4986 left: 50px; /* Was 61 */
4988 .literally .lc-options {
4989 left: 50px; /* Was 61 */
4991 .literally .color-picker-popup {
4992 left: 49px;
4993 bottom: 0px; /* Was 60, 31 */
4995 </style>
4996 EOD;
4999 return $s;
5003 * Test if modifier($test) is in array of options for data type.
5005 * @param json array $options ["G","P","T"], ["G"] or could be legacy string with form "GPT", "G", "012"
5006 * @param string $test
5007 * @return boolean
5009 function isOption($options, string $test): bool
5011 if (empty($options) || !isset($test) || $options == "null") {
5012 return false; // why bother?
5014 if (strpos($options, ',') === false) { // not json array of modifiers.
5015 // could be string of char's or single element of json ["RO"] or "TP" or "P" e.t.c.
5016 json_decode($options, true); // test if options json. json_last_error() will return JSON_ERROR_SYNTAX if not.
5017 // if of form ["RO"] (single modifier) means not legacy so continue on.
5018 if (is_string($options) && (json_last_error() !== JSON_ERROR_NONE)) { // nope, it's string.
5019 $t = str_split(trim($options)); // very good chance it's legacy modifier string.
5020 $options = json_encode($t); // make it json array to convert from legacy to new modifier json schema.
5024 $options = json_decode($options, true); // all should now be json
5026 return is_array($options) && in_array($test, $options, true); // finally the truth!