source: admin/WebConsole/principal/calendars-modify.php @ 7965580

configure-oglivelgromero-new-oglivemainmaint-cronmount-efivarfsmultivmmultivm-ogboot-installerogClonningEngineogboot-installer-jenkinsoglive-ipv6test-python-scriptsticket-301ticket-50ticket-50-oldticket-577ticket-585ticket-611ticket-612ticket-693ticket-700ubu24tplunification2use-local-agent-oglivevarios-instalacion
Last change on this file since 7965580 was 7965580, checked in by Natalia Serrano <natalia.serrano@…>, 18 months ago

Add, modify and delete calendars

  • Property mode set to 100644
File size: 20.1 KB
Line 
1<?php
2include_once("../includes/ctrlacc.php");
3include_once("../includes/CreaComando.php");
4include_once("../clases/AdoPhp.php");
5
6$tz = 'Europe/Madrid';
7
8//print ("_GET ("); print_r ($_GET); print (")\n");
9//print ("_POST ("); print_r ($_POST); print (")\n");
10
11//function build_json ($ruleset) { ICAgIGdsb2JhbCAkdHo7CgogICAgJGpzb25fc3RyID0ganNvbl9lbmNvZGUgKGFycmF5ICgKICAgICAgICAncmVtb3RlcGNfY2FsZW5kYXInID0+IGFycmF5ICgKICAgICAgICAgICAgJ3RpbWV6b25lJyA9PiAkdHosCiAgICAgICAgICAgICdydWxlc2V0JyA9PiAkcnVsZXNldCwKICAgICAgICApCiAgICApLCBKU09OX1VORVNDQVBFRF9TTEFTSEVTKTsKCiAgICAvLyBUT0RPIGNoZWNrIGVycm9ycwoKICAgIHJldHVybiAkanNvbl9zdHI7Cn0K
12
13function add_cal_to_db ($desc, $j) {
14    global $cadenaconexion;
15    $cmd = CreaComando ($cadenaconexion);
16    if (!$cmd) {
17        die ($TbMsg['ACCESS_ERROR']);
18    }
19    $cmd->CreaParametro ('@desc',      $desc, 0);
20    $cmd->CreaParametro ('@json_text', $j,    0);
21    $cmd->texto = 'INSERT INTO calendarios (description, json_text) VALUES (@desc, @json_text);';
22    if (!$cmd->Ejecutar()) {
23        return $cmd->DescripUltimoError();
24    }
25    return;
26}
27
28function modify_cal_in_db ($id, $desc, $j) {
29    global $cadenaconexion;
30    $cmd = CreaComando ($cadenaconexion);
31    if (!$cmd) {
32        die ($TbMsg['ACCESS_ERROR']);
33    }
34    $cmd->CreaParametro ('@id',        $id,   0);
35    $cmd->CreaParametro ('@desc',      $desc, 0);
36    $cmd->CreaParametro ('@json_text', $j,    0);
37    $cmd->texto = 'UPDATE calendarios SET description=@desc, json_text=@json_text WHERE id=@id;';
38    if (!$cmd->Ejecutar()) {
39        return $cmd->DescripUltimoError();
40    }
41    return;
42}
43
44function fetch_cal_from_db ($id) {
45    global $cadenaconexion;
46    $cmd = CreaComando ($cadenaconexion);
47    if (!$cmd) {
48        die ($TbMsg['ACCESS_ERROR']);
49    }
50    $cmd->CreaParametro ('@id', $id, 0);
51    $cmd->texto = 'SELECT description, json_text FROM calendarios WHERE id = @id;';
52    $rs = new Recordset;
53    $rs->Comando = &$cmd;
54
55    if (!$rs->Abrir()) return (false);
56
57    $rs->Primero();
58    if ($rs->EOF) {
59        //err
60    }
61    $desc = $rs->campos['description'];
62    $json = $rs->campos['json_text'];
63    return array ($desc, $json);
64}
65
66function process_post ($cal_id, $desc, $json_str) {
67    $errors = array();
68//error_log (sprintf ('in process_post, cal_id (%s) desc (%s) json_str (%s)', $cal_id, $desc, $json_str));
69
70    if (empty ($desc)) {
71//error_log (sprintf ('in process_post, empty(desc)(%s)', $desc));
72        array_push ($errors, 'Description of new calendar is unset');
73    }
74    if (sizeof ($errors)) {
75        return $errors;
76    }
77
78    if (-1 == $cal_id) {
79        $db_err = add_cal_to_db ($desc, $json_str);
80    } else {
81        $db_err = modify_cal_in_db ($cal_id, $desc, $json_str);
82    }
83
84    if ($db_err) {
85        array_push ($errors, $db_err);
86    }
87
88    if (!sizeof ($errors)) {
89        header ('Location: calendars.php');
90    }
91    return $errors;
92}
93
94
95$error_str = '';
96function parse_params() {
97    global $tz, $error_str;
98
99    if (isset ($_GET['cal_id']) && isset ($_POST['cal_id'])) {
100        $error_str = "Got a cal_id in both a GET and a POST??\n";
101        return 0;
102    }
103
104    if (isset ($_GET['cal_id'])) {
105        $cal_id = $_GET['cal_id'];
106        $is_get = 1;
107    } elseif (isset ($_POST['cal_id'])) {
108        $cal_id = $_POST['cal_id'];
109        $is_get = 0;
110    } else {
111        $error_str = "Didn't get a cal_id in neither GET nor POST\n";
112        return 0;
113    }
114
115    $ret = 0;
116//error_log (sprintf ('00'));
117    if (-1 == $cal_id) {
118        // nuevo calendario
119        // seguro que es un POST
120        //print ("-1 == (cal_id)\n");
121
122//error_log (sprintf ('05'));
123        if (!isset ($_POST['desc']) and !isset ($_POST['json_str'])) {
124            // en la lista de calendarios han pinchado en el boton de "nuevo calendario"
125//error_log (sprintf ('10'));
126            $desc = '';
127            $json = '{"remotepc_calendar":{"timezone":"' . $tz . '","ruleset":[{"remote":false,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"from_hr":"8","to_hr":"17"}]}}';   // hagamos que los calendarios nuevos por defecto ya tengan una regla lun-vie 8-17
128            $ret = 1;
129
130        } else {
131            // en los detalles del calendario han pinchado en "crear"
132//error_log (sprintf ('15'));
133            $desc = $_POST['desc'];
134            $json = $_POST['json_str'];
135            $errors = process_post ($cal_id, $desc, $json);
136//error_log (sprintf ('20'));
137            if (sizeof ($errors)) {
138//error_log (sprintf ('30'));
139                $error_str = implode (',', $errors);
140                //printf ('after process_post(), error_str (%s)', $error_str);
141            } else {
142                $ret = 1;
143            }
144//error_log (sprintf ('40'));
145        }
146
147    } else {
148//error_log (sprintf ('50'));
149        // puede ser GET o POST
150        //print ("-1 != (cal_id)\n");
151
152        if ($is_get) {  // si GET: ver calendario
153//error_log (sprintf ('60'));
154            list ($desc, $json) = fetch_cal_from_db ($cal_id);
155            $ret = 1;
156            //$cal = json_decode ($json, true);
157            //if ($cal) {
158            //    printf ("GET: desc (%s)\n", $desc);
159            //    print ("cal (");
160            //    print_r ($cal);
161            //    print (")\n");
162            //}
163
164        } else {        // si POST: modificar calendario
165//error_log (sprintf ('70'));
166            $desc = $_POST['desc'];
167            $json = $_POST['json_str'];
168            $errors = process_post ($cal_id, $desc, $json);
169            if (sizeof ($errors)) {
170                $error_str = implode (',', $errors);
171                //printf ('after process_post(), error_str (%s)', $error_str);
172            } else {
173                $ret = 1;
174            }
175        }
176    }
177    return array ($ret, $cal_id, $desc, $json);
178}
179
180list ($ok, $cal_id, $desc, $json) = parse_params();
181//error_log (sprintf ('end, ok (%s) error_str (%s)', $ok, $error_str));
182//if (empty ($error_str)) { header ('Location: calendars.php'); }
183?>
184
185<html>
186<head>
187<title>Administración web de calendarios</title>
188<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
189<link rel="shortcut icon" href="images/iconos/logocirculos.png" type="image/png" />
190<link rel="stylesheet" type="text/css" href="estilos.css" />
191<script language="javascript">
192
193var tz = "<?= $tz ?>";
194var cal_id = <?= $cal_id ?>;
195var jsdesc = "<?= $desc ?>";   // if js vars have the same name as html elems, then things like document.forms[0].desc.value return "[object htmlinputelement]" rather than the actual value. Hence, "jsdesc".
196var cal = <?= $json ?>;
197
198// copypaste from elsewhere to handle the calendar popup
199function vertabla_calendario(ofecha) {
200    currentFecha=ofecha;
201    url="../varios/calendario_ventana.php?fecha="+ofecha.value;
202    window.open(url,"vf","top=160,left=250,height=220,width=150,scrollbars=no")
203}
204
205function anade_fecha(fecha){
206    currentFecha.value=fecha
207}
208// end of copypaste
209
210
211// https://stackoverflow.com/questions/15141762/how-to-initialize-a-javascript-date-to-a-particular-time-zone
212function dateWithTimeZone (timeZone, year, month, day, hour, minute, second) {
213    let date = new Date (Date.UTC (year, month-1, day, hour, minute, second));
214
215    let utcDate = new Date (date.toLocaleString ('en-US', { timeZone: "UTC"    }));
216    let tzDate  = new Date (date.toLocaleString ('en-US', { timeZone: timeZone }));
217    let offset = utcDate.getTime() - tzDate.getTime();
218
219    date.setTime (date.getTime() + offset);
220
221    return date;
222};
223
224days_of_week = [
225    ['mon', 'Lun'],
226    ['tue', 'Mar'],
227    ['wed', 'Mié'],
228    ['thu', 'Jue'],
229    ['fri', 'Vie'],
230    ['sat', 'Sáb'],
231    ['sun', 'Dom']
232];
233
234    var MAX_DUMP_DEPTH = 10; function dumpObj(obj, name, indent, depth) {
235        if (depth > MAX_DUMP_DEPTH) { return indent + name + ": <Maximum Depth Reached>\n"; }
236        if (typeof obj == "object") {
237            var child = null; var output = indent + name + "\n"; indent += "\t";
238            for (var item in obj) {
239                try { child = obj[item]; }
240                catch (e) { child = "<Unable to Evaluate>"; }
241                if (typeof child == "object") { output += dumpObj(child, item, indent, depth + 1); }
242                else { output += indent + item + ": " + child + "\n"; }
243            } return output;
244        } else { return obj; } }
245
246function json2tbl() {
247    //alert ('cal_id (' + cal_id + ') desc (' + jsdesc + ') cal (' + cal + ')');
248    new_tbl = document.createElement ('table');
249
250    // desc
251    tr = document.createElement ('tr');
252    tr.innerHTML = '<td>Descripción</td><td><input type="text" name="desc" value="' + jsdesc + '" onblur="jsdesc=document.forms[0].desc.value;"/></td>';
253    new_tbl.appendChild (tr);
254
255    rule_no = -1;
256
257    // para cada regla en el ruleset, añadir un tr
258    for (idx in cal.remotepc_calendar.ruleset) {
259        rule_no++;
260        rule_id = 'rule' + rule_no;
261        rule = cal.remotepc_calendar.ruleset[idx];
262        if (!rule.remote) {
263            tr = document.createElement ('tr');
264
265            // un td con los radio "presencial" (checked) y "remoto", y el boton de borrar
266            td = document.createElement ('td');
267            td.innerHTML = '<input type="radio" name="' + rule_id + '" value="onsite" checked="checked" onclick=\'set_rule_onsite("' + rule_no + '");\' /> Período presencial<br>';
268            td.innerHTML += '<input type="radio" name="' + rule_id + '" value="except" onclick=\'set_rule_exception("' + rule_no + '");\' /> Excepción<br>';
269            td.innerHTML += '<input type="button" name="' + rule_id + '_delete" value="del rule" onclick=\'del_rule("' + rule_no + '");\' />';
270            tr.appendChild (td);
271
272            html = '';
273            // y otro td con los dias...
274            days_of_week.forEach (dow => {
275                if (rule[dow[0]] != undefined) {
276                    html += '<input type="checkbox" name="' + rule_id + '_' + dow[0] + '" checked />' + dow[1] + ' ';
277                } else {
278                    html += '<input type="checkbox" name="' + rule_id + '_' + dow[0] + '" />' + dow[1] + ' ';
279                }
280            });
281
282            // ...la hora inicio...
283            html += 'de <select name="' + rule_id + '_from_hr">';
284            for (var h = 0; h <= 23; h++) {
285                if (h == rule.from_hr) {
286                    html += '<option value="' + h + '" selected>' + h + ':00</option>';
287                } else {
288                    html += '<option value="' + h + '"         >' + h + ':00</option>';
289                }
290            }
291            html += '</select>';
292
293            // ...y la hora fin
294            html += 'a <select  name="' + rule_id + '_to_hr">';
295            for (var h = 0; h <= 23; h++) {
296                if (h == rule.to_hr) {
297                    html += '<option value="' + h + '" selected>' + h + ':59</option>';
298                } else {
299                    html += '<option value="' + h + '"         >' + h + ':59</option>';
300                }
301            }
302            html += '</select>';
303
304            td = document.createElement ('td');
305            td.innerHTML = html;
306            tr.appendChild (td);
307
308            new_tbl.appendChild (tr);
309
310        } else {
311            tr = document.createElement ('tr');
312
313            // un td con los radio "presencial" y "remoto" (checked), y el boton de borrar
314            td = document.createElement ('td');
315            td.innerHTML = '<input type="radio" name="' + rule_id + '" value="onsite" onclick=\'set_rule_onsite("' + rule_no + '");\' /> Período presencial<br>';
316            td.innerHTML += '<input type="radio" name="' + rule_id + '" value="except" checked="checked" onclick=\'set_rule_exception("' + rule_no + '");\' /> Excepción<br>';
317            td.innerHTML += '<input type="button" name="' + rule_id + '_delete" value="del rule" onclick=\'del_rule("' + rule_no + '");\' />';
318            tr.appendChild (td);
319
320            html = '';
321            // y otro td con reason,from,to
322            html += 'Motivo: <input type="text" name="' + rule_id + '_reason" value="' + rule.reason + '" /> ';
323            from_ts = new Intl.DateTimeFormat ('es-ES', { year: 'numeric', month: 'numeric', day: 'numeric', timeZone: tz }).format (new Date (1000 * rule.from_ts));
324            to_ts   = new Intl.DateTimeFormat ('es-ES', { year: 'numeric', month: 'numeric', day: 'numeric', timeZone: tz }).format (new Date (1000 * rule.to_ts  ));
325            html += 'de <input onclick="vertabla_calendario(this)" name="' + rule_id + '_from_ts" value="' + from_ts + '" /> ';
326            html += 'a <input  onclick="vertabla_calendario(this)" name="' + rule_id + '_to_ts"   value="' + to_ts   + '" />';
327            td = document.createElement ('td');
328            td.innerHTML = html;
329            tr.appendChild (td);
330
331            new_tbl.appendChild (tr);
332
333        }
334    }
335   
336    // boton Añadir regla
337    tr = document.createElement ('tr');
338    tr.innerHTML = '<tr><td colspan="2"><input type="submit" value="Añadir regla" onclick=\'add_rule(); return false;\' /></td></tr>';
339    new_tbl.appendChild (tr);
340   
341    // boton Crear/Modificar calendario
342    tr = document.createElement ('tr');
343    btn_lbl = -1 == cal_id ? 'Crear' : 'Modificar';
344    tr.innerHTML = '<tr><td colspan="2"><input type="submit" value="' + btn_lbl + ' calendario" onclick=\'tbl2json(); document.forms[0].json_str.value = JSON.stringify(cal); document.forms[0].action="./calendars-modify.php";\' /></td></tr>';
345    new_tbl.appendChild (tr);
346
347    return new_tbl;
348}
349
350function replace_tbl() {
351    old_tbl = document.getElementById ('tbl');
352    parentElement = old_tbl.parentElement;
353
354    try {
355        new_tbl = json2tbl();
356        new_tbl.id = 'tbl';
357        new_tbl.border = 1;
358        parentElement.replaceChild (new_tbl, old_tbl);
359    } catch (error) {
360        console.error ("Error parsing JSON:", error);
361    }
362}
363
364function tbl2json() {
365//alert ('in tbl2json()');
366    f = new FormData (document.forms[0]);
367    re = /^rule(\d+)(_(.*))?$/;
368    ruleset = [];
369    for (let [key, val] of f.entries()) {
370        if ('cal_id' == key) { cal_id = val; continue; }
371        if ('desc'   == key) { jsdesc = val; continue; }
372        m = re.exec (key);
373        if (m === null) { continue; } // no match
374
375        rule = m[1];
376        k = m[3];
377//alert ('rule (' + rule + ') k (' + k + ') val (' + val + ')');
378        if (k === undefined) {
379            // rule0, rule1...
380            if      ('onsite' == val) { ruleset[rule] = { 'remote': false }; }
381            else if ('except' == val) { ruleset[rule] = { 'remote': true  }; }
382            continue;
383        }
384
385        // rule0_whatever, rule1_whatever...
386        if (['mon','tue','wed','thu','fri','sat','sun'].indexOf (k) >= 0) { val = true; }    // val is "on", turn it into boolean
387        if ('from_ts' == k) {
388            m = /^(\d+)\/(\d+)\/(\d+)$/.exec (val);
389            if (m != null) {
390                day = m[1];
391                month = m[2];
392                year = m[3];
393                val = dateWithTimeZone (tz, year, month, day, 0, 0, 0).getTime() / 1000;
394            }
395        }
396        if ('to_ts' == k) {
397            m = /^(\d+)\/(\d+)\/(\d+)$/.exec (val);
398            if (m != null) {
399                day = m[1];
400                month = m[2];
401                year = m[3];
402                val = dateWithTimeZone (tz, year, month, day, 23, 59, 59).getTime() / 1000;
403            }
404        }
405        ruleset[rule][k] = val;
406    }
407
408    cal = { 'remotepc_calendar': { 'timezone': tz, ruleset: ruleset } };
409    //alert ('exiting tbl2json: new cal: ' + dumpObj (cal));
410}
411
412function set_rule_onsite (idx) {
413//    alert ('in set_rule_onsite (' + idx + ') cal (' + dumpObj(cal) + ')');
414    rule_no = parseInt(idx);
415    rule_id = 'rule' + rule_no;
416    tbl = document.getElementById ('tbl');
417    tr = tbl.rows[rule_no+1];
418    //alert ('tr:' + tr.innerHTML);
419
420    // comprobar si ya está checked, entonces no hacer nada
421    if (!cal.remotepc_calendar.ruleset[idx].remote) { return; }
422
423    td = tr.cells[1];
424    //alert ('td:' + td.innerHTML);
425    td.innerHTML = ' \
426        <input type="checkbox" name="' + rule_id + '_mon" />Lun <input type="checkbox" name="' + rule_id + '_tue" />Mar <input type="checkbox" name="' + rule_id + '_wed" />Mié <input type="checkbox" name="' + rule_id + '_thu" />Jue <input type="checkbox" name="' + rule_id + '_fri" />Vie <input type="checkbox" name="' + rule_id + '_sat" />Sáb <input type="checkbox" name="' + rule_id + '_sun" />Dom, \
427        de <select name="' + rule_id + '_from_hr"><option value="0">0:00</option> <option value="1">1:00</option> <option value="2">2:00</option> <option value="3">3:00</option> <option value="4">4:00</option> <option value="5">5:00</option> <option value="6">6:00</option> <option value="7">7:00</option> <option value="8" selected>8:00</option> <option value="9">9:00</option> <option value="10">10:00</option> <option value="11">11:00</option> <option value="12">12:00</option> <option value="13">13:00</option> <option value="14">14:00</option> <option value="15">15:00</option> <option value="16">16:00</option> <option value="17"         >17:00</option> <option value="18">18:00</option> <option value="19">19:00</option> <option value="20">20:00</option> <option value="21">21:00</option> <option value="22">22:00</option> <option value="23">23:00</option> </select> \
428        a <select  name="' + rule_id + '_to_hr"  ><option value="0">0:59</option> <option value="1">1:59</option> <option value="2">2:59</option> <option value="3">3:59</option> <option value="4">4:59</option> <option value="5">5:59</option> <option value="6">6:59</option> <option value="7">7:59</option> <option value="8"         >8:59</option> <option value="9">9:59</option> <option value="10">10:59</option> <option value="11">11:59</option> <option value="12">12:59</option> <option value="13">13:59</option> <option value="14">14:59</option> <option value="15">15:59</option> <option value="16">16:59</option> <option value="17" selected>17:59</option> <option value="18">18:59</option> <option value="19">19:59</option> <option value="20">20:59</option> <option value="21">21:59</option> <option value="22">22:59</option> <option value="23">23:59</option> </select> \
429    ';
430
431    tbl2json();
432}
433
434function set_rule_exception (idx) {
435//    alert ('in set_rule_exception (' + idx + ') cal (' + dumpObj(cal) + ')');
436    rule_no = parseInt(idx);
437    rule_id = 'rule' + rule_no;
438    tbl = document.getElementById ('tbl');
439    tr = tbl.rows[rule_no+1];
440    //alert ('tr:' + tr.innerHTML);
441
442    // comprobar si ya está checked, entonces no hacer nada
443    if (cal.remotepc_calendar.ruleset[idx].remote) { return; }
444
445    td = tr.cells[1];
446    //alert ('td:' + td.innerHTML);
447    td.innerHTML = ' \
448        Motivo: <input type="text" name="' + rule_id + '_reason" /> \
449        de <input onclick="vertabla_calendario(this)" name="' + rule_id + '_from_ts" /> \
450        a <input  onclick="vertabla_calendario(this)" name="' + rule_id + '_to_ts" /> \
451    ';
452
453    tbl2json();
454}
455
456function add_rule() {
457    cal.remotepc_calendar.ruleset.push ({});
458//ICAgIHRibCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkICgndGJsJyk7CiAgICBudW1fcm93cyA9IHRibC5yb3dzLmxlbmd0aDsKICAgIG51bV9ydWxlcyA9IG51bV9yb3dzLTM7CgogICAgbmV3X3JvdyA9IHRibC5pbnNlcnRSb3cobnVtX3J1bGVzKzEpOwogICAgbmV3X3J1bGVfaWQgPSAicnVsZSIgKyAobnVtX3J1bGVzKTsKICAgIC8vYWxlcnQgKCdudW1fcm93cyAoJyArIG51bV9yb3dzICsgJykgbnVtX3J1bGVzICgnICsgbnVtX3J1bGVzICsgJykgbmV3X3J1bGVfaWQgKCcgKyBuZXdfcnVsZV9pZCArICcpJykKCiAgICByYWRpb19jZWxsID0gbmV3X3Jvdy5pbnNlcnRDZWxsKDApOwogICAgcmFkaW9fY2VsbC5pbm5lckhUTUwgPSAnPGlucHV0IHR5cGU9InJhZGlvIiBuYW1lPSInICsgbmV3X3J1bGVfaWQgKyAnIiB2YWx1ZT0ib25zaXRlIiBvbmNsaWNrPVwnc2V0X3J1bGVfb25zaXRlKCInICsgbmV3X3J1bGVfaWQgKyAnIik7XCcgLz4gUGVyw61vZG8gcHJlc2VuY2lhbDxicj48aW5wdXQgdHlwZT0icmFkaW8iIG5hbWU9IicgKyBuZXdfcnVsZV9pZCArICciIHZhbHVlPSJleGNlcHQiIG9uY2xpY2s9XCdzZXRfcnVsZV9leGNlcHRpb24oIicgKyBuZXdfcnVsZV9pZCArICciKTtcJyAvPiBFeGNlcGNpw7NuJzsKCiAgICBvdGhlcl9jZWxsID0gbmV3X3Jvdy5pbnNlcnRDZWxsKDEpOwogICAgb3RoZXJfY2VsbC5pZCA9IG5ld19ydWxlX2lkOwo=
459
460    replace_tbl();
461    //alert ('exiting add_rule: cal: ' + dumpObj (cal));
462}
463
464function del_rule (idx) {
465    //alert ('in del_rule (' + idx + ')');
466    tbl2json();
467    cal.remotepc_calendar.ruleset.splice (idx, 1);
468    replace_tbl();
469}
470
471document.addEventListener("DOMContentLoaded", function(){
472    replace_tbl();
473});
474</script>
475
476
477</head>
478
479<body>
480    <?php if ($error_str) { printf ('<p>Error: (%s)</p>', $error_str); } ?>
481    <form method="post">
482        <input type="hidden" name="cal_id" value="<?= $cal_id ?>" />
483        <input type="hidden" name="json_str" value="" />
484        <p><table id="tbl"></table></p>
485    </form>
486</body>
487</html>
Note: See TracBrowser for help on using the repository browser.