Refactor previous name into dedicated service (#7571)
[openemr.git] / interface / super / edit_layout.php
blob77b8dba8a0914636df0ed9ee8488532f16153837
1 <?php
3 /**
4 * Edit layouts gui
6 * @package OpenEMR
7 * @link http://www.open-emr.org
8 * @author Rod Roark <rod@sunsetsystems.com>
9 * @author Jerry Padgett <sjpadgett@gmail.com>
10 * @author Brady Miller <brady.g.miller@gmail.com>
11 * @copyright Copyright (c) 2014-2021 Rod Roark <rod@sunsetsystems.com>
12 * @copyright Copyright (c) 2017-2021 Jerry Padgett <sjpadgett@gmail.com>
13 * @copyright Copyright (c) 2017-2018 Brady Miller <brady.g.miller@gmail.com>
14 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
17 require_once("../globals.php");
18 require_once("$srcdir/layout.inc.php");
20 use OpenEMR\Common\Acl\AclMain;
21 use OpenEMR\Common\Csrf\CsrfUtils;
22 use OpenEMR\Common\Logging\EventAuditLogger;
23 use OpenEMR\Common\Twig\TwigContainer;
24 use OpenEMR\Core\Header;
26 // Indicates if deactivated layouts are included in the dropdown.
27 $form_inactive = !empty($_REQUEST['form_inactive']);
29 function setLayoutTimestamp($layout_id)
31 $query = "UPDATE layout_group_properties SET grp_last_update = CURRENT_TIMESTAMP " .
32 "WHERE grp_form_id = ? AND grp_group_id = ''";
33 sqlStatement($query, array($layout_id));
36 function collectLayoutNames($condition, $mapping = '')
38 global $layouts, $form_inactive;
39 $gres = sqlStatement(
40 "SELECT grp_form_id, grp_title, grp_mapping " .
41 "FROM layout_group_properties WHERE " .
42 "grp_group_id = '' " .
43 ($form_inactive ? "" : "AND grp_activity = 1 ") .
44 "AND $condition " .
45 "ORDER BY grp_mapping, grp_seq, grp_title"
47 while ($grow = sqlFetchArray($gres)) {
48 $tmp = $mapping ? $mapping : $grow['grp_mapping'];
49 if (!$tmp) {
50 $tmp = '(' . xl('No Name') . ')';
52 $layouts[$grow['grp_form_id']] = array($tmp, $grow['grp_title']);
55 $layouts = array();
56 collectLayoutNames("grp_form_id NOT LIKE 'LBF%' AND grp_form_id NOT LIKE 'LBT%'", xl('Core'));
57 collectLayoutNames("grp_form_id LIKE 'LBT%'", xl('Transactions'));
58 collectLayoutNames("grp_form_id LIKE 'LBF%'", '');
60 // Include predefined Validation Rules from list
61 $validations = array();
62 $lres = sqlStatement("SELECT * FROM list_options " .
63 "WHERE list_id = 'LBF_Validations' AND activity = 1 ORDER BY seq, title");
64 while ($lrow = sqlFetchArray($lres)) {
65 $validations[$lrow['option_id']] = xl_list_label($lrow['title']);
68 function nextGroupOrder($order)
70 if ($order == '9') {
71 $order = 'A';
72 } elseif ($order == 'Z') {
73 $order = 'a';
74 } else {
75 $order = chr(ord($order) + 1);
78 return $order;
81 // This returns HTML for a <select> that allows choice of a layout group.
82 // Included also are parent groups containing only sub-groups. Groups are listed
83 // in the same order as they appear in the layout.
85 function genGroupSelector($name, $layout_id, $default = '')
87 $res = sqlStatement(
88 "SELECT grp_group_id, grp_title " .
89 "FROM layout_group_properties WHERE " .
90 "grp_form_id = ? AND grp_group_id != '' ORDER BY grp_group_id",
91 array($layout_id)
93 $s = "<select class='form-control form-control-sm' name='" . xla($name) . "'>";
94 $s .= "<option value=''>" . xlt('None{{Group}}') . "</option>";
95 $arr = array();
96 $arrid = '';
97 while ($row = sqlFetchArray($res)) {
98 $thisid = $row['grp_group_id'];
99 $i = 0;
100 // Compute number of initial matching groups.
101 while ($i < strlen($thisid) && $i < strlen($arrid) && $thisid[$i] == $arrid[$i]) {
102 ++$i;
104 $arr = array_slice($arr, 0, $i); // discard the rest
105 while ($i < (strlen($arrid) - 1)) {
106 $arr[$i++] = '???'; // should not happen
108 $arr[$i] = $row['grp_title'];
109 $gval = '';
110 foreach ($arr as $part) {
111 if ($gval) {
112 $gval .= ' / ';
114 $gval .= $part;
116 $s .= "<option value='" . attr($thisid) . "'";
117 if ($thisid == $default) {
118 $s .= ' selected';
120 $s .= ">" . text($gval) . "</option>";
122 $s .= "</select>";
123 return $s;
126 // Compute a new group ID that will become layout_options.group_id and
127 // layout_group_properties.grp_group_id.
128 // $parent is a string of zero or more sequence prefix characters.
129 // If there is a nonempty $parent then its ID will be the prefix for the
130 // new ID and the sequence prefix will be computed within the parent.
132 function genGroupId($parent)
134 global $layout_id;
135 $results = sqlStatement(
136 "SELECT grp_group_id " .
137 "FROM layout_group_properties WHERE " .
138 "grp_form_id = ? AND grp_group_id LIKE ?",
139 array($layout_id, ($parent ?? '') . "_%")
141 $maxnum = '1';
142 while ($result = sqlFetchArray($results)) {
143 $tmp = substr($result['grp_group_id'], strlen($parent), 1);
144 if ($tmp >= $maxnum) {
145 $maxnum = nextGroupOrder($tmp);
148 return $parent . $maxnum;
151 // Changes a group's ID from and to the specified IDs. This also works for groups
152 // that have sub-groups, in which case only the appropriate parent portion of
153 // the ID is changed.
155 function fuzzyRename($from, $to)
157 global $layout_id;
159 $query = "UPDATE layout_options SET group_id = concat(?, substr(group_id, ?)) " .
160 "WHERE form_id = ? AND group_id LIKE ?";
161 sqlStatement($query, array($to, strlen($from) + 1, $layout_id, "$from%"));
163 $query = "UPDATE layout_group_properties SET grp_group_id = concat(?, substr(grp_group_id, ?)) " .
164 "WHERE grp_form_id = ? AND grp_group_id LIKE ?";
165 sqlStatement($query, array($to, strlen($from) + 1, $layout_id, "$from%"));
167 setLayoutTimestamp($layout_id);
170 // Swaps the positions of two groups. To the degree they have matching parents,
171 // only the first differing child positions are swapped.
173 function swapGroups($id1, $id2)
175 $i = 0;
176 while ($i < strlen($id1) && $i < strlen($id2) && $id1[$i] == $id2[$i]) {
177 ++$i;
179 // $i is now the number of matching characters/levels.
180 if ($i < strlen($id1) && $i < strlen($id2)) {
181 $common = substr($id1, 0, $i);
182 $pfx1 = substr($id1, $i, 1);
183 $pfx2 = substr($id2, $i, 1);
184 $tmpname = $common . '#';
185 // To avoid collision use 3 renames.
186 fuzzyRename($common . $pfx1, $common . '#');
187 fuzzyRename($common . $pfx2, $common . $pfx1);
188 fuzzyRename($common . '#', $common . $pfx2);
192 function tableNameFromLayout($layout_id)
194 // Skip layouts that store data in vertical tables.
195 if (substr($layout_id, 0, 3) == 'LBF' || substr($layout_id, 0, 3) == 'LBT' || $layout_id == "FACUSR") {
196 return '';
198 if ($layout_id == "DEM") {
199 $tablename = "patient_data";
200 } elseif (substr($layout_id, 0, 3) == "HIS") {
201 $tablename = "history_data";
202 } elseif ($layout_id == "SRH") {
203 $tablename = "lists_ippf_srh";
204 } elseif ($layout_id == "CON") {
205 $tablename = "lists_ippf_con";
206 } elseif ($layout_id == "GCA") {
207 $tablename = "lists_ippf_gcac";
208 } else {
209 die(xlt('Internal error in tableNameFromLayout') . '(' . text($layout_id) . ')');
211 return $tablename;
214 // This tells you if a column name is required in code and therefore must not
215 // be deleted or renamed.
216 function isColumnReserved($tablename, $field_id)
218 if ($tablename == 'patient_data') {
219 if (
220 in_array($field_id, array(
221 'id',
222 'DOB',
223 'title',
224 'language',
225 'fname',
226 'lname',
227 'mname',
228 'street',
229 'postal_code',
230 'city',
231 'state',
232 'ss',
233 'phone_home',
234 'phone_cell',
235 'date',
236 'sex',
237 'providerID',
238 'email',
239 'pubpid',
240 'pid',
241 'squad',
242 'home_facility',
243 'deceased_date',
244 'deceased_reason',
245 'allow_patient_portal',
246 'soap_import_status',
247 'email_direct',
248 'dupscore',
249 'cmsportal_login',
250 'care_team_provider',
251 'care_team_status',
252 'billing_note',
253 'uuid',
254 'care_team_facility',
255 'name_history',
256 'care_team_status',
257 'patient_groups',
258 'additional_addresses'
261 return true;
263 } elseif ($tablename == 'history_data') {
264 if (
265 in_array($field_id, array(
266 'id',
267 'date',
268 'pid',
271 return true;
274 return false;
277 // Call this when adding or removing a layout field. This will create or drop
278 // the corresponding table column when appropriate. Table columns are not
279 // dropped if they contain any non-empty values or are required internally.
280 function addOrDeleteColumn($layout_id, $field_id, $add = true)
282 $tablename = tableNameFromLayout($layout_id);
283 if (!$tablename) {
284 return;
286 // Check if the column currently exists.
287 $tmp = sqlQuery("SHOW COLUMNS FROM `" . escape_table_name($tablename) . "` LIKE'" . add_escape_custom($field_id) . "'");
288 $column_exists = !empty($tmp);
290 if ($add && !$column_exists) {
291 sqlStatement("ALTER TABLE `" . escape_table_name($tablename) . "` ADD `" . escape_identifier($field_id, 'a-zA-Z0-9_', true) . "` TEXT");
292 EventAuditLogger::instance()->newEvent(
293 "alter_table",
294 $_SESSION['authUser'],
295 $_SESSION['authProvider'],
297 "$tablename ADD $field_id"
299 } elseif (!$add && $column_exists) {
300 // Do not drop a column that has any data.
301 $tmp = sqlQuery(
302 "SELECT `" . escape_sql_column_name($field_id, [$tablename]) .
303 "` AS field_id FROM `" . escape_table_name($tablename) . "` WHERE " .
304 "`" . escape_sql_column_name($field_id, [$tablename]) . "` IS NOT NULL AND `"
305 . escape_sql_column_name($field_id, [$tablename]) . "` != '' LIMIT 1"
307 if (!isset($tmp['field_id']) && !isColumnReserved($tablename, $field_id)) {
308 $lotmp = array();
309 // For History layouts do not delete a field name duplicated in another History layout
310 // (should not happen, but a bug allowed it).
311 if (substr($layout_id, 0, 3) == 'HIS') {
312 $lotmp = sqlQuery(
313 "SELECT COUNT(*) AS count FROM layout_options WHERE " .
314 "form_id LIKE 'HIS%' AND form_id != ? AND field_id = ?",
315 array($layout_id, $field_id)
318 if (empty($lotmp['count'])) {
319 sqlStatement(
320 "ALTER TABLE `" . escape_table_name($tablename) . "` " .
321 "DROP `" . escape_sql_column_name($field_id, [$tablename]) . "`"
323 EventAuditLogger::instance()->newEvent(
324 "alter_table",
325 $_SESSION['authUser'],
326 $_SESSION['authProvider'],
328 "$tablename DROP $field_id "
335 // Call this before renaming a layout field.
336 // Renames the table column (if applicable) and returns a result status:
337 // -1 = There is no table for this layout (not an error).
338 // 0 = Rename successful.
339 // 2 = There is no column having the old name.
340 // 3 = There is already a column having the new name.
341 // 4 = Old name is needed internally and cannot be changed.
343 function renameColumn($layout_id, $old_field_id, $new_field_id)
345 $tablename = tableNameFromLayout($layout_id);
346 if (!$tablename) {
347 return -1; // Indicate rename is not relevant.
349 if (isColumnReserved($tablename, $old_field_id)) {
350 return 4;
352 // Make sure old column exists.
353 $colarr = sqlQuery("SHOW COLUMNS FROM `" . escape_table_name($tablename) . "` LIKE '" . add_escape_custom($old_field_id) . "'");
354 if (empty($colarr)) {
355 // Error, old name does not exist.
356 return 2;
358 // Make sure new column does not exist.
359 $tmp = sqlQuery("SHOW COLUMNS FROM `" . escape_table_name($tablename) . "` LIKE '" . add_escape_custom($new_field_id) . "'");
360 if (!empty($tmp)) {
361 // Error, new name already in use.
362 return 3;
364 // With MySQL you can't change just the name, you have to specify the column definition too.
365 $colstr = $colarr['Type'];
366 if ($colarr['Null'] == 'NO') {
367 $colstr .= " NOT NULL";
369 if ($colarr['Default'] !== null) {
370 $colstr .= " DEFAULT '" . add_escape_custom($colarr['Default']) . "'";
372 if ($colarr['Extra']) {
373 $colstr .= " " . add_escape_custom($colarr['Extra']);
375 $query = "ALTER TABLE `" . escape_table_name($tablename) . "` CHANGE `" . escape_sql_column_name($old_field_id, [$tablename]) . "` `" . escape_identifier($new_field_id, 'a-zA-Z0-9_', true) . "` $colstr";
376 sqlStatement($query);
377 EventAuditLogger::instance()->newEvent(
378 "alter_table",
379 $_SESSION['authUser'],
380 $_SESSION['authProvider'],
382 "$tablename RENAME $old_field_id TO $new_field_id $colstr"
384 return 0; // Indicate rename done and successful.
387 // Test options array for save
388 function encodeModifier($jsonArray)
390 return $jsonArray !== null ? json_encode($jsonArray) : "";
393 // Check authorization.
394 $thisauth = AclMain::aclCheckCore('admin', 'super');
395 if (!$thisauth) {
396 echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Layout Editor")]);
397 exit;
400 // Make a sorted version of the $datatypes array.
401 $sorted_datatypes = $datatypes;
402 natsort($sorted_datatypes);
404 // The layout ID identifies the layout to be edited.
405 $layout_id = empty($_REQUEST['layout_id']) ? '' : $_REQUEST['layout_id'];
406 $layout_tbl = !empty($layout_id) ? tableNameFromLayout($layout_id) : '';
408 // Tag style for stuff to hide if not an LBF layout. Currently just for the Source column.
409 $lbfonly = substr($layout_id, 0, 3) == 'LBF' ? "" : "style='display:none;'";
411 // Handle the Form actions
413 if (!empty($_POST['formaction']) && ($_POST['formaction'] == "save") && $layout_id) {
414 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
415 CsrfUtils::csrfNotVerified();
417 // If we are saving, then save.
418 $fld = $_POST['fld'];
419 for ($lino = 1; isset($fld[$lino]['id']); ++$lino) {
420 $iter = $fld[$lino];
421 $field_id = trim($iter['id']);
422 $field_id_original = trim($iter['originalid']);
423 $data_type = trim($iter['datatype']);
424 $listval = $data_type == 34 ? trim($iter['contextName']) : trim($iter['list_id']);
425 $action = $iter['action'];
426 if ($action == 'value' || $action == 'hsval') {
427 $action .= '=' . $iter['value'];
429 // Skip conditions for the line are stored as a serialized array.
430 $condarr = array('action' => $action);
431 $cix = 0;
432 for (; !empty($iter['condition_id'][$cix]); ++$cix) {
433 $andor = empty($iter['condition_andor'][$cix]) ? '' : $iter['condition_andor'][$cix];
434 $condarr[$cix] = array(
435 'id' => $iter['condition_id' ][$cix],
436 'itemid' => $iter['condition_itemid' ][$cix],
437 'operator' => $iter['condition_operator'][$cix],
438 'value' => $iter['condition_value' ][$cix],
439 'andor' => $andor,
442 $conditions = $cix ? serialize($condarr) : '';
443 if ($field_id) {
444 if ($field_id != $field_id_original) {
445 if (renameColumn($layout_id, $field_id_original, $field_id) > 0) {
446 // If column rename had an error then don't rename it here.
447 $field_id = $field_id_original;
450 sqlStatement("UPDATE layout_options SET " .
451 "field_id = '" . add_escape_custom($field_id) . "', " .
452 "source = '" . add_escape_custom(trim($iter['source'])) . "', " .
453 "title = '" . add_escape_custom($iter['title']) . "', " .
454 "group_id = '" . add_escape_custom(trim($iter['group'])) . "', " .
455 "seq = '" . add_escape_custom(trim($iter['seq'])) . "', " .
456 "uor = '" . add_escape_custom(trim($iter['uor'])) . "', " .
457 "fld_length = '" . add_escape_custom(trim($iter['lengthWidth'])) . "', " .
458 "fld_rows = '" . add_escape_custom(trim($iter['lengthHeight'])) . "', " .
459 "max_length = '" . add_escape_custom(trim($iter['maxSize'])) . "', " .
460 "titlecols = '" . add_escape_custom(trim($iter['titlecols'])) . "', " .
461 "datacols = '" . add_escape_custom(trim($iter['datacols'])) . "', " .
462 "data_type= '" . add_escape_custom($data_type) . "', " .
463 "list_id= '" . add_escape_custom($listval) . "', " .
464 "list_backup_id= '" . add_escape_custom(trim($iter['list_backup_id'])) . "', " .
465 "edit_options = '" . add_escape_custom(encodeModifier($iter['edit_options'] ?? null)) . "', " .
466 "default_value = '" . add_escape_custom(trim($iter['default'])) . "', " .
467 "description = '" . add_escape_custom(trim($iter['desc'])) . "', " .
468 "codes = '" . add_escape_custom(trim($iter['codes'])) . "', " .
469 "conditions = '" . add_escape_custom($conditions) . "', " .
470 "validation = '" . add_escape_custom(trim($iter['validation'])) . "' " .
471 "WHERE form_id = '" . add_escape_custom($layout_id) . "' AND field_id = '" . add_escape_custom($field_id_original) . "'");
473 setLayoutTimestamp($layout_id);
476 } elseif (!empty($_POST['formaction']) && ($_POST['formaction'] == "addfield") && $layout_id) {
477 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
478 CsrfUtils::csrfNotVerified();
480 // Add a new field to a specific group
481 $data_type = trim($_POST['newdatatype']);
482 $max_length = $data_type == 3 ? 3 : 255;
483 $listval = $data_type == 34 ? trim($_POST['contextName']) : trim($_POST['newlistid']);
484 sqlStatement("INSERT INTO layout_options (" .
485 " form_id, source, field_id, title, group_id, seq, uor, fld_length, fld_rows" .
486 ", titlecols, datacols, data_type, edit_options, default_value, codes, description" .
487 ", max_length, list_id, list_backup_id " .
488 ") VALUES ( " .
489 "'" . add_escape_custom(trim($_POST['layout_id'])) . "'" .
490 ",'" . add_escape_custom(trim($_POST['newsource'])) . "'" .
491 ",'" . add_escape_custom(trim($_POST['newid'])) . "'" .
492 ",'" . add_escape_custom($_POST['newtitle']) . "'" .
493 ",'" . add_escape_custom(trim($_POST['newfieldgroupid'])) . "'" .
494 ",'" . add_escape_custom(trim($_POST['newseq'])) . "'" .
495 ",'" . add_escape_custom(trim($_POST['newuor'])) . "'" .
496 ",'" . add_escape_custom(trim($_POST['newlengthWidth'])) . "'" .
497 ",'" . add_escape_custom(trim($_POST['newlengthHeight'])) . "'" .
498 ",'" . add_escape_custom(trim($_POST['newtitlecols'])) . "'" .
499 ",'" . add_escape_custom(trim($_POST['newdatacols'])) . "'" .
500 ",'" . add_escape_custom($data_type) . "'" .
501 ",'" . add_escape_custom(encodeModifier($_POST['newedit_options'] ?? null)) . "'" .
502 ",'" . add_escape_custom(trim($_POST['newdefault'])) . "'" .
503 ",'" . add_escape_custom(trim($_POST['newcodes'])) . "'" .
504 ",'" . add_escape_custom(trim($_POST['newdesc'])) . "'" .
505 ",'" . add_escape_custom(trim($_POST['newmaxSize'])) . "'" .
506 ",'" . add_escape_custom($listval) . "'" .
507 ",'" . add_escape_custom(trim($_POST['newbackuplistid'])) . "'" .
508 " )");
509 addOrDeleteColumn($layout_id, trim($_POST['newid']), true);
510 setLayoutTimestamp($layout_id);
511 } elseif (!empty($_POST['formaction']) && ($_POST['formaction'] == "movefields") && $layout_id) {
512 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
513 CsrfUtils::csrfNotVerified();
515 // Move field(s) to a new group in the layout
516 $sqlstmt = "UPDATE layout_options SET " .
517 " group_id = '" . add_escape_custom($_POST['targetgroup']) . "' " .
518 " WHERE " .
519 " form_id = '" . add_escape_custom($_POST['layout_id']) . "' " .
520 " AND field_id IN (";
521 $comma = "";
522 foreach (explode(" ", $_POST['selectedfields']) as $onefield) {
523 $sqlstmt .= $comma . "'" . add_escape_custom($onefield) . "'";
524 $comma = ", ";
526 $sqlstmt .= ")";
527 //echo $sqlstmt;
528 sqlStatement($sqlstmt);
529 setLayoutTimestamp($layout_id);
530 } elseif (($_POST['formaction'] ?? '') == "copytolayout" && $layout_id && !empty($_POST['targetlayout'])) {
531 // Copy field(s) to the specified group in another layout.
532 // It's important to skip any duplicate field names.
533 $tlayout = $_POST['targetlayout'];
534 $tgroup = $_POST['targetgroup'];
535 foreach (explode(" ", $_POST['selectedfields']) as $onefield) {
536 $srow = sqlQuery(
537 "SELECT * FROM layout_options WHERE " .
538 "form_id = ? AND field_id = ? LIMIT 1",
539 array($layout_id, $onefield)
541 if (empty($srow)) {
542 die("Internal error: Field '" . text($onefield) . "' not found in layout '" . text($layout_id) . "'.");
544 $trow = sqlQuery(
545 "SELECT * FROM layout_options WHERE " .
546 "form_id = ? AND field_id = ? LIMIT 1",
547 array($tlayout, $onefield)
549 if (!empty($trow)) {
550 echo "<!-- Field '" . text($onefield) . "' already exists in layout '" . text($tlayout) . "'. -->\n";
551 continue;
553 $qstr = "INSERT INTO layout_options SET `form_id` = ?, `field_id` = ?, `group_id` = ?";
554 $qarr = array($tlayout, $onefield, $tgroup);
555 foreach ($srow as $key => $value) {
556 if ($key == 'form_id' || $key == 'field_id' || $key == 'group_id') {
557 continue;
559 $qstr .= ", `$key` = ?";
560 $qarr[] = $value;
562 // echo "<!-- $qstr ("; foreach ($qarr as $tmp) echo "'$tmp',"; echo ") -->\n"; // debugging
563 sqlStatement($qstr, $qarr);
564 addOrDeleteColumn($tlayout, $onefield, true);
565 setLayoutTimestamp($tlayout);
567 } elseif (!empty($_POST['formaction']) && ($_POST['formaction'] == "deletefields") && $layout_id) {
568 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
569 CsrfUtils::csrfNotVerified();
571 // Delete a field from a specific group
572 $sqlstmt = "DELETE FROM layout_options WHERE " .
573 " form_id = '" . add_escape_custom($_POST['layout_id']) . "' " .
574 " AND field_id IN (";
575 $comma = "";
576 $cntr = 0;
577 foreach (explode(" ", $_POST['selectedfields']) as $onefield) {
578 if (!isColumnReserved(tableNameFromLayout($_POST['layout_id']), $onefield)) {
579 $sqlstmt .= $comma . "'" . add_escape_custom($onefield) . "'";
580 $comma = ", ";
581 $cntr++;
584 $sqlstmt .= ")";
585 if (!empty($cntr)) {
586 sqlStatement($sqlstmt);
587 foreach (explode(" ", $_POST['selectedfields']) as $onefield) {
588 addOrDeleteColumn($layout_id, $onefield, false);
590 setLayoutTimestamp($layout_id);
592 } elseif (!empty($_POST['formaction']) && ($_POST['formaction'] == "addgroup") && $layout_id) {
593 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
594 CsrfUtils::csrfNotVerified();
596 // Generate new value for layout_items.group_id.
597 $newgroupid = genGroupId($_POST['newgroupparent']);
598 sqlStatement(
599 "INSERT INTO layout_group_properties SET " .
600 "grp_form_id = ?, " .
601 "grp_group_id = ?, " .
602 "grp_title = ?",
603 array($layout_id, $newgroupid, $_POST['newgroupname'])
605 setLayoutTimestamp($layout_id);
606 } elseif (!empty($_POST['formaction']) && $_POST['formaction'] == "deletegroup" && $layout_id) {
607 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
608 CsrfUtils::csrfNotVerified();
610 // drop the fields from the related table (this is critical)
611 $res = sqlStatement(
612 "SELECT field_id FROM layout_options WHERE " .
613 "form_id = ? AND group_id = ?",
614 array($_POST['layout_id'], $_POST['deletegroupid'])
616 while ($row = sqlFetchArray($res)) {
617 addOrDeleteColumn($layout_id, $row['field_id'], false);
619 // Delete an entire group from the form
620 sqlStatement(
621 "DELETE FROM layout_options WHERE " .
622 " form_id = ? AND group_id = ?",
623 array($_POST['layout_id'], $_POST['deletegroupid'])
625 sqlStatement(
626 "DELETE FROM layout_group_properties WHERE " .
627 "grp_form_id = ? AND grp_group_id = ?",
628 array($_POST['layout_id'], $_POST['deletegroupid'])
630 setLayoutTimestamp($layout_id);
631 } elseif (!empty($_POST['formaction']) && ($_POST['formaction'] == "movegroup") && $layout_id) {
632 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
633 CsrfUtils::csrfNotVerified();
635 // Note that in some cases below the swapGroups() call will do nothing.
636 $res = sqlStatement(
637 "SELECT DISTINCT group_id " .
638 "FROM layout_options WHERE form_id = ? ORDER BY group_id",
639 array($layout_id)
641 $row = sqlFetchArray($res);
642 $id1 = $row['group_id'];
643 while ($row = sqlFetchArray($res)) {
644 $id2 = $row['group_id'];
645 if ($_POST['movedirection'] == 'up') { // moving up
646 if ($id2 == $_POST['movegroupname']) {
647 swapGroups($id2, $id1);
648 break;
650 } else { // moving down
651 if ($id1 == $_POST['movegroupname']) {
652 swapGroups($id1, $id2);
653 break;
656 $id1 = $id2;
658 setLayoutTimestamp($layout_id);
659 } elseif (!empty($_POST['formaction']) && ($_POST['formaction'] == "renamegroup") && $layout_id) {
660 // Renaming a group. This might include moving to a different parent group.
661 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
662 CsrfUtils::csrfNotVerified();
664 $newparent = $_POST['renamegroupparent']; // this is an ID
665 $oldid = $_POST['renameoldgroupname']; // this is an ID
666 $oldparent = substr($oldid, 0, -1);
667 $newid = $oldid;
668 if ($newparent != $oldparent) {
669 // Different parent, generate a new child prefix character.
670 $newid = genGroupId($newparent);
671 sqlStatement(
672 "UPDATE layout_options SET group_id = ? " .
673 "WHERE form_id = ? AND group_id = ?",
674 array($newid, $layout_id, $oldid)
677 $query = "UPDATE layout_group_properties SET " .
678 "grp_group_id = ?, grp_title = ? " .
679 "WHERE grp_form_id = ? AND grp_group_id = ?";
680 sqlStatement($query, array($newid, $_POST['renamegroupname'], $layout_id, $oldid));
683 // global counter for field numbers
684 $fld_line_no = 0;
686 $extra_html = '';
688 // This is called to generate a select option list for fields within this form.
689 // Used for selecting a field for testing in a skip condition.
691 function genFieldOptionList($current = '')
693 global $layout_id;
694 $option_list = "<option value=''>-- " . xlt('Please Select') . " --</option>";
695 if ($layout_id) {
696 $query = "SELECT field_id FROM layout_options WHERE form_id = ? ORDER BY group_id, seq";
697 $res = sqlStatement($query, array($layout_id));
698 while ($row = sqlFetchArray($res)) {
699 $field_id = $row['field_id'];
700 $option_list .= "<option value='" . attr($field_id) . "'";
701 if ($field_id == $current) {
702 $option_list .= " selected";
704 $option_list .= ">" . text($field_id) . "</option>";
707 return $option_list;
710 // Write one option line to the form.
712 function writeFieldLine($linedata)
714 global $fld_line_no, $sources, $lbfonly, $extra_html, $validations, $UOR;
715 ++$fld_line_no;
716 $checked = $linedata['default_value'] ? " checked" : "";
718 //echo " <tr bgcolor='$bgcolor'>\n";
719 echo " <tr id='fld[" . attr($fld_line_no) . "]' class='" . ($fld_line_no % 2 ? 'even' : 'odd') . "'>\n";
721 echo " <td class='optcell'>";
722 // tuck the group_name INPUT in here
723 echo "<input type='hidden' name='fld[" . attr($fld_line_no) . "][group]' value='" .
724 attr($linedata['group_id']) . "' class='optin' />";
725 // Original field ID.
726 echo "<input type='hidden' name='fld[" . attr($fld_line_no) . "][originalid]' value='" .
727 attr($linedata['field_id']) . "' />";
729 echo "<div class='input-group'><div class='input-group-prepend'><div class='input-group-text'><input type='checkbox' class='selectfield' " .
730 "name='" . attr($linedata['group_id']) . "~" . attr($linedata['field_id']) . "' " .
731 "id='" . attr($linedata['group_id']) . "~" . attr($linedata['field_id']) . "' " .
732 "title='" . xla('Select field') . "' /></div></div>";
734 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][seq]' id='fld[" . attr($fld_line_no) . "][seq]' value='" .
735 attr($linedata['seq']) . "' size='2' maxlength='4' class='form-control form-control-sm optin' />";
736 echo "</td></div>\n";
738 echo " <td class='text-center optcell' $lbfonly>";
739 echo "<select name='fld[" . attr($fld_line_no) . "][source]' class='form-control form-control-sm optin' $lbfonly>";
740 foreach ($sources as $key => $value) {
741 echo "<option value='" . attr($key) . "'";
742 if ($key == $linedata['source']) {
743 echo " selected";
746 echo ">" . text($value) . "</option>\n";
749 echo "</select>";
750 echo "</td>\n";
752 echo " <td class='text-left optcell'>";
753 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][id]' value='" .
754 attr($linedata['field_id']) . "' size='15' maxlength='31' " .
755 "class='form-control form-control-sm optin' onclick='FieldIDClicked(this)' />";
756 echo "</td>\n";
758 echo " <td class='text-center optcell'>";
759 echo "<input type='text' id='fld[" . attr($fld_line_no) . "][title]' name='fld[" . attr($fld_line_no) . "][title]' value='" .
760 attr($linedata['title']) . "' size='15' maxlength='3000' class='form-control form-control-sm optin' />";
761 echo "</td>\n";
763 // if not english and set to translate layout labels, then show the translation
764 if ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) {
765 echo "<td class='text-center translation'>" . xlt($linedata['title']) . "</td>\n";
768 echo " <td class='text-center optcell'>";
769 echo "<select name='fld[" . attr($fld_line_no) . "][uor]' class='form-control form-control-sm optin'>";
770 foreach ($UOR as $key => $value) {
771 echo "<option value='" . attr($key) . "'";
772 if ($key == $linedata['uor']) {
773 echo " selected";
775 echo ">" . text($value) . "</option>\n";
778 echo "</select>";
779 echo "</td>\n";
781 echo " <td class='text-center optcell'>";
782 echo "<select class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][datatype]' id='fld[" . attr($fld_line_no) . "][datatype]' onchange=NationNotesContext(" . attr_js($fld_line_no) . ",this.value)>";
783 echo "<option value=''></option>";
784 global $datatypes;
785 global $sorted_datatypes;
786 foreach ($sorted_datatypes as $key => $value) {
787 if ($linedata['data_type'] == $key) {
788 echo "<option value='" . attr($key) . "' selected>" . text($value) . "</option>";
789 } else {
790 echo "<option value='" . attr($key) . "'>" . text($value) . "</option>";
794 echo "</select>";
795 echo " </td>";
797 echo " <td class='text-center optcell'>";
799 if (
800 in_array(
801 $linedata['data_type'],
802 array(1, 2, 3, 15, 21, 22, 23, 25, 26, 27, 28, 32, 33, 37, 40, 51, 52)
805 // Show the width field
806 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][lengthWidth]' value='" .
807 attr($linedata['fld_length']) .
808 "' size='2' maxlength='10' class='form-control form-control-sm optin' title='" . xla('Width') . "' />";
809 if (in_array($linedata['data_type'], array(3, 40))) {
810 // Show the height field
811 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][lengthHeight]' value='" .
812 attr($linedata['fld_rows']) .
813 "' size='2' maxlength='10' class='form-control form-control-sm optin' title='" . xla('Height') . "' />";
814 } else {
815 // Hide the height field
816 echo "<input type='hidden' name='fld[" . attr($fld_line_no) . "][lengthHeight]' value='' />";
818 } else {
819 // all other data_types (hide both the width and height fields
820 echo "<input type='hidden' name='fld[" . attr($fld_line_no) . "][lengthWidth]' value='' />";
821 echo "<input type='hidden' name='fld[" . attr($fld_line_no) . "][lengthHeight]' value='' />";
824 echo "</td>\n";
826 echo " <td class='text-center optcell'>";
827 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][maxSize]' value='" .
828 attr($linedata['max_length']) .
829 "' size='1' maxlength='10' class='form-control form-control-sm optin' " .
830 "title='" . xla('Maximum Size (entering 0 will allow any size)') . "' />";
831 echo "</td>\n";
833 echo " <td class='text-center optcell'>";
834 if (
835 $linedata['data_type'] == 1 || $linedata['data_type'] == 21 ||
836 $linedata['data_type'] == 22 || $linedata['data_type'] == 23 ||
837 $linedata['data_type'] == 25 || $linedata['data_type'] == 26 ||
838 $linedata['data_type'] == 27 || $linedata['data_type'] == 32 ||
839 $linedata['data_type'] == 33 || $linedata['data_type'] == 34 ||
840 $linedata['data_type'] == 36 || $linedata['data_type'] == 37 ||
841 $linedata['data_type'] == 43 || $linedata['data_type'] == 46
843 $type = "";
844 $disp = "style='display: none'";
845 if ($linedata['data_type'] == 34) {
846 $type = "style='display: none'";
847 $disp = "";
850 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][list_id]' id='fld[" . attr($fld_line_no) . "][list_id]' value='" .
851 attr($linedata['list_id']) . "' " . $type .
852 " size='6' maxlength='100' class='form-control form-control-sm optin listid' style='cursor: pointer;'" .
853 "title='" . xla('Choose list') . "' />";
855 echo "<select class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][contextName]' id='fld[" . attr($fld_line_no) . "][contextName]' " . $disp . ">";
856 $res = sqlStatement("SELECT * FROM customlists WHERE cl_list_type=2 AND cl_deleted=0");
857 while ($row = sqlFetchArray($res)) {
858 $sel = '';
859 if ($linedata['list_id'] == $row['cl_list_item_long']) {
860 $sel = 'selected';
863 echo "<option value='" . attr($row['cl_list_item_long']) . "' " . $sel . ">" . text($row['cl_list_item_long']) . "</option>";
866 echo "</select>";
867 } else {
868 // all other data_types
869 echo "<input type='hidden' name='fld[" . attr($fld_line_no) . "][list_id]' value='' />";
872 echo "</td>\n";
874 //Backup List Begin
875 echo " <td class='text-center optcell'>";
876 if (
877 $linedata['data_type'] == 1 || $linedata['data_type'] == 26 ||
878 $linedata['data_type'] == 33 || $linedata['data_type'] == 36 ||
879 $linedata['data_type'] == 43 || $linedata['data_type'] == 46
881 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][list_backup_id]' value='" .
882 attr($linedata['list_backup_id']) .
883 "' size='3' maxlength='100' class='form-control form-control-sm optin listid' style='cursor:pointer;' />";
884 } else {
885 echo "<input type='hidden' name='fld[" . attr($fld_line_no) . "][list_backup_id]' value='' />";
888 echo "</td>\n";
889 //Backup List End
891 echo " <td class='text-center optcell'>";
892 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][titlecols]' value='" .
893 attr($linedata['titlecols']) . "' size='3' maxlength='10' class='form-control form-control-sm optin' />";
894 echo "</td>\n";
896 echo " <td class='text-center optcell'>";
897 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][datacols]' value='" .
898 attr($linedata['datacols']) . "' size='3' maxlength='10' class='form-control form-control-sm optin' />";
899 echo "</td>\n";
900 /* Below for compatibility with existing string modifiers. */
901 if (!str_contains($linedata['edit_options'], ',') && isset($linedata['edit_options'])) {
902 $t = json_decode($linedata['edit_options']);
903 if (json_last_error() !== JSON_ERROR_NONE || $t === 0) { // hopefully string of characters and 0 handled.
904 $t = str_split(trim($linedata['edit_options']));
905 $linedata['edit_options'] = json_encode($t); // convert to array select understands.
908 echo " <td class='text-center optcell' title='" . xla("Add modifiers for this field type. You may select more than one.") . "'>";
909 echo "<select id='fld[" . attr($fld_line_no) . "][edit_options]' name='fld[" . attr($fld_line_no) . "][edit_options][]' class='typeAddons optin' size='3' multiple data-set='" .
910 attr(trim($linedata['edit_options'])) . "' ></select></td>\n";
912 if ($linedata['data_type'] == 31) {
913 echo " <td class='text-center optcell'>";
914 echo "<textarea name='fld[" . attr($fld_line_no) . "][desc]' rows='3' cols='35' class='form-control form-control-sm optin'>" .
915 text($linedata['description']) . "</textarea>";
916 echo "<input type='hidden' name='fld[" . attr($fld_line_no) . "][default]' value='" .
917 attr($linedata['default_value']) . "' />";
918 echo "</td>\n";
919 } else {
920 echo " <td class='text-center optcell'>";
921 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][desc]' value='" .
922 attr($linedata['description']) . "' size='20' class='form-control form-control-sm optin' />";
923 echo "<input type='hidden' name='fld[" . attr($fld_line_no) . "][default]' value='" .
924 attr($linedata['default_value']) . "' />";
925 echo "</td>\n";
926 // if not english and showing layout labels, then show the translation of Description
927 if ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) {
928 echo "<td class='text-center translation'>" . xlt($linedata['description']) . "</td>\n";
931 echo " <td class='text-center optcell'>";
932 echo "<input type='text' name='fld[" . attr($fld_line_no) . "][codes]' id='codes_fld[" . attr($fld_line_no) . "][codes]' value='" . attr($linedata['codes']) . "' title='" . xla('Code(s)') . "' onclick='select_clin_term_code(this)' size='10' maxlength='255' class='form-control form-control-sm optin' />";
933 echo "</td>\n";
935 // The "?" to click on for yet more field attributes.
936 echo " <td class='font-weight-bold' id='querytd_" . attr($fld_line_no) . "' style='cursor:pointer;";
937 if (!empty($linedata['conditions']) || !empty($linedata['validation'])) {
938 echo "background-color: var(--success);";
941 echo "' onclick='extShow(" . attr($fld_line_no) . ", this)' align='center' ";
942 echo "title='" . xla('Click here to view/edit more details') . "'>";
943 echo "&nbsp;?&nbsp;";
944 echo "</td>\n";
946 echo " </tr>\n";
948 // Create a floating div for the additional attributes of this field.
949 $conditions = empty($linedata['conditions']) ?
950 array(0 => array('id' => '', 'itemid' => '', 'operator' => '', 'value' => '')) :
951 unserialize($linedata['conditions'], ['allowed_classes' => false]);
952 $action = empty($conditions['action']) ? 'skip' : $conditions['action'];
953 $action_value = '';
954 if ($action != 'skip') {
955 $action_value = substr($action, 6);
956 $action = substr($action, 0, 5); // "value" or "hsval"
959 $extra_html .= "<div id='ext_" . attr($fld_line_no) . "' " .
960 "style='width: 750px; border: 1px solid var(--black);" .
961 "padding: 2px; background-color: var(--gray300); visibility: hidden;" .
962 "z-index: 1000; left:-1000px; top:0; font-size: 0.6875rem;' class='position-absolute'>\n" .
963 "<table class='w-100'>\n" .
964 " <tr>\n" .
965 " <th colspan='3' class='text-left font-weight-bold'>" .
966 xlt('For') . " " . text($linedata['field_id']) . " " .
967 "<select class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][action]' onchange='actionChanged(" . attr_js($fld_line_no) . ")'>" .
968 "<option value='skip' " . ($action == 'skip' ? 'selected' : '') . ">" . xlt('hide this field') . "</option>" .
969 "<option value='value' " . ($action == 'value' ? 'selected' : '') . ">" . xlt('set value to') . "</option>" .
970 "<option value='hsval' " . ($action == 'hsval' ? 'selected' : '') . ">" . xlt('hide else set to') . "</option>" .
971 "</select>" .
972 "<input type='text' class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][value]' value='" . attr($action_value) . "' size='15' />" .
973 " " . xlt('if') .
974 "</th>\n" .
975 " <th colspan='2' class='text-right text'><input class='btn btn-secondary' type='button' " .
976 "value='" . xla('Close') . "' onclick='extShow(" . attr_js($fld_line_no) . ", false)' />&nbsp;</th>\n" .
977 " </tr>\n" .
978 " <tr class='text-left'>\n" .
979 " <th class='font-weight-bold'>" . xlt('Field ID') . "</th>\n" .
980 " <th class='font-weight-bold'>" . xlt('List item ID') . "</th>\n" .
981 " <th class='font-weight-bold'>" . xlt('Operator') . "</th>\n" .
982 " <th class='font-weight-bold'>" . xlt('Value if comparing') . "</th>\n" .
983 " <th class='font-weight-bold'>&nbsp;</th>\n" .
984 " </tr>\n";
985 // There may be multiple condition lines for each field.
986 foreach ($conditions as $i => $condition) {
987 if (!is_numeric($i)) {
988 continue; // skip if 'action'
990 $extra_html .=
991 " <tr>\n" .
992 " <td class='text-left'>\n" .
993 " <select class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][condition_id][" . attr($i) . "]' onchange='cidChanged(" . attr_js($fld_line_no) . ", " . attr_js($i) . ")'>" .
994 genFieldOptionList($condition['id']) . " </select>\n" .
995 " </td>\n" .
996 " <td class='text-left'>\n" .
997 // List item choices are populated on the client side but will need the current value,
998 // so we insert a temporary option here to hold that value.
999 " <select class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][condition_itemid][" . attr($i) . "]'><option value='" .
1000 attr($condition['itemid']) . "'>...</option></select>\n" .
1001 " </td>\n" .
1002 " <td class='text-left'>\n" .
1003 " <select class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][condition_operator][" . attr($i) . "]'>\n";
1004 foreach (
1005 array(
1006 'eq' => xl('Equals'),
1007 'ne' => xl('Does not equal'),
1008 'se' => xl('Is selected'),
1009 'ns' => xl('Is not selected'),
1010 ) as $key => $value
1012 $extra_html .= " <option value='" . attr($key) . "'";
1013 if ($key == $condition['operator']) {
1014 $extra_html .= " selected";
1017 $extra_html .= ">" . text($value) . "</option>\n";
1020 $extra_html .=
1021 " </select>\n" .
1022 " </td>\n" .
1023 " <td class='text-left' title='" . xla('Only for comparisons') . "'>\n" .
1024 " <input type='text' class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][condition_value][" . attr($i) . "]' value='" .
1025 attr($condition['value']) . "' size='15' maxlength='63' />\n" .
1026 " </td>\n";
1027 if (!isset($conditions[$i + 1])) {
1028 $extra_html .=
1029 " <td class='text-right' title='" . xla('Add a condition') . "'>\n" .
1030 " <input type='button' class='btn btn-primary btn-sm' value='+' onclick='extAddCondition(" . attr_js($fld_line_no) . ",this)' />\n" .
1031 " </td>\n";
1032 } else {
1033 $extra_html .=
1034 " <td class='text-right'>\n" .
1035 " <select class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][condition_andor][" . attr($i) . "]'>\n";
1036 foreach (
1037 array(
1038 'and' => xl('And'),
1039 'or' => xl('Or'),
1040 ) as $key => $value
1042 $extra_html .= " <option value='" . attr($key) . "'";
1043 if ($key == $condition['andor']) {
1044 $extra_html .= " selected";
1047 $extra_html .= ">" . text($value) . "</option>\n";
1050 $extra_html .=
1051 " </select>\n" .
1052 " </td>\n";
1055 $extra_html .=
1056 " </tr>\n";
1059 $extra_html .=
1060 "</table>\n";
1062 $extra_html .= "<table class='w-100'>\n" .
1063 " <tr>\n" .
1064 " <td colspan='3' class='text-left font-weight-bold'>\"" . text($linedata['field_id']) . "\" " .
1065 xlt('will have the following validation rules') . ":</td>\n" .
1066 " </tr>\n" .
1067 " <tr>\n" .
1068 " <td class='text-left font-weight-bold'>" . xlt('Validation rule') . " </td>\n" .
1069 " </tr>\n" .
1070 " <tr>\n" .
1071 " <td class='text-left' title='" . xla('Select a validation rule') . "'>\n" .
1074 " <select class='form-control form-control-sm' name='fld[" . attr($fld_line_no) . "][validation]' onchange='valChanged(" . attr_js($fld_line_no) . ")'>\n" .
1075 " <option value=''";
1076 if (empty($linedata['validation'])) {
1077 $extra_html .= " selected";
1080 $extra_html .= ">-- " . xlt('Please Select') . " --</option>";
1081 foreach ($validations as $key => $value) {
1082 $extra_html .= " <option value='" . attr($key) . "'";
1083 if ($key == $linedata['validation']) {
1084 $extra_html .= " selected";
1087 $extra_html .= ">" . text($value) . "</option>\n";
1090 $extra_html .= "</select>\n" .
1091 " </td>\n";
1093 $extra_html .=
1094 "</table>\n" .
1095 "</div>\n";
1098 // Generates <optgroup> and <option> tags for all layouts.
1100 function genLayoutOptions($title = '?', $default = '')
1102 global $layouts;
1103 $s = " <option value=''>" . text($title) . "</option>\n";
1104 $lastgroup = '';
1105 foreach ($layouts as $key => $value) {
1106 if ($value[0] != $lastgroup) {
1107 if ($lastgroup) {
1108 $s .= " </optgroup>\n";
1110 $s .= " <optgroup label='" . attr($value[0]) . "'>\n";
1111 $lastgroup = $value[0];
1113 $s .= " <option value='" . attr($key) . "'";
1114 if ($key == $default) {
1115 $s .= " selected";
1117 $s .= ">" . text($value[1]) . "</option>\n";
1119 if ($lastgroup) {
1120 $s .= " </optgroup>\n";
1122 return $s;
1126 <!DOCTYPE HTML>
1127 <html>
1128 <head>
1129 <?php Header::setupHeader(['select2']); ?>
1130 <title><?php echo xlt('Layout Editor'); ?></title>
1131 <style>
1132 .sticky-top {
1133 top: 80px;
1134 z-index: 999;
1137 .orgTable tr.head {
1138 font-size: 0.6875rem;
1139 background-color: var(--gray400);
1142 .orgTable tr.detail {
1143 font-size: 0.6875rem;
1146 .orgTable td {
1147 font-size: 0.6875rem;
1150 .orgTable input {
1151 font-size: 0.6875rem;
1154 .orgTable select {
1155 font-size: 0.6875rem;
1159 a:visited,
1160 a:hover {
1161 color: var(--primary);
1164 .optin {
1165 background: transparent;
1168 .group {
1169 margin: 0 0 11px 0;
1170 padding: 0;
1171 width: 100%;
1174 .group table {
1175 border-collapse: collapse;
1176 width: 100%;
1180 .orgTable .odd td {
1181 background-color: var(--gray300);
1182 padding: 3px 0px 3px 0px;
1185 .orgTable .even td {
1186 background-color: var(--light);
1187 padding: 3px 0px 3px 0px;
1190 .help {
1191 cursor: help;
1194 .translation {
1195 color: var(--success);
1196 font-size: 0.6875rem;
1199 .highlight * {
1200 border: 2px solid var(--primary);
1201 background-color: var(--yellow);
1202 color: var(--black);
1205 .select2-container--default .select2-selection--multiple {
1206 cursor: pointer;
1209 .select2-search__field {
1210 cursor: pointer;
1211 width: 0 !important;
1214 /* Can't be responsive here cause of select2 */
1215 .select2-selection__choice {
1216 font-size: 0.75rem;
1219 .select2-container {
1220 cursor: pointer;
1221 opacity: 0.99 !important;
1224 .select2-dropdown {
1225 opacity: 0.99 !important;
1228 .tips {
1229 display: none;
1232 .select2-container--default .select2-selection--multiple {
1233 background-color: var(--white) !important;
1236 .select2-container--default .select2-selection--multiple .select2-selection__choice {
1237 background-color: var(--light) !important;
1239 .optin {
1240 color: var(--black) !important;
1242 </style>
1243 <script>
1245 // Called when the "Include inactive" checkbox is clicked.
1246 // This reloads the page, any edits are lost.
1247 function inactiveClicked() {
1248 top.restoreSession();
1249 location.href = 'edit_layout.php?form_inactive=<?php echo $form_inactive ? "0" : "1"; ?>';
1252 // Helper functions for positioning the floating divs.
1253 function extGetX(elem) {
1254 var x = 0;
1255 while(elem != null) {
1256 x += elem.offsetLeft;
1257 elem = elem.offsetParent;
1259 return x;
1261 function extGetY(elem) {
1262 var y = 0;
1263 while(elem != null) {
1264 y += elem.offsetTop;
1265 elem = elem.offsetParent;
1267 return y;
1270 // Show or hide the "extras" div for a row.
1271 var extdiv = null;
1272 function extShow(lino, show) {
1273 var thisdiv = document.getElementById("ext_" + lino);
1274 if (extdiv) {
1275 extdiv.style.visibility = 'hidden';
1276 extdiv.style.left = '-1000px';
1277 extdiv.style.top = '0';
1279 if (show && thisdiv != extdiv) {
1280 extdiv = thisdiv;
1281 var dw = window.innerWidth ? window.innerWidth - 20 : document.body.clientWidth;
1282 x = dw - extdiv.offsetWidth;
1283 if (x < 0) x = 0;
1284 var y = extGetY(show) + show.offsetHeight;
1285 extdiv.style.left = x + 'px';
1286 extdiv.style.top = y + 'px';
1287 extdiv.style.visibility = 'visible';
1289 else {
1290 extdiv = null;
1294 // Show or hide the value field for a "Set value to" condition.
1295 function actionChanged(lino) {
1296 var f = document.forms[0];
1297 var eaction = f['fld[' + lino + '][action]'];
1298 var evalue = f['fld[' + lino + '][value]'];
1299 evalue.style.display = eaction.value == 'skip' ? 'none' : '';
1302 // Add an extra condition line for the given row.
1303 function extAddCondition(lino, btnelem) {
1304 var f = document.forms[0];
1305 var i = 0;
1307 // Get index of next condition line.
1308 while (f['fld[' + lino + '][condition_id][' + i + ']']) ++i;
1309 if (i == 0) alert('f["fld[' + lino + '][condition_id][' + i + ']"]' + <?php echo xlj('not found') ?>);
1311 // Get containing <td>, <tr> and <table> nodes of the "+" button.
1312 var tdplus = btnelem.parentNode;
1313 var trelem = tdplus.parentNode;
1314 var telem = trelem.parentNode;
1316 // Replace contents of the tdplus cell.
1317 tdplus.innerHTML =
1318 "<select class='form-control form-control-sm' name='fld[" + lino + "][condition_andor][" + (i-1) + "]'>" +
1319 "<option value='and'>" + jsText(<?php echo xlj('And') ?>) + "</option>" +
1320 "<option value='or' >" + jsText(<?php echo xlj('Or') ?>) + "</option>" +
1321 "</select>";
1323 // Add the new row.
1324 var newtrelem = telem.insertRow(i+2);
1325 newtrelem.innerHTML =
1326 "<td class='text-left'>" +
1327 "<select class='form-control form-control-sm' name='fld[" + lino + "][condition_id][" + i + "]' onchange='cidChanged(" + lino + "," + i + ")'>" +
1328 <?php echo js_escape(genFieldOptionList()) ?> +
1329 "</select>" +
1330 "</td>" +
1331 "<td class='text-left'>" +
1332 "<select class='form-control form-control-sm' name='fld[" + lino + "][condition_itemid][" + i + "]' style='display:none' />" +
1333 "</td>" +
1334 "<td class='text-left'>" +
1335 "<select class='form-control form-control-sm' name='fld[" + lino + "][condition_operator][" + i + "]'>" +
1336 "<option value='eq'>" + jsText(<?php echo xlj('Equals') ?>) + "</option>" +
1337 "<option value='ne'>" + jsText(<?php echo xlj('Does not equal') ?>) + "</option>" +
1338 "<option value='se'>" + jsText(<?php echo xlj('Is selected') ?>) + "</option>" +
1339 "<option value='ns'>" + jsText(<?php echo xlj('Is not selected') ?>) + "</option>" +
1340 "</select>" +
1341 "</td>" +
1342 "<td class='text-left'>" +
1343 "<input type='text' class='form-control form-control-sm' name='fld[" + lino + "][condition_value][" + i + "]' value='' size='15' maxlength='63' />" +
1344 "</td>" +
1345 "<td class='text-right'>" +
1346 "<input type='button' class='btn btn-primary btn-sm' value='+' onclick='extAddCondition(" + lino + ",this)' />" +
1347 "</td>";
1350 // This is called when a field ID is chosen for testing within a skip condition.
1351 // It checks to see if a corresponding list item must also be chosen for the test, and
1352 // if so then inserts the dropdown for selecting an item from the appropriate list.
1353 function setListItemOptions(lino, seq, init) {
1354 var f = document.forms[0];
1355 var target = 'fld[' + lino + '][condition_itemid][' + seq + ']';
1356 // field_id is the ID of the field that the condition will test.
1357 var field_id = f['fld[' + lino + '][condition_id][' + seq + ']'].value;
1358 if (!field_id) {
1359 f[target].options.length = 0;
1360 f[target].style.display = 'none';
1361 return;
1363 // Find the occurrence of that field in the layout.
1364 var i = 1;
1365 while (true) {
1366 var idname = 'fld[' + i + '][id]';
1367 if (!f[idname]) {
1368 alert(<?php echo xlj('Condition field not found') ?> + ': ' + field_id);
1369 return;
1371 if (f[idname].value == field_id) break;
1372 ++i;
1374 // If this is startup initialization then preserve the current value.
1375 var current = init ? f[target].value : '';
1376 f[target].options.length = 0;
1377 // Get the corresponding data type and list ID.
1378 var data_type = f['fld[' + i + '][datatype]'].value;
1379 var list_id = f['fld[' + i + '][list_id]'].value;
1380 // WARNING: If new data types are defined the following test may need enhancing.
1381 // We're getting out if the type does not generate multiple fields with different names.
1382 if (data_type != '21' && data_type != '22' && data_type != '23' && data_type != '25' && data_type != '37') {
1383 f[target].style.display = 'none';
1384 return;
1386 // OK, list item IDs do apply so go get 'em.
1387 // This happens asynchronously so the generated code needs to stand alone.
1388 f[target].style.display = '';
1389 $.getScript('layout_listitems_ajax.php' +
1390 '?listid=' + encodeURIComponent(list_id) +
1391 '&target=' + encodeURIComponent(target) +
1392 '&current=' + encodeURIComponent(current) +
1393 '&csrf_token_form=' + <?php echo js_url(CsrfUtils::collectCsrfToken()); ?>);
1396 // This is called whenever a condition's field ID selection is changed.
1397 function cidChanged(lino, seq) {
1398 changeColor(lino);
1399 setListItemOptions(lino, seq, false);
1402 // This invokes the popup to edit layout properties or add a new layout.
1403 function edit_layout_props(groupid) {
1404 var title = <?php echo xlj('Layout Properties');?>;
1405 dlgopen('edit_layout_props.php?layout_id=' + <?php echo js_url($layout_id); ?> + '&group_id=' + encodeURIComponent(groupid),
1406 '_blank', 775, 550, "", title);
1409 // callback from edit_layout_props.php:
1410 function refreshme(layout_id) {
1411 location.href = 'edit_layout.php?layout_id=' + encodeURIComponent(layout_id);
1414 // This is called whenever a validation rule field ID selection is changed.
1415 function valChanged(lino) {
1416 changeColor(lino);
1419 function changeColor(lino){
1420 var thisid = document.forms[0]['fld[' + lino + '][condition_id][0]'].value;
1421 var thisValId = document.forms[0]['fld[' + lino + '][validation]'].value;
1422 var thistd = document.getElementById("querytd_" + lino);
1423 if(thisid !='' || thisValId!='') {
1424 thistd.style.backgroundColor = 'var(--success)';
1425 }else{
1426 thistd.style.backgroundColor = '';
1430 // Call this to disable the warning about unsaved changes and submit the form.
1431 function mySubmit() {
1432 somethingChanged = false;
1433 top.restoreSession();
1434 document.forms[0].submit();
1437 // User is about to do something that would discard any unsaved changes.
1438 // Return true if that is OK.
1439 function myChangeCheck() {
1440 if (somethingChanged) {
1441 if (!confirm(<?php echo xlj('You have unsaved changes. Abandon them?'); ?>)) {
1442 return false;
1444 // Do not set somethingChanged to false here because if they cancel the
1445 // action then the previously changed values will still be of interest.
1447 return true;
1450 </script>
1452 </head>
1454 <body class="body_top admin-layout">
1455 <form method='post' name='theform' id='theform' action='edit_layout.php'>
1456 <input type="hidden" name="csrf_token_form" value="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
1457 <input type="hidden" name="formaction" id="formaction" value="" />
1458 <!-- elements used to identify a field to delete -->
1459 <input type="hidden" name="deletefieldid" id="deletefieldid" value="" />
1460 <input type="hidden" name="deletefieldgroup" id="deletefieldgroup" value="" />
1461 <!-- elements used to identify a group to delete -->
1462 <input type="hidden" name="deletegroupid" id="deletegroupid" value="">
1463 <!-- elements used to change the group order -->
1464 <input type="hidden" name="movegroupname" id="movegroupname" value="" />
1465 <input type="hidden" name="movedirection" id="movedirection" value="" />
1466 <!-- elements used to select more than one field -->
1467 <input type="hidden" name="selectedfields" id="selectedfields" value="" />
1468 <input type="hidden" id="targetgroup" name="targetgroup" value="" />
1469 <input type="hidden" id="targetlayout" name="targetlayout" value="" />
1471 <div class="fixed-top py-2 px-1 bg-light text-dark">
1472 <strong><?php echo xlt('Edit layout'); ?>:</strong>&nbsp;
1473 <select name='layout_id' id='layout_id' class='form-control form-control-sm form-control form-control-sm-sm d-inline-block' style='margin-bottom:5px; width:20%;'>
1474 <?php echo genLayoutOptions('-- ' . xl('Select') . ' --', $layout_id); ?>
1475 </select>
1477 &nbsp;
1478 <label><input type='checkbox' name='form_inactive' value='1'
1479 title='<?php echo xla('This will abandon any edits!'); ?>'
1480 onclick='inactiveClicked()' <?php if ($form_inactive) {
1481 echo 'checked';} ?> />
1482 <?php echo xlt('Include inactive'); ?></label>
1484 <?php if ($layout_id) { ?>
1485 <div class="btn-group ml-auto">
1486 <button type='button' class='btn btn-secondary btn-sm' onclick='edit_layout_props("")'><?php echo xla('Layout Properties'); ?></button>
1487 <button type='button' class='btn btn-secondary btn-sm addgroup' id='addgroup'><?php echo xla('Add Group'); ?></button>
1488 <button type='button' class="btn btn-primary btn-save btn-sm" name='save' id='save'><?php echo xla('Save Changes'); ?></button>
1489 </div>
1490 <br>
1491 <?php echo xlt('With selected');?>:&nbsp;
1492 <input type='button' class='btn btn-secondary btn-sm' name='deletefields' id='deletefields' value='<?php echo xla('Delete'); ?>' disabled="disabled" />
1493 <input type='button' class='btn btn-secondary btn-sm' name='movefields' id='movefields' value='<?php echo xla('Move to...'); ?>' disabled="disabled" />
1494 <select id='copytolayout' class='form-control form-control-sm form-control form-control-sm-sm d-inline-block'
1495 style='width:20%;' disabled="disabled" onchange="CopyToLayout(this)">
1496 <?php echo genLayoutOptions(xl('Copy to Layout...')); ?>
1497 </select>
1498 &nbsp;&nbsp;&nbsp;
1499 <input type='button' class='btn btn-secondary btn-sm' value='<?php echo xla('Tips'); ?>' onclick='$("#tips").toggle();' />&nbsp;
1500 <input type='button' class='btn btn-secondary btn-sm' value='<?php echo xla('Encounter Preview'); ?>' onclick='layoutLook();' />
1501 <?php } else { ?>
1502 <button type='button' class='btn btn-primary btn-sm btn-add btn-new' onclick='edit_layout_props("")'><?php echo xla('New Layout'); ?></button>
1503 <?php } ?>
1505 <div id="tips" class="container tips">
1506 <section class="card bg-light p-3">
1507 <header class="card-heading">
1508 <h3 class="card-title"><?php echo xlt('Usage Tips') ?></h3>
1509 </header>
1510 <div class="card-body">
1511 <ul>
1512 <?php
1513 echo "<li>" . xlt("Clicking Options will present a multiselection drop menu to add behaviors to the selected data type. Typing after pull down activates allows search in options.") . "</li>";
1514 echo "<li>" . xlt("The option Span Entire Row is useful when using Static Text in allowing text to wrap and span entire row regardless of column settings. Another use could be to create an empty row as spacer or add additional option Add Bottom Border to create a line break.Only Bottom Border Row is useful here.") . "</li>";
1515 echo "<li>" . xlt("The options for Outline and Border will either wrap a row in thin border or add a border to the bottom of an item.") . "</li>";
1516 echo "<li>" . xlt("If a field's Label Col = 0 the label will immediately follow the previous data field in the Order sequence, on the same line as the Data field.") . "</li>";
1517 echo "<li>" . xlt("If a field's Data Col = 0 the data field will immediately follow its label field on the same line") . "</li>";
1518 echo "<li>" . xlt("If a field's Label Col = 1 the label field will go to a new line unless the previous field's total column values (Label + Data) is less than number of Layout columns from Group Properties or Layout Properties.") . "</li>";
1519 echo "<li>" . xlt("Generally, the first field in a group should be Label Cols = 1 Data Cols = number of Layout columns from Group Properties.") . "</li>";
1520 echo "<li>" . xlt("Make subsequent fields in the same row, Label = 0 Data = 0 and ensure enough columns are available from previous items to allow space for this new item. Otherwise result could be unpredictable") . "</li>";
1521 echo "<li>" . xlt("The Encounter Preview button is useful for showing encounter type layout forms as seen when using form in an encounter. Note, this feature is only useful for showing encounter forms and won't display system forms like Demographics") . "</li>";
1522 //echo "<li>" . xlt("") . "</li>";
1523 echo "<li>" . xlt("Please see http://www.open-emr.org/wiki/index.php/LBV_Forms for more on this topic") . "</li>";
1525 </ul>
1526 <button class='btn btn-success btn-sm float-right' onclick='$("#tips").toggle();return false;'><?php echo xlt('Dismiss')?></button>
1527 </div>
1528 </section></div></div>
1529 <?php
1530 // Load array of properties for this layout and its groups.
1531 $grparr = array();
1532 $gres = sqlStatement("SELECT * FROM layout_group_properties WHERE grp_form_id = ? " .
1533 "ORDER BY grp_group_id", array($layout_id));
1534 while ($grow = sqlFetchArray($gres)) {
1535 $grparr[$grow['grp_group_id']] = $grow;
1538 $prevgroup = "!@#asdf1234"; // an unlikely group ID
1539 $firstgroup = true; // flag indicates it's the first group to be displayed
1541 // Get the selected form's elements.
1542 if ($layout_id) {
1543 $res = sqlStatement(
1544 "SELECT p.grp_group_id, l.* FROM layout_group_properties AS p " .
1545 "LEFT JOIN layout_options AS l ON l.form_id = p.grp_form_id AND l.group_id = p.grp_group_id " .
1546 "WHERE p.grp_form_id = ? " .
1547 "ORDER BY p.grp_group_id, l.seq, l.field_id",
1548 array($layout_id)
1550 while ($row = sqlFetchArray($res)) {
1551 $group_id = $row['grp_group_id'];
1552 // Skip if this is the top level layout and (as expected) it has no fields.
1553 if ($group_id === '' && empty($row['form_id'])) {
1554 continue;
1556 if ($group_id != $prevgroup) {
1557 if ($firstgroup == false) {
1558 echo "</tbody></table></div>\n";
1559 echo "<div id='" . attr($group_id) . "' class='group'>";
1560 } else {
1561 // making first group flag useful for maintaining top fixed nav bar.
1562 echo "<div id='" . attr($group_id) . "' class='group' style='padding-top:80px'>";
1565 // echo "<div id='" . $group_id . "' class='group'>";
1566 echo "<div class='text bold layouts_title'>";
1568 // Get the fully qualified descriptive name of this group (i.e. including ancestor names).
1569 $gdispname = '';
1570 for ($i = 1; $i <= strlen($group_id); ++$i) {
1571 if ($gdispname) {
1572 $gdispname .= ' / ';
1574 $gdispname .= $grparr[substr($group_id, 0, $i)]['grp_title'];
1576 $gmyname = $grparr[$group_id]['grp_title'];
1578 $group_id_attr = attr($group_id);
1579 $group_id_attr_js = attr_js($group_id);
1580 $t_vars = [
1581 "xla_add_field" => xla("Add Field"),
1582 "xla_rename_group" => xla("Rename Group"),
1583 "xla_delete_group" => xla("Delete Group"),
1584 "xla_move_up" => xla("Move Up"),
1585 "xla_move_down" => xla("Move Down"),
1586 "xla_group_props" => xla("Group Properties"),
1587 'text_group_name' => text($gdispname),
1588 'translate_layout' => ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) ? xlt($gdispname) : "",
1589 'attr_gmyname' => attr($gmyname),
1591 echo <<<HTML
1592 <nav class="navbar navbar-expand-lg navbar-light bg-light sticky-top">
1593 <span class="navbar-brand">{$t_vars['translate_layout']}&nbsp;{$t_vars['text_group_name']}</span>
1594 <div class="btn-toolbar" role="toolbar" aria-label="Group Toolbar">
1595 <div class="btn-group mr-2" role="group" aria-label="Field Group">
1596 <button type="button" class="addfield btn btn-secondary btn-add btn-sm" id="addto~{$group_id_attr}">{$t_vars['xla_add_field']}</button>
1597 </div>
1598 <div class="btn-group ml-2 mr-2" role="group" aria-label="Move Group">
1599 <button type="button" class="movegroup btn btn-secondary btn-sm" id="{$group_id_attr}~up"><i class="fa fa-angle-up"></i>&nbsp;{$t_vars['xla_move_up']}</button>
1600 <button type="button" class="movegroup btn btn-secondary btn-sm" id="{$group_id_attr}~down"><i class="fa fa-angle-down"></i>&nbsp;{$t_vars['xla_move_down']}</button>
1601 </div>
1602 <div class="btn-group mr-2" role="group" aria-label="Group Options">
1603 <button type="button" class="renamegroup btn btn-secondary btn-sm" id="{$group_id_attr}~{$t_vars['attr_gmyname']}">{$t_vars['xla_rename_group']}</button>
1604 <button type="button" class="btn btn-secondary btn-sm" onclick="edit_layout_props({$group_id_attr_js})">{$t_vars['xla_group_props']}</button>
1605 <button type="button" class="deletegroup btn btn-secondary text-danger btn-sm" id="{$group_id_attr}">{$t_vars['xla_delete_group']}</button>
1606 </div>
1607 </div>
1609 </nav>
1610 HTML;
1611 $firstgroup = false;
1612 if (!empty($row['form_id'])) { // if this is not an empty group
1615 <div class="table-responsive">
1616 <table class='table table-sm table-striped'>
1617 <thead>
1618 <tr class='head'>
1619 <th><?php echo xlt('Order{{Sequence}}'); ?></th>
1620 <th <?php echo " $lbfonly"; ?>><?php echo xlt('Source'); ?></th>
1621 <th><?php echo xlt('ID'); ?>&nbsp;<span class="help" title='<?php echo xla('A unique value to identify this field, not visible to the user'); ?>' >(?)</span></th>
1622 <th><?php echo xlt('Label'); ?>&nbsp;<span class="help" title='<?php echo xla('The label that appears to the user on the form'); ?>' >(?)</span></th>
1623 <?php // if not english and showing layout label translations, then show translation header for title
1624 if ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) {
1625 echo "<th>" . xlt('Translation') . "<span class='help' title='" . xla('The translated label that will appear on the form in current language') . "'>&nbsp;(?)</span></th>";
1626 } ?>
1627 <th><?php echo xlt('UOR'); ?></th>
1628 <th><?php echo xlt('Data Type'); ?></th>
1629 <th><?php echo xlt('Size'); ?></th>
1630 <th><?php echo xlt('Max Size'); ?></th>
1631 <th><?php echo xlt('List'); ?></th>
1632 <th><?php echo xlt('Backup List'); ?></th>
1633 <th ><?php echo xlt('Label Cols'); ?></th>
1634 <th><?php echo xlt('Data Cols'); ?></th>
1635 <th><?php echo xlt('Options'); ?></th>
1636 <th><?php echo xlt('Description'); ?></th>
1637 <?php // if not english and showing layout label translations, then show translation header for description
1638 if ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) {
1639 echo "<th>" . xlt('Translation') . "<span class='help' title='" . xla('The translation of description in current language') . "'>&nbsp;(?)</span></th>";
1640 } ?>
1641 <th><?php echo xlt('Code(s)'); ?></th>
1642 <th style='width:1%'><?php echo xlt('?'); ?></th>
1643 </tr>
1644 </thead>
1645 <tbody>
1647 <?php
1648 } // end not empty group
1649 } // end new group
1651 if (!empty($row['form_id'])) {
1652 writeFieldLine($row);
1654 $prevgroup = $group_id;
1655 } // end while loop
1656 } // end if $layout_id
1659 </tbody>
1660 </table>
1661 </div>
1663 <?php echo $extra_html; ?>
1665 </form>
1667 <!-- template DIV that appears when user chooses to rename an existing group -->
1668 <div id="renamegroupdetail" class="bg-light p-3" style="border: 1px solid black; display: none; visibility: hidden;">
1669 <input type="hidden" name="renameoldgroupname" id="renameoldgroupname" value="" />
1670 <div class="form-group">
1671 <label for="renamegroupname"><?php echo xlt('Group Name'); ?>:</label>
1672 <input type="text" class="form-control form-control-sm" size="20" maxlength="30" name="renamegroupname" id="renamegroupname" />
1673 </div>
1674 <div class="form-group">
1675 <label for="renamegroupparent"><?php echo xlt('Parent'); ?>:</label>
1676 <?php echo genGroupSelector('renamegroupparent', $layout_id); ?>
1677 </div>
1678 <div class="btn-group">
1679 <input type="button" class="btn btn-primary btn-sm saverenamegroup btn-save" value="<?php echo xla('Rename Group'); ?>" />
1680 <input type="button" class="btn btn-secondary btn-sm cancelrenamegroup" value="<?php echo xla('Cancel'); ?>" />
1681 </div>
1682 </div>
1684 <!-- template DIV that appears when user chooses to add a new group -->
1685 <div id="groupdetail" style="border: 1px solid black; padding: 3px; display: none; visibility: hidden; background-color: var(--gray);">
1686 <span class='font-weight-bold'>
1687 <?php echo xlt('Group Name'); ?>:
1688 <input type="text" size="20" maxlength="30" name="newgroupname" id="newgroupname" />
1689 &nbsp;&nbsp;
1690 <?php echo xlt('Parent'); ?>:
1691 <?php echo genGroupSelector('newgroupparent', $layout_id); ?>
1692 <br />
1694 <input type="button" class="btn btn-primary btn-sm savenewgroup" value='<?php echo xla('Save New Group'); ?>' />
1695 <input type="button" class="btn btn-secondary btn-sm cancelnewgroup" value='<?php echo xla('Cancel'); ?>' />
1696 </span>
1697 </div>
1699 <!-- template DIV that appears when user chooses to add a new field to a group -->
1700 <div id="fielddetail" class="fielddetail" style="display: none; visibility: hidden">
1701 <input type="hidden" name="newfieldgroupid" id="newfieldgroupid" value="" />
1702 <div class="table-responsive">
1703 <table class="table table-sm" style="border-collapse: collapse;">
1704 <thead>
1705 <tr class='head'>
1706 <th><?php echo xlt('Order{{Sequence}}'); ?></th>
1707 <th <?php echo " $lbfonly"; ?>><?php echo xlt('Source'); ?></th>
1708 <th><?php echo xlt('ID'); ?>&nbsp;<span class="help" title='<?php echo xla('A unique value to identify this field, not visible to the user'); ?>' >(?)</span></th>
1709 <th><?php echo xlt('Label'); ?>&nbsp;<span class="help" title='<?php echo xla('The label that appears to the user on the form'); ?>' >(?)</span></th>
1710 <th><?php echo xlt('UOR'); ?></th>
1711 <th><?php echo xlt('Data Type'); ?></th>
1712 <th><?php echo xlt('Size Width'); ?></th>
1713 <th><?php echo xlt('Size Height'); ?></th>
1714 <th><?php echo xlt('Max Size'); ?></th>
1715 <th><?php echo xlt('List'); ?></th>
1716 <th><?php echo xlt('Backup List'); ?></th>
1717 <th><?php echo xlt('Label Cols'); ?></th>
1718 <th><?php echo xlt('Data Cols'); ?></th>
1719 <th><?php echo xlt('Options'); ?></th>
1720 <th><?php echo xlt('Description'); ?></th>
1721 <th><?php echo xlt('Code(s)'); ?></th>
1722 </tr>
1723 </thead>
1724 <tbody>
1725 <tr class='text-center'>
1726 <td><input type="text" class="form-control form-control-sm" name="newseq" id="newseq" value="" size="2" maxlength="4" /> </td>
1727 <td<?php echo " $lbfonly"; ?>>
1728 <select class='form-control form-control-sm' name='newsource' id='newsource'>
1729 <?php
1730 foreach ($sources as $key => $value) {
1731 echo " <option value='" . attr($key) . "'>" . text($value) . "</option>\n";
1734 </select>
1735 </td>
1736 <td ><input type="text" class="form-control form-control-sm" name="newid" id="newid" value="" size="10" maxlength="31" onclick='FieldIDClicked(this)' /> </td>
1737 <td><input type="text" class="form-control form-control-sm" name="newtitle" id="newtitle" value="" size="20" maxlength="63" /> </td>
1738 <td>
1739 <select class='form-control form-control-sm' name="newuor" id="newuor">
1740 <option value="0"><?php echo xlt('Unused'); ?></option>
1741 <option value="1" selected><?php echo xlt('Optional'); ?></option>
1742 <option value="2"><?php echo xlt('Required'); ?></option>
1743 </select>
1744 </td>
1745 <td align='center'>
1746 <select class='form-control form-control-sm' name='newdatatype' id='newdatatype'>
1747 <option value=''></option>
1748 <?php
1749 global $sorted_datatypes;
1750 foreach ($sorted_datatypes as $key => $value) {
1751 echo " <option value='" . attr($key) . "'>" . text($value) . "</option>\n";
1754 </select>
1755 </td>
1756 <td><input class='form-control form-control-sm' type="text" name="newlengthWidth" id="newlengthWidth" value="" size="1" maxlength="3" title="<?php echo xla('Width'); ?>" /></td>
1757 <td><input class='form-control form-control-sm' type="text" name="newlengthHeight" id="newlengthHeight" value="" size="1" maxlength="3" title="<?php echo xla('Height'); ?>" /></td>
1758 <td><input class='form-control form-control-sm' type="text" name="newmaxSize" id="newmaxSize" value="" size="1" maxlength="3" title="<?php echo xla('Maximum Size (entering 0 will allow any size)'); ?>" /></td>
1759 <td><input type="text" name="newlistid" id="newlistid" value="" size="8" maxlength="31" class="form-control form-control-sm listid" />
1760 <select class='form-control form-control-sm' name='contextName' id='contextName' style='display:none'>
1761 <?php
1762 $res = sqlStatement("SELECT * FROM customlists WHERE cl_list_type=2 AND cl_deleted=0");
1763 while ($row = sqlFetchArray($res)) {
1764 echo "<option value='" . attr($row['cl_list_item_long']) . "'>" . text($row['cl_list_item_long']) . "</option>";
1767 </select>
1768 </td>
1769 <td><input type="text" name="newbackuplistid" id="newbackuplistid" value="" size="8" maxlength="31" class="form-control form-control-sm listid" /></td>
1770 <td><input class='form-control form-control-sm' type="text" name="newtitlecols" id="newtitlecols" value="" size="3" maxlength="3" /> </td>
1771 <td><input class='form-control form-control-sm' type="text" name="newdatacols" id="newdatacols" value="" size="3" maxlength="3" /> </td>
1772 <td><select name="newedit_options[]" id="newedit_options" multiple class='form-control form-control-sm typeAddons'></select>
1773 <input type="hidden" name="newdefault" id="newdefault" value="" /> </td>
1774 <td><input type="text" class='form-control form-control-sm' name="newdesc" id="newdesc" value="" size="20" /> </td>
1775 <td><input type='text' class='form-control form-control-sm' name="newcodes" id="newcodes" value="" onclick='select_clin_term_code(this)' size='10' maxlength='255' /> </td>
1776 </tr>
1777 <tr>
1778 <td colspan="9">
1779 <input type="button" class="btn btn-primary btn-sm savenewfield" value='<?php echo xla('Save New Field'); ?>' />
1780 <input type="button" class="btn btn-secondary btn-sm cancelnewfield" value='<?php echo xla('Cancel'); ?>' />
1781 </td>
1782 </tr>
1783 </tbody>
1784 </table>
1785 </div>
1786 </div>
1788 <script>
1789 /* Field modifier objects - heading towards context based.
1790 Used by Select2 so rtl may be enabled*/
1791 <?php echo "var fldOptions = [";
1792 echo "{id: 'EP',text:" . xlj('Exclude in Portal') . "},
1793 {id: 'A',text:" . xlj('Age') . ",ctx:['4'],ctxExcp:['0']},
1794 {id: 'B',text:" . xlj('Gestational Age') . ",ctx:['4'],ctxExcp:['0']},
1795 {id: 'F',text:" . xlj('Add Time to Date') . ",ctx:['4'],ctxExcp:['0']},
1796 {id: 'C',text:" . xlj('Capitalize') . ",ctx:['0'],ctxExcp:['4','15','40']},
1797 {id: 'D',text:" . xlj('Dup Check') . "},
1798 {id: 'E',text:" . xlj('Dup Check on only Edit, or Extra billing codes OK') . "},
1799 {id: 'W',text:" . xlj('Dup Check on only New') . "},
1800 {id: 'G',text:" . xlj('Graphable') . "},
1801 {id: 'J',text:" . xlj('Jump to Next Row') . "},
1802 {id: 'K',text:" . xlj('Prepend Blank Row') . "},
1803 {id: 'L',text:" . xlj('Lab Order') . "},
1804 {id: 'M',text:" . xlj('Radio Group Master') . "},
1805 {id: 'm',text:" . xlj('Radio Group Member') . "},
1806 {id: 'N',text:" . xlj('New Patient Form') . "},
1807 {id: 'O',text:" . xlj('Order Processor') . "},
1808 {id: 'P',text:" . xlj('Default to previous value') . "},
1809 {id: 'R',text:" . xlj('Distributor') . "},
1810 {id: 'T',text:" . xlj('Description is default text') . "},
1811 {id: 'DAP',text:" . xlj('Description is Placeholder') . "},
1812 {id: 'U',text:" . xlj('Capitalize all') . "},
1813 {id: 'V',text:" . xlj('Vendor') . "},
1814 {id: 'X',text:" . xlj('Do Not Print') . "},
1815 {id:'grp',text:" . xlj('Stylings') . ",children:[
1816 {id: 'RS',text:" . xlj('Add Bottom Border Row') . "},
1817 {id: 'RO',text:" . xlj('Outline Entire Row') . "},
1818 {id: 'DS',text:" . xlj('Add Data Bottom Border') . "},
1819 {id: 'DO',text:" . xlj('Outline Data Col') . "},
1820 {id: 'SP',text:" . xlj('Span Entire Row') . "}
1822 {id: '0',text:" . xlj('Read Only') . "},
1823 {id: '1',text:" . xlj('Write Once') . "},
1824 {id: '2',text:" . xlj('Billing Code Descriptions') . "}];\n";
1826 // Language direction for select2
1827 echo 'var langDirection = ' . js_escape($_SESSION['language_direction']) . ';';
1830 // used when selecting a list-name for a field
1831 var selectedfield;
1833 // Support for beforeunload handler.
1834 var somethingChanged = false;
1836 // Get the next logical sequence number for a field in the specified group.
1837 // Note it guesses and uses the existing increment value.
1838 function getNextSeq(group) {
1839 var f = document.forms[0];
1840 var seq = 0;
1841 var delta = 10;
1842 for (var i = 1; true; ++i) {
1843 var gelem = f['fld[' + i + '][group]'];
1844 if (!gelem) break;
1845 if (gelem.value != group) continue;
1846 var tmp = parseInt(f['fld[' + i + '][seq]'].value);
1847 if (isNaN(tmp)) continue;
1848 if (tmp <= seq) continue;
1849 delta = tmp - seq;
1850 seq = tmp;
1852 return seq + delta;
1855 // Helper function for validating new fields.
1856 function validateNewField(idpfx) {
1857 var f = document.forms[0];
1858 var pfx = '#' + idpfx;
1859 var newid = $(pfx + "id").val();
1861 // seq must be numeric and <= 9999
1862 if (! IsNumeric($(pfx + "seq").val(), 0, 9999)) {
1863 alert(<?php echo xlj('Order must be a number between 1 and 9999'); ?>);
1864 return false;
1866 // length must be numeric and less than 999
1867 if (! IsNumeric($(pfx + "lengthWidth").val(), 0, 999)) {
1868 alert(<?php echo xlj('Size must be a number between 1 and 999'); ?>);
1869 return false;
1871 // titlecols must be numeric and less than 100
1872 if (! IsNumeric($(pfx + "titlecols").val(), 0, 999)) {
1873 alert(<?php echo xlj('LabelCols must be a number between 1 and 999'); ?>);
1874 return false;
1876 // datacols must be numeric and less than 100
1877 if (! IsNumeric($(pfx + "datacols").val(), 0, 999)) {
1878 alert(<?php echo xlj('DataCols must be a number between 1 and 999'); ?>);
1879 return false;
1881 // the id field can only have letters, numbers and underscores
1882 if ($(pfx + "id").val() == "") {
1883 alert(<?php echo xlj('ID cannot be blank'); ?>);
1884 return false;
1887 // Make sure the field ID is not duplicated.
1888 for (var j = 1; f['fld[' + j + '][id]']; ++j) {
1889 if (newid.toLowerCase() == f['fld[' + j + '][id]'].value.toLowerCase() ||
1890 newid.toLowerCase() == f['fld[' + j + '][originalid]'].value.toLowerCase())
1892 alert(<?php echo xlj('Error: Duplicated field ID'); ?> + ': ' + newid);
1893 return false;
1897 // the id field can only have letters, numbers and underscores
1898 var validid = $(pfx + "id").val().replace(/(\s|\W)/g, "_"); // match any non-word characters and replace them
1899 $(pfx + "id").val(validid);
1900 // similarly with the listid field
1901 validid = $(pfx + "listid").val().replace(/(\s|\W)/g, "_");
1902 $(pfx + "listid").val(validid);
1903 // similarly with the backuplistid field
1904 validid = $(pfx + "backuplistid").val().replace(/(\s|\W)/g, "_");
1905 $(pfx + "backuplistid").val(validid);
1907 return true;
1910 // jQuery stuff to make the page a little easier to use
1912 $(function () {
1914 $(function () {
1915 $('.typeAddons').select2({
1916 data: fldOptions,
1917 theme: 'default',
1918 multiple: true,
1919 closeOnSelect: false,
1920 width:'100%',
1921 minimumResultsForSearch: 'Infinity',
1922 containerCssClass: ':all:',
1923 allowClear: false,
1924 <?php require($GLOBALS['srcdir'] . '/js/xl/select2.js.php'); ?>
1927 // Populate field option selects
1928 $(function () {
1929 $('.typeAddons').each(function(i, obj) {
1930 var v = $(this).data('set')
1931 if(typeof v !== 'undefined' && v > ""){
1932 $(this).val(v).trigger("change")
1935 somethingChanged = false;
1938 $("#save").click(function() { SaveChanges(); });
1939 $("#layout_id").change(function() {
1940 if (!myChangeCheck()) {
1941 $("#layout_id").val(<?php echo js_escape($layout_id); ?>);
1942 return;
1944 mySubmit();
1946 $(".addgroup").click(function() { AddGroup(this); });
1947 $(".savenewgroup").click(function() { SaveNewGroup(this); });
1948 $(".deletegroup").click(function() { DeleteGroup(this); });
1949 $(".cancelnewgroup").click(function() { CancelNewGroup(this); });
1950 $(".movegroup").click(function() { MoveGroup(this); });
1951 $(".renamegroup").click(function() { RenameGroup(this); });
1952 $(".saverenamegroup").click(function() { SaveRenameGroup(this); });
1953 $(".cancelrenamegroup").click(function() { CancelRenameGroup(this); });
1954 $(".addfield").click(function() { AddField(this); });
1955 $("#deletefields").click(function() { DeleteFields(this); });
1956 $(".selectfield").click(function() {
1957 var TRparent = $(this).parent().parent();
1958 $(TRparent).children("td").toggleClass("highlight");
1959 // disable the delete-move buttons
1960 $("#deletefields").attr("disabled", "disabled");
1961 $("#movefields").attr("disabled", "disabled");
1962 $("#copytolayout").attr("disabled", "disabled");
1963 $(".selectfield").each(function(i) {
1964 // if any field is selected, enable the delete-move buttons
1965 if ($(this).prop("checked") == true) {
1966 $("#deletefields").removeAttr("disabled");
1967 $("#movefields").removeAttr("disabled");
1968 $("#copytolayout").removeAttr("disabled");
1972 $("#movefields").click(function() { ShowGroups(this); });
1973 $(".savenewfield").click(function() { SaveNewField(this); });
1974 $(".cancelnewfield").click(function() { CancelNewField(this); });
1975 $("#newtitle").blur(function() { if ($("#newid").val() == "") $("#newid").val($("#newtitle").val()); });
1976 $("#newdatatype").change(function() { ChangeList(this.value);});
1977 $(".listid").click(function() { ShowLists(this); });
1979 // special class that skips the element
1980 $(".noselect").focus(function() { $(this).blur(); });
1982 // Save the changes made to the form
1983 var SaveChanges = function () {
1984 var f = document.forms[0];
1985 for (var i = 1; f['fld['+i+'][id]']; ++i) {
1986 var ival = f['fld['+i+'][id]'].value;
1987 for (var j = i + 1; f['fld['+j+'][id]']; ++j) {
1988 if (ival.toLowerCase() == f['fld['+j+'][id]'].value.toLowerCase() ||
1989 ival.toLowerCase() == f['fld['+j+'][originalid]'].value.toLowerCase())
1991 alert(<?php echo xlj('Error: Duplicated field ID'); ?> + ': ' + ival);
1992 return;
1996 $("#formaction").val("save");
1997 mySubmit();
2000 /****************************************************/
2001 /************ Group functions ***********************/
2002 /****************************************************/
2004 // display the 'new group' DIV
2005 var AddGroup = function(btnObj) {
2006 if (!myChangeCheck()) return;
2007 $("#save").attr("disabled", true);
2008 // show the field details DIV
2009 $('#groupdetail').css('visibility', 'visible');
2010 $('#groupdetail').css('display', 'block');
2011 $('#groupdetail').css('margin-top', '85px');
2012 $(btnObj).parent().after($("#groupdetail"));
2013 $("html, body").animate({ scrollTop: 0 }, "slow");
2014 $('#groupdetail > #newgroupname').focus();
2017 // save the new group to the form
2018 var SaveNewGroup = function(btnObj) {
2019 // the group name field can only have letters, numbers, spaces and underscores
2020 // AND it cannot start with a number
2021 if ($("#newgroupname").val() == "") {
2022 alert(<?php echo xlj('Group names cannot be blank'); ?>);
2023 return false;
2025 if ($("#newgroupname").val().match(/^(\d+|\s+)/)) {
2026 alert(<?php echo xlj('Group names cannot start with numbers or spaces.'); ?>);
2027 return false;
2029 var validname = $("#newgroupname").val().replace(/[^A-za-z0-9 ]/g, "_"); // match any non-word characters and replace them
2030 $("#newgroupname").val(validname);
2031 $("#formaction").val("addgroup");
2032 mySubmit();
2035 // actually delete an entire group from the database
2036 var DeleteGroup = function(btnObj) {
2037 var parts = $(btnObj).attr("id");
2038 if (confirm(<?php echo xlj('WARNING') ?> + " - " +
2039 <?php echo xlj('This action cannot be undone.') ?> + "\n" +
2040 <?php echo xlj('Are you sure you wish to delete this entire group?'); ?>)
2042 // submit the form to add a new field to a specific group
2043 $("#formaction").val("deletegroup");
2044 $("#deletegroupid").val(parts);
2045 $("#theform").submit();
2049 // just hide the new field DIV
2050 var CancelNewGroup = function(btnObj) {
2051 // hide the field details DIV
2052 $('#groupdetail').css('visibility', 'hidden');
2053 $('#groupdetail').css('display', 'none');
2054 // reset the new group values to a default
2055 $('#groupdetail > #newgroupname').val("");
2056 $('#groupdetail > #newgroupparent').val("");
2057 $("#save").attr("disabled", false);
2060 // display the 'new field' DIV
2061 var MoveGroup = function(btnObj) {
2062 if (!myChangeCheck()) return;
2063 var btnid = $(btnObj).attr("id");
2064 var parts = btnid.split("~");
2065 var groupid = parts[0];
2066 var direction = parts[1];
2067 // submit the form to change group order
2068 $("#formaction").val("movegroup");
2069 $("#movegroupname").val(groupid);
2070 $("#movedirection").val(direction);
2071 mySubmit();
2074 // show the rename group DIV
2075 var RenameGroup = function(btnObj) {
2076 if (!myChangeCheck()) return;
2077 $("#save").attr("disabled", true);
2078 $('#renamegroupdetail').css('visibility', 'visible');
2079 $('#renamegroupdetail').css('display', 'block');
2080 $(btnObj).parent().append($("#renamegroupdetail"));
2081 var parts = $(btnObj).attr("id").split("~");
2082 $('#renameoldgroupname').val(parts[0]); // this is actually the existing group ID
2083 $('#renamegroupname').val(parts[1]); // the textual name of just this group
2084 var i = parts[0].length;
2085 $('[name=renamegroupparent]').val(i > 0 ? parts[0].substr(0, i-1) : ''); // parent ID
2088 // save the new group to the form
2089 var SaveRenameGroup = function(btnObj) {
2090 // the group name field can only have letters, numbers, spaces and underscores
2091 // AND it cannot start with a number
2092 if ($("#renamegroupname").val().match(/^\d+/)) {
2093 alert(<?php echo xlj('Group names cannot start with numbers.'); ?>);
2094 return false;
2096 var validname = $("#renamegroupname").val().replace(/[^A-za-z0-9 ]/g, "_"); // match any non-word characters and replace them
2097 $("#renamegroupname").val(validname);
2099 // submit the form to add a new field to a specific group
2100 $("#formaction").val("renamegroup");
2101 mySubmit();
2104 // just hide the new field DIV
2105 var CancelRenameGroup = function(btnObj) {
2106 // hide the field details DIV
2107 $('#renamegroupdetail').css('visibility', 'hidden');
2108 $('#renamegroupdetail').css('display', 'none');
2109 // reset the rename group values to a default
2110 $('#renameoldgroupname').val("");
2111 $('#renamegroupname').val("");
2112 $('#renamegroupparent').val("");
2115 /****************************************************/
2116 /************ Field functions ***********************/
2117 /****************************************************/
2119 // display the 'new field' DIV
2120 var AddField = function(btnObj) {
2121 if (!myChangeCheck()) return;
2122 $("#save").attr("disabled", true);
2123 // update the fieldgroup value to be the groupid
2124 var btnid = $(btnObj).attr("id");
2125 var parts = btnid.split("~");
2126 var groupid = parts[1];
2127 $('#fielddetail > #newfieldgroupid').attr('value', groupid);
2128 // show the field details DIV
2129 $('#fielddetail').css('visibility', 'visible');
2130 $('#fielddetail').css('display', 'block');
2131 $(btnObj).parent().append($("#fielddetail"));
2132 // Assign a sensible default sequence number.
2133 $('#newseq').val(getNextSeq(groupid));
2136 var DeleteFields = function(btnObj) {
2137 if (!myChangeCheck()) return;
2138 if (confirm(<?php echo xlj('WARNING'); ?> + " - " + <?php echo xlj('This action cannot be undone.'); ?> + '\n' + <?php echo xlj('Are you sure you wish to delete the selected fields?'); ?>)) {
2139 var delim = "";
2140 $(".selectfield").each(function(i) {
2141 // build a list of selected field names to be moved
2142 if ($(this).prop("checked") == true) {
2143 var parts = this.id.split("~");
2144 var currval = $("#selectedfields").val();
2145 $("#selectedfields").val(currval+delim+parts[1]);
2146 delim = " ";
2149 // submit the form to delete the field(s)
2150 $("#formaction").val("deletefields");
2151 mySubmit();
2155 // save the new field to the form
2156 var SaveNewField = function(btnObj) {
2157 // check the new field values for correct formatting
2158 if (!validateNewField('new')) return false;
2160 // submit the form to add a new field to a specific group
2161 $("#formaction").val("addfield");
2162 mySubmit();
2165 // just hide the new field DIV
2166 var CancelNewField = function(btnObj) {
2167 // hide the field details DIV
2168 $('#fielddetail').css('visibility', 'hidden');
2169 $('#fielddetail').css('display', 'none');
2170 // reset the new field values to a default
2171 ResetNewFieldValues();
2172 $("#save").attr("disabled", false);
2175 // show the popup choice of lists
2176 var ShowLists = function(btnObj) {
2177 var title = <?php echo xlj('Select List');?>;
2178 dlgopen('../patient_file/encounter/find_code_dynamic.php?what=lists',"_blank", 850, 750, "", title);
2179 selectedfield = btnObj;
2182 // show the popup choice of groups
2183 var ShowGroups = function(btnObj) {
2184 if (!myChangeCheck()) return;
2185 $("#targetlayout").val("");
2186 var title = <?php echo xlj('Select Group');?>;
2187 dlgopen('../patient_file/encounter/find_code_dynamic.php?what=groups&layout_id=' + <?php echo js_url($layout_id); ?>,
2188 "_blank",850, 600,"", title);
2191 // Show context DD for NationNotes
2192 var ChangeList = function(btnObj){
2193 if(btnObj==34){
2194 $('#newlistid').hide();
2195 $('#contextName').show();
2197 else{
2198 $('#newlistid').show();
2199 $('#contextName').hide();
2203 // Initialize list item selectors and value field visibilities in skip conditions.
2204 var f = document.forms[0];
2205 for (var lino = 1; f['fld[' + lino + '][id]']; ++lino) {
2206 for (var seq = 0; f['fld[' + lino + '][condition_itemid][' + seq + ']']; ++seq) {
2207 setListItemOptions(lino, seq, true);
2209 actionChanged(lino);
2212 // Support for beforeunload handler.
2213 $('tbody input, tbody select, tbody textarea').not('.selectfield').change(function() {
2214 somethingChanged = true;
2216 window.addEventListener("beforeunload", function (e) {
2217 if (somethingChanged && !top.timed_out) {
2218 var msg = <?php echo xlj('You have unsaved changes.'); ?>;
2219 e.returnValue = msg; // Gecko, Trident, Chrome 34+
2220 return msg; // Gecko, WebKit, Chrome <34
2224 }); /* Ready Done */
2226 function layoutLook(){
2227 var form = <?php echo js_escape($layout_id);?>;
2228 var btnName = <?php echo xlj('Back To Editor');?>;
2229 var url = "../patient_file/encounter/view_form.php?isShow&id=0&formname=" + encodeURIComponent(form);
2230 var title = <?php echo xlj('LBF Encounter Form Preview');?>;
2231 dlgopen(url, '_blank', 1250, 800, "", title);
2232 return false;
2235 // show the popup choice of groups
2236 // TBD: Make this less redundant to ShowGroups which is used for moving fields.
2237 function CopyToLayout(selObj) {
2238 if (!selObj.value || !myChangeCheck()) return;
2239 $("#targetlayout").val(selObj.value);
2240 var title = <?php echo xlj('Select Group');?>;
2241 dlgopen('../patient_file/encounter/find_code_dynamic.php?what=groups&layout_id=' + selObj.value,
2242 "_blank",850, 600,"", title);
2245 function typeUsesList(datatype) {
2246 <?php
2247 foreach ($typesUsingList as $mytype) {
2248 // echo " if (datatype == '$mytype') return true;\n";
2249 echo " if (datatype == " . js_escape($mytype) . ") return true;\n";
2252 return false;
2255 function NationNotesContext(lineitem, val) {
2256 // Check if function is needed.
2257 if (!document.getElementById("fld[" + lineitem + "][contextName]") || !document.getElementById("fld[" + lineitem + "][list_id]")) {
2258 return false; // these elements don't exist yet so do nothing.
2260 if (val == 34) {
2261 document.getElementById("fld[" + lineitem + "][contextName]").style.display = '';
2262 document.getElementById("fld[" + lineitem + "][list_id]").style.display = 'none';
2263 document.getElementById("fld[" + lineitem + "][list_id]").value = '';
2265 else {
2266 document.getElementById("fld[" + lineitem + "][list_id]").style.display = '';
2267 document.getElementById("fld[" + lineitem + "][contextName]").style.display = 'none';
2268 if (!typeUsesList(val)) {
2269 document.getElementById("fld["+lineitem+"][listid]").value='';
2274 function SetList(listid) {
2275 $(selectedfield).val(listid);
2278 //////////////////////////////////////////////////////////////////////
2279 // The following supports the field ID selection pop-up.
2280 //////////////////////////////////////////////////////////////////////
2282 var fieldselectfield;
2284 function elemFromPart(part) {
2285 var ename = fieldselectfield.name;
2286 // ename is like one of the following:
2287 // fld[$fld_line_no][id]
2288 // gnewid
2289 // newid
2290 // and "part" is what we substitute for the "id" part.
2291 var i = ename.lastIndexOf('id');
2292 ename = ename.substr(0, i) + part + ename.substr(i+2);
2293 return document.forms[0][ename];
2296 function FieldIDClicked(elem) {
2297 <?php if (substr($layout_id, 0, 3) == 'LBF') { ?>
2298 fieldselectfield = elem;
2299 var srcval = elemFromPart('source').value;
2300 // If the field ID is for the local form, allow direct entry.
2301 if (srcval == 'F') return;
2302 // Otherwise pop up the selection window.
2303 var title = <?php echo xlj('Select Field');?>;
2304 dlgopen('../patient_file/encounter/find_code_dynamic.php?what=fields&source='
2305 + encodeURIComponent(srcval), "_blank", 700, 600, "", title);
2306 <?php } ?>
2309 function SetField(field_id, title, data_type, uor, fld_length, max_length,
2310 list_id, titlecols, datacols, edit_options, description, fld_rows)
2312 fieldselectfield.value = field_id;
2313 elemFromPart('title' ).value = title;
2314 elemFromPart('datatype' ).value = data_type;
2315 elemFromPart('uor' ).value = uor;
2316 elemFromPart('lengthWidth' ).value = fld_length;
2317 elemFromPart('maxSize' ).value = max_length;
2318 elemFromPart('list_id' ).value = list_id;
2319 elemFromPart('titlecols' ).value = titlecols;
2320 elemFromPart('datacols' ).value = datacols;
2321 elemFromPart('edit_options').value = edit_options;
2322 elemFromPart('desc' ).value = description;
2323 elemFromPart('codes' ).value = codes;
2324 elemFromPart('lengthHeight').value = fld_rows;
2327 //////////////////////////////////////////////////////////////////////
2328 // End code for field ID selection pop-up.
2329 //////////////////////////////////////////////////////////////////////
2331 /* This is called after the user chooses a new group from the popup window.
2332 * It will submit the page so the selected fields can be moved into
2333 * the target group or copied to the target layout.
2335 function MoveFields(targetgroup) {
2336 $("#targetgroup").val(targetgroup);
2337 var delim = "";
2338 $(".selectfield").each(function(i) {
2339 // build a list of selected field names to be moved
2340 if ($(this).prop("checked") == true) {
2341 var parts = this.id.split("~");
2342 var currval = $("#selectedfields").val();
2343 $("#selectedfields").val(currval+delim+parts[1]);
2344 delim = " ";
2347 if ($("#targetlayout").val()) {
2348 $("#formaction").val("copytolayout");
2350 else {
2351 $("#formaction").val("movefields");
2353 mySubmit();
2356 // set the new-field values to a default state
2357 function ResetNewFieldValues () {
2358 $("#newseq").val("");
2359 $("#newsource").val("");
2360 $("#newid").val("");
2361 $("#newtitle").val("");
2362 $("#newuor").val(1);
2363 $("#newlengthWidth").val("");
2364 $("#newlengthHeight").val("");
2365 $("#newmaxSize").val("");
2366 $("#newdatatype").val("");
2367 $("#newlistid").val("");
2368 $("#newbackuplistid").val("");
2369 $("#newtitlecols").val("");
2370 $("#newdatacols").val("");
2371 $("#newedit_options").val("");
2372 $("#newdefault").val("");
2373 $("#newdesc").val("");
2376 // is value an integer and between min and max
2377 function IsNumeric(value, min, max) {
2378 if (value == "" || value == null) return false;
2379 if (! IsN(value) ||
2380 parseInt(value) < min ||
2381 parseInt(value) > max)
2382 return false;
2384 return true;
2387 // This invokes the find-code popup.
2388 function select_clin_term_code(e) {
2389 current_sel_name = '';
2390 current_sel_clin_term = e.id;
2391 dlgopen('../patient_file/encounter/find_code_dynamic.php', '_blank', 900, 600);
2394 // This is for callback by the find-code popup.
2395 function set_related(codetype, code, selector, codedesc) {
2396 // Coming from the Clinical Terms Code(s) edit
2397 var e = document.getElementById(current_sel_clin_term);
2398 var s = e.value;
2399 if (code) {
2400 if (s.length > 0) s += ';';
2401 s += codetype + ':' + code;
2403 else {
2404 s = '';
2406 e.value = s;
2409 // This is for callback by the find-code popup.
2410 // Deletes the specified codetype:code from the currently selected list.
2411 function del_related(s) {
2412 my_del_related(s, document.getElementById(current_sel_clin_term), false);
2415 // This is for callback by the find-code popup.
2416 // Returns the array of currently selected codes with each element in codetype:code format.
2417 function get_related() {
2418 return document.getElementById(current_sel_clin_term).value.split(';');
2421 /****************************************************/
2422 /****************************************************/
2423 /****************************************************/
2425 // tell if num is an Integer
2426 function IsN(num) { return !/\D/.test(num); }
2428 </script>
2429 </body>
2430 </html>