Allow for zero in string or singular in legacy modifiers.
[openemr.git] / interface / super / edit_layout.php
blob3c10924ddd0a461efff5540248fe06272be59cb4
1 <?php
2 /**
3 * Edit layouts gui
5 * @package OpenEMR
6 * @link http://www.open-emr.org
7 * @author Rod Roark <rod@sunsetsystems.com>
8 * @author Jerry Padgett <sjpadgett@gmail.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @copyright Copyright (c) 2014-2017 Rod Roark <rod@sunsetsystems.com>
11 * @copyright Copyright (c) 2017 Jerry Padgett <sjpadgett@gmail.com>
12 * @copyright Copyright (c) 2017 Brady Miller <brady.g.miller@gmail.com>
13 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
16 require_once("../globals.php");
17 require_once("$srcdir/acl.inc");
18 require_once("$srcdir/log.inc");
20 use OpenEMR\Core\Header;
22 function collectLayoutNames($condition, $mapping = '')
24 global $layouts;
25 $gres = sqlStatement("SELECT grp_form_id, grp_title, grp_mapping " .
26 "FROM layout_group_properties WHERE " .
27 "grp_group_id = '' AND grp_activity = 1 AND $condition " .
28 "ORDER BY grp_mapping, grp_seq, grp_title");
29 while ($grow = sqlFetchArray($gres)) {
30 $tmp = $mapping ? $mapping : $grow['grp_mapping'];
31 if (!$tmp) {
32 $tmp = '(' . xl('No Name') . ')';
34 $layouts[$grow['grp_form_id']] = array($tmp, $grow['grp_title']);
37 $layouts = array();
38 collectLayoutNames("grp_form_id NOT LIKE 'LBF%' AND grp_form_id NOT LIKE 'LBT%'", xl('Core'));
39 collectLayoutNames("grp_form_id LIKE 'LBT%'", xl('Transactions'));
40 collectLayoutNames("grp_form_id LIKE 'LBF%'", '');
42 // Include predefined Validation Rules from list
43 $validations = array();
44 $lres = sqlStatement("SELECT * FROM list_options " .
45 "WHERE list_id = 'LBF_Validations' AND activity = 1 ORDER BY seq, title");
46 while ($lrow = sqlFetchArray($lres)) {
47 $validations[$lrow['option_id']] = xl_list_label($lrow['title']);
50 // Field modifier objects in script
51 // array of the data_types of the fields
52 $datatypes = array(
53 "1" => xl("List box"),
54 "2" => xl("Textbox"),
55 "3" => xl("Textarea"),
56 "4" => xl("Text-date"),
57 "10" => xl("Providers"),
58 "11" => xl("Providers NPI"),
59 "12" => xl("Pharmacies"),
60 "13" => xl("Squads"),
61 "14" => xl("Organizations"),
62 "15" => xl("Billing codes"),
63 "16" => xl("Insurances"),
64 "18" => xl("Visit Categories"),
65 "21" => xl("Checkbox(es)"),
66 "22" => xl("Textbox list"),
67 "23" => xl("Exam results"),
68 "24" => xl("Patient allergies"),
69 "25" => xl("Checkboxes w/text"),
70 "26" => xl("List box w/add"),
71 "27" => xl("Radio buttons"),
72 "28" => xl("Lifestyle status"),
73 "31" => xl("Static Text"),
74 "32" => xl("Smoking Status"),
75 "33" => xl("Race/Ethnicity"),
76 "34" => xl("NationNotes"),
77 "35" => xl("Facilities"),
78 "36" => xl("Multiple Select List"),
79 "40" => xl("Image canvas"),
82 $sources = array(
83 'F' => xl('Form'),
84 'D' => xl('Patient'),
85 'H' => xl('History'),
86 'E' => xl('Visit'),
87 'V' => xl('VisForm'),
90 function nextGroupOrder($order)
92 if ($order == '9') {
93 $order = 'A';
94 } else if ($order == 'Z') {
95 $order = 'a';
96 } else {
97 $order = chr(ord($order) + 1);
100 return $order;
103 // This returns HTML for a <select> that allows choice of a layout group.
104 // Included also are parent groups containing only sub-groups. Groups are listed
105 // in the same order as they appear in the layout.
107 function genGroupSelector($name, $layout_id, $default = '')
109 $res = sqlStatement(
110 "SELECT grp_group_id, grp_title " .
111 "FROM layout_group_properties WHERE " .
112 "grp_form_id = ? AND grp_group_id != '' ORDER BY grp_group_id",
113 array($layout_id)
115 $s = "<select class='form-control' name='" . xla($name) . "'>";
116 $s .= "<option value=''>" . xlt('None') . "</option>";
117 $arr = array();
118 $arrid = '';
119 while ($row = sqlFetchArray($res)) {
120 $thisid = $row['grp_group_id'];
121 $i = 0;
122 // Compute number of initial matching groups.
123 while ($i < strlen($thisid) && $i < strlen($arrid) && $thisid[$i] == $arrid[$i]) {
124 ++$i;
126 $arr = array_slice($arr, 0, $i); // discard the rest
127 while ($i < (strlen($arrid) - 1)) {
128 $arr[$i++] = '???'; // should not happen
130 $arr[$i] = $row['grp_title'];
131 $gval = '';
132 foreach ($arr as $part) {
133 if ($gval) {
134 $gval .= ' / ';
136 $gval .= $part;
138 $s .= "<option value='" . attr($thisid) . "'";
139 if ($thisid == $default) {
140 $s .= ' selected';
142 $s .= ">" . text($gval) . "</option>";
144 $s .= "</select>";
145 return $s;
148 // Compute a new group ID that will become layout_options.group_id and
149 // layout_group_properties.grp_group_id.
150 // $parent is a string of zero or more sequence prefix characters.
151 // If there is a nonempty $parent then its ID will be the prefix for the
152 // new ID and the sequence prefix will be computed within the parent.
154 function genGroupId($parent)
156 global $layout_id;
157 $results = sqlStatement(
158 "SELECT grp_group_id " .
159 "FROM layout_group_properties WHERE " .
160 "grp_form_id = ? AND grp_group_id LIKE ?",
161 array($layout_id, "$parent_%")
163 $maxnum = '1';
164 while ($result = sqlFetchArray($results)) {
165 $tmp = substr($result['grp_group_id'], strlen($parent), 1);
166 if ($tmp >= $maxnum) {
167 $maxnum = nextGroupOrder($tmp);
170 return $parent . $maxnum;
173 // Changes a group's ID from and to the specified IDs. This also works for groups
174 // that have sub-groups, in which case only the appropriate parent portion of
175 // the ID is changed.
177 function fuzzyRename($from, $to)
179 global $layout_id;
181 $query = "UPDATE layout_options SET group_id = concat(?, substr(group_id, ?)) " .
182 "WHERE form_id = ? AND group_id LIKE ?";
183 sqlStatement($query, array($to, strlen($from) + 1, $layout_id, "$from%"));
185 $query = "UPDATE layout_group_properties SET grp_group_id = concat(?, substr(grp_group_id, ?)) " .
186 "WHERE grp_form_id = ? AND grp_group_id LIKE ?";
187 sqlStatement($query, array($to, strlen($from) + 1, $layout_id, "$from%"));
190 // Swaps the positions of two groups. To the degree they have matching parents,
191 // only the first differing child positions are swapped.
193 function swapGroups($id1, $id2)
195 $i = 0;
196 while ($i < strlen($id1) && $i < strlen($id2) && $id1[$i] == $id2[$i]) {
197 ++$i;
199 // $i is now the number of matching characters/levels.
200 if ($i < strlen($id1) && $i < strlen($id2)) {
201 $common = substr($id1, 0, $i);
202 $pfx1 = substr($id1, $i, 1);
203 $pfx2 = substr($id2, $i, 1);
204 $tmpname = $common . '#';
205 // To avoid collision use 3 renames.
206 fuzzyRename($common . $pfx1, $common . '#');
207 fuzzyRename($common . $pfx2, $common . $pfx1);
208 fuzzyRename($common . '#', $common . $pfx2);
212 function tableNameFromLayout($layout_id)
214 // Skip layouts that store data in vertical tables.
215 if (substr($layout_id, 0, 3) == 'LBF' || substr($layout_id, 0, 3) == 'LBT' || $layout_id == "FACUSR") {
216 return '';
218 if ($layout_id == "DEM") {
219 $tablename = "patient_data";
220 } elseif ($layout_id == "HIS") {
221 $tablename = "history_data";
222 } elseif ($layout_id == "SRH") {
223 $tablename = "lists_ippf_srh";
224 } elseif ($layout_id == "CON") {
225 $tablename = "lists_ippf_con";
226 } elseif ($layout_id == "GCA") {
227 $tablename = "lists_ippf_gcac";
228 } else {
229 die(xlt('Internal error in tableNameFromLayout') . '(' . text($layout_id) . ')');
231 return $tablename;
234 // Call this when adding or removing a layout field. This will create or drop
235 // the corresponding table column when appropriate. Table columns are not
236 // dropped if they contain any non-empty values.
237 function addOrDeleteColumn($layout_id, $field_id, $add = true)
239 $tablename = tableNameFromLayout($layout_id);
240 if (!$tablename) {
241 return;
243 // Check if the column currently exists.
244 $tmp = sqlQuery("SHOW COLUMNS FROM `$tablename` LIKE '$field_id'");
245 $column_exists = !empty($tmp);
247 if ($add && !$column_exists) {
248 sqlStatement("ALTER TABLE `$tablename` ADD `$field_id` TEXT");
249 newEvent(
250 "alter_table",
251 $_SESSION['authUser'],
252 $_SESSION['authProvider'],
254 "$tablename ADD $field_id"
256 } elseif (!$add && $column_exists) {
257 // Do not drop a column that has any data.
258 $tmp = sqlQuery("SELECT `$field_id` FROM `$tablename` WHERE " .
259 "`$field_id` IS NOT NULL AND `$field_id` != '' LIMIT 1");
260 if (!isset($tmp['field_id'])) {
261 sqlStatement("ALTER TABLE `$tablename` DROP `$field_id`");
262 newEvent(
263 "alter_table",
264 $_SESSION['authUser'],
265 $_SESSION['authProvider'],
267 "$tablename DROP $field_id "
273 // Call this before renaming a layout field.
274 // Renames the table column (if applicable) and returns a result status:
275 // -1 = There is no table for this layout (not an error).
276 // 0 = Rename successful.
277 // 2 = There is no column having the old name.
278 // 3 = There is already a column having the new name.
280 function renameColumn($layout_id, $old_field_id, $new_field_id)
282 $tablename = tableNameFromLayout($layout_id);
283 if (!$tablename) {
284 return -1; // Indicate rename is not relevant.
286 // Make sure old column exists.
287 $colarr = sqlQuery("SHOW COLUMNS FROM `$tablename` LIKE ?", array($old_field_id));
288 if (empty($colarr)) {
289 // Error, old name does not exist.
290 return 2;
292 // Make sure new column does not exist.
293 $tmp = sqlQuery("SHOW COLUMNS FROM `$tablename` LIKE ?", array($new_field_id));
294 if (!empty($tmp)) {
295 // Error, new name already in use.
296 return 3;
298 // With MySQL you can't change just the name, you have to specify the column definition too.
299 $colstr = $colarr['Type'];
300 if ($colarr['Null'] == 'NO') {
301 $colstr .= " NOT NULL";
303 if ($colarr['Default'] !== null) {
304 $colstr .= " DEFAULT '" . add_escape_custom($colarr['Default']) . "'";
306 if ($colarr['Extra']) {
307 $colstr .= " " . $colarr['Extra'];
309 $query = "ALTER TABLE `$tablename` CHANGE `$old_field_id` `$new_field_id` $colstr";
310 sqlStatement($query);
311 newEvent(
312 "alter_table",
313 $_SESSION['authUser'],
314 $_SESSION['authProvider'],
316 "$tablename RENAME $old_field_id TO $new_field_id $colstr"
318 return 0; // Indicate rename done and successful.
321 // Test options array for save
322 function encodeModifier($jsonArray)
324 return $jsonArray !== null ? json_encode($jsonArray) : "";
327 // Check authorization.
328 $thisauth = acl_check('admin', 'super');
329 if (!$thisauth) {
330 die(xl('Not authorized'));
333 // The layout ID identifies the layout to be edited.
334 $layout_id = empty($_REQUEST['layout_id']) ? '' : $_REQUEST['layout_id'];
336 // Tag style for stuff to hide if not an LBF layout. Currently just for the Source column.
337 $lbfonly = substr($layout_id, 0, 3) == 'LBF' ? "" : "style='display:none;'";
339 // Handle the Form actions
341 if ($_POST['formaction'] == "save" && $layout_id) {
342 // If we are saving, then save.
343 $fld = $_POST['fld'];
344 for ($lino = 1; isset($fld[$lino]['id']); ++$lino) {
345 $iter = $fld[$lino];
346 $field_id = formTrim($iter['id']);
347 $field_id_original = formTrim($iter['originalid']);
348 $data_type = formTrim($iter['datatype']);
349 $listval = $data_type == 34 ? formTrim($iter['contextName']) : formTrim($iter['list_id']);
350 $action = $iter['action'];
351 if ($action == 'value') {
352 $action = 'value=' . $iter['value'];
355 // Skip conditions for the line are stored as a serialized array.
356 $condarr = array('action' => $action);
357 $cix = 0;
358 for (; !empty($iter['condition_id'][$cix]); ++$cix) {
359 $andor = empty($iter['condition_andor'][$cix]) ? '' : $iter['condition_andor'][$cix];
360 $condarr[$cix] = array(
361 'id' => strip_escape_custom($iter['condition_id' ][$cix]),
362 'itemid' => strip_escape_custom($iter['condition_itemid' ][$cix]),
363 'operator' => strip_escape_custom($iter['condition_operator'][$cix]),
364 'value' => strip_escape_custom($iter['condition_value' ][$cix]),
365 'andor' => strip_escape_custom($andor),
368 $conditions = $cix ? serialize($condarr) : '';
370 if ($field_id) {
371 if ($field_id != $field_id_original) {
372 if (renameColumn($layout_id, $field_id_original, $field_id) > 0) {
373 // If column rename had an error then don't rename it here.
374 $field_id = $field_id_original;
377 sqlStatement("UPDATE layout_options SET " .
378 "field_id = '" . formDataCore($field_id) . "', " .
379 "source = '" . formTrim($iter['source']) . "', " .
380 "title = '" . formDataCore($iter['title']) . "', " .
381 "group_id = '" . formTrim($iter['group']) . "', " .
382 "seq = '" . formTrim($iter['seq']) . "', " .
383 "uor = '" . formTrim($iter['uor']) . "', " .
384 "fld_length = '" . formTrim($iter['lengthWidth']) . "', " .
385 "fld_rows = '" . formTrim($iter['lengthHeight']) . "', " .
386 "max_length = '" . formTrim($iter['maxSize']) . "', " .
387 "titlecols = '" . formTrim($iter['titlecols']) . "', " .
388 "datacols = '" . formTrim($iter['datacols']) . "', " .
389 "data_type= '$data_type', " .
390 "list_id= '" . $listval . "', " .
391 "list_backup_id= '" . formTrim($iter['list_backup_id']) . "', " .
392 "edit_options = '" . encodeModifier($iter['edit_options']) . "', " .
393 "default_value = '" . formTrim($iter['default']) . "', " .
394 "description = '" . formTrim($iter['desc']) . "', " .
395 "conditions = '" . add_escape_custom($conditions) . "', " .
396 "validation = '" . formTrim($iter['validation']) . "' " .
397 "WHERE form_id = '$layout_id' AND field_id = '$field_id_original'");
400 } else if ($_POST['formaction'] == "addfield" && $layout_id) {
401 // Add a new field to a specific group
402 $data_type = formTrim($_POST['newdatatype']);
403 $max_length = $data_type == 3 ? 3 : 255;
404 $listval = $data_type == 34 ? formTrim($_POST['contextName']) : formTrim($_POST['newlistid']);
405 sqlStatement("INSERT INTO layout_options (" .
406 " form_id, source, field_id, title, group_id, seq, uor, fld_length, fld_rows" .
407 ", titlecols, datacols, data_type, edit_options, default_value, description" .
408 ", max_length, list_id, list_backup_id " .
409 ") VALUES ( " .
410 "'" . formTrim($_POST['layout_id']) . "'" .
411 ",'" . formTrim($_POST['newsource']) . "'" .
412 ",'" . formTrim($_POST['newid']) . "'" .
413 ",'" . formDataCore($_POST['newtitle']) . "'" .
414 ",'" . formTrim($_POST['newfieldgroupid']) . "'" .
415 ",'" . formTrim($_POST['newseq']) . "'" .
416 ",'" . formTrim($_POST['newuor']) . "'" .
417 ",'" . formTrim($_POST['newlengthWidth']) . "'" .
418 ",'" . formTrim($_POST['newlengthHeight']) . "'" .
419 ",'" . formTrim($_POST['newtitlecols']) . "'" .
420 ",'" . formTrim($_POST['newdatacols']) . "'" .
421 ",'$data_type'" .
422 ",'" . encodeModifier($_POST['newedit_options']) . "'" .
423 ",'" . formTrim($_POST['newdefault']) . "'" .
424 ",'" . formTrim($_POST['newdesc']) . "'" .
425 ",'" . formTrim($_POST['newmaxSize']) . "'" .
426 ",'" . $listval . "'" .
427 ",'" . formTrim($_POST['newbackuplistid']) . "'" .
428 " )");
429 addOrDeleteColumn($layout_id, formTrim($_POST['newid']), true);
430 } else if ($_POST['formaction'] == "movefields" && $layout_id) {
431 // Move field(s) to a new group in the layout
432 $sqlstmt = "UPDATE layout_options SET ".
433 " group_id = '" . $_POST['targetgroup'] . "' " .
434 " WHERE ".
435 " form_id = '" . $_POST['layout_id'] . "' ".
436 " AND field_id IN (";
437 $comma = "";
438 foreach (explode(" ", $_POST['selectedfields']) as $onefield) {
439 $sqlstmt .= $comma."'".$onefield."'";
440 $comma = ", ";
443 $sqlstmt .= ")";
444 //echo $sqlstmt;
445 sqlStatement($sqlstmt);
446 } else if ($_POST['formaction'] == "deletefields" && $layout_id) {
447 // Delete a field from a specific group
448 $sqlstmt = "DELETE FROM layout_options WHERE ".
449 " form_id = '".$_POST['layout_id']."' ".
450 " AND field_id IN (";
451 $comma = "";
452 foreach (explode(" ", $_POST['selectedfields']) as $onefield) {
453 $sqlstmt .= $comma."'".$onefield."'";
454 $comma = ", ";
457 $sqlstmt .= ")";
458 sqlStatement($sqlstmt);
459 foreach (explode(" ", $_POST['selectedfields']) as $onefield) {
460 addOrDeleteColumn($layout_id, $onefield, false);
462 } else if ($_POST['formaction'] == "addgroup" && $layout_id) {
463 // Generate new value for layout_items.group_id.
464 $newgroupid = genGroupId($_POST['newgroupparent']);
466 sqlStatement(
467 "INSERT INTO layout_group_properties SET " .
468 "grp_form_id = ?, " .
469 "grp_group_id = ?, " .
470 "grp_title = ?",
471 array($layout_id, $newgroupid, $_POST['newgroupname'])
474 $data_type = formTrim($_POST['gnewdatatype']);
475 $max_length = $data_type == 3 ? 3 : 255;
476 $listval = $data_type == 34 ? formTrim($_POST['gcontextName']) : formTrim($_POST['gnewlistid']);
477 // add a new group to the layout, with the defined field
478 sqlStatement("INSERT INTO layout_options (" .
479 " form_id, source, field_id, title, group_id, seq, uor, fld_length, fld_rows" .
480 ", titlecols, datacols, data_type, edit_options, default_value, description" .
481 ", max_length, list_id, list_backup_id " .
482 ") VALUES ( " .
483 "'" . formTrim($_POST['layout_id']) . "'" .
484 ",'" . formTrim($_POST['gnewsource']) . "'" .
485 ",'" . formTrim($_POST['gnewid']) . "'" .
486 ",'" . formDataCore($_POST['gnewtitle']) . "'" .
487 ",'" . formTrim($newgroupid) . "'" .
488 ",'" . formTrim($_POST['gnewseq']) . "'" .
489 ",'" . formTrim($_POST['gnewuor']) . "'" .
490 ",'" . formTrim($_POST['gnewlengthWidth']) . "'" .
491 ",'" . formTrim($_POST['gnewlengthHeight']) . "'" .
492 ",'" . formTrim($_POST['gnewtitlecols']) . "'" .
493 ",'" . formTrim($_POST['gnewdatacols']) . "'" .
494 ",'$data_type'" .
495 ",'" . encodeModifier($_POST['gnewedit_options']) . "'" .
496 ",'" . formTrim($_POST['gnewdefault']) . "'" .
497 ",'" . formTrim($_POST['gnewdesc']) . "'" .
498 ",'" . formTrim($_POST['gnewmaxSize']) . "'" .
499 ",'" . $listval . "'" .
500 ",'" . formTrim($_POST['gnewbackuplistid']) . "'" .
501 " )");
502 addOrDeleteColumn($layout_id, formTrim($_POST['gnewid']), true);
503 } /**********************************************************************
504 else if ($_POST['formaction'] == "deletegroup" && $layout_id) {
505 // drop the fields from the related table (this is critical)
506 $res = sqlStatement("SELECT field_id FROM layout_options WHERE " .
507 "form_id = '" . $_POST['layout_id'] . "' ".
508 "AND group_name = '" . $_POST['deletegroupname'] . "'");
509 while ($row = sqlFetchArray($res)) {
510 addOrDeleteColumn($layout_id, $row['field_id'], false);
513 // Delete an entire group from the form
514 sqlStatement("DELETE FROM layout_options WHERE ".
515 " form_id = '".$_POST['layout_id']."' ".
516 " AND group_name = '".$_POST['deletegroupname']."'"
519 **********************************************************************/
521 else if ($_POST['formaction'] == "movegroup" && $layout_id) {
522 // Note that in some cases below the swapGroups() call will do nothing.
523 $res = sqlStatement(
524 "SELECT DISTINCT group_id " .
525 "FROM layout_options WHERE form_id = ? ORDER BY group_id",
526 array($layout_id)
528 $row = sqlFetchArray($res);
529 $id1 = $row['group_id'];
530 while ($row = sqlFetchArray($res)) {
531 $id2 = $row['group_id'];
532 if ($_POST['movedirection'] == 'up') { // moving up
533 if ($id2 == $_POST['movegroupname']) {
534 swapGroups($id2, $id1);
535 break;
537 } else { // moving down
538 if ($id1 == $_POST['movegroupname']) {
539 swapGroups($id1, $id2);
540 break;
543 $id1 = $id2;
545 } // Renaming a group. This might include moving to a different parent group.
546 else if ($_POST['formaction'] == "renamegroup" && $layout_id) {
547 $newparent = $_POST['renamegroupparent']; // this is an ID
548 $oldid = $_POST['renameoldgroupname']; // this is an ID
549 $oldparent = substr($oldid, 0, -1);
550 $newid = $oldid;
551 if ($newparent != $oldparent) {
552 // Different parent, generate a new child prefix character.
553 $newid = genGroupId($newparent);
554 sqlStatement(
555 "UPDATE layout_options SET group_id = ? " .
556 "WHERE form_id = ? AND group_id = ?",
557 array($newid, $layout_id, $oldid)
560 $query = "UPDATE layout_group_properties SET " .
561 "grp_group_id = ?, grp_title = ? " .
562 "WHERE grp_form_id = ? AND grp_group_id = ?";
563 sqlStatement($query, array($newid, $_POST['renamegroupname'], $layout_id, $oldid));
566 // Get the selected form's elements.
567 if ($layout_id) {
568 $res = sqlStatement("SELECT * FROM layout_options WHERE " .
569 "form_id = '$layout_id' ORDER BY group_id, seq");
572 // global counter for field numbers
573 $fld_line_no = 0;
575 $extra_html = '';
577 // This is called to generate a select option list for fields within this form.
578 // Used for selecting a field for testing in a skip condition.
580 function genFieldOptionList($current = '')
582 global $layout_id;
583 $option_list = "<option value=''>-- " . xlt('Please Select') . " --</option>";
584 if ($layout_id) {
585 $query = "SELECT field_id FROM layout_options WHERE form_id = ? ORDER BY group_id, seq";
586 $res = sqlStatement($query, array($layout_id));
587 while ($row = sqlFetchArray($res)) {
588 $field_id = $row['field_id'];
589 $option_list .= "<option value='" . attr($field_id) . "'";
590 if ($field_id == $current) {
591 $option_list .= " selected";
593 $option_list .= ">" . text($field_id) . "</option>";
596 return $option_list;
599 // Write one option line to the form.
601 function writeFieldLine($linedata)
603 global $fld_line_no, $sources, $lbfonly, $extra_html,$validations;
604 ++$fld_line_no;
605 $checked = $linedata['default_value'] ? " checked" : "";
607 //echo " <tr bgcolor='$bgcolor'>\n";
608 echo " <tr id='fld[$fld_line_no]' class='".($fld_line_no % 2 ? 'even' : 'odd')."'>\n";
610 echo " <td class='optcell' nowrap>";
611 // tuck the group_name INPUT in here
612 echo "<input type='hidden' name='fld[$fld_line_no][group]' value='" .
613 htmlspecialchars($linedata['group_id'], ENT_QUOTES) . "' class='optin' />";
614 // Original field ID.
615 echo "<input type='hidden' name='fld[$fld_line_no][originalid]' value='" .
616 attr($linedata['field_id']) . "' />";
618 echo "<input type='checkbox' class='selectfield' ".
619 "name='" . $linedata['group_id'] . "~" . $linedata['field_id'] . "' " .
620 "id='" . $linedata['group_id'] . "~" . $linedata['field_id'] . "' " .
621 "title='" . xla('Select field') . "' />";
623 echo "<input type='text' name='fld[$fld_line_no][seq]' id='fld[$fld_line_no][seq]' value='" .
624 htmlspecialchars($linedata['seq'], ENT_QUOTES) . "' size='2' maxlength='4' " .
625 "class='optin' style='width:32pt' />";
626 echo "</td>\n";
628 echo " <td align='center' class='optcell' $lbfonly>";
629 echo "<select class='form-control' name='fld[$fld_line_no][source]' class='optin' $lbfonly>";
630 foreach ($sources as $key => $value) {
631 echo "<option value='" . attr($key) . "'";
632 if ($key == $linedata['source']) {
633 echo " selected";
636 echo ">" . text($value) . "</option>\n";
639 echo "</select>";
640 echo "</td>\n";
642 echo " <td align='left' class='optcell'>";
643 echo "<input type='text' name='fld[$fld_line_no][id]' value='" .
644 htmlspecialchars($linedata['field_id'], ENT_QUOTES) . "' size='15' maxlength='63' " .
645 "class='optin' style='width:100%' onclick='FieldIDClicked(this)' />";
646 echo "</td>\n";
648 echo " <td align='center' class='optcell'>";
649 echo "<input type='text' id='fld[$fld_line_no][title]' name='fld[$fld_line_no][title]' value='" .
650 htmlspecialchars($linedata['title'], ENT_QUOTES) . "' size='15' maxlength='63' class='optin' style='width:100%' />";
651 echo "</td>\n";
653 // if not english and set to translate layout labels, then show the translation
654 if ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) {
655 echo "<td align='center' class='translation' >" . htmlspecialchars(xl($linedata['title']), ENT_QUOTES) . "</td>\n";
658 echo " <td align='center' class='optcell'>";
659 echo "<select class='form-control' name='fld[$fld_line_no][uor]' class='optin'>";
660 foreach (array(0 =>xl('Unused'), 1 =>xl('Optional'), 2 =>xl('Required')) as $key => $value) {
661 echo "<option value='$key'";
662 if ($key == $linedata['uor']) {
663 echo " selected";
666 echo ">$value</option>\n";
669 echo "</select>";
670 echo "</td>\n";
672 echo " <td align='center' class='optcell'>";
673 echo "<select class='form-control' name='fld[$fld_line_no][datatype]' id='fld[$fld_line_no][datatype]' onchange=NationNotesContext('".$fld_line_no."',this.value)>";
674 echo "<option value=''></option>";
675 global $datatypes;
676 foreach ($datatypes as $key => $value) {
677 if ($linedata['data_type'] == $key) {
678 echo "<option value='$key' selected>$value</option>";
679 } else {
680 echo "<option value='$key'>$value</option>";
684 echo "</select>";
685 echo " </td>";
687 echo " <td align='center' class='optcell'>";
688 if ($linedata['data_type'] == 2 || $linedata['data_type'] == 3 ||
689 $linedata['data_type'] == 21 || $linedata['data_type'] == 22 ||
690 $linedata['data_type'] == 23 || $linedata['data_type'] == 25 ||
691 $linedata['data_type'] == 27 || $linedata['data_type'] == 28 ||
692 $linedata['data_type'] == 32 || $linedata['data_type'] == 15 ||
693 $linedata['data_type'] == 40
695 // Show the width field
696 echo "<input type='text' name='fld[$fld_line_no][lengthWidth]' value='" .
697 htmlspecialchars($linedata['fld_length'], ENT_QUOTES) .
698 "' size='2' maxlength='10' class='optin' title='" . xla('Width') . "' />";
699 if ($linedata['data_type'] == 3 || $linedata['data_type'] == 40) {
700 // Show the height field
701 echo "<input type='text' name='fld[$fld_line_no][lengthHeight]' value='" .
702 htmlspecialchars($linedata['fld_rows'], ENT_QUOTES) .
703 "' size='2' maxlength='10' class='optin' title='" . xla('Height') . "' />";
704 } else {
705 // Hide the height field
706 echo "<input type='hidden' name='fld[$fld_line_no][lengthHeight]' value=''>";
708 } else {
709 // all other data_types (hide both the width and height fields
710 echo "<input type='hidden' name='fld[$fld_line_no][lengthWidth]' value=''>";
711 echo "<input type='hidden' name='fld[$fld_line_no][lengthHeight]' value=''>";
714 echo "</td>\n";
716 echo " <td align='center' class='optcell'>";
717 echo "<input type='text' name='fld[$fld_line_no][maxSize]' value='" .
718 htmlspecialchars($linedata['max_length'], ENT_QUOTES) .
719 "' size='1' maxlength='10' class='optin' style='width:100%' " .
720 "title='" . xla('Maximum Size (entering 0 will allow any size)') . "' />";
721 echo "</td>\n";
723 echo " <td align='center' class='optcell'>";
724 if ($linedata['data_type'] == 1 || $linedata['data_type'] == 21 ||
725 $linedata['data_type'] == 22 || $linedata['data_type'] == 23 ||
726 $linedata['data_type'] == 25 || $linedata['data_type'] == 26 ||
727 $linedata['data_type'] == 27 || $linedata['data_type'] == 32 ||
728 $linedata['data_type'] == 33 || $linedata['data_type'] == 34 ||
729 $linedata['data_type'] == 36) {
730 $type = "";
731 $disp = "style='display:none'";
732 if ($linedata['data_type'] == 34) {
733 $type = "style='display:none'";
734 $disp = "";
737 echo "<input type='text' name='fld[$fld_line_no][list_id]' id='fld[$fld_line_no][list_id]' value='" .
738 htmlspecialchars($linedata['list_id'], ENT_QUOTES) . "'".$type.
739 " size='6' maxlength='100' class='optin listid' style='width:100%;cursor:pointer'".
740 "title='". xl('Choose list') . "' />";
742 echo "<select class='form-control' name='fld[$fld_line_no][contextName]' id='fld[$fld_line_no][contextName]' ".$disp.">";
743 $res = sqlStatement("SELECT * FROM customlists WHERE cl_list_type=2 AND cl_deleted=0");
744 while ($row = sqlFetchArray($res)) {
745 $sel = '';
746 if ($linedata['list_id'] == $row['cl_list_item_long']) {
747 $sel = 'selected';
750 echo "<option value='".htmlspecialchars($row['cl_list_item_long'], ENT_QUOTES)."' ".$sel.">".htmlspecialchars($row['cl_list_item_long'], ENT_QUOTES)."</option>";
753 echo "</select>";
754 } else {
755 // all other data_types
756 echo "<input type='hidden' name='fld[$fld_line_no][list_id]' value=''>";
759 echo "</td>\n";
761 //Backup List Begin
762 echo " <td align='center' class='optcell'>";
763 if ($linedata['data_type'] == 1 || $linedata['data_type'] == 26 ||
764 $linedata['data_type'] == 33 || $linedata['data_type'] == 36) {
765 echo "<input type='text' name='fld[$fld_line_no][list_backup_id]' value='" .
766 htmlspecialchars($linedata['list_backup_id'], ENT_QUOTES) .
767 "' size='3' maxlength='100' class='optin listid' style='cursor:pointer; width:100%' />";
768 } else {
769 echo "<input type='hidden' name='fld[$fld_line_no][list_backup_id]' value=''>";
772 echo "</td>\n";
773 //Backup List End
775 echo " <td align='center' class='optcell'>";
776 echo "<input type='text' name='fld[$fld_line_no][titlecols]' value='" .
777 htmlspecialchars($linedata['titlecols'], ENT_QUOTES) . "' size='3' maxlength='10' class='optin' style='width:100%' />";
778 echo "</td>\n";
780 echo " <td align='center' class='optcell'>";
781 echo "<input type='text' name='fld[$fld_line_no][datacols]' value='" .
782 htmlspecialchars($linedata['datacols'], ENT_QUOTES) . "' size='3' maxlength='10' class='optin' style='width:100%' />";
783 echo "</td>\n";
784 /* Below for compatabilty with existing string modifiers. */
785 if (strpos($linedata['edit_options'], ',') === false && isset($linedata['edit_options'])) {
786 $t = json_decode($linedata['edit_options']);
787 if (json_last_error() !== JSON_ERROR_NONE || $t === 0) { // hopefully string of characters and 0 handled.
788 $t = str_split(trim($linedata['edit_options']));
789 $linedata['edit_options'] = json_encode($t); // convert to array select understands.
792 echo " <td align='center' class='optcell' title='" . xla("Add modifiers for this field type. You may select more than one.") . "'>";
793 echo "<select id='fld[$fld_line_no][edit_options]' name='fld[$fld_line_no][edit_options][]' class='typeAddons optin' size=3 multiple data-set='" .
794 trim($linedata['edit_options']) . "' ></select></td>\n";
796 if ($linedata['data_type'] == 31) {
797 echo " <td align='center' class='optcell'>";
798 echo "<textarea name='fld[$fld_line_no][desc]' rows='3' cols='35' class='optin' style='width:100%'>" .
799 $linedata['description'] . "</textarea>";
800 echo "<input type='hidden' name='fld[$fld_line_no][default]' value='" .
801 htmlspecialchars($linedata['default_value'], ENT_QUOTES) . "' />";
802 echo "</td>\n";
803 } else {
804 echo " <td align='center' class='optcell' >";
805 echo "<input type='text' name='fld[$fld_line_no][desc]' value='" .
806 htmlspecialchars($linedata['description'], ENT_QUOTES) .
807 "' size='30' class='optin' style='width:100%' />";
808 echo "<input type='hidden' name='fld[$fld_line_no][default]' value='" .
809 htmlspecialchars($linedata['default_value'], ENT_QUOTES) . "' />";
810 echo "</td>\n";
811 // if not english and showing layout labels, then show the translation of Description
812 if ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) {
813 echo "<td align='center' class='translation'>" .
814 htmlspecialchars(xl($linedata['description']), ENT_QUOTES) . "</td>\n";
818 // The "?" to click on for yet more field attributes.
819 echo " <td class='bold' id='querytd_$fld_line_no' style='cursor:pointer;";
820 if (!empty($linedata['conditions']) || !empty($linedata['validation'])) {
821 echo "background-color:#77ff77;";
824 echo "' onclick='extShow($fld_line_no, this)' align='center' ";
825 echo "title='" . xla('Click here to view/edit more details') . "'>";
826 echo "&nbsp;?&nbsp;";
827 echo "</td>\n";
829 echo " </tr>\n";
831 // Create a floating div for the additional attributes of this field.
832 $conditions = empty($linedata['conditions']) ?
833 array(0 => array('id' => '', 'itemid' => '', 'operator' => '', 'value' => '')) :
834 unserialize($linedata['conditions']);
835 $action = empty($conditions['action']) ? 'skip' : $conditions['action'];
836 $action_value = $action == 'skip' ? '' : substr($action, 6);
838 $extra_html .= "<div id='ext_$fld_line_no' " .
839 "style='position:absolute;width:750px;border:1px solid black;" .
840 "padding:2px;background-color:#cccccc;visibility:hidden;" .
841 "z-index:1000;left:-1000px;top:0px;font-size:8pt;'>\n" .
842 "<table width='100%'>\n" .
843 " <tr>\n" .
844 " <th colspan='3' align='left' class='bold'>" .
845 xlt('For') . " " . text($linedata['field_id']) . " " .
846 "<select name='fld[$fld_line_no][action]' onchange='actionChanged($fld_line_no)'>" .
847 "<option value='skip' " . ($action == 'skip' ? 'selected' : '') . ">" . xlt('hide this field') . "</option>" .
848 "<option value='value' " . ($action != 'skip' ? 'selected' : '') . ">" . xlt('set value to') . "</option>" .
849 "</select>" .
850 "<input type='text' name='fld[$fld_line_no][value]' value='" . attr($action_value) . "' size='15' />" .
851 " " . xlt('if') .
852 "</th>\n" .
853 " <th colspan='2' align='right' class='text'><input type='button' " .
854 "value='" . xla('Close') . "' onclick='extShow($fld_line_no, false)' />&nbsp;</th>\n" .
855 " </tr>\n" .
856 " <tr>\n" .
857 " <th align='left' class='bold'>" . xlt('Field ID') . "</th>\n" .
858 " <th align='left' class='bold'>" . xlt('List item ID') . "</th>\n" .
859 " <th align='left' class='bold'>" . xlt('Operator') . "</th>\n" .
860 " <th align='left' class='bold'>" . xlt('Value if comparing') . "</th>\n" .
861 " <th align='left' class='bold'>&nbsp;</th>\n" .
862 " </tr>\n";
863 // There may be multiple condition lines for each field.
864 foreach ($conditions as $i => $condition) {
865 if (!is_numeric($i)) {
866 continue; // skip if 'action'
868 $extra_html .=
869 " <tr>\n" .
870 " <td align='left'>\n" .
871 " <select name='fld[$fld_line_no][condition_id][$i]' onchange='cidChanged($fld_line_no, $i)'>" .
872 genFieldOptionList($condition['id']) . " </select>\n" .
873 " </td>\n" .
874 " <td align='left'>\n" .
875 // List item choices are populated on the client side but will need the current value,
876 // so we insert a temporary option here to hold that value.
877 " <select name='fld[$fld_line_no][condition_itemid][$i]'><option value='" .
878 attr($condition['itemid']) . "'>...</option></select>\n" .
879 " </td>\n" .
880 " <td align='left'>\n" .
881 " <select name='fld[$fld_line_no][condition_operator][$i]'>\n";
882 foreach (array(
883 'eq' => xl('Equals'),
884 'ne' => xl('Does not equal'),
885 'se' => xl('Is selected'),
886 'ns' => xl('Is not selected'),
887 ) as $key => $value) {
888 $extra_html .= " <option value='$key'";
889 if ($key == $condition['operator']) {
890 $extra_html .= " selected";
893 $extra_html .= ">" . text($value) . "</option>\n";
896 $extra_html .=
897 " </select>\n" .
898 " </td>\n" .
899 " <td align='left' title='" . xla('Only for comparisons') . "'>\n" .
900 " <input type='text' name='fld[$fld_line_no][condition_value][$i]' value='" .
901 attr($condition['value']) . "' size='15' maxlength='63' />\n" .
902 " </td>\n";
903 if (!isset($conditions[$i + 1])) {
904 $extra_html .=
905 " <td align='right' title='" . xla('Add a condition') . "'>\n" .
906 " <input type='button' value='+' onclick='extAddCondition($fld_line_no,this)' />\n" .
907 " </td>\n";
908 } else {
909 $extra_html .=
910 " <td align='right'>\n" .
911 " <select name='fld[$fld_line_no][condition_andor][$i]'>\n";
912 foreach (array(
913 'and' => xl('And'),
914 'or' => xl('Or'),
915 ) as $key => $value) {
916 $extra_html .= " <option value='$key'";
917 if ($key == $condition['andor']) {
918 $extra_html .= " selected";
921 $extra_html .= ">" . text($value) . "</option>\n";
924 $extra_html .=
925 " </select>\n" .
926 " </td>\n";
929 $extra_html .=
930 " </tr>\n";
933 $extra_html .=
934 "</table>\n";
936 $extra_html .= "<table width='100%'>\n" .
937 " <tr>\n" .
938 " <td colspan='3' align='left' class='bold'>\"" . text($linedata['field_id']) . "\" " .
939 xlt('will have the following validation rules') . ":</td>\n" .
940 " </tr>\n" .
941 " <tr>\n" .
942 " <td align='left' class='bold'>" . xlt('Validation rule') . " </td>\n" .
943 " </tr>\n".
944 " <tr>\n" .
945 " <td align='left' title='" . xla('Select a validation rule') . "'>\n" .
948 " <select name='fld[$fld_line_no][validation]' onchange='valChanged($fld_line_no)'>\n" .
949 " <option value=''";
950 if (empty($linedata['validation'])) {
951 $extra_html .= " selected";
954 $extra_html .= ">-- " . xlt('Please Select') . " --</option>";
955 foreach ($validations as $key => $value) {
956 $extra_html .= " <option value='$key'";
957 if ($key == $linedata['validation']) {
958 $extra_html .= " selected";
961 $extra_html .= ">" . text($value) . "</option>\n";
964 $extra_html .="</select>\n" .
965 " </td>\n";
967 $extra_html .=
968 "</table>\n" .
969 "</div>\n";
972 <html>
974 <head>
976 <?php Header::setupHeader(['select2','emodal']); ?>
978 <title><?php xl('Layout Editor', 'e'); ?></title>
980 <style>
981 .orgTable tr.head { font-size:8pt; background-color:#cccccc; }
982 .orgTable tr.detail { font-size:8pt; }
983 .orgTable td { font-size:8pt; }
984 .orgTable input { font-size:8pt; }
985 .orgTable select { font-size:8pt; }
986 a, a:visited, a:hover { color:#0000cc; }
987 .optcell { }
988 .optin { background: transparent; }
989 .group {
990 margin: 0pt 0pt 8pt 0pt;
991 padding :0pt;
992 width: 100%;
995 .group table {
996 border-collapse: collapse;
997 width: 100%;
1000 .orgTable .odd td {
1001 background-color: #ddddff;
1002 padding: 3px 0px 3px 0px;
1004 .orgTable .even td {
1005 background-color: #ffdddd;
1006 padding: 3px 0px 3px 0px;
1008 .help { cursor: help; }
1009 .layouts_title { font-size: 110%; }
1010 .translation {
1011 color: green;
1012 font-size:8pt;
1014 .highlight * {
1015 border: 2px solid blue;
1016 background-color: yellow;
1017 color: black;
1019 .select2-container--default .select2-selection--multiple {
1020 cursor: pointer;
1022 .select2-search__field {
1023 cursor: pointer;
1024 width: 0 !important;
1026 .select2-selection__choice {
1027 font-size: 12px;
1029 .select2-container {
1030 cursor: pointer;
1031 opacity: 0.99 !important;
1033 .select2-dropdown {
1034 opacity: 0.99 !important;
1036 .tips {
1037 display: none;
1040 </style>
1042 <script>
1043 // Helper functions for positioning the floating divs.
1044 function extGetX(elem) {
1045 var x = 0;
1046 while(elem != null) {
1047 x += elem.offsetLeft;
1048 elem = elem.offsetParent;
1050 return x;
1052 function extGetY(elem) {
1053 var y = 0;
1054 while(elem != null) {
1055 y += elem.offsetTop;
1056 elem = elem.offsetParent;
1058 return y;
1061 // Show or hide the "extras" div for a row.
1062 var extdiv = null;
1063 function extShow(lino, show) {
1064 var thisdiv = document.getElementById("ext_" + lino);
1065 if (extdiv) {
1066 extdiv.style.visibility = 'hidden';
1067 extdiv.style.left = '-1000px';
1068 extdiv.style.top = '0px';
1070 if (show && thisdiv != extdiv) {
1071 extdiv = thisdiv;
1072 var dw = window.innerWidth ? window.innerWidth - 20 : document.body.clientWidth;
1073 x = dw - extdiv.offsetWidth;
1074 if (x < 0) x = 0;
1075 var y = extGetY(show) + show.offsetHeight;
1076 extdiv.style.left = x;
1077 extdiv.style.top = y;
1078 extdiv.style.visibility = 'visible';
1080 else {
1081 extdiv = null;
1085 // Show or hide the value field for a "Set value to" condition.
1086 function actionChanged(lino) {
1087 var f = document.forms[0];
1088 var eaction = f['fld[' + lino + '][action]'];
1089 var evalue = f['fld[' + lino + '][value]'];
1090 evalue.style.display = eaction.value == 'skip' ? 'none' : '';
1093 // Add an extra condition line for the given row.
1094 function extAddCondition(lino, btnelem) {
1095 var f = document.forms[0];
1096 var i = 0;
1098 // Get index of next condition line.
1099 while (f['fld[' + lino + '][condition_id][' + i + ']']) ++i;
1100 if (i == 0) alert('f["fld[' + lino + '][condition_id][' + i + ']"] <?php echo xls('not found') ?>');
1102 // Get containing <td>, <tr> and <table> nodes of the "+" button.
1103 var tdplus = btnelem.parentNode;
1104 var trelem = tdplus.parentNode;
1105 var telem = trelem.parentNode;
1107 // Replace contents of the tdplus cell.
1108 tdplus.innerHTML =
1109 "<select name='fld[" + lino + "][condition_andor][" + i + "]'>" +
1110 "<option value='and'><?php echo xls('And') ?></option>" +
1111 "<option value='or' ><?php echo xls('Or') ?></option>" +
1112 "</select>";
1114 // Add the new row.
1115 var newtrelem = telem.insertRow(i+2);
1116 newtrelem.innerHTML =
1117 "<td align='left'>" +
1118 "<select name='fld[" + lino + "][condition_id][" + i + "]' onchange='cidChanged(" + lino + "," + i + ")'>" +
1119 "<?php echo addslashes(genFieldOptionList()) ?>" +
1120 "</select>" +
1121 "</td>" +
1122 "<td align='left'>" +
1123 "<select name='fld[" + lino + "][condition_itemid][" + i + "]' style='display:none' />" +
1124 "</td>" +
1125 "<td align='left'>" +
1126 "<select name='fld[" + lino + "][condition_operator][" + i + "]'>" +
1127 "<option value='eq'><?php echo xls('Equals') ?></option>" +
1128 "<option value='ne'><?php echo xls('Does not equal') ?></option>" +
1129 "<option value='se'><?php echo xls('Is selected') ?></option>" +
1130 "<option value='ns'><?php echo xls('Is not selected') ?></option>" +
1131 "</select>" +
1132 "</td>" +
1133 "<td align='left'>" +
1134 "<input type='text' name='fld[" + lino + "][condition_value][" + i + "]' value='' size='15' maxlength='63' />" +
1135 "</td>" +
1136 "<td align='right'>" +
1137 "<input type='button' value='+' onclick='extAddCondition(" + lino + ",this)' />" +
1138 "</td>";
1141 // This is called when a field ID is chosen for testing within a skip condition.
1142 // It checks to see if a corresponding list item must also be chosen for the test, and
1143 // if so then inserts the dropdown for selecting an item from the appropriate list.
1144 function setListItemOptions(lino, seq, init) {
1145 var f = document.forms[0];
1146 var target = 'fld[' + lino + '][condition_itemid][' + seq + ']';
1147 // field_id is the ID of the field that the condition will test.
1148 var field_id = f['fld[' + lino + '][condition_id][' + seq + ']'].value;
1149 if (!field_id) {
1150 f[target].options.length = 0;
1151 f[target].style.display = 'none';
1152 return;
1154 // Find the occurrence of that field in the layout.
1155 var i = 1;
1156 while (true) {
1157 var idname = 'fld[' + i + '][id]';
1158 if (!f[idname]) {
1159 alert('<?php echo xls('Condition field not found') ?>: ' + field_id);
1160 return;
1162 if (f[idname].value == field_id) break;
1163 ++i;
1165 // If this is startup initialization then preserve the current value.
1166 var current = init ? f[target].value : '';
1167 f[target].options.length = 0;
1168 // Get the corresponding data type and list ID.
1169 var data_type = f['fld[' + i + '][datatype]'].value;
1170 var list_id = f['fld[' + i + '][list_id]'].value;
1171 // WARNING: If new data types are defined the following test may need enhancing.
1172 // We're getting out if the type does not generate multiple fields with different names.
1173 if (data_type != '21' && data_type != '22' && data_type != '23' && data_type != '25') {
1174 f[target].style.display = 'none';
1175 return;
1177 // OK, list item IDs do apply so go get 'em.
1178 // This happens asynchronously so the generated code needs to stand alone.
1179 f[target].style.display = '';
1180 $.getScript('layout_listitems_ajax.php' +
1181 '?listid=' + encodeURIComponent(list_id) +
1182 '&target=' + encodeURIComponent(target) +
1183 '&current=' + encodeURIComponent(current));
1186 // This is called whenever a condition's field ID selection is changed.
1187 function cidChanged(lino, seq) {
1188 changeColor(lino);
1189 setListItemOptions(lino, seq, false);
1192 // This invokes the popup to edit layout properties or add a new layout.
1193 function edit_layout_props(groupid) {
1194 dlgopen('edit_layout_props.php?layout_id=<?php echo attr($layout_id); ?>&group_id=' + groupid,
1195 '_blank', 700, 550);
1198 // callback from edit_layout_props.php:
1199 function refreshme(layout_id) {
1200 location.href = 'edit_layout.php?layout_id=' + layout_id;
1203 // This is called whenever a validation rule field ID selection is changed.
1204 function valChanged(lino) {
1205 changeColor(lino);
1208 function changeColor(lino){
1209 var thisid = document.forms[0]['fld[' + lino + '][condition_id][0]'].value;
1210 var thisValId = document.forms[0]['fld[' + lino + '][validation]'].value;
1211 var thistd = document.getElementById("querytd_" + lino);
1212 if(thisid !='' || thisValId!='') {
1213 thistd.style.backgroundColor = '#77ff77';
1214 }else{
1215 thistd.style.backgroundColor ='';
1219 // Call this to disable the warning about unsaved changes and submit the form.
1220 function mySubmit() {
1221 somethingChanged = false;
1222 top.restoreSession();
1223 document.forms[0].submit();
1226 // User is about to do something that would discard any unsaved changes.
1227 // Return true if that is OK.
1228 function myChangeCheck() {
1229 if (somethingChanged) {
1230 if (!confirm('<?php echo xls('You have unsaved changes. Abandon them?'); ?>')) {
1231 return false;
1233 // Do not set somethingChanged to false here because if they cancel the
1234 // action then the previously changed values will still be of interest.
1236 return true;
1239 </script>
1241 </head>
1243 <body class="body_top admin-layout">
1244 <div class="container-responsive">
1245 <form method='post' name='theform' id='theform' action='edit_layout.php'>
1246 <input type="hidden" name="formaction" id="formaction" value="">
1247 <!-- elements used to identify a field to delete -->
1248 <input type="hidden" name="deletefieldid" id="deletefieldid" value="">
1249 <input type="hidden" name="deletefieldgroup" id="deletefieldgroup" value="">
1250 <!-- elements used to identify a group to delete -->
1251 <!--
1252 <input type="hidden" name="deletegroupname" id="deletegroupname" value="">
1254 <!-- elements used to change the group order -->
1255 <input type="hidden" name="movegroupname" id="movegroupname" value="">
1256 <input type="hidden" name="movedirection" id="movedirection" value="">
1257 <!-- elements used to select more than one field -->
1258 <input type="hidden" name="selectedfields" id="selectedfields" value="">
1259 <input type="hidden" id="targetgroup" name="targetgroup" value="">
1261 <div class="menubar" style='padding:5px 0;'>
1263 <b><?php xl('Edit layout', 'e'); ?>:</b>&nbsp;
1264 <select name='layout_id' id='layout_id' class='form-control' style='display:inline-block;margin-bottom:5px;width:20%;'>
1265 <option value=''>-- <?php echo xl('Select') ?> --</option>
1266 <?php
1267 $lastgroup = '';
1268 foreach ($layouts as $key => $value) {
1269 if ($value[0] != $lastgroup) {
1270 if ($lastgroup) {
1271 echo " </optgroup>\n";
1273 echo " <optgroup label='" . attr($value[0]) . "'>\n";
1274 $lastgroup = $value[0];
1276 echo " <option value='" . attr($key) . "'";
1277 if ($key == $layout_id) {
1278 echo " selected";
1280 echo ">" . text($value[1]) . "</option>\n";
1282 if ($lastgroup) {
1283 echo " </optgroup>\n";
1286 </select>
1288 <?php if ($layout_id) { ?>
1289 <input type='button' value='<?php echo xla('Layout Properties'); ?>' onclick='edit_layout_props("")' />&nbsp;
1290 <input type='button' class='addgroup' id='addgroup' value='<?php echo xla('Add Group'); ?>' />
1291 <span style="font-size:90%"> &nbsp;
1292 <input type='button' class="btn btn-danger" name='save' id='save' value='<?php xl('Save Changes', 'e'); ?>' /></span> &nbsp;&nbsp;
1293 <?php xl('With selected:', 'e');?>
1294 <input type='button' name='deletefields' id='deletefields' value='<?php xl('Delete', 'e'); ?>' style="font-size:90%" disabled="disabled" />
1295 <input type='button' name='movefields' id='movefields' value='<?php xl('Move to...', 'e'); ?>' style="font-size:90%" disabled="disabled" />
1296 <input type='button' value='<?php echo xla('Tips'); ?>' onclick='$("#tips").toggle();' />&nbsp;
1297 <input type='button' value='<?php echo xla('Encounter Preview'); ?>' onclick='layoutLook();' />
1299 <?php } else { ?>
1300 <input type='button' value='<?php echo xla('New Layout'); ?>' onclick='edit_layout_props("")' />&nbsp;
1301 <?php } ?>
1303 <div id="tips" class="container tips"><section class="panel panel-primary">
1304 <header class="panel-heading">
1305 <h3 class="panel-title"><?php echo xlt('Usage Tips') ?></h3>
1306 </header>
1307 <div class="panel-body">
1308 <ul>
1309 <?php
1310 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>";
1311 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>";
1312 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>";
1313 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>";
1314 echo "<li>" . xlt("If a field's Data Col = 0 the data field will immediately follow its label field on the same line") . "</li>";
1315 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>";
1316 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>";
1317 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>";
1318 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>";
1319 //echo "<li>" . xlt("") . "</li>";
1320 echo "<li>" . xlt("Please see http://www.open-emr.org/wiki/index.php/LBV_Forms for more on this topic") . "</li>";
1322 </ul>
1323 <button class='btn btn-xs btn-success pull-right' onclick='$("#tips").toggle();return false;'><?php echo xlt('Dismiss')?></button>
1324 </div>
1325 </section></div></div>
1326 <?php
1327 // Load array of properties for this layout and its groups.
1328 $grparr = array();
1329 $gres = sqlStatement("SELECT * FROM layout_group_properties WHERE grp_form_id = ? " .
1330 "ORDER BY grp_group_id", array($layout_id));
1331 while ($grow = sqlFetchArray($gres)) {
1332 $grparr[$grow['grp_group_id']] = $grow;
1335 $prevgroup = "!@#asdf1234"; // an unlikely group ID
1336 $firstgroup = true; // flag indicates it's the first group to be displayed
1338 while ($row = sqlFetchArray($res)) {
1339 $group_id = $row['group_id'];
1340 if ($group_id != $prevgroup) {
1341 if ($firstgroup == false) {
1342 echo "</tbody></table></div>\n";
1343 echo "<div id='" . $group_id . "' class='group'>";
1344 } else { // making first group flag useful for maintaining top fixed nav bar.
1345 echo "<div id='" . $group_id . "' class='group' style='padding-top:40px'>";
1347 echo "<div class='text bold layouts_title' style='position:relative; background-color: #c9dbf2;'>";
1349 // Get the fully qualified descriptive name of this group (i.e. including ancestor names).
1350 $gdispname = '';
1351 for ($i = 1; $i <= strlen($group_id); ++$i) {
1352 if ($gdispname) {
1353 $gdispname .= ' / ';
1355 $gdispname .= $grparr[substr($group_id, 0, $i)]['grp_title'];
1357 $gmyname = $grparr[$group_id]['grp_title'];
1359 echo text($gdispname);
1361 // if not english and set to translate layout labels, then show the translation of group name
1362 if ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) {
1363 echo "<span class='translation'&gt;&gt;&gt;&nbsp; " . xlt($gdispname) . "</span>";
1364 echo "&nbsp; ";
1367 echo "&nbsp; ";
1368 echo " <input type='button' class='addfield' id='addto~$group_id' value='" . xla('Add Field') . "'/>";
1369 echo "&nbsp; &nbsp; ";
1370 echo " <input type='button' class='renamegroup' id='$group_id~$gmyname' value='" . xla('Rename Group') . "'/>";
1371 /******************************************************************
1372 echo "&nbsp; &nbsp; ";
1373 echo " <input type='button' class='deletegroup' id='$group_id' value='" . xl('Delete Group') . "'/>";
1374 ******************************************************************/
1375 echo "&nbsp; &nbsp; ";
1376 echo " <input type='button' class='movegroup' id='$group_id~up' value='" . xl('Move Up') . "'/>";
1377 echo "&nbsp; &nbsp; ";
1378 echo " <input type='button' class='movegroup' id='$group_id~down' value='" . xl('Move Down') . "'/>";
1379 echo "&nbsp; &nbsp; ";
1380 echo "<input type='button' value='" . xla('Group Properties') . "' onclick='edit_layout_props(\"$group_id\")' />";
1381 echo "</div>";
1382 $firstgroup = false;
1384 <table class='table table-condensed table-striped'>
1385 <thead>
1386 <tr class='head'>
1387 <th style='width:1%'><?php xl('Order', 'e'); ?></th>
1388 <th <?php echo " $lbfonly"; ?>style='width:5%'><?php xl('Source', 'e'); ?></th>
1389 <th style='width:5%'><?php xl('ID', 'e'); ?>&nbsp;<span class="help" title=<?php xl('A unique value to identify this field, not visible to the user', 'e', '\'', '\''); ?> >(?)</span></th>
1390 <th style='width:10%'><?php xl('Label', 'e'); ?>&nbsp;<span class="help" title=<?php xl('The label that appears to the user on the form', 'e', '\'', '\''); ?> >(?)</span></th>
1391 <?php // if not english and showing layout label translations, then show translation header for title
1392 if ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) {
1393 echo "<th>" . xl('Translation')."<span class='help' title='" . xl('The translated label that will appear on the form in current language') . "'>&nbsp;(?)</span></th>";
1394 } ?>
1395 <th style='width:6%'><?php xl('UOR', 'e'); ?></th>
1396 <th style='width:10%'><?php xl('Data Type', 'e'); ?></th>
1397 <th style='width:1%'><?php xl('Size', 'e'); ?></th>
1398 <th style='width:3%'><?php xl('Max Size', 'e'); ?></th>
1399 <th style='width:10%'><?php xl('List', 'e'); ?></th>
1400 <th style='width:10%'><?php xl('Backup List', 'e'); ?></th>
1401 <th style='width:1%'><?php xl('Label Cols', 'e'); ?></th>
1402 <th style='width:1%'><?php xl('Data Cols', 'e'); ?></th>
1403 <th style='width:10%'><?php xl('Options', 'e'); ?></th>
1404 <th style='width:20%'><?php xl('Description', 'e'); ?></th>
1405 <?php // if not english and showing layout label translations, then show translation header for description
1406 if ($GLOBALS['translate_layout'] && $_SESSION['language_choice'] > 1) {
1407 echo "<th>" . xl('Translation')."<span class='help' title='" . xl('The translation of description in current language')."'>&nbsp;(?)</span></th>";
1408 } ?>
1409 <th style='width:1%'><?php echo xlt('?'); ?></th>
1410 </tr>
1411 </thead>
1412 <tbody>
1414 <?php
1415 } // end if-group_name
1417 writeFieldLine($row);
1418 $prevgroup = $group_id;
1419 } // end while loop
1422 </tbody>
1423 </table>
1425 <?php echo $extra_html; ?>
1427 </form>
1429 <!-- template DIV that appears when user chooses to rename an existing group -->
1430 <div id="renamegroupdetail"
1431 style="border: 1px solid black; padding: 3px; display: none; visibility: hidden; background-color: lightgrey;">
1432 <input type="hidden" name="renameoldgroupname" id="renameoldgroupname" value="" />
1433 <?php echo xlt('Group Name'); ?>:
1434 <input type="text" size="20" maxlength="30" name="renamegroupname" id="renamegroupname" />
1435 &nbsp;&nbsp;
1436 <?php echo xlt('Parent'); ?>:
1437 <?php echo genGroupSelector('renamegroupparent', $layout_id); ?>
1438 <br>
1439 <input type="button" class="saverenamegroup .btn-save" value="<?php echo xla('Rename Group'); ?>" />
1440 <input type="button" class="cancelrenamegroup" value="<?php echo xla('Cancel'); ?>" />
1441 </div>
1443 <!-- template DIV that appears when user chooses to add a new group -->
1444 <div id="groupdetail"
1445 style="border: 1px solid black; padding: 3px; display: none; visibility: hidden; background-color: lightgrey;">
1446 <span class='bold'>
1447 <?php echo xlt('Group Name'); ?>:
1448 <input type="text" size="20" maxlength="30" name="newgroupname" id="newgroupname" />
1449 &nbsp;&nbsp;
1450 <?php echo xlt('Parent'); ?>:
1451 <?php echo genGroupSelector('newgroupparent', $layout_id); ?>
1452 <br>
1453 <table class='table table-condensed table-striped' style="border-collapse: collapse; margin-top: 5px;">
1454 <thead>
1455 <tr class='head'>
1456 <th style='width:1%'><?php xl('Order', 'e'); ?></th>
1457 <th <?php echo " $lbfonly"; ?>style='width:5%'><?php xl('Source', 'e'); ?></th>
1458 <th style='width:5%'><?php xl('ID', 'e'); ?>&nbsp;<span class="help" title=<?php xl('A unique value to identify this field, not visible to the user', 'e', '\'', '\''); ?> >(?)</span></th>
1459 <th style='width:10%'><?php xl('Label', 'e'); ?>&nbsp;<span class="help" title=<?php xl('The label that appears to the user on the form', 'e', '\'', '\''); ?> >(?)</span></th>
1460 <th style='width:6%'><?php xl('UOR', 'e'); ?></th>
1461 <th style='width:10%'><?php xl('Data Type', 'e'); ?></th>
1462 <th style='width:1%'><?php xl('Size', 'e'); ?></th>
1463 <th style='width:1%'><?php xl('Max Size', 'e'); ?></th>
1464 <th style='width:10%'><?php xl('List', 'e'); ?></th>
1465 <th style='width:10%'><?php xl('Backup List', 'e'); ?></th>
1466 <th style='width:1%'><?php xl('Label Cols', 'e'); ?></th>
1467 <th style='width:1%'><?php xl('Data Cols', 'e'); ?></th>
1468 <th style='width:10%'><?php xl('Options', 'e'); ?></th>
1469 <th style='width:20%'><?php xl('Description', 'e'); ?></th>
1470 </tr>
1471 </thead>
1472 <tbody>
1473 <tr class='center'>
1474 <td ><input type="text" name="gnewseq" id="gnewseq" value="" size="2" maxlength="4"> </td>
1475 <td<?php echo " $lbfonly"; ?>>
1476 <select class='form-control' name='gnewsource' id='gnewsource'>
1477 <?php
1478 foreach ($sources as $key => $value) {
1479 echo "<option value='" . attr($key) . "'>" . text($value) . "</option>\n";
1482 </select>
1483 </td>
1484 <td><input type="text" name="gnewid" id="gnewid" value="" size="10" maxlength="20"
1485 onclick='FieldIDClicked(this)'> </td>
1486 <td><input type="text" name="gnewtitle" id="gnewtitle" value="" size="20" maxlength="63"> </td>
1487 <td>
1488 <select class='form-control' name="gnewuor" id="gnewuor">
1489 <option value="0"><?php xl('Unused', 'e'); ?></option>
1490 <option value="1" selected><?php xl('Optional', 'e'); ?></option>
1491 <option value="2"><?php xl('Required', 'e'); ?></option>
1492 </select>
1493 </td>
1494 <td align='center'>
1495 <select class='form-control' name='gnewdatatype' id='gnewdatatype'>
1496 <option value=''></option>
1497 <?php
1498 global $datatypes;
1499 foreach ($datatypes as $key => $value) {
1500 echo "<option value='$key'>$value</option>";
1503 </select>
1504 </td>
1505 <td><input type="text" name="gnewlengthWidth" id="gnewlengthWidth" value="" size="1" maxlength="3" title="<?php echo xla('Width'); ?>">
1506 <input type="text" name="gnewlengthHeight" id="gnewlengthHeight" value="" size="1" maxlength="3" title="<?php echo xla('Height'); ?>"></td>
1507 <td><input type="text" name="gnewmaxSize" id="gnewmaxSize" value="" size="1" maxlength="3" title="<?php echo xla('Maximum Size (entering 0 will allow any size)'); ?>"></td>
1508 <td><input type="text" name="gnewlistid" id="gnewlistid" value="" size="8" maxlength="100" class="listid">
1509 <select class='form-control' name='gcontextName' id='gcontextName' style='display:none'>
1510 <?php
1511 $res = sqlStatement("SELECT * FROM customlists WHERE cl_list_type=2 AND cl_deleted=0");
1512 while ($row = sqlFetchArray($res)) {
1513 echo "<option value='".htmlspecialchars($row['cl_list_item_long'], ENT_QUOTES)."'>".htmlspecialchars($row['cl_list_item_long'], ENT_QUOTES)."</option>";
1516 </select>
1517 </td>
1518 <td><input type="text" name="gnewbackuplistid" id="gnewbackuplistid" value="" size="8" maxlength="100" class="listid"></td>
1519 <td><input type="text" name="gnewtitlecols" id="gnewtitlecols" value="" size="3" maxlength="3"> </td>
1520 <td><input type="text" name="gnewdatacols" id="gnewdatacols" value="" size="3" maxlength="3"> </td>
1521 <td><select name="gnewedit_options[]" id="gnewedit_options" class="typeAddons" multiple style='width:100%' value="" size="3"></select>
1522 <input type="hidden" name="gnewdefault" id="gnewdefault" value="" /> </td>
1523 <td><input type="text" name="gnewdesc" id="gnewdesc" value="" size="30"> </td>
1524 </tr>
1525 </tbody>
1526 </table>
1527 <br>
1528 <input type="button" class="savenewgroup" value=<?php xl('Save New Group', 'e', '\'', '\''); ?>>
1529 <input type="button" class="cancelnewgroup" value=<?php xl('Cancel', 'e', '\'', '\''); ?>>
1530 </span>
1531 </div>
1533 <!-- template DIV that appears when user chooses to add a new field to a group -->
1534 <div id="fielddetail" class="fielddetail" style="display: none; visibility: hidden">
1535 <input type="hidden" name="newfieldgroupid" id="newfieldgroupid" value="">
1536 <table class="table table-condensed" style="border-collapse: collapse;">
1537 <thead>
1538 <tr class='head'>
1539 <th style='width:1%'><?php xl('Order', 'e'); ?></th>
1540 <th <?php echo " $lbfonly"; ?>style='width:5%'><?php xl('Source', 'e'); ?></th>
1541 <th style='width:5%'><?php xl('ID', 'e'); ?>&nbsp;<span class="help" title=<?php xl('A unique value to identify this field, not visible to the user', 'e', '\'', '\''); ?> >(?)</span></th>
1542 <th style='width:10%'><?php xl('Label', 'e'); ?>&nbsp;<span class="help" title=<?php xl('The label that appears to the user on the form', 'e', '\'', '\''); ?> >(?)</span></th>
1543 <th style='width:6%'><?php xl('UOR', 'e'); ?></th>
1544 <th style='width:10%'><?php xl('Data Type', 'e'); ?></th>
1545 <th style='width:1%'><?php xl('Size', 'e'); ?></th>
1546 <th style='width:1%'><?php xl('Max Size', 'e'); ?></th>
1547 <th style='width:10%'><?php xl('List', 'e'); ?></th>
1548 <th style='width:10%'><?php xl('Backup List', 'e'); ?></th>
1549 <th style='width:1%'><?php xl('Label Cols', 'e'); ?></th>
1550 <th style='width:1%'><?php xl('Data Cols', 'e'); ?></th>
1551 <th style='width:10%'><?php xl('Options', 'e'); ?></th>
1552 <th style='width:20%'><?php xl('Description', 'e'); ?></th>
1553 </tr>
1554 </thead>
1555 <tbody>
1556 <tr class='center'>
1557 <td ><input type="text" name="newseq" id="newseq" value="" size="2" maxlength="4"> </td>
1558 <td<?php echo " $lbfonly"; ?>>
1559 <select class='form-control' name='newsource' id='newsource'>
1560 <?php
1561 foreach ($sources as $key => $value) {
1562 echo " <option value='" . attr($key) . "'>" . text($value) . "</option>\n";
1565 </select>
1566 </td>
1567 <td ><input type="text" name="newid" id="newid" value="" size="10" maxlength="20"
1568 onclick='FieldIDClicked(this)'> </td>
1569 <td><input type="text" name="newtitle" id="newtitle" value="" size="20" maxlength="63"> </td>
1570 <td>
1571 <select class='form-control' name="newuor" id="newuor">
1572 <option value="0"><?php xl('Unused', 'e'); ?></option>
1573 <option value="1" selected><?php xl('Optional', 'e'); ?></option>
1574 <option value="2"><?php xl('Required', 'e'); ?></option>
1575 </select>
1576 </td>
1577 <td align='center'>
1578 <select class='form-control' name='newdatatype' id='newdatatype'>
1579 <option value=''></option>
1580 <?php
1581 global $datatypes;
1582 foreach ($datatypes as $key => $value) {
1583 echo " <option value='$key'>$value</option>\n";
1586 </select>
1587 </td>
1588 <td><input type="text" name="newlengthWidth" id="newlengthWidth" value="" size="1" maxlength="3" title="<?php echo xla('Width'); ?>">
1589 <input type="text" name="newlengthHeight" id="newlengthHeight" value="" size="1" maxlength="3" title="<?php echo xla('Height'); ?>"></td>
1590 <td><input type="text" name="newmaxSize" id="newmaxSize" value="" size="1" maxlength="3" title="<?php echo xla('Maximum Size (entering 0 will allow any size)'); ?>"></td>
1591 <td><input type="text" name="newlistid" id="newlistid" value="" size="8" maxlength="31" class="listid">
1592 <select class='form-control' name='contextName' id='contextName' style='display:none'>
1593 <?php
1594 $res = sqlStatement("SELECT * FROM customlists WHERE cl_list_type=2 AND cl_deleted=0");
1595 while ($row = sqlFetchArray($res)) {
1596 echo "<option value='".htmlspecialchars($row['cl_list_item_long'], ENT_QUOTES)."'>".htmlspecialchars($row['cl_list_item_long'], ENT_QUOTES)."</option>";
1599 </select>
1600 </td>
1601 <td><input type="text" name="newbackuplistid" id="newbackuplistid" value="" size="8" maxlength="31" class="listid"></td>
1602 <td><input type="text" name="newtitlecols" id="newtitlecols" value="" size="3" maxlength="3"> </td>
1603 <td><input type="text" name="newdatacols" id="newdatacols" value="" size="3" maxlength="3"> </td>
1604 <td><select name="newedit_options[]" id="newedit_options" multiple class='typeAddons' style='width:100%'></select>
1605 <input type="hidden" name="newdefault" id="newdefault" value="" /> </td>
1606 <td><input type="text" name="newdesc" id="newdesc" value="" size="30"> </td>
1607 </tr>
1608 <tr>
1609 <td colspan="9">
1610 <input type="button" class="savenewfield" value=<?php xl('Save New Field', 'e', '\'', '\''); ?>>
1611 <input type="button" class="cancelnewfield" value=<?php xl('Cancel', 'e', '\'', '\''); ?>>
1612 </td>
1613 </tr>
1614 </tbody>
1615 </table>
1616 </div>
1617 </div>
1618 </body>
1620 <script>
1621 /* Field modifier objects - heading towards context based.
1622 Used by Select2 so rtl may be enabled*/
1623 <?php echo "var fldOptions = [
1624 {id: 'A',text:'" . xla('Age') . "',ctx:['4'],ctxExcp:['0']},
1625 {id: 'B',text:'" . xla('Gestational Age') . "',ctx:['4'],ctxExcp:['0']},
1626 {id: 'F',text:'" . xla('Add Time to Date') . "',ctx:['4'],ctxExcp:['0']},
1627 {id: 'C',text:'" . xla('Capitalize') . "',ctx:['0'],ctxExcp:['4','15','40']},
1628 {id: 'D',text:'" . xla('Dup Check') . "'},
1629 {id: 'E',text:'" . xla('Dup Check on only Edit') . "'},
1630 {id: 'W',text:'" . xla('Dup Check on only New') . "'},
1631 {id: 'G',text:'" . xla('Graphable') . "'},
1632 {id: 'I',text:'" . xla('Initially Open Group') . "'},
1633 {id: 'L',text:'" . xla('Lab Order') . "'},
1634 {id: 'N',text:'" . xla('New Patient Form') . "'},
1635 {id: 'O',text:'" . xla('Order Processor') . "'},
1636 {id: 'P',text:'" . xla('Default to previous value') . "'},
1637 {id: 'R',text:'" . xla('Distributor') . "'},
1638 {id: 'T',text:'" . xla('Description is default text') . "'},
1639 {id: 'U',text:'" . xla('Capitalize all') . "'},
1640 {id: 'V',text:'" . xla('Vendor') . "'},
1641 {id: 'X',text:'" . xla('Do Not Print') . "'},
1642 {id:'grp',text:'" . xla('Stylings') . "',children:[
1643 {id: 'RS',text:'" . xla('Add Bottom Border Row') . "'},
1644 {id: 'RO',text:'" . xla('Outline Entire Row') . "'},
1645 {id: 'DS',text:'" . xla('Add Data Bottom Border') . "'},
1646 {id: 'DO',text:'" . xla('Outline Data Col') . "'},
1647 {id: 'SP',text:'" . xla('Span Entire Row') . "'}
1649 {id: '0',text:'" . xla('Read Only') . "'},
1650 {id: '1',text:'" . xla('Write Once') . "'},
1651 {id: '2',text:'" . xla('Billing Code Descriptions') . "'}];\n";
1653 // Language direction for select2
1654 echo 'var langDirection = "' . $_SESSION['language_direction'] . '";';
1657 // used when selecting a list-name for a field
1658 var selectedfield;
1660 // Support for beforeunload handler.
1661 var somethingChanged = false;
1663 // Get the next logical sequence number for a field in the specified group.
1664 // Note it guesses and uses the existing increment value.
1665 function getNextSeq(group) {
1666 var f = document.forms[0];
1667 var seq = 0;
1668 var delta = 10;
1669 for (var i = 1; true; ++i) {
1670 var gelem = f['fld[' + i + '][group]'];
1671 if (!gelem) break;
1672 if (gelem.value != group) continue;
1673 var tmp = parseInt(f['fld[' + i + '][seq]'].value);
1674 if (isNaN(tmp)) continue;
1675 if (tmp <= seq) continue;
1676 delta = tmp - seq;
1677 seq = tmp;
1679 return seq + delta;
1682 // jQuery stuff to make the page a little easier to use
1684 $(document).ready(function(){
1686 $(function () {
1687 $('.typeAddons').select2({
1688 data: fldOptions,
1689 theme: 'default',
1690 multiple: true,
1691 closeOnSelect: false,
1692 width:'100%',
1693 minimumResultsForSearch: 'Infinity',
1694 containerCssClass: ':all:',
1695 dir: langDirection,
1696 allowClear: false
1699 // Populate field option selects
1700 $(function() {
1701 $('.typeAddons').each(function(i, obj) {
1702 var v = $(this).data('set')
1703 if(typeof v !== 'undefined' && v > ""){
1704 $(this).val(v).trigger("change")
1707 somethingChanged = false;
1710 $("#save").click(function() { SaveChanges(); });
1711 $("#layout_id").change(function() {
1712 if (!myChangeCheck()) {
1713 $("#layout_id").val("<?php echo $layout_id; ?>");
1714 return;
1716 mySubmit();
1718 $(".addgroup").click(function() { AddGroup(this); });
1719 $(".savenewgroup").click(function() { SaveNewGroup(this); });
1720 /******************************************************************
1721 $(".deletegroup").click(function() { DeleteGroup(this); });
1722 ******************************************************************/
1723 $(".cancelnewgroup").click(function() { CancelNewGroup(this); });
1724 $(".movegroup").click(function() { MoveGroup(this); });
1725 $(".renamegroup").click(function() { RenameGroup(this); });
1726 $(".saverenamegroup").click(function() { SaveRenameGroup(this); });
1727 $(".cancelrenamegroup").click(function() { CancelRenameGroup(this); });
1728 $(".addfield").click(function() { AddField(this); });
1729 $("#deletefields").click(function() { DeleteFields(this); });
1730 $(".selectfield").click(function() {
1731 var TRparent = $(this).parent().parent();
1732 $(TRparent).children("td").toggleClass("highlight");
1733 // disable the delete-move buttons
1734 $("#deletefields").attr("disabled", "disabled");
1735 $("#movefields").attr("disabled", "disabled");
1736 $(".selectfield").each(function(i) {
1737 // if any field is selected, enable the delete-move buttons
1738 if ($(this).prop("checked") == true) {
1739 $("#deletefields").removeAttr("disabled");
1740 $("#movefields").removeAttr("disabled");
1745 $("#movefields").click(function() { ShowGroups(this); });
1746 $(".savenewfield").click(function() { SaveNewField(this); });
1747 $(".cancelnewfield").click(function() { CancelNewField(this); });
1748 $("#newtitle").blur(function() { if ($("#newid").val() == "") $("#newid").val($("#newtitle").val()); });
1749 $("#newdatatype").change(function() { ChangeList(this.value);});
1750 $("#gnewdatatype").change(function() { ChangeListg(this.value);});
1751 $(".listid").click(function() { ShowLists(this); });
1753 // special class that skips the element
1754 $(".noselect").focus(function() { $(this).blur(); });
1756 // Save the changes made to the form
1757 var SaveChanges = function () {
1758 var f = document.forms[0];
1759 for (var i = 1; f['fld['+i+'][id]']; ++i) {
1760 var ival = f['fld['+i+'][id]'].value;
1761 for (var j = i + 1; f['fld['+j+'][id]']; ++j) {
1762 if (ival == f['fld['+j+'][id]'].value || ival == f['fld['+j+'][originalid]'].value) {
1763 alert('<?php echo xls('Error: Duplicated field ID'); ?>: ' + ival);
1764 return;
1768 $("#formaction").val("save");
1769 mySubmit();
1772 /****************************************************/
1773 /************ Group functions ***********************/
1774 /****************************************************/
1776 // display the 'new group' DIV
1777 var AddGroup = function(btnObj) {
1778 if (!myChangeCheck()) return;
1779 $("#save").attr("disabled", true);
1780 // show the field details DIV
1781 $('#groupdetail').css('visibility', 'visible');
1782 $('#groupdetail').css('display', 'block');
1783 $('#groupdetail').css('margin-top', '85px');
1784 $(btnObj).parent().after($("#groupdetail"));
1785 $("html, body").animate({ scrollTop: 0 }, "slow");
1786 $('#groupdetail > #newgroupname').focus();
1787 // Assign a sensible default sequence number.
1788 $('#gnewseq').val(10);
1791 // save the new group to the form
1792 var SaveNewGroup = function(btnObj) {
1793 // the group name field can only have letters, numbers, spaces and underscores
1794 // AND it cannot start with a number
1795 if ($("#newgroupname").val() == "") {
1796 alert("<?php xl('Group names cannot be blank', 'e'); ?>");
1797 return false;
1799 if ($("#newgroupname").val().match(/^(\d+|\s+)/)) {
1800 alert("<?php xl('Group names cannot start with numbers or spaces.', 'e'); ?>");
1801 return false;
1803 var validname = $("#newgroupname").val().replace(/[^A-za-z0-9 ]/g, "_"); // match any non-word characters and replace them
1804 $("#newgroupname").val(validname);
1806 // now, check the first group field values
1808 // seq must be numeric and <= 9999
1809 if (! IsNumeric($("#gnewseq").val(), 0, 9999)) {
1810 alert("<?php xl('Order must be a number between 1 and 9999', 'e'); ?>");
1811 return false;
1813 // length must be numeric and less than 999
1814 if (! IsNumeric($("#gnewlengthWidth").val(), 0, 999)) {
1815 alert("<?php xl('Size must be a number between 1 and 999', 'e'); ?>");
1816 return false;
1818 // titlecols must be numeric and less than 100
1819 if (! IsNumeric($("#gnewtitlecols").val(), 0, 999)) {
1820 alert("<?php xl('LabelCols must be a number between 1 and 999', 'e'); ?>");
1821 return false;
1823 // datacols must be numeric and less than 100
1824 if (! IsNumeric($("#gnewdatacols").val(), 0, 999)) {
1825 alert("<?php xl('DataCols must be a number between 1 and 999', 'e'); ?>");
1826 return false;
1828 // the id field can only have letters, numbers and underscores
1829 if ($("#gnewid").val() == "") {
1830 alert("<?php xl('ID cannot be blank', 'e'); ?>");
1831 return false;
1833 var validid = $("#gnewid").val().replace(/(\s|\W)/g, "_"); // match any non-word characters and replace them
1834 $("#gnewid").val(validid);
1835 // similarly with the listid field
1836 validid = $("#gnewlistid").val().replace(/(\s|\W)/g, "_");
1837 $("#gnewlistid").val(validid);
1838 // similarly with the backuplistid field
1839 validid = $("#gnewbackuplistid").val().replace(/(\s|\W)/g, "_");
1840 $("#gnewbackuplistid").val(validid);
1843 // submit the form to add a new field to a specific group
1844 $("#formaction").val("addgroup");
1845 mySubmit();
1848 /******************************************************************
1849 // actually delete an entire group from the database
1850 var DeleteGroup = function(btnObj) {
1851 var parts = $(btnObj).attr("id");
1852 var groupname = parts.replace(/^\d+/, "");
1853 if (confirm("<?php xl('WARNING', 'e', '', ' - ') . xl('This action cannot be undone.', 'e', '', '\n') . xl('Are you sure you wish to delete the entire group named', 'e', '', ' '); ?>'"+groupname+"'?")) {
1854 // submit the form to add a new field to a specific group
1855 $("#formaction").val("deletegroup");
1856 $("#deletegroupname").val(parts);
1857 $("#theform").submit();
1860 ******************************************************************/
1862 // just hide the new field DIV
1863 var CancelNewGroup = function(btnObj) {
1864 // hide the field details DIV
1865 $('#groupdetail').css('visibility', 'hidden');
1866 $('#groupdetail').css('display', 'none');
1867 // reset the new group values to a default
1868 $('#groupdetail > #newgroupname').val("");
1869 $('#groupdetail > #newgroupparent').val("");
1870 $("#save").attr("disabled", false);
1873 // display the 'new field' DIV
1874 var MoveGroup = function(btnObj) {
1875 if (!myChangeCheck()) return;
1876 var btnid = $(btnObj).attr("id");
1877 var parts = btnid.split("~");
1878 var groupid = parts[0];
1879 var direction = parts[1];
1880 // submit the form to change group order
1881 $("#formaction").val("movegroup");
1882 $("#movegroupname").val(groupid);
1883 $("#movedirection").val(direction);
1884 mySubmit();
1887 // show the rename group DIV
1888 var RenameGroup = function(btnObj) {
1889 if (!myChangeCheck()) return;
1890 $("#save").attr("disabled", true);
1891 $('#renamegroupdetail').css('visibility', 'visible');
1892 $('#renamegroupdetail').css('display', 'block');
1893 $(btnObj).parent().append($("#renamegroupdetail"));
1894 var parts = $(btnObj).attr("id").split("~");
1895 $('#renameoldgroupname').val(parts[0]); // this is actually the existing group ID
1896 $('#renamegroupname').val(parts[1]); // the textual name of just this group
1897 var i = parts[0].length;
1898 $('[name=renamegroupparent]').val(i > 0 ? parts[0].substr(0, i-1) : ''); // parent ID
1901 // save the new group to the form
1902 var SaveRenameGroup = function(btnObj) {
1903 // the group name field can only have letters, numbers, spaces and underscores
1904 // AND it cannot start with a number
1905 if ($("#renamegroupname").val().match(/^\d+/)) {
1906 alert("<?php xl('Group names cannot start with numbers.', 'e'); ?>");
1907 return false;
1909 var validname = $("#renamegroupname").val().replace(/[^A-za-z0-9 ]/g, "_"); // match any non-word characters and replace them
1910 $("#renamegroupname").val(validname);
1912 // submit the form to add a new field to a specific group
1913 $("#formaction").val("renamegroup");
1914 mySubmit();
1917 // just hide the new field DIV
1918 var CancelRenameGroup = function(btnObj) {
1919 // hide the field details DIV
1920 $('#renamegroupdetail').css('visibility', 'hidden');
1921 $('#renamegroupdetail').css('display', 'none');
1922 // reset the rename group values to a default
1923 $('#renameoldgroupname').val("");
1924 $('#renamegroupname').val("");
1925 $('#renamegroupparent').val("");
1928 /****************************************************/
1929 /************ Field functions ***********************/
1930 /****************************************************/
1932 // display the 'new field' DIV
1933 var AddField = function(btnObj) {
1934 if (!myChangeCheck()) return;
1935 $("#save").attr("disabled", true);
1936 // update the fieldgroup value to be the groupid
1937 var btnid = $(btnObj).attr("id");
1938 var parts = btnid.split("~");
1939 var groupid = parts[1];
1940 $('#fielddetail > #newfieldgroupid').attr('value', groupid);
1941 // show the field details DIV
1942 $('#fielddetail').css('visibility', 'visible');
1943 $('#fielddetail').css('display', 'block');
1944 $(btnObj).parent().append($("#fielddetail"));
1945 // Assign a sensible default sequence number.
1946 $('#newseq').val(getNextSeq(groupid));
1949 var DeleteFields = function(btnObj) {
1950 if (!myChangeCheck()) return;
1951 if (confirm("<?php xl('WARNING', 'e', '', ' - ') . xl('This action cannot be undone.', 'e', '', '\n') . xl('Are you sure you wish to delete the selected fields?', 'e'); ?>")) {
1952 var delim = "";
1953 $(".selectfield").each(function(i) {
1954 // build a list of selected field names to be moved
1955 if ($(this).prop("checked") == true) {
1956 var parts = this.id.split("~");
1957 var currval = $("#selectedfields").val();
1958 $("#selectedfields").val(currval+delim+parts[1]);
1959 delim = " ";
1962 // submit the form to delete the field(s)
1963 $("#formaction").val("deletefields");
1964 mySubmit();
1968 // save the new field to the form
1969 var SaveNewField = function(btnObj) {
1970 // check the new field values for correct formatting
1972 // seq must be numeric and <= 9999
1973 if (! IsNumeric($("#newseq").val(), 0, 9999)) {
1974 alert("<?php xl('Order must be a number between 1 and 9999', 'e'); ?>");
1975 return false;
1977 // length must be numeric and less than 999
1978 if (! IsNumeric($("#newlengthWidth").val(), 0, 999)) {
1979 alert("<?php xl('Size must be a number between 1 and 999', 'e'); ?>");
1980 return false;
1982 // titlecols must be numeric and less than 100
1983 if (! IsNumeric($("#newtitlecols").val(), 0, 999)) {
1984 alert("<?php xl('LabelCols must be a number between 1 and 999', 'e'); ?>");
1985 return false;
1987 // datacols must be numeric and less than 100
1988 if (! IsNumeric($("#newdatacols").val(), 0, 999)) {
1989 alert("<?php xl('DataCols must be a number between 1 and 999', 'e'); ?>");
1990 return false;
1992 // the id field can only have letters, numbers and underscores
1993 var validid = $("#newid").val().replace(/(\s|\W)/g, "_"); // match any non-word characters and replace them
1994 $("#newid").val(validid);
1995 // similarly with the listid field
1996 validid = $("#newlistid").val().replace(/(\s|\W)/g, "_");
1997 $("#newlistid").val(validid);
1998 // similarly with the backuplistid field
1999 validid = $("#newbackuplistid").val().replace(/(\s|\W)/g, "_");
2000 $("#newbackuplistid").val(validid);
2002 // submit the form to add a new field to a specific group
2003 $("#formaction").val("addfield");
2004 mySubmit();
2007 // just hide the new field DIV
2008 var CancelNewField = function(btnObj) {
2009 // hide the field details DIV
2010 $('#fielddetail').css('visibility', 'hidden');
2011 $('#fielddetail').css('display', 'none');
2012 // reset the new field values to a default
2013 ResetNewFieldValues();
2014 $("#save").attr("disabled", false);
2017 // show the popup choice of lists
2018 var ShowLists = function(btnObj) {
2019 window.open('../patient_file/encounter/find_code_dynamic.php?what=lists',
2020 'lists', 'width=700,height=600,scrollbars=yes');
2021 selectedfield = btnObj;
2024 // show the popup choice of groups
2025 var ShowGroups = function(btnObj) {
2026 if (!myChangeCheck()) return;
2027 window.open('../patient_file/encounter/find_code_dynamic.php?what=groups&layout_id=<?php echo addslashes($layout_id); ?>',
2028 'groups', 'width=700,height=600,scrollbars=yes');
2031 // Show context DD for NationNotes
2032 var ChangeList = function(btnObj){
2033 if(btnObj==34){
2034 $('#newlistid').hide();
2035 $('#contextName').show();
2037 else{
2038 $('#newlistid').show();
2039 $('#contextName').hide();
2042 var ChangeListg = function(btnObj){
2043 if(btnObj==34){
2044 $('#gnewlistid').hide();
2045 $('#gcontextName').show();
2047 else{
2048 $('#gnewlistid').show();
2049 $('#gcontextName').hide();
2053 // Initialize list item selectors and value field visibilities in skip conditions.
2054 var f = document.forms[0];
2055 for (var lino = 1; f['fld[' + lino + '][id]']; ++lino) {
2056 for (var seq = 0; f['fld[' + lino + '][condition_itemid][' + seq + ']']; ++seq) {
2057 setListItemOptions(lino, seq, true);
2059 actionChanged(lino);
2062 // Support for beforeunload handler.
2063 $('tbody input, tbody select, tbody textarea').not('.selectfield').change(function() {
2064 somethingChanged = true;
2066 window.addEventListener("beforeunload", function (e) {
2067 if (somethingChanged && !top.timed_out) {
2068 var msg = "<?php echo xls('You have unsaved changes.'); ?>";
2069 e.returnValue = msg; // Gecko, Trident, Chrome 34+
2070 return msg; // Gecko, WebKit, Chrome <34
2074 }); /* Ready Done */
2076 function layoutLook(){
2077 var form = "<?php echo attr($layout_id);?>";
2078 var btnName = "<?php echo xla('Back To Editor');?>";
2079 var url = "../patient_file/encounter/view_form.php?isShow&id=0&formname=" + form;
2080 var title = "<?php echo xla('LBF Encounter Form Preview');?>";
2081 eModal.setEModalOptions({
2082 loadingHtml: '<span class="fa fa-circle-o-notch fa-spin fa-3x text-primary"></span><h4><?php echo xla('Loading Form');?></h4>'
2084 var params = {
2085 buttons: [{ text: btnName, style: 'success btn-md', close:true}],
2086 size: eModal.size.xl,
2087 title: title,
2088 url: url
2090 return eModal.iframe(params);
2093 function NationNotesContext(lineitem,val){
2094 if(val==34){
2095 document.getElementById("fld["+lineitem+"][contextName]").style.display='';
2096 document.getElementById("fld["+lineitem+"][list_id]").style.display='none';
2097 document.getElementById("fld["+lineitem+"][list_id]").value='';
2099 else{
2100 document.getElementById("fld["+lineitem+"][list_id]").style.display='';
2101 document.getElementById("fld["+lineitem+"][contextName]").style.display='none';
2102 document.getElementById("fld["+lineitem+"][list_id]").value='';
2106 function SetList(listid) {
2107 $(selectedfield).val(listid);
2110 //////////////////////////////////////////////////////////////////////
2111 // The following supports the field ID selection pop-up.
2112 //////////////////////////////////////////////////////////////////////
2114 var fieldselectfield;
2116 function elemFromPart(part) {
2117 var ename = fieldselectfield.name;
2118 // ename is like one of the following:
2119 // fld[$fld_line_no][id]
2120 // gnewid
2121 // newid
2122 // and "part" is what we substitute for the "id" part.
2123 var i = ename.lastIndexOf('id');
2124 ename = ename.substr(0, i) + part + ename.substr(i+2);
2125 return document.forms[0][ename];
2128 function FieldIDClicked(elem) {
2129 <?php if (substr($layout_id, 0, 3) == 'LBF') { ?>
2130 fieldselectfield = elem;
2131 var srcval = elemFromPart('source').value;
2132 // If the field ID is for the local form, allow direct entry.
2133 if (srcval == 'F') return;
2134 // Otherwise pop up the selection window.
2135 window.open('../patient_file/encounter/find_code_dynamic.php?what=fields&source='
2136 + srcval, 'fields', 'width=700,height=600,scrollbars=yes');
2137 <?php } ?>
2140 function SetField(field_id, title, data_type, uor, fld_length, max_length,
2141 list_id, titlecols, datacols, edit_options, description, fld_rows)
2143 fieldselectfield.value = field_id;
2144 elemFromPart('title' ).value = title;
2145 elemFromPart('datatype' ).value = data_type;
2146 elemFromPart('uor' ).value = uor;
2147 elemFromPart('lengthWidth' ).value = fld_length;
2148 elemFromPart('maxSize' ).value = max_length;
2149 elemFromPart('list_id' ).value = list_id;
2150 elemFromPart('titlecols' ).value = titlecols;
2151 elemFromPart('datacols' ).value = datacols;
2152 elemFromPart('edit_options').value = edit_options;
2153 elemFromPart('desc' ).value = description;
2154 elemFromPart('lengthHeight').value = fld_rows;
2157 //////////////////////////////////////////////////////////////////////
2158 // End code for field ID selection pop-up.
2159 //////////////////////////////////////////////////////////////////////
2161 /* this is called after the user chooses a new group from the popup window
2162 * it will submit the page so the selected fields can be moved into
2163 * the target group
2165 function MoveFields(targetgroup) {
2166 $("#targetgroup").val(targetgroup);
2167 var delim = "";
2168 $(".selectfield").each(function(i) {
2169 // build a list of selected field names to be moved
2170 if ($(this).prop("checked") == true) {
2171 var parts = this.id.split("~");
2172 var currval = $("#selectedfields").val();
2173 $("#selectedfields").val(currval+delim+parts[1]);
2174 delim = " ";
2177 $("#formaction").val("movefields");
2178 mySubmit();
2181 // set the new-field values to a default state
2182 function ResetNewFieldValues () {
2183 $("#newseq").val("");
2184 $("#newsource").val("");
2185 $("#newid").val("");
2186 $("#newtitle").val("");
2187 $("#newuor").val(1);
2188 $("#newlengthWidth").val("");
2189 $("#newlengthHeight").val("");
2190 $("#newmaxSize").val("");
2191 $("#newdatatype").val("");
2192 $("#newlistid").val("");
2193 $("#newbackuplistid").val("");
2194 $("#newtitlecols").val("");
2195 $("#newdatacols").val("");
2196 $("#newedit_options").val("");
2197 $("#newdefault").val("");
2198 $("#newdesc").val("");
2201 // is value an integer and between min and max
2202 function IsNumeric(value, min, max) {
2203 if (value == "" || value == null) return false;
2204 if (! IsN(value) ||
2205 parseInt(value) < min ||
2206 parseInt(value) > max)
2207 return false;
2209 return true;
2212 /****************************************************/
2213 /****************************************************/
2214 /****************************************************/
2216 // tell if num is an Integer
2217 function IsN(num) { return !/\D/.test(num); }
2219 </script>
2221 </html>