2 // Copyright (C) 2005-2013 Rod Roark <rod@sunsetsystems.com>
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 include_once("../../globals.php");
10 include_once("$srcdir/patient.inc");
12 // check access controls
13 if (!acl_check('patients','appt','',array('write','wsome') ))
14 die(xl('Access not allowed'));
16 // If the caller is updating an existing event, then get its ID so
17 // we don't count it as a reserved time slot.
18 $eid = empty($_REQUEST['eid']) ?
0 : 0 +
$_REQUEST['eid'];
20 $input_catid = $_REQUEST['catid'];
22 // Record an event into the slots array for a specified day.
23 function doOneDay($catid, $udate, $starttime, $duration, $prefcatid) {
24 global $slots, $slotsecs, $slotstime, $slotbase, $slotcount, $input_catid;
25 $udate = strtotime($starttime, $udate);
26 if ($udate < $slotstime) return;
27 $i = (int) ($udate / $slotsecs) - $slotbase;
28 $iend = (int) (($duration +
$slotsecs - 1) / $slotsecs) +
$i;
29 if ($iend > $slotcount) $iend = $slotcount;
30 if ($iend <= $i) $iend = $i +
1;
31 for (; $i < $iend; ++
$i) {
32 if ($catid == 2) { // in office
33 // If a category ID was specified when this popup was invoked, then select
34 // only IN events with a matching preferred category or with no preferred
35 // category; other IN events are to be treated as OUT events.
37 if ($prefcatid == $input_catid ||
!$prefcatid)
44 break; // ignore any positive duration for IN
45 } else if ($catid == 3) { // out of office
47 break; // ignore any positive duration for OUT
48 } else { // all other events reserve time
54 // seconds per time slot
55 $slotsecs = $GLOBALS['calendar_interval'] * 60;
59 $srow = sqlQuery("SELECT pc_duration FROM openemr_postcalendar_categories WHERE pc_catid = '$input_catid'");
60 if ($srow['pc_duration']) $catslots = ceil($srow['pc_duration'] / $slotsecs);
65 $searchdays = 7; // default to a 1-week lookahead
66 if ($_REQUEST['searchdays']) $searchdays = $_REQUEST['searchdays'];
69 if ($_REQUEST['startdate'] && preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/",
70 $_REQUEST['startdate'], $matches))
72 $sdate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
74 $sdate = date("Y-m-d");
77 // Get an end date - actually the date after the end date.
78 preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/", $sdate, $matches);
79 $edate = date("Y-m-d",
80 mktime(0, 0, 0, $matches[2], $matches[3] +
$searchdays, $matches[1]));
82 // compute starting time slot number and number of slots.
83 $slotstime = strtotime("$sdate 00:00:00");
84 $slotetime = strtotime("$edate 00:00:00");
85 $slotbase = (int) ($slotstime / $slotsecs);
86 $slotcount = (int) ($slotetime / $slotsecs) - $slotbase;
88 if ($slotcount <= 0 ||
$slotcount > 100000) die("Invalid date range.");
90 $slotsperday = (int) (60 * 60 * 24 / $slotsecs);
92 // Compute the number of time slots for the given event duration, or if
93 // none is given then assume the default category duration.
95 if (isset($_REQUEST['evdur'])) {
96 $evslots = 60 * $_REQUEST['evdur'];
97 $evslots = (int) (($evslots +
$slotsecs - 1) / $slotsecs);
100 // If we have a provider, search.
102 if ($_REQUEST['providerid']) {
103 $providerid = $_REQUEST['providerid'];
105 // Create and initialize the slot array. Values are bit-mapped:
106 // bit 0 = in-office occurs here
107 // bit 1 = out-of-office occurs here
109 // So, values may range from 0 to 7.
111 $slots = array_pad(array(), $slotcount, 0);
113 // Note there is no need to sort the query results.
114 $query = "SELECT pc_eventDate, pc_endDate, pc_startTime, pc_duration, " .
115 "pc_recurrtype, pc_recurrspec, pc_alldayevent, pc_catid, pc_prefcatid " .
116 "FROM openemr_postcalendar_events " .
117 "WHERE pc_aid = '$providerid' AND " .
118 "pc_eid != '$eid' AND " .
119 "((pc_endDate >= '$sdate' AND pc_eventDate < '$edate') OR " .
120 "(pc_endDate = '0000-00-00' AND pc_eventDate >= '$sdate' AND pc_eventDate < '$edate'))";
121 // phyaura whimmel facility filtering
122 if ($_REQUEST['facility'] > 0 ) {
123 $facility = $_REQUEST['facility'];
124 $query .= " AND pc_facility = $facility";
126 // end facility filtering whimmel 29apr08
127 $res = sqlStatement($query);
129 while ($row = sqlFetchArray($res)) {
130 $thistime = strtotime($row['pc_eventDate'] . " 00:00:00");
131 if ($row['pc_recurrtype']) {
133 preg_match('/"event_repeat_freq_type";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
134 $repeattype = $matches[1];
136 preg_match('/"event_repeat_freq";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
137 $repeatfreq = $matches[1];
138 if ($row['pc_recurrtype'] == 2) {
139 // Repeat type is 2 so frequency comes from event_repeat_on_freq.
140 preg_match('/"event_repeat_on_freq";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
141 $repeatfreq = $matches[1];
143 if (! $repeatfreq) $repeatfreq = 1;
145 preg_match('/"event_repeat_on_num";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
146 $my_repeat_on_num = $matches[1];
148 preg_match('/"event_repeat_on_day";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
149 $my_repeat_on_day = $matches[1];
151 // This gets an array of exception dates for the event.
153 if (preg_match('/"exdate";s:\d+:"([0-9,]*)"/', $row['pc_recurrspec'], $matches)) {
154 $exdates = explode(",", $matches[1]);
157 $endtime = strtotime($row['pc_endDate'] . " 00:00:00") +
(24 * 60 * 60);
158 if ($endtime > $slotetime) $endtime = $slotetime;
161 while ($thistime < $endtime) {
162 $adate = getdate($thistime);
163 $thisymd = sprintf('%04d%02d%02d', $adate['year'], $adate['mon'], $adate['mday']);
165 // Skip the event if a repeat frequency > 1 was specified and this is
166 // not the desired occurrence, or if this date is in the exception array.
167 if (!$repeatix && !in_array($thisymd, $exdates)) {
168 doOneDay($row['pc_catid'], $thistime, $row['pc_startTime'],
169 $row['pc_duration'], $row['pc_prefcatid']);
171 if (++
$repeatix >= $repeatfreq) $repeatix = 0;
173 if ($row['pc_recurrtype'] == 2) {
174 // Need to skip to nth or last weekday of the next month.
176 if ($adate['mon'] > 12) {
180 if ($my_repeat_on_num < 5) { // not last
182 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN
, $adate['mon'], $adate['mday'], $adate['year']));
183 if ($dow > $my_repeat_on_day) $dow -= 7;
184 $adate['mday'] +
= ($my_repeat_on_num - 1) * 7 +
$my_repeat_on_day - $dow;
186 else { // last weekday of month
187 $adate['mday'] = cal_days_in_month(CAL_GREGORIAN
, $adate['mon'], $adate['year']);
188 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN
, $adate['mon'], $adate['mday'], $adate['year']));
189 if ($dow < $my_repeat_on_day) $dow +
= 7;
190 $adate['mday'] +
= $my_repeat_on_day - $dow;
192 } // end recurrtype 2
194 else { // recurrtype 1
195 if ($repeattype == 0) { // daily
197 } else if ($repeattype == 1) { // weekly
199 } else if ($repeattype == 2) { // monthly
201 } else if ($repeattype == 3) { // yearly
203 } else if ($repeattype == 4) { // work days
204 if ($adate['wday'] == 5) // if friday, skip to monday
206 else if ($adate['wday'] == 6) // saturday should not happen
211 die("Invalid repeat type '$repeattype'");
213 } // end recurrtype 1
215 $thistime = mktime(0, 0, 0, $adate['mon'], $adate['mday'], $adate['year']);
218 doOneDay($row['pc_catid'], $thistime, $row['pc_startTime'],
219 $row['pc_duration'], $row['pc_prefcatid']);
223 // Mark all slots reserved where the provider is not in-office.
224 // Actually we could do this in the display loop instead.
226 for ($i = 0; $i < $slotcount; ++
$i) {
227 if (($i %
$slotsperday) == 0) $inoffice = false;
228 if ($slots[$i] & 1) $inoffice = true;
229 if ($slots[$i] & 2) $inoffice = false;
230 if (! $inoffice) $slots[$i] |
= 4;
234 // The cktime parameter is a number of minutes into the starting day of a
235 // tentative appointment that is to be checked. If it is present then we are
236 // being asked to check if this indicated slot is available, and to submit
237 // the opener and go away quietly if it is. If it's not then we have more
240 if (isset($_REQUEST['cktime'])) {
241 $cktime = 0 +
$_REQUEST['cktime'];
242 $ckindex = (int) ($cktime * 60 / $slotsecs);
243 for ($j = $ckindex; $j < $ckindex +
$evslots; ++
$j) {
244 if ($slots[$j] >= 4) $ckavail = false;
247 // The chosen appointment time is available.
248 echo "<html><script language='JavaScript'>\n";
249 echo "function mytimeout() {\n";
250 echo " opener.top.restoreSession();\n";
251 echo " opener.document.forms[0].submit();\n";
252 echo " window.close();\n";
254 echo "</script></head><body onload='setTimeout(\"mytimeout()\",250);'>" .
255 xlt('Time slot is open, saving event') . "...</body></html>";
258 // The appointment slot is not available. A message will be displayed
259 // after this page is loaded.
264 <?php
html_header_show(); ?
>
265 <title
><?php
xl('Find Available Appointments','e'); ?
></title
>
266 <link rel
="stylesheet" href
='<?php echo $css_header ?>' type
='text/css'>
268 <!-- for the pop up calendar
-->
269 <style type
="text/css">@import
url(../../../library
/dynarch_calendar
.css
);</style
>
270 <script type
="text/javascript" src
="../../../library/dynarch_calendar.js"></script
>
271 <?php
include_once("{$GLOBALS['srcdir']}/dynarch_calendar_en.inc.php"); ?
>
272 <script type
="text/javascript" src
="../../../library/dynarch_calendar_setup.js"></script
>
274 <!-- for ajax
-y stuff
-->
275 <script type
="text/javascript" src
="<?php echo $GLOBALS['webroot'] ?>/library/js/jquery-1.2.2.min.js"></script
>
277 <script language
="JavaScript">
279 function setappt(year
,mon
,mday
,hours
,minutes
) {
280 if (opener
.closed ||
! opener
.setappt
)
281 alert('<?php xl('The destination form was closed
; I cannot act on your selection
.','e
'); ?>');
283 opener
.setappt(year
,mon
,mday
,hours
,minutes
);
292 /* this eliminates the padding normally around a FORM tag */
300 background
-color
: #ddddff;
304 #searchResultsHeader {
306 background
-color
: lightgrey
;
308 #searchResultsHeader table {
309 width
: 96%
; /* not 100% because the 'searchResults' table has a scrollbar */
310 border
-collapse
: collapse
;
312 #searchResultsHeader th {
321 .srDate
{ width
: 20%
; }
322 .srTimes
{ width
: 80%
; }
324 #searchResults table {
326 border
-collapse
: collapse
;
327 background
-color
: white
;
331 border
-bottom
: 1px solid gray
;
332 padding
: 1px
5px
1px
5px
;
334 .highlight
{ background
-color
: #ff9; }
335 .blue_highlight
{ background
-color
: #336699; color: white; }
337 border
-bottom
: 1px solid lightgrey
;
341 #pm a { color: #c00; }
346 <body
class="body_top">
348 <div id
="searchCriteria">
349 <form method
='post' name
='theform' action
='find_appt_popup.php?providerid=<?php echo $providerid ?>&catid=<?php echo $input_catid ?>'>
350 <?php
xl('Start date:','e'); ?
>
351 <input type
='text' name
='startdate' id
='startdate' size
='10' value
='<?php echo $sdate ?>'
352 title
='yyyy-mm-dd starting date for search' />
353 <img src
='../../pic/show_calendar.gif' align
='absbottom' width
='24' height
='22'
354 id
='img_date' border
='0' alt
='[?]' style
='cursor:pointer'
355 title
='<?php xl('Click here to choose a date
','e
'); ?>'>
356 <?php
xl('for','e'); ?
>
357 <input type
='text' name
='searchdays' size
='3' value
='<?php echo $searchdays ?>'
358 title
='Number of days to search from the start date' />
359 <?php
xl('days','e'); ?
> 
;
360 <input type
='submit' value
='<?php xl('Search
','e
'); ?>'>
363 <?php
if (!empty($slots)) : ?
>
365 <div id
="searchResultsHeader">
368 <th
class="srDate"><?php
xl ('Day','e'); ?
></th
>
369 <th
class="srTimes"><?php
xl ('Available Times','e'); ?
></th
>
374 <div id
="searchResults">
378 $ampmFlag = "am"; // establish an AM-PM line break flag
379 for ($i = 0; $i < $slotcount; ++
$i) {
382 for ($j = $i; $j < $i +
$evslots; ++
$j) {
383 if ($slots[$j] >= 4) $available = false;
385 if (!$available) continue; // skip reserved slots
387 $utime = ($slotbase +
$i) * $slotsecs;
388 $thisdate = date("Y-m-d", $utime);
389 if ($thisdate != $lastdate) {
390 // if a new day, start a new row
396 $lastdate = $thisdate;
397 echo " <tr class='oneresult'>\n";
398 echo " <td class='srDate'>" . date("l", $utime)."<br>".date("Y-m-d", $utime) . "</td>\n";
399 echo " <td class='srTimes'>";
400 echo "<div id='am'>AM ";
401 $ampmFlag = "am"; // reset the AMPM flag
404 $ampm = date('a', $utime);
405 if ($ampmFlag != $ampm) { echo "</div><div id='pm'>PM "; }
408 $atitle = "Choose ".date("h:i a", $utime);
409 $adate = getdate($utime);
410 $anchor = "<a href='' onclick='return setappt(" .
411 $adate['year'] . "," .
412 $adate['mon'] . "," .
413 $adate['mday'] . "," .
414 $adate['hours'] . "," .
415 $adate['minutes'] . ")'".
416 " title='$atitle' alt='$atitle'".
418 echo (strlen(date('g',$utime)) < 2 ?
"<span style='visibility:hidden'>0</span>" : "") .
419 $anchor . date("g:i", $utime) . "</a> ";
421 // If the duration is more than 1 slot, increment $i appropriately.
422 // This is to avoid reporting available times on undesirable boundaries.
429 echo " <tr><td colspan='2'> " . xl('No openings were found for this period.','e') . "</td></tr>\n";
440 <!-- for the pop up calendar
-->
441 <script language
='JavaScript'>
442 Calendar
.setup({inputField
:"startdate", ifFormat
:"%Y-%m-%d", button
:"img_date"});
444 // jQuery stuff to make the page a little easier to use
446 $
(document
).ready(function(){
447 $
(".oneresult").mouseover(function() { $
(this
).toggleClass("highlight"); });
448 $
(".oneresult").mouseout(function() { $
(this
).toggleClass("highlight"); });
449 $
(".oneresult a").mouseover(function () { $
(this
).toggleClass("blue_highlight"); $
(this
).children().toggleClass("blue_highlight"); });
450 $
(".oneresult a").mouseout(function() { $
(this
).toggleClass("blue_highlight"); $
(this
).children().toggleClass("blue_highlight"); });
451 //$(".event").dblclick(function() { EditEvent(this); });
454 <?php
if (!$ckavail) { ?
>
455 <?php
if (acl_check('patients','appt','','write')) { ?
>
456 if (confirm('<?php echo addslashes(xl('This appointment slot is already used
, use it anyway?
')); ?>')) {
457 opener
.top
.restoreSession();
458 opener
.document
.forms
[0].submit();
462 alert('<?php echo addslashes(xl('This appointment slot is not available
, please choose another
.')); ?>');