Openemr fix #5255 forms reason code (#5263)
[openemr.git] / portal / find_appt_popup_user.php
blob515874c5a6d349760174151369dde7284480ce53
1 <?php
3 /**
5 * Modified from main codebase for the patient portal.
7 * @package OpenEMR
8 * @link http://www.open-emr.org
9 * @author Rod Roark <rod@sunsetsystems.com>
10 * @author Jerry Padgett <sjpadgett@gmail.com>
11 * @author Brady Miller <brady.g.miller@gmail.com>
12 * @author Ian Jardine ( github.com/epsdky )
13 * @copyright Copyright (C) 2005-2013 Rod Roark <rod@sunsetsystems.com>
14 * @copyright Copyright (C) 2016-2017 Jerry Padgett <sjpadgett@gmail.com>
15 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
16 * @copyright Copyright (C) 2019 Ian Jardine ( github.com/epsdky )
17 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
20 // Note from Rod 2013-01-22:
21 // This module needs to be refactored to share the same code that is in
22 // interface/main/calendar/find_appt_popup.php. It contains an old version
23 // of that logic and does not support exception dates for repeating events.
25 // Rod mentioned in the previous comment that the code "does not support exception dates for repeating events".
26 // This issue no longer exists - epsdky 2019
28 //continue session
29 // Will start the (patient) portal OpenEMR session/cookie.
30 require_once(dirname(__FILE__) . "/../src/Common/Session/SessionUtil.php");
31 OpenEMR\Common\Session\SessionUtil::portalSessionStart();
34 //landing page definition -- where to go if something goes wrong
35 $landingpage = "index.php?site=" . urlencode($_SESSION['site_id']);
38 // kick out if patient not authenticated
39 if (isset($_SESSION['pid']) && isset($_SESSION['patient_portal_onsite_two'])) {
40 $pid = $_SESSION['pid'];
41 } else {
42 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();
43 header('Location: ' . $landingpage . '&w');
44 exit();
49 $ignoreAuth_onsite_portal = true;
51 require_once("../interface/globals.php");
52 require_once("$srcdir/patient.inc");
53 require_once(dirname(__FILE__) . "/../library/appointments.inc.php");
55 use OpenEMR\Core\Header;
57 $input_catid = $_REQUEST['catid'];
59 // Record an event into the slots array for a specified day.
60 function doOneDay($catid, $udate, $starttime, $duration, $prefcatid)
62 global $slots, $slotsecs, $slotstime, $slotbase, $slotcount, $input_catid;
63 $udate = strtotime($starttime, $udate);
64 if ($udate < $slotstime) {
65 return;
68 $i = (int)($udate / $slotsecs) - $slotbase;
69 $iend = (int)(($duration + $slotsecs - 1) / $slotsecs) + $i;
70 if ($iend > $slotcount) {
71 $iend = $slotcount;
74 if ($iend <= $i) {
75 $iend = $i + 1;
78 for (; $i < $iend; ++$i) {
79 if ($catid == 2) { // in office
80 // If a category ID was specified when this popup was invoked, then select
81 // only IN events with a matching preferred category or with no preferred
82 // category; other IN events are to be treated as OUT events.
83 if ($input_catid) {
84 if ($prefcatid == $input_catid || !$prefcatid) {
85 $slots[$i] |= 1;
86 } else {
87 $slots[$i] |= 2;
89 } else {
90 $slots[$i] |= 1;
93 break; // ignore any positive duration for IN
94 } elseif ($catid == 3) { // out of office
95 $slots[$i] |= 2;
96 break; // ignore any positive duration for OUT
97 } else { // all other events reserve time
98 $slots[$i] |= 4;
103 // seconds per time slot
104 $slotsecs = $GLOBALS['calendar_interval'] * 60;
106 $catslots = 1;
107 if ($input_catid) {
108 $srow = sqlQuery("SELECT pc_duration FROM openemr_postcalendar_categories WHERE pc_catid = ?", array($input_catid));
109 if ($srow['pc_duration']) {
110 $catslots = ceil($srow['pc_duration'] / $slotsecs);
114 $info_msg = "";
116 $searchdays = 7; // default to a 1-week lookahead
117 if ($_REQUEST['searchdays']) {
118 $searchdays = $_REQUEST['searchdays'];
121 // Get a start date.
122 if (
123 $_REQUEST['startdate'] && preg_match(
124 "/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/",
125 $_REQUEST['startdate'],
126 $matches
129 $sdate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
130 } else {
131 $sdate = date("Y-m-d");
134 // Get an end date - actually the date after the end date.
135 preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/", $sdate, $matches);
136 $edate = date(
137 "Y-m-d",
138 mktime(0, 0, 0, $matches[2], $matches[3] + $searchdays, $matches[1])
141 // compute starting time slot number and number of slots.
142 $slotstime = strtotime("$sdate 00:00:00");
143 $slotetime = strtotime("$edate 00:00:00");
144 $slotbase = (int)($slotstime / $slotsecs);
145 $slotcount = (int)($slotetime / $slotsecs) - $slotbase;
147 if ($slotcount <= 0 || $slotcount > 100000) {
148 die("Invalid date range.");
151 $slotsperday = (int)(60 * 60 * 24 / $slotsecs);
153 // If we have a provider, search.
155 if ($_REQUEST['providerid']) {
156 $providerid = $_REQUEST['providerid'];
158 // Create and initialize the slot array. Values are bit-mapped:
159 // bit 0 = in-office occurs here
160 // bit 1 = out-of-office occurs here
161 // bit 2 = reserved
162 // So, values may range from 0 to 7.
164 $slots = array_pad(array(), $slotcount, 0);
166 // Note there is no need to sort the query results.
167 // echo $sdate." -- ".$edate;
168 $query = "SELECT pc_eventDate, pc_endDate, pc_startTime, pc_duration, " .
169 "pc_recurrtype, pc_recurrspec, pc_alldayevent, pc_catid, pc_prefcatid, pc_title " .
170 "FROM openemr_postcalendar_events " .
171 "WHERE pc_aid = ? AND " .
172 "((pc_endDate >= ? AND pc_eventDate < ?) OR " .
173 "(pc_endDate = '0000-00-00' AND pc_eventDate >= ? AND pc_eventDate < ?))";
175 $sqlBindArray = array();
176 array_push($sqlBindArray, $providerid, $sdate, $edate, $sdate, $edate);
177 //////
178 $events2 = fetchEvents($sdate, $edate, null, null, false, 0, $sqlBindArray, $query);
179 foreach ($events2 as $row) {
180 $thistime = strtotime($row['pc_eventDate'] . " 00:00:00");
181 doOneDay(
182 $row['pc_catid'],
183 $thistime,
184 $row['pc_startTime'],
185 $row['pc_duration'],
186 $row['pc_prefcatid']
189 //////
191 // Mark all slots reserved where the provider is not in-office.
192 // Actually we could do this in the display loop instead.
193 $inoffice = false;
194 for ($i = 0; $i < $slotcount; ++$i) {
195 if (($i % $slotsperday) == 0) {
196 $inoffice = false;
199 if ($slots[$i] & 1) {
200 $inoffice = true;
203 if ($slots[$i] & 2) {
204 $inoffice = false;
207 if (!$inoffice) {
208 $slots[$i] |= 4;
213 <!DOCTYPE html>
214 <html>
215 <head>
216 <title><?php echo xlt('Find Available Appointments'); ?></title>
217 <?php Header::setupHeader(['no_main-theme', 'datetime-picker', 'opener']); ?>
218 <script>
220 function setappt(year, mon, mday, hours, minutes) {
221 opener.setappt(year, mon, mday, hours, minutes);
222 dlgclose();
223 return false;
226 </script>
228 <style>
229 form {
230 /* this eliminates the padding normally around a FORM tag */
231 padding: 0;
232 margin: 0;
235 #searchCriteria {
236 text-align: center;
237 width: 100%;
238 font-weight: bold;
239 padding: 0.1875rem;
242 #searchResults {
243 width: 100%;
244 height: 100%;
245 overflow: auto;
248 #searchResults table {
249 width: 100%;
250 border-collapse: collapse;
251 background-color: var(--white);
254 #searchResults td {
255 border-bottom: 1px solid var(--gray600);
256 padding: 1px 5px;
259 .blue_highlight {
260 background-color: #BBCCDD;
261 color: var(--white);
264 #am a, #am a:hover {
265 padding: 4px;
266 text-decoration: none;
269 #pm a, #pm a:hover {
270 color: var(--danger);
271 padding: 4px;
272 text-decoration: none;
274 </style>
276 </head>
278 <body class="body_top">
280 <div class="table-primary" id="searchCriteria">
281 <form method='post' name='theform' action='./find_appt_popup_user.php?providerid=<?php echo attr_url($providerid); ?>&catid=<?php echo attr_url($input_catid); ?>'>
282 <input type="hidden" name='bypatient' />
283 <div class="form-row mx-0 align-items-center">
284 <label for="startdate" class="col-1 mx-2 col-form-label"><?php echo xlt('Start date:'); ?></label>
285 <div class="col-auto">
286 <input type='text' class='datepicker form-control' name='startdate' id='startdate' size='10' value='<?php echo attr($sdate); ?>' title='yyyy-mm-dd starting date for search' />
287 </div>
288 <label for="searchdays" class="col-auto col-form-label"><?php echo xlt('for'); ?></label>
289 <div class="col-auto">
290 <input type='text' class="form-control" name='searchdays' id='searchdays' size='3' value='<?php echo attr($searchdays); ?>' title='Number of days to search from the start date' />
291 </div>
292 <label for="searchdays" class="col-auto col-form-label"><?php echo xlt('days'); ?></label>
293 <div class="col-auto">
294 <input type='submit' class="btn btn-primary btn-sm btn-block" value='<?php echo xla('Search'); ?>' />
295 </div>
296 </div>
297 </div>
299 <?php if (!empty($slots)) : ?>
300 <div id="searchResultsHeader">
301 <table class='table table-bordered'>
303 </table>
304 </div>
306 <div id="searchResults" class="container-fluid">
307 <div class="table-responsive">
308 <table class='table table-sm table-striped table-bordered'>
309 <thead id="searchResultsHeader">
310 <tr>
311 <th class="table-dark text-light"><?php echo xlt('Day'); ?></th>
312 <th class="srTimes"><?php echo xlt('Available Times'); ?></th>
313 </tr>
314 </thead>
315 <?php
316 $lastdate = "";
317 $ampmFlag = "am"; // establish an AM-PM line break flag
318 for ($i = 0; $i < $slotcount; ++$i) {
319 $available = true;
320 for ($j = $i; $j < $i + $catslots; ++$j) {
321 if ($slots[$j] >= 4) {
322 $available = false;
326 if (!$available) {
327 continue; // skip reserved slots
330 $utime = ($slotbase + $i) * $slotsecs;
331 $thisdate = date("Y-m-d", $utime);
332 if ($thisdate != $lastdate) {
333 // if a new day, start a new row
334 if ($lastdate) {
335 echo "</div>";
336 echo "</td>\n";
337 echo " </tr>\n";
340 $lastdate = $thisdate;
341 echo " <tr class='oneresult'>\n";
342 echo " <td class='table-dark text-light'>" . date("l", $utime) . "<br />" . date("Y-m-d", $utime) . "</td>\n";
343 echo " <td class='srTimes'>";
344 echo "<div id='am'>AM<hr class='m-0 p-0 mb-n3'/><br/>";
345 $ampmFlag = "am"; // reset the AMPM flag
348 $ampm = date('a', $utime);
349 if ($ampmFlag != $ampm) {
350 echo "</div><div id='pm'><hr class='m-0 p-0' />PM<hr class='m-0 p-0 mb-n3' /><br/>";
353 $ampmFlag = $ampm;
355 $atitle = "Choose " . date("h:i a", $utime);
356 $adate = getdate($utime);
358 $anchor = "<a href='' onclick='return setappt(" .
359 attr_js(date("Y", $utime)) . "," .
360 attr_js($adate['mon']) . "," .
361 attr_js($adate['mday']) . "," .
362 attr_js(date("G", $utime)) . "," .
363 attr_js(date("i", $utime)) . "," .
364 attr_js(date('a', $utime)) . ")'" .
365 " title='" . attr($atitle) . "' alt='" . attr($atitle) . "'" .
366 ">";
367 echo (strlen(date('g', $utime)) < 2 ? "<span style='visibility:hidden'>0</span>" : "") .
368 $anchor . date("g:i", $utime) . "</a> ";
370 // If category duration is more than 1 slot, increment $i appropriately.
371 // This is to avoid reporting available times on undesirable boundaries.
372 $i += $catslots - 1;
375 if ($lastdate) {
376 echo "</td>\n";
377 echo " </tr>\n";
378 } else {
379 echo " <tr><td colspan='2'> " . xlt('No openings were found for this period.') . "</td></tr>\n";
382 </table>
383 </div>
384 </div>
385 </div>
386 <?php endif; ?>
388 </form>
390 <script>
392 // jQuery stuff to make the page a little easier to use
393 $(function () {
394 $(".oneresult").hover(function () {
395 $(this).toggleClass("highlight");
396 }, function () {
397 $(this).toggleClass("highlight");
399 $(".oneresult a").hover(function () {
400 $(this).toggleClass("blue_highlight");
401 $(this).children().toggleClass("blue_highlight");
402 }, function () {
403 $(this).toggleClass("blue_highlight");
404 $(this).children().toggleClass("blue_highlight");
407 $('.datepicker').datetimepicker({
408 <?php $datetimepicker_timepicker = false; ?>
409 <?php $datetimepicker_formatInput = false; ?>
410 <?php require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?>
411 <?php // can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
416 </script>
417 </body>
418 </html>