Ejemplo n.º 1
0
 public function getMyTransactions($page = 1, $recordsPerPage = -1, $sortOption = 'dueDate')
 {
     global $timer;
     $patronDump = $this->driver->_getPatronDump($this->driver->_getBarcode());
     $timer->logTime("Ready to load checked out titles from Millennium");
     //Load the information from millennium using CURL
     $sResult = $this->driver->_fetchPatronInfoPage($patronDump, 'items');
     $timer->logTime("Loaded checked out titles from Millennium");
     $sResult = preg_replace("/<[^<]+?>\\W<[^<]+?>\\W\\d* ITEM.? CHECKED OUT<[^<]+?>\\W<[^<]+?>/", "", $sResult);
     $s = substr($sResult, stripos($sResult, 'patFunc'));
     $s = substr($s, strpos($s, ">") + 1);
     $s = substr($s, 0, stripos($s, "</table"));
     $s = preg_replace("/<br \\/>/", "", $s);
     $sRows = preg_split("/<tr([^>]*)>/", $s);
     $sCount = 0;
     $sKeys = array_pad(array(), 10, "");
     $checkedOutTitles = array();
     //Get patron's location to determine if renewals are allowed.
     global $locationSingleton;
     /** @var Location $patronLocation */
     $patronLocation = $locationSingleton->getUserHomeLocation();
     if (isset($patronLocation)) {
         $patronPType = $this->driver->getPType();
         $patronCanRenew = false;
         if ($patronLocation->ptypesToAllowRenewals == '*') {
             $patronCanRenew = true;
         } else {
             if (preg_match("/^({$patronLocation->ptypesToAllowRenewals})\$/", $patronPType)) {
                 $patronCanRenew = true;
             }
         }
     } else {
         $patronCanRenew = true;
     }
     $timer->logTime("Determined if patron can renew");
     foreach ($sRows as $srow) {
         $scols = preg_split("/<t(h|d)([^>]*)>/", $srow);
         $curTitle = array();
         for ($i = 0; $i < sizeof($scols); $i++) {
             $scols[$i] = str_replace("&nbsp;", " ", $scols[$i]);
             $scols[$i] = preg_replace("/<br+?>/", " ", $scols[$i]);
             $scols[$i] = html_entity_decode(trim(substr($scols[$i], 0, stripos($scols[$i], "</t"))));
             //print_r($scols[$i]);
             if ($sCount == 1) {
                 $sKeys[$i] = $scols[$i];
             } else {
                 if ($sCount > 1) {
                     if (stripos($sKeys[$i], "TITLE") > -1) {
                         if (preg_match('/.*?<a href=\\"\\/record=(.*?)(?:~S\\d{1,2})\\">(.*?)<\\/a>.*/', $scols[$i], $matches)) {
                             $shortId = $matches[1];
                             $bibid = '.' . $matches[1];
                             $title = $matches[2];
                         } else {
                             $title = $scols[$i];
                             $shortId = '';
                             $bibid = '';
                         }
                         $curTitle['shortId'] = $shortId;
                         $curTitle['id'] = $bibid;
                         $curTitle['title'] = $title;
                     }
                     if (stripos($sKeys[$i], "STATUS") > -1) {
                         // $sret[$scount-2]['duedate'] = strip_tags($scols[$i]);
                         $due = trim(str_replace("DUE", "", strip_tags($scols[$i])));
                         $renewCount = 0;
                         if (preg_match('/FINE\\(up to now\\) (\\$\\d+\\.\\d+)/i', $due, $matches)) {
                             $curTitle['fine'] = trim($matches[1]);
                         }
                         if (preg_match('/(.*)Renewed (\\d+) time(?:s)?/i', $due, $matches)) {
                             $due = trim($matches[1]);
                             $renewCount = $matches[2];
                         } else {
                             if (preg_match('/(.*)\\+\\d+ HOLD.*/i', $due, $matches)) {
                                 $due = trim($matches[1]);
                             }
                         }
                         if (preg_match('/(\\d{2}-\\d{2}-\\d{2})/', $due, $dueMatches)) {
                             $dateDue = DateTime::createFromFormat('m-d-y', $dueMatches[1]);
                             if ($dateDue) {
                                 $dueTime = $dateDue->getTimestamp();
                             } else {
                                 $dueTime = null;
                             }
                         } else {
                             $dueTime = strtotime($due);
                         }
                         if ($dueTime != null) {
                             $daysUntilDue = ceil(($dueTime - time()) / (24 * 60 * 60));
                             $overdue = $daysUntilDue < 0;
                             $curTitle['duedate'] = $dueTime;
                             $curTitle['overdue'] = $overdue;
                             $curTitle['daysUntilDue'] = $daysUntilDue;
                         }
                         $curTitle['renewCount'] = $renewCount;
                     }
                     if (stripos($sKeys[$i], "BARCODE") > -1) {
                         $curTitle['barcode'] = strip_tags($scols[$i]);
                     }
                     if (stripos($sKeys[$i], "RENEW") > -1) {
                         $matches = array();
                         if (preg_match('/<input\\s*type="checkbox"\\s*name="renew(\\d+)"\\s*id="renew(\\d+)"\\s*value="(.*?)"\\s*\\/>/', $scols[$i], $matches)) {
                             $curTitle['canrenew'] = $patronCanRenew;
                             $curTitle['itemindex'] = $matches[1];
                             $curTitle['itemid'] = $matches[3];
                             $curTitle['renewIndicator'] = $curTitle['itemid'] . '|' . $curTitle['itemindex'];
                         } else {
                             $curTitle['canrenew'] = false;
                         }
                     }
                     if (stripos($sKeys[$i], "CALL NUMBER") > -1) {
                         $curTitle['request'] = "null";
                     }
                 }
             }
         }
         if ($sCount > 1) {
             //Get additional information from resources table
             if ($curTitle['shortId'] && strlen($curTitle['shortId']) > 0) {
                 /** @var Resource|object $resource */
                 $resource = new Resource();
                 $resource->source = 'VuFind';
                 $resource->shortId = $curTitle['shortId'];
                 if ($resource->find(true)) {
                     $timer->logTime("Found resource for " . $curTitle['shortId']);
                     $curTitle = array_merge($curTitle, get_object_vars($resource));
                     $curTitle['recordId'] = $resource->record_id;
                     $curTitle['id'] = $resource->record_id;
                 } else {
                     $timer->logTime("Did not find resource for " . $curTitle['shortId']);
                     //echo("Warning did not find resource for {$historyEntry['shortId']}");
                 }
             }
             $sortTitle = isset($curTitle['title_sort']) ? $curTitle['title_sort'] : $curTitle['title'];
             $sortKey = $sortTitle;
             if ($sortOption == 'title') {
                 $sortKey = $sortTitle;
             } elseif ($sortOption == 'author') {
                 $sortKey = (isset($curTitle['author']) ? $curTitle['author'] : "Unknown") . '-' . $sortTitle;
             } elseif ($sortOption == 'dueDate') {
                 if (isset($curTitle['duedate'])) {
                     if (preg_match('/.*?(\\d{1,2})[-\\/](\\d{1,2})[-\\/](\\d{2,4}).*/', $curTitle['duedate'], $matches)) {
                         $sortKey = $matches[3] . '-' . $matches[1] . '-' . $matches[2] . '-' . $sortTitle;
                     } else {
                         $sortKey = $curTitle['duedate'] . '-' . $sortTitle;
                     }
                 }
             } elseif ($sortOption == 'format') {
                 $sortKey = (isset($curTitle['format']) ? $curTitle['format'] : "Unknown") . '-' . $sortTitle;
             } elseif ($sortOption == 'renewed') {
                 $sortKey = (isset($curTitle['renewCount']) ? $curTitle['renewCount'] : 0) . '-' . $sortTitle;
             } elseif ($sortOption == 'holdQueueLength') {
                 $sortKey = (isset($curTitle['holdQueueLength']) ? $curTitle['holdQueueLength'] : 0) . '-' . $sortTitle;
             }
             $sortKey .= "_{$sCount}";
             $checkedOutTitles[$sortKey] = $curTitle;
         }
         $sCount++;
     }
     ksort($checkedOutTitles);
     $timer->logTime("Parsed checkout information");
     $numTransactions = count($checkedOutTitles);
     //Process pagination
     if ($recordsPerPage != -1) {
         $startRecord = ($page - 1) * $recordsPerPage;
         if ($startRecord > $numTransactions) {
             $startRecord = 0;
         }
         $checkedOutTitles = array_slice($checkedOutTitles, $startRecord, $recordsPerPage);
     }
     return array('transactions' => $checkedOutTitles, 'numTransactions' => $numTransactions);
 }
 public function getReadingHistory($patron, $page = 1, $recordsPerPage = -1, $sortOption = "checkedOut")
 {
     global $timer;
     $patronDump = $this->driver->_getPatronDump($this->driver->_getBarcode());
     //Load the information from millennium using CURL
     $pageContents = $this->driver->_fetchPatronInfoPage($patronDump, 'readinghistory');
     //Check to see if there are multiple pages of reading history
     $hasPagination = preg_match('/<td[^>]*class="browsePager"/', $pageContents);
     if ($hasPagination) {
         //Load a list of extra pages to load.  The pagination links display multiple times, so load into an associative array to make them unique
         preg_match_all('/<a href="readinghistory&page=(\\d+)">/', $pageContents, $additionalPageMatches);
         $maxPageNum = 0;
         foreach ($additionalPageMatches[1] as $additionalPageMatch) {
             if ($additionalPageMatch > $maxPageNum) {
                 $maxPageNum = $additionalPageMatch;
             }
         }
     }
     $recordsRead = 0;
     $readingHistoryTitles = $this->parseReadingHistoryPage($pageContents, $patron, $sortOption, $recordsRead);
     $recordsRead += count($readingHistoryTitles);
     if (isset($maxPageNum)) {
         for ($pageNum = 2; $pageNum <= $maxPageNum; $pageNum++) {
             $pageContents = $this->driver->_fetchPatronInfoPage($patronDump, 'readinghistory&page=' . $pageNum);
             $additionalTitles = $this->parseReadingHistoryPage($pageContents, $patron, $sortOption, $recordsRead);
             $recordsRead += count($additionalTitles);
             $readingHistoryTitles = array_merge($readingHistoryTitles, $additionalTitles);
         }
     }
     if ($sortOption == "checkedOut" || $sortOption == "returned") {
         krsort($readingHistoryTitles);
     } else {
         ksort($readingHistoryTitles);
     }
     $numTitles = count($readingHistoryTitles);
     //process pagination
     if ($recordsPerPage != -1) {
         $startRecord = ($page - 1) * $recordsPerPage;
         $readingHistoryTitles = array_slice($readingHistoryTitles, $startRecord, $recordsPerPage);
     }
     set_time_limit(20 * count($readingHistoryTitles));
     foreach ($readingHistoryTitles as $key => $historyEntry) {
         //Get additional information from resources table
         $historyEntry['ratingData'] = null;
         $historyEntry['permanentId'] = null;
         $historyEntry['linkUrl'] = null;
         $historyEntry['coverUrl'] = null;
         $historyEntry['format'] = array();
         if (isset($historyEntry['shortId']) && strlen($historyEntry['shortId']) > 0) {
             $historyEntry['recordId'] = "." . $historyEntry['shortId'] . $this->driver->getCheckDigit($historyEntry['shortId']);
             require_once ROOT_DIR . '/RecordDrivers/MarcRecord.php';
             $recordDriver = new MarcRecord($historyEntry['recordId']);
             if ($recordDriver->isValid()) {
                 $historyEntry['ratingData'] = $recordDriver->getRatingData();
                 $historyEntry['permanentId'] = $recordDriver->getPermanentId();
                 $historyEntry['linkUrl'] = $recordDriver->getLinkUrl();
                 $historyEntry['coverUrl'] = $recordDriver->getBookcoverUrl('medium');
                 $historyEntry['format'] = $recordDriver->getFormats();
             }
             $recordDriver = null;
         }
         $readingHistoryTitles[$key] = $historyEntry;
     }
     //The history is active if there is an opt out link.
     $historyActive = strpos($pageContents, 'OptOut') > 0;
     $timer->logTime("Loaded Reading history for patron");
     global $user;
     if ($historyActive && !$user->trackReadingHistory) {
         //The user does have reading history even though we hadn't detected it before.
         $user->trackReadingHistory = true;
         $user->update();
         $_SESSION['userinfo'] = serialize($user);
     }
     if (!$historyActive && $user->trackReadingHistory) {
         //The user does have reading history even though we hadn't detected it before.
         $user->trackReadingHistory = false;
         $user->update();
         $_SESSION['userinfo'] = serialize($user);
     }
     return array('historyActive' => $historyActive, 'titles' => $readingHistoryTitles, 'numTitles' => $numTitles);
 }
Ejemplo n.º 3
0
 public function getMyHolds($patron = null, $page = 1, $recordsPerPage = -1, $sortOption = 'title')
 {
     global $timer;
     global $configArray;
     global $user;
     $patronDump = $this->driver->_getPatronDump($this->driver->_getBarcode());
     //Load the information from millennium using CURL
     $sResult = $this->driver->_fetchPatronInfoPage($patronDump, 'holds');
     $timer->logTime("Got holds page from Millennium");
     $holds = $this->parseHoldsPage($sResult);
     $timer->logTime("Parsed Holds page");
     //Get a list of all record id so we can load supplemental information
     $recordIds = array();
     foreach ($holds as $holdSections) {
         foreach ($holdSections as $hold) {
             $recordIds[] = "'" . $hold['shortId'] . "'";
         }
     }
     //Get records from resource table
     $resourceInfo = new Resource();
     if (count($recordIds) > 0) {
         $recordIdString = implode(",", $recordIds);
         mysql_select_db($configArray['Database']['database_vufind_dbname']);
         $resourceSql = "SELECT * FROM resource where source = 'VuFind' AND shortId in (" . $recordIdString . ")";
         $resourceInfo->query($resourceSql);
         $timer->logTime('Got records for all titles');
         //Load title author, etc. information
         while ($resourceInfo->fetch()) {
             foreach ($holds as $section => $holdSections) {
                 foreach ($holdSections as $key => $hold) {
                     $hold['recordId'] = $hold['id'];
                     if ($hold['shortId'] == $resourceInfo->shortId) {
                         $hold['recordId'] = $resourceInfo->record_id;
                         $hold['id'] = $resourceInfo->record_id;
                         $hold['shortId'] = $resourceInfo->shortId;
                         //Load title, author, and format information about the title
                         $hold['title'] = isset($resourceInfo->title) ? $resourceInfo->title : 'Unknown';
                         $hold['sortTitle'] = isset($resourceInfo->title_sort) ? $resourceInfo->title_sort : 'unknown';
                         $hold['author'] = isset($resourceInfo->author) ? $resourceInfo->author : null;
                         $hold['format'] = isset($resourceInfo->format) ? $resourceInfo->format : null;
                         $hold['isbn'] = isset($resourceInfo->isbn) ? $resourceInfo->isbn : '';
                         $hold['upc'] = isset($resourceInfo->upc) ? $resourceInfo->upc : '';
                         $hold['format_category'] = isset($resourceInfo->format_category) ? $resourceInfo->format_category : '';
                         //Load rating information
                         $hold['ratingData'] = $resourceInfo->getRatingData($user);
                         $holds[$section][$key] = $hold;
                     }
                 }
             }
         }
     }
     //Process sorting
     //echo ("<br/>\r\nSorting by $sortOption");
     foreach ($holds as $sectionName => $section) {
         $sortKeys = array();
         $i = 0;
         foreach ($section as $key => $hold) {
             $sortTitle = isset($hold['sortTitle']) ? $hold['sortTitle'] : (isset($hold['title']) ? $hold['title'] : "Unknown");
             if ($sectionName == 'available') {
                 $sortKeys[$key] = $sortTitle;
             } else {
                 if ($sortOption == 'title') {
                     $sortKeys[$key] = $sortTitle;
                 } elseif ($sortOption == 'author') {
                     $sortKeys[$key] = (isset($hold['author']) ? $hold['author'] : "Unknown") . '-' . $sortTitle;
                 } elseif ($sortOption == 'placed') {
                     $sortKeys[$key] = $hold['createTime'] . '-' . $sortTitle;
                 } elseif ($sortOption == 'format') {
                     $sortKeys[$key] = (isset($hold['format']) ? $hold['format'] : "Unknown") . '-' . $sortTitle;
                 } elseif ($sortOption == 'location') {
                     $sortKeys[$key] = (isset($hold['location']) ? $hold['location'] : "Unknown") . '-' . $sortTitle;
                 } elseif ($sortOption == 'holdQueueLength') {
                     $sortKeys[$key] = (isset($hold['holdQueueLength']) ? $hold['holdQueueLength'] : 0) . '-' . $sortTitle;
                 } elseif ($sortOption == 'position') {
                     $sortKeys[$key] = str_pad(isset($hold['position']) ? $hold['position'] : 1, 3, "0", STR_PAD_LEFT) . '-' . $sortTitle;
                 } elseif ($sortOption == 'status') {
                     $sortKeys[$key] = (isset($hold['status']) ? $hold['status'] : "Unknown") . '-' . (isset($hold['reactivateTime']) ? $hold['reactivateTime'] : "0") . '-' . $sortTitle;
                 } else {
                     $sortKeys[$key] = $sortTitle;
                 }
                 //echo ("<br/>\r\nSort Key for $key = {$sortKeys[$key]}");
             }
             $sortKeys[$key] = strtolower($sortKeys[$key] . '-' . $i++);
         }
         array_multisort($sortKeys, $section);
         $holds[$sectionName] = $section;
     }
     //Limit to a specific number of records
     if (isset($holds['unavailable'])) {
         $numUnavailableHolds = count($holds['unavailable']);
         if ($recordsPerPage != -1) {
             $startRecord = ($page - 1) * $recordsPerPage;
             $holds['unavailable'] = array_slice($holds['unavailable'], $startRecord, $recordsPerPage);
         }
     } else {
         $numUnavailableHolds = 0;
     }
     if (!isset($holds['available'])) {
         $holds['available'] = array();
     }
     if (!isset($holds['unavailable'])) {
         $holds['unavailable'] = array();
     }
     //Sort the hold sections so available holds are first.
     ksort($holds);
     $patronId = isset($patron) ? $patron['id'] : $this->driver->_getBarcode();
     $this->holds[$patronId] = $holds;
     $timer->logTime("Processed hold pagination and sorting");
     return array('holds' => $holds, 'numUnavailableHolds' => $numUnavailableHolds);
 }
Ejemplo n.º 4
0
 public function getMyHolds($patron = null, $page = 1, $recordsPerPage = -1, $sortOption = 'title')
 {
     global $timer;
     $patronDump = $this->driver->_getPatronDump($this->driver->_getBarcode());
     //Load the information from millennium using CURL
     $sResult = $this->driver->_fetchPatronInfoPage($patronDump, 'holds');
     $timer->logTime("Got holds page from Millennium");
     $holds = $this->parseHoldsPage($sResult);
     $timer->logTime("Parsed Holds page");
     require_once ROOT_DIR . '/RecordDrivers/MarcRecord.php';
     foreach ($holds as $section => $holdSections) {
         foreach ($holdSections as $key => $hold) {
             disableErrorHandler();
             $recordDriver = new MarcRecord($hold['recordId']);
             if ($recordDriver->isValid()) {
                 $hold['id'] = $recordDriver->getUniqueID();
                 $hold['shortId'] = $recordDriver->getShortId();
                 //Load title, author, and format information about the title
                 $hold['title'] = $recordDriver->getTitle();
                 $hold['sortTitle'] = $recordDriver->getSortableTitle();
                 $hold['author'] = $recordDriver->getAuthor();
                 $hold['format'] = $recordDriver->getFormat();
                 $hold['isbn'] = $recordDriver->getCleanISBN();
                 $hold['upc'] = $recordDriver->getCleanUPC();
                 $hold['format_category'] = $recordDriver->getFormatCategory();
                 //Load rating information
                 $hold['ratingData'] = $recordDriver->getRatingData();
                 $holds[$section][$key] = $hold;
             }
             enableErrorHandler();
         }
     }
     //Process sorting
     //echo ("<br/>\r\nSorting by $sortOption");
     foreach ($holds as $sectionName => $section) {
         $sortKeys = array();
         $i = 0;
         foreach ($section as $key => $hold) {
             $sortTitle = isset($hold['sortTitle']) ? $hold['sortTitle'] : (isset($hold['title']) ? $hold['title'] : "Unknown");
             if ($sectionName == 'available') {
                 $sortKeys[$key] = $sortTitle;
             } else {
                 if ($sortOption == 'title') {
                     $sortKeys[$key] = $sortTitle;
                 } elseif ($sortOption == 'author') {
                     $sortKeys[$key] = (isset($hold['author']) ? $hold['author'] : "Unknown") . '-' . $sortTitle;
                 } elseif ($sortOption == 'placed') {
                     $sortKeys[$key] = $hold['createTime'] . '-' . $sortTitle;
                 } elseif ($sortOption == 'format') {
                     $sortKeys[$key] = (isset($hold['format']) ? $hold['format'] : "Unknown") . '-' . $sortTitle;
                 } elseif ($sortOption == 'location') {
                     $sortKeys[$key] = (isset($hold['location']) ? $hold['location'] : "Unknown") . '-' . $sortTitle;
                 } elseif ($sortOption == 'holdQueueLength') {
                     $sortKeys[$key] = (isset($hold['holdQueueLength']) ? $hold['holdQueueLength'] : 0) . '-' . $sortTitle;
                 } elseif ($sortOption == 'position') {
                     $sortKeys[$key] = str_pad(isset($hold['position']) ? $hold['position'] : 1, 3, "0", STR_PAD_LEFT) . '-' . $sortTitle;
                 } elseif ($sortOption == 'status') {
                     $sortKeys[$key] = (isset($hold['status']) ? $hold['status'] : "Unknown") . '-' . (isset($hold['reactivateTime']) ? $hold['reactivateTime'] : "0") . '-' . $sortTitle;
                 } else {
                     $sortKeys[$key] = $sortTitle;
                 }
                 //echo ("<br/>\r\nSort Key for $key = {$sortKeys[$key]}");
             }
             $sortKeys[$key] = strtolower($sortKeys[$key] . '-' . $i++);
         }
         array_multisort($sortKeys, $section);
         $holds[$sectionName] = $section;
     }
     //Limit to a specific number of records
     if (isset($holds['unavailable'])) {
         $numUnavailableHolds = count($holds['unavailable']);
         if ($recordsPerPage != -1) {
             $startRecord = ($page - 1) * $recordsPerPage;
             $holds['unavailable'] = array_slice($holds['unavailable'], $startRecord, $recordsPerPage);
         }
     } else {
         $numUnavailableHolds = 0;
     }
     if (!isset($holds['available'])) {
         $holds['available'] = array();
     }
     if (!isset($holds['unavailable'])) {
         $holds['unavailable'] = array();
     }
     //Sort the hold sections so available holds are first.
     ksort($holds);
     $patronId = isset($patron) ? $patron['id'] : $this->driver->_getBarcode();
     $this->holds[$patronId] = $holds;
     $timer->logTime("Processed hold pagination and sorting");
     return array('holds' => $holds, 'numUnavailableHolds' => $numUnavailableHolds);
 }
 public function getReadingHistory($patron, $page = 1, $recordsPerPage = -1, $sortOption = "checkedOut")
 {
     global $timer;
     $patronDump = $this->driver->_getPatronDump($this->driver->_getBarcode());
     //Load the information from millennium using CURL
     $pageContents = $this->driver->_fetchPatronInfoPage($patronDump, 'readinghistory');
     $sResult = preg_replace("/<[^<]+?><[^<]+?>Reading History.\\(.\\d*.\\)<[^<]+?>\\W<[^<]+?>/", "", $pageContents);
     $s = substr($sResult, stripos($sResult, 'patFunc'));
     $s = substr($s, strpos($s, ">") + 1);
     $s = substr($s, 0, stripos($s, "</table"));
     $s = preg_replace("/<br \\/>/", "", $s);
     $srows = preg_split("/<tr([^>]*)>/", $s);
     $scount = 0;
     $skeys = array_pad(array(), 10, "");
     $readingHistoryTitles = array();
     $itemindex = 0;
     foreach ($srows as $srow) {
         $scols = preg_split("/<t(h|d)([^>]*)>/", $srow);
         $historyEntry = array();
         for ($i = 0; $i < sizeof($scols); $i++) {
             $scols[$i] = str_replace("&nbsp;", " ", $scols[$i]);
             $scols[$i] = preg_replace("/<br+?>/", " ", $scols[$i]);
             $scols[$i] = html_entity_decode(trim(substr($scols[$i], 0, stripos($scols[$i], "</t"))));
             //print_r($scols[$i]);
             if ($scount == 1) {
                 $skeys[$i] = $scols[$i];
             } else {
                 if ($scount > 1) {
                     if (stripos($skeys[$i], "Mark") > -1) {
                         if (preg_match('/id="rsh(\\d+)"/', $scols[$i], $matches)) {
                             $itemIndex = $matches[1];
                             $historyEntry['itemindex'] = $itemIndex;
                         }
                         $historyEntry['deletable'] = "BOX";
                     }
                     if (stripos($skeys[$i], "Title") > -1) {
                         if (preg_match('/.*?<a href=\\"\\/record=(.*?)(?:~S\\d{1,2})\\">(.*?)<\\/a>.*/', $scols[$i], $matches)) {
                             $shortId = $matches[1];
                             $bibId = '.' . $matches[2];
                             $title = $matches[2];
                             $historyEntry['id'] = $bibId;
                             $historyEntry['shortId'] = $shortId;
                         } else {
                             $title = strip_tags($scols[$i]);
                         }
                         $historyEntry['title'] = $title;
                     }
                     if (stripos($skeys[$i], "Author") > -1) {
                         $historyEntry['author'] = strip_tags($scols[$i]);
                     }
                     if (stripos($skeys[$i], "Checked Out") > -1) {
                         $historyEntry['checkout'] = strip_tags($scols[$i]);
                     }
                     if (stripos($skeys[$i], "Details") > -1) {
                         $historyEntry['details'] = strip_tags($scols[$i]);
                     }
                     $historyEntry['borrower_num'] = $patron['id'];
                 }
             }
             //Done processing column
         }
         //Done processing row
         if ($scount > 1) {
             $historyEntry['title_sort'] = strtolower($historyEntry['title']);
             //$historyEntry['itemindex'] = $itemindex++;
             //Get additional information from resources table
             if (isset($historyEntry['shortId']) && strlen($historyEntry['shortId']) > 0) {
                 /** @var Resource|Object $resource */
                 $resource = new Resource();
                 $resource->shortId = $historyEntry['shortId'];
                 if ($resource->find(true)) {
                     $historyEntry = array_merge($historyEntry, get_object_vars($resource));
                     $historyEntry['recordId'] = $resource->record_id;
                     $historyEntry['shortId'] = str_replace('.b', 'b', $resource->record_id);
                     $historyEntry['ratingData'] = $resource->getRatingData();
                 } else {
                     //echo("Warning did not find resource for {$historyEntry['shortId']}");
                 }
             }
             if ($sortOption == "title") {
                 $titleKey = $historyEntry['title_sort'];
             } elseif ($sortOption == "author") {
                 $titleKey = $historyEntry['author'] . "_" . $historyEntry['title_sort'];
             } elseif ($sortOption == "checkedOut" || $sortOption == "returned") {
                 $checkoutTime = DateTime::createFromFormat('m-d-Y', $historyEntry['checkout']);
                 if ($checkoutTime) {
                     $titleKey = $checkoutTime->getTimestamp() . "_" . $historyEntry['title_sort'];
                 } else {
                     //print_r($historyEntry);
                     $titleKey = $historyEntry['title_sort'];
                 }
             } elseif ($sortOption == "format") {
                 $titleKey = $historyEntry['format'] . "_" . $historyEntry['title_sort'];
             } else {
                 $titleKey = $historyEntry['title_sort'];
             }
             $titleKey .= '_' . $scount;
             $readingHistoryTitles[$titleKey] = $historyEntry;
         }
         $scount++;
     }
     //processed all rows in the table
     if ($sortOption == "checkedOut" || $sortOption == "returned") {
         krsort($readingHistoryTitles);
     } else {
         ksort($readingHistoryTitles);
     }
     $numTitles = count($readingHistoryTitles);
     //process pagination
     if ($recordsPerPage != -1) {
         $startRecord = ($page - 1) * $recordsPerPage;
         $readingHistoryTitles = array_slice($readingHistoryTitles, $startRecord, $recordsPerPage);
     }
     //The history is active if there is an opt out link.
     $historyActive = strpos($pageContents, 'OptOut') > 0;
     $timer->logTime("Loaded Reading history for patron");
     return array('historyActive' => $historyActive, 'titles' => $readingHistoryTitles, 'numTitles' => $numTitles);
 }