2 // Copyright (C) 2005-2006, 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 // 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.
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'];
27 header('Location: '.$landingpage.'&w');
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
);
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)
49 global $slots, $slotsecs, $slotstime, $slotbase, $slotcount, $input_catid;
50 $udate = strtotime($starttime, $udate);
51 if ($udate < $slotstime) {
55 $i = (int) ($udate / $slotsecs) - $slotbase;
56 $iend = (int) (($duration +
$slotsecs - 1) / $slotsecs) +
$i;
57 if ($iend > $slotcount) {
65 for (; $i < $iend; ++
$i) {
66 if ($catid == 2) { // in office
67 // If a category ID was specified when this popup was invoked, then select
68 // only IN events with a matching preferred category or with no preferred
69 // category; other IN events are to be treated as OUT events.
71 if ($prefcatid == $input_catid ||
!$prefcatid) {
80 break; // ignore any positive duration for IN
81 } else if ($catid == 3) { // out of office
83 break; // ignore any positive duration for OUT
84 } else { // all other events reserve time
90 // seconds per time slot
91 $slotsecs = $GLOBALS['calendar_interval'] * 60;
95 $srow = sqlQuery("SELECT pc_duration FROM openemr_postcalendar_categories WHERE pc_catid = '$input_catid'");
96 if ($srow['pc_duration']) {
97 $catslots = ceil($srow['pc_duration'] / $slotsecs);
103 $searchdays = 7; // default to a 1-week lookahead
104 if ($_REQUEST['searchdays']) {
105 $searchdays = $_REQUEST['searchdays'];
109 if ($_REQUEST['startdate'] && preg_match(
110 "/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/",
111 $_REQUEST['startdate'],
114 $sdate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
116 $sdate = date("Y-m-d");
119 // Get an end date - actually the date after the end date.
120 preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/", $sdate, $matches);
123 mktime(0, 0, 0, $matches[2], $matches[3] +
$searchdays, $matches[1])
126 // compute starting time slot number and number of slots.
127 $slotstime = strtotime("$sdate 00:00:00");
128 $slotetime = strtotime("$edate 00:00:00");
129 $slotbase = (int) ($slotstime / $slotsecs);
130 $slotcount = (int) ($slotetime / $slotsecs) - $slotbase;
132 if ($slotcount <= 0 ||
$slotcount > 100000) {
133 die("Invalid date range");
136 $slotsperday = (int) (60 * 60 * 24 / $slotsecs);
138 // If we have a provider, search.
140 if ($_REQUEST['providerid']) {
141 $providerid = $_REQUEST['providerid'];
143 // Create and initialize the slot array. Values are bit-mapped:
144 // bit 0 = in-office occurs here
145 // bit 1 = out-of-office occurs here
147 // So, values may range from 0 to 7.
149 $slots = array_pad(array(), $slotcount, 0);
151 // Note there is no need to sort the query results.
152 // echo $sdate." -- ".$edate;
153 $query = "SELECT pc_eventDate, pc_endDate, pc_startTime, pc_duration, " .
154 "pc_recurrtype, pc_recurrspec, pc_alldayevent, pc_catid, pc_prefcatid, pc_title " .
155 "FROM openemr_postcalendar_events " .
156 "WHERE pc_aid = '$providerid' AND " .
157 "((pc_endDate >= '$sdate' AND pc_eventDate < '$edate') OR " .
158 "(pc_endDate = '0000-00-00' AND pc_eventDate >= '$sdate' AND pc_eventDate < '$edate'))";
159 $res = sqlStatement($query);
162 while ($row = sqlFetchArray($res)) {
163 $thistime = strtotime($row['pc_eventDate'] . " 00:00:00");
164 if ($row['pc_recurrtype']) {
165 preg_match('/"event_repeat_freq_type";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
166 $repeattype = $matches[1];
168 preg_match('/"event_repeat_freq";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
169 $repeatfreq = $matches[1];
170 if ($row['pc_recurrtype'] == 2) {
171 // Repeat type is 2 so frequency comes from event_repeat_on_freq.
172 preg_match('/"event_repeat_on_freq";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
173 $repeatfreq = $matches[1];
180 preg_match('/"event_repeat_on_num";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
181 $my_repeat_on_num = $matches[1];
183 preg_match('/"event_repeat_on_day";s:1:"(\d)"/', $row['pc_recurrspec'], $matches);
184 $my_repeat_on_day = $matches[1];
186 $endtime = strtotime($row['pc_endDate'] . " 00:00:00") +
(24 * 60 * 60);
187 if ($endtime > $slotetime) {
188 $endtime = $slotetime;
192 while ($thistime < $endtime) {
193 // Skip the event if a repeat frequency > 1 was specified and this is
194 // not the desired occurrence.
199 $row['pc_startTime'],
205 if (++
$repeatix >= $repeatfreq) {
209 $adate = getdate($thistime);
211 if ($row['pc_recurrtype'] == 2) {
212 // Need to skip to nth or last weekday of the next month.
214 if ($adate['mon'] > 12) {
219 if ($my_repeat_on_num < 5) { // not last
221 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN
, $adate['mon'], $adate['mday'], $adate['year']));
222 if ($dow > $my_repeat_on_day) {
226 $adate['mday'] +
= ($my_repeat_on_num - 1) * 7 +
$my_repeat_on_day - $dow;
227 } else { // last weekday of month
228 $adate['mday'] = cal_days_in_month(CAL_GREGORIAN
, $adate['mon'], $adate['year']);
229 $dow = jddayofweek(cal_to_jd(CAL_GREGORIAN
, $adate['mon'], $adate['mday'], $adate['year']));
230 if ($dow < $my_repeat_on_day) {
234 $adate['mday'] +
= $my_repeat_on_day - $dow;
236 } // end recurrtype 2
238 else { // recurrtype 1
240 if ($repeattype == 0) { // daily
242 } else if ($repeattype == 1) { // weekly
244 } else if ($repeattype == 2) { // monthly
246 } else if ($repeattype == 3) { // yearly
248 } else if ($repeattype == 4) { // work days
249 if ($adate['wday'] == 5) { // if friday, skip to monday
251 } else if ($adate['wday'] == 6) { // saturday should not happen
256 } else if ($repeattype == 5) { // monday
258 } else if ($repeattype == 6) { // tuesday
260 } else if ($repeattype == 7) { // wednesday
262 } else if ($repeattype == 8) { // thursday
264 } else if ($repeattype == 9) { // friday
267 die("Invalid repeat type '$repeattype'");
269 } // end recurrtype 1
271 $thistime = mktime(0, 0, 0, $adate['mon'], $adate['mday'], $adate['year']);
277 $row['pc_startTime'],
284 // Mark all slots reserved where the provider is not in-office.
285 // Actually we could do this in the display loop instead.
287 for ($i = 0; $i < $slotcount; ++
$i) {
288 if (($i %
$slotsperday) == 0) {
292 if ($slots[$i] & 1) {
296 if ($slots[$i] & 2) {
308 <?php
html_header_show(); ?
>
309 <title
><?php
xl('Find Available Appointments', 'e'); ?
></title
>
310 <link rel
="stylesheet" href
='<?php echo $css_header ?>' type
='text/css'>
312 <!-- for the pop up calendar
-->
313 <style type
="text/css">@import
url(../library
/dynarch_calendar
.css
);</style
>
314 <script type
="text/javascript" src
="../library/dynarch_calendar.js"></script
>
315 <script type
="text/javascript" src
="../library/dynarch_calendar_en.js"></script
>
316 <script type
="text/javascript" src
="../library/dynarch_calendar_setup.js"></script
>
318 <!-- for ajax
-y stuff
-->
319 <script type
="text/javascript" src
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-min-1-2-2/index.js"></script
>
321 <script language
="JavaScript">
323 function setappt(year
,mon
,mday
,hours
,minutes
) {
324 if (opener
.closed ||
! opener
.setappt
)
325 alert('<?php xl('The destination form was closed
; I cannot act on your selection
.', 'e
'); ?>');
327 opener
.setappt(year
,mon
,mday
,hours
,minutes
);
337 /* this eliminates the padding normally around a FORM tag */
345 background
-color
: #ddddff;
349 #searchResultsHeader {
351 background
-color
: lightgrey
;
353 #searchResultsHeader table {
354 width
: 96%
; /* not 100% because the 'searchResults' table has a scrollbar */
355 border
-collapse
: collapse
;
357 #searchResultsHeader th {
366 .srDate
{ width
: 20%
; }
367 .srTimes
{ width
: 80%
; }
369 #searchResults table {
371 border
-collapse
: collapse
;
372 background
-color
: white
;
376 border
-bottom
: 1px solid gray
;
377 padding
: 1px
5px
1px
5px
;
379 .highlight
{ background
-color
: #ff9; }
380 .blue_highlight
{ background
-color
: #336699; color: white; }
382 border
-bottom
: 1px solid lightgrey
;
386 #pm a { color: #c00; }
391 <body
class="body_top">
393 <div id
="searchCriteria">
394 <form method
='post' name
='theform' action
='find_appt_popup.php?providerid=<?php echo $providerid ?>&catid=<?php echo $input_catid ?>'>
395 <input type
="hidden" name
='bypatient' />
397 <?php
xl('Start date:', 'e'); ?
>
400 <input type
='text' name
='startdate' id
='startdate' size
='10' value
='<?php echo $sdate ?>'
401 title
='yyyy-mm-dd starting date for search'/>
403 <img src
='../interface/pic/show_calendar.gif' align
='absbottom' width
='24' height
='22'
404 id
='img_date' border
='0' alt
='[?]' style
='cursor:pointer'
405 title
='<?php xl('Click here to choose a date
', 'e
'); ?>'>
408 <?php
xl('for', 'e'); ?
>
409 <input type
='text' name
='searchdays' size
='3' value
='<?php echo $searchdays ?>'
410 title
='Number of days to search from the start date' />
411 <?php
xl('days', 'e'); ?
> 
;
412 <input type
='submit' value
='<?php xl('Search
', 'e
'); ?>'>
415 <?php
if (!empty($slots)) : ?
>
417 <div id
="searchResultsHeader">
420 <th
class="srDate"><?php
xl('Day', 'e'); ?
></th
>
421 <th
class="srTimes"><?php
xl('Available Times', 'e'); ?
></th
>
426 <div id
="searchResults">
430 $ampmFlag = "am"; // establish an AM-PM line break flag
431 for ($i = 0; $i < $slotcount; ++
$i) {
433 for ($j = $i; $j < $i +
$catslots; ++
$j) {
434 if ($slots[$j] >= 4) {
440 continue; // skip reserved slots
443 $utime = ($slotbase +
$i) * $slotsecs;
444 $thisdate = date("Y-m-d", $utime);
445 if ($thisdate != $lastdate) {
446 // if a new day, start a new row
453 $lastdate = $thisdate;
454 echo " <tr class='oneresult'>\n";
455 echo " <td class='srDate'>" . date("l", $utime)."<br>".date("Y-m-d", $utime) . "</td>\n";
456 echo " <td class='srTimes'>";
457 echo "<div id='am'>AM ";
458 $ampmFlag = "am"; // reset the AMPM flag
461 $ampm = date('a', $utime);
462 if ($ampmFlag != $ampm) {
463 echo "</div><div id='pm'>PM ";
468 $atitle = "Choose ".date("h:i a", $utime);
469 $adate = getdate($utime);
470 $anchor = "<a href='' onclick='return setappt(" .
471 $adate['year'] . "," .
472 $adate['mon'] . "," .
473 $adate['mday'] . "," .
474 $adate['hours'] . "," .
475 $adate['minutes'] . ")'".
476 " title='$atitle' alt='$atitle'".
478 echo (strlen(date('g', $utime)) < 2 ?
"<span style='visibility:hidden'>0</span>" : "") .
479 $anchor . date("g:i", $utime) . "</a> ";
481 // If category duration is more than 1 slot, increment $i appropriately.
482 // This is to avoid reporting available times on undesirable boundaries.
490 echo " <tr><td colspan='2'> " . xl('No openings were found for this period.', 'e') . "</td></tr>\n";
501 <!-- for the pop up calendar
-->
502 <script language
='JavaScript'>
503 Calendar
.setup({inputField
:"startdate", ifFormat
:"%Y-%m-%d", button
:"img_date"});
505 // jQuery stuff to make the page a little easier to use
507 $
(document
).ready(function(){
508 $
(".oneresult").mouseover(function() { $
(this
).toggleClass("highlight"); });
509 $
(".oneresult").mouseout(function() { $
(this
).toggleClass("highlight"); });
510 $
(".oneresult a").mouseover(function () { $
(this
).toggleClass("blue_highlight"); $
(this
).children().toggleClass("blue_highlight"); });
511 $
(".oneresult a").mouseout(function() { $
(this
).toggleClass("blue_highlight"); $
(this
).children().toggleClass("blue_highlight"); });
512 //$(".event").dblclick(function() { EditEvent(this); });