Additional mods to previous commit (still discussing UTF8 enforcing)
[openemr.git] / patients / find_appt_popup_user.php
blob54a260d3145b8239724bc38f80811bbf31d15d47
1 <?php
2 // Copyright (C) 2005-2006, 2013 Rod Roark <rod@sunsetsystems.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 // Note from Rod 2013-01-22:
10 // This module needs to be refactored to share the same code that is in
11 // interface/main/calendar/find_appt_popup.php. It contains an old version
12 // of that logic and does not support exception dates for repeating events.
14 //continue session
15 session_start();
18 //landing page definition -- where to go if something goes wrong
19 $landingpage = "index.php?site=".$_SESSION['site_id'];
22 // kick out if patient not authenticated
23 if ( isset($_SESSION['pid']) && isset($_SESSION['patient_portal_onsite']) ) {
24 $pid = $_SESSION['pid'];
26 else {
27 session_destroy();
28 header('Location: '.$landingpage.'&w');
29 exit;
33 $ignoreAuth = 1;
35 include_once("../interface/globals.php");
36 include_once("$srcdir/patient.inc");
38 // Exit if the modify calendar for portal flag is not set
39 if (!($GLOBALS['portal_onsite_appt_modify'])) {
40 echo htmlspecialchars( xl('You are not authorized to schedule appointments.'),ENT_NOQUOTES);
41 exit;
44 $input_catid = $_REQUEST['catid'];
46 // Record an event into the slots array for a specified day.
47 function doOneDay($catid, $udate, $starttime, $duration, $prefcatid) {
48 global $slots, $slotsecs, $slotstime, $slotbase, $slotcount, $input_catid;
49 $udate = strtotime($starttime, $udate);
50 if ($udate < $slotstime) return;
51 $i = (int) ($udate / $slotsecs) - $slotbase;
52 $iend = (int) (($duration + $slotsecs - 1) / $slotsecs) + $i;
53 if ($iend > $slotcount) $iend = $slotcount;
54 if ($iend <= $i) $iend = $i + 1;
55 for (; $i < $iend; ++$i) {
56 if ($catid == 2) { // in office
57 // If a category ID was specified when this popup was invoked, then select
58 // only IN events with a matching preferred category or with no preferred
59 // category; other IN events are to be treated as OUT events.
60 if ($input_catid) {
61 if ($prefcatid == $input_catid || !$prefcatid)
62 $slots[$i] |= 1;
63 else
64 $slots[$i] |= 2;
65 } else {
66 $slots[$i] |= 1;
68 break; // ignore any positive duration for IN
69 } else if ($catid == 3) { // out of office
70 $slots[$i] |= 2;
71 break; // ignore any positive duration for OUT
72 } else { // all other events reserve time
73 $slots[$i] |= 4;
78 // seconds per time slot
79 $slotsecs = $GLOBALS['calendar_interval'] * 60;
81 $catslots = 1;
82 if ($input_catid) {
83 $srow = sqlQuery("SELECT pc_duration FROM openemr_postcalendar_categories WHERE pc_catid = '$input_catid'");
84 if ($srow['pc_duration']) $catslots = ceil($srow['pc_duration'] / $slotsecs);
87 $info_msg = "";
89 $searchdays = 7; // default to a 1-week lookahead
90 if ($_REQUEST['searchdays']) $searchdays = $_REQUEST['searchdays'];
92 // Get a start date.
93 if ($_REQUEST['startdate'] && preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/",
94 $_REQUEST['startdate'], $matches))
96 $sdate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
97 } else {
98 $sdate = date("Y-m-d");
101 // Get an end date - actually the date after the end date.
102 preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/", $sdate, $matches);
103 $edate = date("Y-m-d",
104 mktime(0, 0, 0, $matches[2], $matches[3] + $searchdays, $matches[1]));
106 // compute starting time slot number and number of slots.
107 $slotstime = strtotime("$sdate 00:00:00");
108 $slotetime = strtotime("$edate 00:00:00");
109 $slotbase = (int) ($slotstime / $slotsecs);
110 $slotcount = (int) ($slotetime / $slotsecs) - $slotbase;
112 if ($slotcount <= 0 || $slotcount > 100000) die("Invalid date range.");
114 $slotsperday = (int) (60 * 60 * 24 / $slotsecs);
116 // If we have a provider, search.
118 if ($_REQUEST['providerid']) {
119 $providerid = $_REQUEST['providerid'];
121 // Create and initialize the slot array. Values are bit-mapped:
122 // bit 0 = in-office occurs here
123 // bit 1 = out-of-office occurs here
124 // bit 2 = reserved
125 // So, values may range from 0 to 7.
127 $slots = array_pad(array(), $slotcount, 0);
129 // Note there is no need to sort the query results.
130 // echo $sdate." -- ".$edate;
131 $query = "SELECT pc_eventDate, pc_endDate, pc_startTime, pc_duration, " .
132 "pc_recurrtype, pc_recurrspec, pc_alldayevent, pc_catid, pc_prefcatid, pc_title " .
133 "FROM openemr_postcalendar_events " .
134 "WHERE pc_aid = '$providerid' AND " .
135 "((pc_endDate >= '$sdate' AND pc_eventDate < '$edate') OR " .
136 "(pc_endDate = '0000-00-00' AND pc_eventDate >= '$sdate' AND pc_eventDate < '$edate'))";
137 $res = sqlStatement($query);
138 // print_r($res);
140 while ($row = sqlFetchArray($res)) {
141 $thistime = strtotime($row['pc_eventDate'] . " 00:00:00");
142 if ($row['pc_recurrtype']) {
144 preg_match('/"event_repeat_freq_type";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
145 $repeattype = $matches[1];
147 preg_match('/"event_repeat_freq";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
148 $repeatfreq = $matches[1];
149 if ($row['pc_recurrtype'] == 2) {
150 // Repeat type is 2 so frequency comes from event_repeat_on_freq.
151 preg_match('/"event_repeat_on_freq";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
152 $repeatfreq = $matches[1];
154 if (! $repeatfreq) $repeatfreq = 1;
156 preg_match('/"event_repeat_on_num";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
157 $my_repeat_on_num = $matches[1];
159 preg_match('/"event_repeat_on_day";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
160 $my_repeat_on_day = $matches[1];
162 $endtime = strtotime($row['pc_endDate'] . " 00:00:00") + (24 * 60 * 60);
163 if ($endtime > $slotetime) $endtime = $slotetime;
165 $repeatix = 0;
166 while ($thistime < $endtime) {
168 // Skip the event if a repeat frequency > 1 was specified and this is
169 // not the desired occurrence.
170 if (! $repeatix) {
171 doOneDay($row['pc_catid'], $thistime, $row['pc_startTime'],
172 $row['pc_duration'], $row['pc_prefcatid']);
174 if (++$repeatix >= $repeatfreq) $repeatix = 0;
176 $adate = getdate($thistime);
178 if ($row['pc_recurrtype'] == 2) {
179 // Need to skip to nth or last weekday of the next month.
180 $adate['mon'] += 1;
181 if ($adate['mon'] > 12) {
182 $adate['year'] += 1;
183 $adate['mon'] -= 12;
185 if ($my_repeat_on_num < 5) { // not last
186 $adate['mday'] = 1;
187 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN, $adate['mon'], $adate['mday'], $adate['year']));
188 if ($dow > $my_repeat_on_day) $dow -= 7;
189 $adate['mday'] += ($my_repeat_on_num - 1) * 7 + $my_repeat_on_day - $dow;
191 else { // last weekday of month
192 $adate['mday'] = cal_days_in_month(CAL_GREGORIAN, $adate['mon'], $adate['year']);
193 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN, $adate['mon'], $adate['mday'], $adate['year']));
194 if ($dow < $my_repeat_on_day) $dow += 7;
195 $adate['mday'] += $my_repeat_on_day - $dow;
197 } // end recurrtype 2
199 else { // recurrtype 1
201 if ($repeattype == 0) { // daily
202 $adate['mday'] += 1;
203 } else if ($repeattype == 1) { // weekly
204 $adate['mday'] += 7;
205 } else if ($repeattype == 2) { // monthly
206 $adate['mon'] += 1;
207 } else if ($repeattype == 3) { // yearly
208 $adate['year'] += 1;
209 } else if ($repeattype == 4) { // work days
210 if ($adate['wday'] == 5) // if friday, skip to monday
211 $adate['mday'] += 3;
212 else if ($adate['wday'] == 6) // saturday should not happen
213 $adate['mday'] += 2;
214 else
215 $adate['mday'] += 1;
216 } else if ($repeattype == 5) { // monday
217 $adate['mday'] += 7;
218 } else if ($repeattype == 6) { // tuesday
219 $adate['mday'] += 7;
220 } else if ($repeattype == 7) { // wednesday
221 $adate['mday'] += 7;
222 } else if ($repeattype == 8) { // thursday
223 $adate['mday'] += 7;
224 } else if ($repeattype == 9) { // friday
225 $adate['mday'] += 7;
226 } else {
227 die("Invalid repeat type '$repeattype'");
230 } // end recurrtype 1
232 $thistime = mktime(0, 0, 0, $adate['mon'], $adate['mday'], $adate['year']);
234 } else {
235 doOneDay($row['pc_catid'], $thistime, $row['pc_startTime'],
236 $row['pc_duration'], $row['pc_prefcatid']);
240 // Mark all slots reserved where the provider is not in-office.
241 // Actually we could do this in the display loop instead.
242 $inoffice = false;
243 for ($i = 0; $i < $slotcount; ++$i) {
244 if (($i % $slotsperday) == 0) $inoffice = false;
245 if ($slots[$i] & 1) $inoffice = true;
246 if ($slots[$i] & 2) $inoffice = false;
247 if (! $inoffice) $slots[$i] |= 4;
251 <html>
252 <head>
253 <?php html_header_show(); ?>
254 <title><?php xl('Find Available Appointments','e'); ?></title>
255 <link rel="stylesheet" href='<?php echo $css_header ?>' type='text/css'>
257 <!-- for the pop up calendar -->
258 <style type="text/css">@import url(../library/dynarch_calendar.css);</style>
259 <script type="text/javascript" src="../library/dynarch_calendar.js"></script>
260 <script type="text/javascript" src="../library/dynarch_calendar_en.js"></script>
261 <script type="text/javascript" src="../library/dynarch_calendar_setup.js"></script>
263 <!-- for ajax-y stuff -->
264 <script type="text/javascript" src="<?php echo $GLOBALS['webroot'] ?>/library/js/jquery-1.2.2.min.js"></script>
266 <script language="JavaScript">
268 function setappt(year,mon,mday,hours,minutes) {
269 if (opener.closed || ! opener.setappt)
270 alert('<?php xl('The destination form was closed; I cannot act on your selection.','e'); ?>');
271 else
272 opener.setappt(year,mon,mday,hours,minutes);
273 window.close();
274 return false;
277 </script>
280 <style>
281 form {
282 /* this eliminates the padding normally around a FORM tag */
283 padding: 0px;
284 margin: 0px;
286 #searchCriteria {
287 text-align: center;
288 width: 100%;
289 font-size: 0.8em;
290 background-color: #ddddff;
291 font-weight: bold;
292 padding: 3px;
294 #searchResultsHeader {
295 width: 100%;
296 background-color: lightgrey;
298 #searchResultsHeader table {
299 width: 96%; /* not 100% because the 'searchResults' table has a scrollbar */
300 border-collapse: collapse;
302 #searchResultsHeader th {
303 font-size: 0.7em;
305 #searchResults {
306 width: 100%;
307 height: 350px;
308 overflow: auto;
311 .srDate { width: 20%; }
312 .srTimes { width: 80%; }
314 #searchResults table {
315 width: 100%;
316 border-collapse: collapse;
317 background-color: white;
319 #searchResults td {
320 font-size: 0.7em;
321 border-bottom: 1px solid gray;
322 padding: 1px 5px 1px 5px;
324 .highlight { background-color: #ff9; }
325 .blue_highlight { background-color: #336699; color: white; }
326 #am {
327 border-bottom: 1px solid lightgrey;
328 color: #00c;
330 #pm { color: #c00; }
331 #pm a { color: #c00; }
332 </style>
334 </head>
336 <body class="body_top">
338 <div id="searchCriteria">
339 <form method='post' name='theform' action='find_appt_popup.php?providerid=<?php echo $providerid ?>&catid=<?php echo $input_catid ?>'>
340 <input type="hidden" name='bypatient' />
342 <?php xl('Start date:','e'); ?>
345 <input type='text' name='startdate' id='startdate' size='10' value='<?php echo $sdate ?>'
346 title='yyyy-mm-dd starting date for search'/>
348 <img src='../interface/pic/show_calendar.gif' align='absbottom' width='24' height='22'
349 id='img_date' border='0' alt='[?]' style='cursor:pointer'
350 title='<?php xl('Click here to choose a date','e'); ?>'>
353 <?php xl('for','e'); ?>
354 <input type='text' name='searchdays' size='3' value='<?php echo $searchdays ?>'
355 title='Number of days to search from the start date' />
356 <?php xl('days','e'); ?>&nbsp;
357 <input type='submit' value='<?php xl('Search','e'); ?>'>
358 </div>
360 <?php if (!empty($slots)) : ?>
362 <div id="searchResultsHeader">
363 <table>
364 <tr>
365 <th class="srDate"><?php xl ('Day','e'); ?></th>
366 <th class="srTimes"><?php xl ('Available Times','e'); ?></th>
367 </tr>
368 </table>
369 </div>
371 <div id="searchResults">
372 <table>
373 <?php
374 $lastdate = "";
375 $ampmFlag = "am"; // establish an AM-PM line break flag
376 for ($i = 0; $i < $slotcount; ++$i) {
378 $available = true;
379 for ($j = $i; $j < $i + $catslots; ++$j) {
380 if ($slots[$j] >= 4) $available = false;
382 if (!$available) continue; // skip reserved slots
384 $utime = ($slotbase + $i) * $slotsecs;
385 $thisdate = date("Y-m-d", $utime);
386 if ($thisdate != $lastdate) {
387 // if a new day, start a new row
388 if ($lastdate) {
389 echo "</div>";
390 echo "</td>\n";
391 echo " </tr>\n";
393 $lastdate = $thisdate;
394 echo " <tr class='oneresult'>\n";
395 echo " <td class='srDate'>" . date("l", $utime)."<br>".date("Y-m-d", $utime) . "</td>\n";
396 echo " <td class='srTimes'>";
397 echo "<div id='am'>AM ";
398 $ampmFlag = "am"; // reset the AMPM flag
401 $ampm = date('a', $utime);
402 if ($ampmFlag != $ampm) { echo "</div><div id='pm'>PM "; }
403 $ampmFlag = $ampm;
405 $atitle = "Choose ".date("h:i a", $utime);
406 $adate = getdate($utime);
407 $anchor = "<a href='' onclick='return setappt(" .
408 $adate['year'] . "," .
409 $adate['mon'] . "," .
410 $adate['mday'] . "," .
411 $adate['hours'] . "," .
412 $adate['minutes'] . ")'".
413 " title='$atitle' alt='$atitle'".
414 ">";
415 echo (strlen(date('g',$utime)) < 2 ? "<span style='visibility:hidden'>0</span>" : "") .
416 $anchor . date("g:i", $utime) . "</a> ";
418 // If category duration is more than 1 slot, increment $i appropriately.
419 // This is to avoid reporting available times on undesirable boundaries.
420 $i += $catslots - 1;
422 if ($lastdate) {
423 echo "</td>\n";
424 echo " </tr>\n";
425 } else {
426 echo " <tr><td colspan='2'> " . xl('No openings were found for this period.','e') . "</td></tr>\n";
429 </table>
430 </div>
431 </div>
432 <?php endif; ?>
434 </form>
435 </body>
437 <!-- for the pop up calendar -->
438 <script language='JavaScript'>
439 Calendar.setup({inputField:"startdate", ifFormat:"%Y-%m-%d", button:"img_date"});
441 // jQuery stuff to make the page a little easier to use
443 $(document).ready(function(){
444 $(".oneresult").mouseover(function() { $(this).toggleClass("highlight"); });
445 $(".oneresult").mouseout(function() { $(this).toggleClass("highlight"); });
446 $(".oneresult a").mouseover(function () { $(this).toggleClass("blue_highlight"); $(this).children().toggleClass("blue_highlight"); });
447 $(".oneresult a").mouseout(function() { $(this).toggleClass("blue_highlight"); $(this).children().toggleClass("blue_highlight"); });
448 //$(".event").dblclick(function() { EditEvent(this); });
451 </script>
453 </html>