translation fixes to prepare for a translation spreadsheet update
[openemr.git] / interface / main / calendar / find_appt_popup.php
blob970be91792e42b8c562e6672f8cabb0a2450ebdd
1 <?php
2 /**
4 * Script to find open appointmnent slots
6 * Copyright (C) 2005-2013 Rod Roark <rod@sunsetsystems.com>
8 * LICENSE: This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
19 * @package OpenEMR
20 * @author Rod Roark <rod@sunsetsystems.com>
21 * @author Roberto Vasquez <robertogagliotta@gmail.com>
22 * @link http://www.open-emr.org
25 $fake_register_globals=false;
26 $sanitize_all_escapes=true;
28 include_once("../../globals.php");
29 include_once("$srcdir/patient.inc");
31 // check access controls
32 if (!acl_check('patients','appt','',array('write','wsome') ))
33 die(xlt('Access not allowed'));
35 // If the caller is updating an existing event, then get its ID so
36 // we don't count it as a reserved time slot.
37 $eid = empty($_REQUEST['eid']) ? 0 : 0 + $_REQUEST['eid'];
39 $input_catid = $_REQUEST['catid'];
41 // Record an event into the slots array for a specified day.
42 function doOneDay($catid, $udate, $starttime, $duration, $prefcatid) {
43 global $slots, $slotsecs, $slotstime, $slotbase, $slotcount, $input_catid;
44 $udate = strtotime($starttime, $udate);
45 if ($udate < $slotstime) return;
46 $i = (int) ($udate / $slotsecs) - $slotbase;
47 $iend = (int) (($duration + $slotsecs - 1) / $slotsecs) + $i;
48 if ($iend > $slotcount) $iend = $slotcount;
49 if ($iend <= $i) $iend = $i + 1;
50 for (; $i < $iend; ++$i) {
51 if ($catid == 2) { // in office
52 // If a category ID was specified when this popup was invoked, then select
53 // only IN events with a matching preferred category or with no preferred
54 // category; other IN events are to be treated as OUT events.
55 if ($input_catid) {
56 if ($prefcatid == $input_catid || !$prefcatid)
57 $slots[$i] |= 1;
58 else
59 $slots[$i] |= 2;
60 } else {
61 $slots[$i] |= 1;
63 break; // ignore any positive duration for IN
64 } else if ($catid == 3) { // out of office
65 $slots[$i] |= 2;
66 break; // ignore any positive duration for OUT
67 } else { // all other events reserve time
68 $slots[$i] |= 4;
73 // seconds per time slot
74 $slotsecs = $GLOBALS['calendar_interval'] * 60;
76 $catslots = 1;
77 if ($input_catid) {
78 $srow = sqlQuery("SELECT pc_duration FROM openemr_postcalendar_categories WHERE pc_catid = ?", array($input_catid) );
79 if ($srow['pc_duration']) $catslots = ceil($srow['pc_duration'] / $slotsecs);
82 $info_msg = "";
84 $searchdays = 7; // default to a 1-week lookahead
85 if ($_REQUEST['searchdays']) $searchdays = $_REQUEST['searchdays'];
87 // Get a start date.
88 if ($_REQUEST['startdate'] && preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/",
89 $_REQUEST['startdate'], $matches))
91 $sdate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
92 } else {
93 $sdate = date("Y-m-d");
96 // Get an end date - actually the date after the end date.
97 preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/", $sdate, $matches);
98 $edate = date("Y-m-d",
99 mktime(0, 0, 0, $matches[2], $matches[3] + $searchdays, $matches[1]));
101 // compute starting time slot number and number of slots.
102 $slotstime = strtotime("$sdate 00:00:00");
103 $slotetime = strtotime("$edate 00:00:00");
104 $slotbase = (int) ($slotstime / $slotsecs);
105 $slotcount = (int) ($slotetime / $slotsecs) - $slotbase;
107 if ($slotcount <= 0 || $slotcount > 100000) die(xlt("Invalid date range"));
109 $slotsperday = (int) (60 * 60 * 24 / $slotsecs);
111 // Compute the number of time slots for the given event duration, or if
112 // none is given then assume the default category duration.
113 $evslots = $catslots;
114 if (isset($_REQUEST['evdur'])) {
115 $evslots = 60 * $_REQUEST['evdur'];
116 $evslots = (int) (($evslots + $slotsecs - 1) / $slotsecs);
119 // If we have a provider, search.
121 if ($_REQUEST['providerid']) {
122 $providerid = $_REQUEST['providerid'];
124 // Create and initialize the slot array. Values are bit-mapped:
125 // bit 0 = in-office occurs here
126 // bit 1 = out-of-office occurs here
127 // bit 2 = reserved
128 // So, values may range from 0 to 7.
130 $slots = array_pad(array(), $slotcount, 0);
132 $sqlBindArray = array();
134 // Note there is no need to sort the query results.
135 $query = "SELECT pc_eventDate, pc_endDate, pc_startTime, pc_duration, " .
136 "pc_recurrtype, pc_recurrspec, pc_alldayevent, pc_catid, pc_prefcatid " .
137 "FROM openemr_postcalendar_events " .
138 "WHERE pc_aid = ? AND " .
139 "pc_eid != ? AND " .
140 "((pc_endDate >= ? AND pc_eventDate < ? ) OR " .
141 "(pc_endDate = '0000-00-00' AND pc_eventDate >= ? AND pc_eventDate < ?))";
143 array_push($sqlBindArray, $providerid, $eid, $sdate, $edate, $sdate, $edate);
145 // phyaura whimmel facility filtering
146 if ($_REQUEST['facility'] > 0 ) {
147 $facility = $_REQUEST['facility'];
148 $query .= " AND pc_facility = ?";
149 array_push($sqlBindArray, $facility);
151 // end facility filtering whimmel 29apr08
152 $res = sqlStatement($query, $sqlBindArray);
154 while ($row = sqlFetchArray($res)) {
155 $thistime = strtotime($row['pc_eventDate'] . " 00:00:00");
156 if ($row['pc_recurrtype']) {
158 preg_match('/"event_repeat_freq_type";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
159 $repeattype = $matches[1];
161 preg_match('/"event_repeat_freq";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
162 $repeatfreq = $matches[1];
163 if ($row['pc_recurrtype'] == 2) {
164 // Repeat type is 2 so frequency comes from event_repeat_on_freq.
165 preg_match('/"event_repeat_on_freq";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
166 $repeatfreq = $matches[1];
168 if (! $repeatfreq) $repeatfreq = 1;
170 preg_match('/"event_repeat_on_num";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
171 $my_repeat_on_num = $matches[1];
173 preg_match('/"event_repeat_on_day";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
174 $my_repeat_on_day = $matches[1];
176 // This gets an array of exception dates for the event.
177 $exdates = array();
178 if (preg_match('/"exdate";s:\d+:"([0-9,]*)"/', $row['pc_recurrspec'], $matches)) {
179 $exdates = explode(",", $matches[1]);
182 $endtime = strtotime($row['pc_endDate'] . " 00:00:00") + (24 * 60 * 60);
183 if ($endtime > $slotetime) $endtime = $slotetime;
185 $repeatix = 0;
186 while ($thistime < $endtime) {
187 $adate = getdate($thistime);
188 $thisymd = sprintf('%04d%02d%02d', $adate['year'], $adate['mon'], $adate['mday']);
190 // Skip the event if a repeat frequency > 1 was specified and this is
191 // not the desired occurrence, or if this date is in the exception array.
192 if (!$repeatix && !in_array($thisymd, $exdates)) {
193 doOneDay($row['pc_catid'], $thistime, $row['pc_startTime'],
194 $row['pc_duration'], $row['pc_prefcatid']);
196 if (++$repeatix >= $repeatfreq) $repeatix = 0;
198 if ($row['pc_recurrtype'] == 2) {
199 // Need to skip to nth or last weekday of the next month.
200 $adate['mon'] += 1;
201 if ($adate['mon'] > 12) {
202 $adate['year'] += 1;
203 $adate['mon'] -= 12;
205 if ($my_repeat_on_num < 5) { // not last
206 $adate['mday'] = 1;
207 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN, $adate['mon'], $adate['mday'], $adate['year']));
208 if ($dow > $my_repeat_on_day) $dow -= 7;
209 $adate['mday'] += ($my_repeat_on_num - 1) * 7 + $my_repeat_on_day - $dow;
211 else { // last weekday of month
212 $adate['mday'] = cal_days_in_month(CAL_GREGORIAN, $adate['mon'], $adate['year']);
213 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN, $adate['mon'], $adate['mday'], $adate['year']));
214 if ($dow < $my_repeat_on_day) $dow += 7;
215 $adate['mday'] += $my_repeat_on_day - $dow;
217 } // end recurrtype 2
219 else { // recurrtype 1
220 if ($repeattype == 0) { // daily
221 $adate['mday'] += 1;
222 } else if ($repeattype == 1) { // weekly
223 $adate['mday'] += 7;
224 } else if ($repeattype == 2) { // monthly
225 $adate['mon'] += 1;
226 } else if ($repeattype == 3) { // yearly
227 $adate['year'] += 1;
228 } else if ($repeattype == 4) { // work days
229 if ($adate['wday'] == 5) // if friday, skip to monday
230 $adate['mday'] += 3;
231 else if ($adate['wday'] == 6) // saturday should not happen
232 $adate['mday'] += 2;
233 else
234 $adate['mday'] += 1;
235 } else {
236 die("Invalid repeat type '" . text($repeattype) ."'");
238 } // end recurrtype 1
240 $thistime = mktime(0, 0, 0, $adate['mon'], $adate['mday'], $adate['year']);
242 } else {
243 doOneDay($row['pc_catid'], $thistime, $row['pc_startTime'],
244 $row['pc_duration'], $row['pc_prefcatid']);
248 // Mark all slots reserved where the provider is not in-office.
249 // Actually we could do this in the display loop instead.
250 $inoffice = false;
251 for ($i = 0; $i < $slotcount; ++$i) {
252 if (($i % $slotsperday) == 0) $inoffice = false;
253 if ($slots[$i] & 1) $inoffice = true;
254 if ($slots[$i] & 2) $inoffice = false;
255 if (! $inoffice) { $slots[$i] |= 4; $prov[$i] = $i; }
259 // The cktime parameter is a number of minutes into the starting day of a
260 // tentative appointment that is to be checked. If it is present then we are
261 // being asked to check if this indicated slot is available, and to submit
262 // the opener and go away quietly if it is. If it's not then we have more
263 // work to do.
264 $ckavail = true;
265 if (isset($_REQUEST['cktime'])) {
266 $cktime = 0 + $_REQUEST['cktime'];
267 $ckindex = (int) ($cktime * 60 / $slotsecs);
268 for ($j = $ckindex; $j < $ckindex + $evslots; ++$j) {
269 if ($slots[$j] >= 4) {
270 $ckavail = false;
271 $isProv = FALSE;
272 if(isset($prov[$j])){
273 $isProv = 'TRUE';
277 if ($ckavail) {
278 // The chosen appointment time is available.
279 echo "<html><script language='JavaScript'>\n";
280 echo "function mytimeout() {\n";
281 echo " opener.top.restoreSession();\n";
282 echo " opener.document.forms[0].submit();\n";
283 echo " window.close();\n";
284 echo "}\n";
285 echo "</script></head><body onload='setTimeout(\"mytimeout()\",250);'>" .
286 xlt('Time slot is open, saving event') . "...</body></html>";
287 exit();
289 // The appointment slot is not available. A message will be displayed
290 // after this page is loaded.
293 <html>
294 <head>
295 <?php html_header_show(); ?>
296 <title><?php echo xlt('Find Available Appointments'); ?></title>
297 <link rel="stylesheet" href='<?php echo $css_header ?>' type='text/css'>
299 <!-- for the pop up calendar -->
300 <style type="text/css">@import url(../../../library/dynarch_calendar.css);</style>
301 <script type="text/javascript" src="../../../library/dynarch_calendar.js"></script>
302 <?php include_once("{$GLOBALS['srcdir']}/dynarch_calendar_en.inc.php"); ?>
303 <script type="text/javascript" src="../../../library/dynarch_calendar_setup.js"></script>
305 <!-- for ajax-y stuff -->
306 <script type="text/javascript" src="<?php echo $GLOBALS['webroot'] ?>/library/js/jquery-1.2.2.min.js"></script>
308 <script language="JavaScript">
310 function setappt(year,mon,mday,hours,minutes) {
311 if (opener.closed || ! opener.setappt)
312 alert('<?php echo xls('The destination form was closed; I cannot act on your selection.'); ?>');
313 else
314 opener.setappt(year,mon,mday,hours,minutes);
315 window.close();
316 return false;
319 </script>
321 <style>
322 form {
323 /* this eliminates the padding normally around a FORM tag */
324 padding: 0px;
325 margin: 0px;
327 #searchCriteria {
328 text-align: center;
329 width: 100%;
330 font-size: 0.8em;
331 background-color: #ddddff;
332 font-weight: bold;
333 padding: 3px;
335 #searchResultsHeader {
336 width: 100%;
337 background-color: lightgrey;
339 #searchResultsHeader table {
340 width: 96%; /* not 100% because the 'searchResults' table has a scrollbar */
341 border-collapse: collapse;
343 #searchResultsHeader th {
344 font-size: 0.7em;
346 #searchResults {
347 width: 100%;
348 height: 350px;
349 overflow: auto;
352 .srDate { width: 20%; }
353 .srTimes { width: 80%; }
355 #searchResults table {
356 width: 100%;
357 border-collapse: collapse;
358 background-color: white;
360 #searchResults td {
361 font-size: 0.7em;
362 border-bottom: 1px solid gray;
363 padding: 1px 5px 1px 5px;
365 .highlight { background-color: #ff9; }
366 .blue_highlight { background-color: #336699; color: white; }
367 #am {
368 border-bottom: 1px solid lightgrey;
369 color: #00c;
371 #pm { color: #c00; }
372 #pm a { color: #c00; }
373 </style>
375 </head>
377 <body class="body_top">
379 <div id="searchCriteria">
380 <form method='post' name='theform' action='find_appt_popup.php?providerid=<?php echo attr($providerid) ?>&catid=<?php echo attr($input_catid) ?>'>
381 <?php echo xlt('Start date:'); ?>
382 <input type='text' name='startdate' id='startdate' size='10' value='<?php echo attr($sdate) ?>'
383 title='<?php echo xla('yyyy-mm-dd starting date for search'); ?> '/>
384 <img src='../../pic/show_calendar.gif' align='absbottom' width='24' height='22'
385 id='img_date' border='0' alt='[?]' style='cursor:pointer'
386 title='<?php echo xla('Click here to choose a date'); ?>'>
387 <?php echo xlt('for'); ?>
388 <input type='text' name='searchdays' size='3' value='<?php echo attr($searchdays) ?>'
389 title='<?php echo xla('Number of days to search from the start date'); ?>' />
390 <?php echo xlt('days'); ?>&nbsp;
391 <input type='submit' value='<?php echo xla('Search'); ?>'>
392 </div>
394 <?php if (!empty($slots)) : ?>
396 <div id="searchResultsHeader">
397 <table>
398 <tr>
399 <th class="srDate"><?php echo xlt('Day'); ?></th>
400 <th class="srTimes"><?php echo xlt('Available Times'); ?></th>
401 </tr>
402 </table>
403 </div>
405 <div id="searchResults">
406 <table>
407 <?php
408 $lastdate = "";
409 $ampmFlag = "am"; // establish an AM-PM line break flag
410 for ($i = 0; $i < $slotcount; ++$i) {
412 $available = true;
413 for ($j = $i; $j < $i + $evslots; ++$j) {
414 if ($slots[$j] >= 4) $available = false;
416 if (!$available) continue; // skip reserved slots
418 $utime = ($slotbase + $i) * $slotsecs;
419 $thisdate = date("Y-m-d", $utime);
420 if ($thisdate != $lastdate) {
421 // if a new day, start a new row
422 if ($lastdate) {
423 echo "</div>";
424 echo "</td>\n";
425 echo " </tr>\n";
427 $lastdate = $thisdate;
428 echo " <tr class='oneresult'>\n";
429 echo " <td class='srDate'>" . date("l", $utime)."<br>".date("Y-m-d", $utime) . "</td>\n";
430 echo " <td class='srTimes'>";
431 echo "<div id='am'>AM ";
432 $ampmFlag = "am"; // reset the AMPM flag
435 $ampm = date('a', $utime);
436 if ($ampmFlag != $ampm) { echo "</div><div id='pm'>PM "; }
437 $ampmFlag = $ampm;
439 $atitle = "Choose ".date("h:i a", $utime);
440 $adate = getdate($utime);
441 $anchor = "<a href='' onclick='return setappt(" .
442 $adate['year'] . "," .
443 $adate['mon'] . "," .
444 $adate['mday'] . "," .
445 $adate['hours'] . "," .
446 $adate['minutes'] . ")'".
447 " title='$atitle' alt='$atitle'".
448 ">";
449 echo (strlen(date('g',$utime)) < 2 ? "<span style='visibility:hidden'>0</span>" : "") .
450 $anchor . date("g:i", $utime) . "</a> ";
452 // If the duration is more than 1 slot, increment $i appropriately.
453 // This is to avoid reporting available times on undesirable boundaries.
454 $i += $evslots - 1;
456 if ($lastdate) {
457 echo "</td>\n";
458 echo " </tr>\n";
459 } else {
460 echo " <tr><td colspan='2'> " . xlt('No openings were found for this period.') . "</td></tr>\n";
463 </table>
464 </div>
465 </div>
466 <?php endif; ?>
468 </form>
469 </body>
471 <!-- for the pop up calendar -->
472 <script language='JavaScript'>
473 Calendar.setup({inputField:"startdate", ifFormat:"%Y-%m-%d", button:"img_date"});
475 // jQuery stuff to make the page a little easier to use
477 $(document).ready(function(){
478 $(".oneresult").mouseover(function() { $(this).toggleClass("highlight"); });
479 $(".oneresult").mouseout(function() { $(this).toggleClass("highlight"); });
480 $(".oneresult a").mouseover(function () { $(this).toggleClass("blue_highlight"); $(this).children().toggleClass("blue_highlight"); });
481 $(".oneresult a").mouseout(function() { $(this).toggleClass("blue_highlight"); $(this).children().toggleClass("blue_highlight"); });
482 //$(".event").dblclick(function() { EditEvent(this); });
486 <?php if (!$ckavail) { ?>
487 <?php if (acl_check('patients','appt','','write')) {
488 if($isProv): ?>
489 if (confirm('<?php echo xls('Provider not available, use it anyway?'); ?>')) {
490 <?php else: ?>
491 if (confirm('<?php echo xls('This appointment slot is already used, use it anyway?'); ?>')) {
492 <?php endif; ?>
493 opener.top.restoreSession();
494 opener.document.forms[0].submit();
495 window.close();
497 <?php } else {
498 if($isProv): ?>
499 alert('<?php echo xls('Provider not available, please choose another.'); ?>');
500 <?php else: ?>
501 alert('<?php echo xls('This appointment slot is already used, please choose another.'); ?>');
502 <?php endif; ?>
503 <?php } ?>
504 <?php } ?>
506 </script>
508 </html>