public function renewItem($itemId, $itemIndex) { global $logger; global $configArray; global $analytics; //Setup the call to Millennium $patronDump = $this->driver->_getPatronDump($this->driver->_getBarcode()); $extraGetInfo = array('currentsortorder' => 'current_checkout', 'renewsome' => 'YES', 'renew' . $itemIndex => $itemId); $get_items = array(); foreach ($extraGetInfo as $key => $value) { $get_items[] = $key . '=' . urlencode($value); } $renewItemParams = implode('&', $get_items); //Login to the patron's account $cookieJar = tempnam("/tmp", "CURLCOOKIE"); $curl_url = $configArray['Catalog']['url'] . "/patroninfo"; //$logger->log('Loading page ' . $curl_url, PEAR_LOG_INFO); $curl_connection = curl_init($curl_url); curl_setopt($curl_connection, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($curl_connection, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"); curl_setopt($curl_connection, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl_connection, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl_connection, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($curl_connection, CURLOPT_UNRESTRICTED_AUTH, true); curl_setopt($curl_connection, CURLOPT_COOKIEJAR, $cookieJar); curl_setopt($curl_connection, CURLOPT_COOKIESESSION, false); curl_setopt($curl_connection, CURLOPT_POST, true); $post_data = $this->driver->_getLoginFormValues(); $post_items = array(); foreach ($post_data as $key => $value) { $post_items[] = $key . '=' . urlencode($value); } $post_string = implode('&', $post_items); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $post_string); curl_exec($curl_connection); //Go to the items page $scope = $this->driver->getDefaultScope(); $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/items"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_HTTPGET, true); curl_exec($curl_connection); //Post renewal information $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/items"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_POST, true); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $renewItemParams); $checkedOutPageText = curl_exec($curl_connection); //Parse the checked out titles into individual rows $message = 'Unable to load renewal information for this entry'; $success = false; if (preg_match('/<h2>\\s*You cannot renew items because:\\s*<\\/h2><ul><li>(.*?)<\\/ul>/si', $checkedOutPageText, $matches)) { $success = false; $message = 'Unable to renew this item, ' . strtolower($matches[1]) . '.'; if ($analytics) { $analytics->addEvent('ILS Integration', 'Renew Failed', strtolower($matches[1])); } } else { if (preg_match('/Your record is in use/si', $checkedOutPageText, $matches)) { $success = false; $message = 'Unable to renew this item now, your account is in use by the system. Please try again later.'; if ($analytics) { $analytics->addEvent('ILS Integration', 'Renew Failed', 'Account in Use'); } } else { if (preg_match('/<table border="0" class="patFunc">(.*?)<\\/table>/s', $checkedOutPageText, $matches)) { $checkedOutTitleTable = $matches[1]; //$logger->log("Found checked out titles table", PEAR_LOG_DEBUG); if (preg_match_all('/<tr class="patFuncEntry">(.*?)<\\/tr>/s', $checkedOutTitleTable, $rowMatches, PREG_SET_ORDER)) { //$logger->log("Checked out titles table has " . count($rowMatches) . "rows", PEAR_LOG_DEBUG); //$logger->log(print_r($rowMatches, true), PEAR_LOG_DEBUG); for ($i = 0; $i < count($rowMatches); $i++) { $rowData = $rowMatches[$i][1]; if (preg_match("/{$itemId}/", $rowData)) { //$logger->log("Found the row for this item", PEAR_LOG_DEBUG); //Extract the renewal message if (preg_match('/<td align="left" class="patFuncStatus">.*?<em><font color="red">(.*?)<\\/font><\\/em>.*?<\\/td>/s', $rowData, $statusMatches)) { $success = false; $message = 'Unable to renew this item, ' . $statusMatches[1]; } elseif (preg_match('/<td align="left" class="patFuncStatus">.*?<em>(.*?)<\\/em>.*?<\\/td>/s', $rowData, $statusMatches)) { $success = true; $message = 'Your item was successfully renewed'; } $logger->log("Renew success = {$success}, {$message}", PEAR_LOG_DEBUG); } } } else { $logger->log("Did not find any rows for the table {$checkedOutTitleTable}", PEAR_LOG_DEBUG); } } else { $success = true; $message = 'Your item was successfully renewed'; if ($analytics) { $analytics->addEvent('ILS Integration', 'Renew Successful'); } } } } curl_close($curl_connection); unlink($cookieJar); return array('itemId' => $itemId, 'result' => $success, 'message' => $message); }
/** * Do an update or edit of reading history information. Current actions are: * deleteMarked * deleteAll * exportList * optOut * * @param string $action The action to perform * @param array $selectedTitles The titles to do the action on if applicable */ function doReadingHistoryAction($action, $selectedTitles) { global $configArray; global $analytics; $patronDump = $this->driver->_getPatronDump($this->driver->_getBarcode()); //Load the reading history page $scope = $this->driver->getDefaultScope(); $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/readinghistory"; $cookie = tempnam("/tmp", "CURLCOOKIE"); $curl_connection = curl_init($curl_url); curl_setopt($curl_connection, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($curl_connection, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"); curl_setopt($curl_connection, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl_connection, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl_connection, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($curl_connection, CURLOPT_UNRESTRICTED_AUTH, true); curl_setopt($curl_connection, CURLOPT_COOKIEJAR, $cookie); curl_setopt($curl_connection, CURLOPT_COOKIESESSION, true); curl_setopt($curl_connection, CURLOPT_POST, true); $post_data = $this->driver->_getLoginFormValues(); $post_items = array(); foreach ($post_data as $key => $value) { $post_items[] = $key . '=' . urlencode($value); } $post_string = implode('&', $post_items); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $post_string); $loginResult = curl_exec($curl_connection); //When a library uses Encore, the initial login does a redirect and requires additional parameters. if (preg_match('/<input type="hidden" name="lt" value="(.*?)" \\/>/si', $loginResult, $loginMatches)) { //Get the lt value $lt = $loginMatches[1]; //Login again $post_data['lt'] = $lt; $post_data['_eventId'] = 'submit'; $post_items = array(); foreach ($post_data as $key => $value) { $post_items[] = $key . '=' . $value; } $post_string = implode('&', $post_items); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $post_string); curl_exec($curl_connection); } if ($action == 'deleteMarked') { //Load patron page readinghistory/rsh with selected titles marked if (!isset($selectedTitles) || count($selectedTitles) == 0) { return; } $titles = array(); foreach ($selectedTitles as $titleId) { $titles[] = $titleId . '=1'; } $title_string = implode('&', $titles); //Issue a get request to delete the item from the reading history. //Note: Millennium really does issue a malformed url, and it is required //to make the history delete properly. $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/readinghistory/rsh&" . $title_string; curl_setopt($curl_connection, CURLOPT_HTTPGET, true); curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_exec($curl_connection); if ($analytics) { $analytics->addEvent('ILS Integration', 'Delete Marked Reading History Titles'); } } elseif ($action == 'deleteAll') { //load patron page readinghistory/rah $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/readinghistory/rah"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_HTTPGET, true); curl_exec($curl_connection); if ($analytics) { $analytics->addEvent('ILS Integration', 'Delete All Reading History Titles'); } } elseif ($action == 'exportList') { //Leave this unimplemented for now. } elseif ($action == 'optOut') { //load patron page readinghistory/OptOut $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/readinghistory/OptOut"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_HTTPGET, true); curl_exec($curl_connection); if ($analytics) { $analytics->addEvent('ILS Integration', 'Opt Out of Reading History'); } } elseif ($action == 'optIn') { //load patron page readinghistory/OptIn $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/readinghistory/OptIn"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_HTTPGET, true); curl_exec($curl_connection); if ($analytics) { $analytics->addEvent('ILS Integration', 'Opt in to Reading History'); } } curl_close($curl_connection); unlink($cookie); }
/** * Update a hold that was previously placed in the system. * Can cancel the hold or update pickup locations. */ public function updateHoldDetailed($patronId, $type, $titles, $xNum, $cancelId, $locationId = '', $freezeValue = 'off') { global $logger; global $configArray; $patronId = $this->driver->_getBarcode(); $patronDump = $this->driver->_getPatronDump($patronId); // Millennium has a "quirk" where you can still freeze and thaw a hold even if it is in the wrong status. // therefore we need to check the current status before we freeze or unfreeze. $scope = $this->driver->getDefaultScope(); //go to the holds page and get the number of holds on the account // $holds = $this->getMyHolds(); // only needed for loading titles at this point. plb 2-3-2015 // $numHoldsStart = count($holds['holds']['available'] + $holds['holds']['unavailable']); if (!isset($xNum)) { // below requests variables should be deprecated as of now. plb 2-9-2015 if (isset($_REQUEST['waitingholdselected']) || isset($_REQUEST['availableholdselected'])) { $waitingHolds = isset($_REQUEST['waitingholdselected']) ? $_REQUEST['waitingholdselected'] : array(); $availableHolds = isset($_REQUEST['availableholdselected']) ? $_REQUEST['availableholdselected'] : array(); $xNum = array_merge($waitingHolds, $availableHolds); } else { $xNum = is_array($cancelId) ? $cancelId : array($cancelId); } } $location = new Location(); if (isset($locationId) && is_numeric($locationId)) { $location->whereAdd("locationId = '{$locationId}'"); $location->find(); if ($location->N == 1) { $location->fetch(); $paddedLocation = str_pad(trim($location->code), 5, "+"); } } else { $paddedLocation = isset($locationId) ? $locationId : null; } $cancelValue = $type == 'cancel' || $type == 'recall' ? 'on' : 'off'; $loadTitles = empty($titles); if ($loadTitles) { $holds = $this->getMyHolds(); $combined_holds = array_merge($holds['holds']['unavailable'], $holds['holds']['available']); } $logger->log("Load titles = {$loadTitles}", PEAR_LOG_DEBUG); // move out of foreach loop $extraGetInfo = array('updateholdssome' => 'YES', 'currentsortorder' => 'current_pickup'); foreach ($xNum as $tmpXnumInfo) { list($tmpBib, $tmpXnum) = explode('~', $tmpXnumInfo); if ($type == 'cancel') { $extraGetInfo['cancel' . $tmpBib . 'x' . $tmpXnum] = $cancelValue; } elseif ($type == 'update') { // $holdForXNum = $this->getHoldByXNum($holds, $tmpXnum); //$holds isn't actually used by the function $holdForXNum = $this->getHoldByXNum('', $tmpXnum); //$holds isn't actually used by the function $canUpdate = false; if ($holdForXNum != null) { if ($freezeValue == 'off') { if ($holdForXNum['frozen']) { $canUpdate = true; } } elseif ($freezeValue == 'on') { if ($holdForXNum['frozen'] == false && $holdForXNum['freezeable'] == true) { $canUpdate = true; } } elseif ($freezeValue == '') { if (isset($paddedLocation) && $holdForXNum['locationUpdateable']) { $canUpdate = true; } } } if ($canUpdate) { if (isset($paddedLocation)) { $extraGetInfo['loc' . $tmpBib . 'x' . $tmpXnum] = $paddedLocation; } if (!empty($freezeValue)) { $extraGetInfo['freeze' . $tmpBib . 'x' . $tmpXnum] = $freezeValue; } } } if ($loadTitles) { // Get Title for Each Item $tmp_title = ''; foreach ($combined_holds as $hold) { if ($hold['shortId'] == $tmpBib) { $tmp_title = $hold['title']; break; } } $titles[$tmpBib] = $tmp_title; } } // End of foreach loop $holdUpdateParams = http_build_query($extraGetInfo); if (1) { // just to hide curl ops //Login to the patron's account $cookieJar = tempnam("/tmp", "CURLCOOKIE"); $success = false; $curl_url = $configArray['Catalog']['url'] . "/patroninfo"; $logger->log('Loading page ' . $curl_url, PEAR_LOG_INFO); $curl_connection = curl_init($curl_url); $header = array(); $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,"; $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; $header[] = "Cache-Control: max-age=0"; $header[] = "Connection: keep-alive"; $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7"; $header[] = "Accept-Language: en-us,en;q=0.5"; curl_setopt($curl_connection, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($curl_connection, CURLOPT_HTTPHEADER, $header); curl_setopt($curl_connection, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"); curl_setopt($curl_connection, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl_connection, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl_connection, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($curl_connection, CURLOPT_UNRESTRICTED_AUTH, true); curl_setopt($curl_connection, CURLOPT_COOKIEJAR, $cookieJar); curl_setopt($curl_connection, CURLOPT_COOKIESESSION, false); curl_setopt($curl_connection, CURLOPT_POST, true); $post_data = $this->driver->_getLoginFormValues(); $post_string = http_build_query($post_data); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $post_string); //Load the page, but we don't need to do anything with the results. $loginResult = curl_exec($curl_connection); //When a library uses Encore, the initial login does a redirect and requires additional parameters. if (preg_match('/<input type="hidden" name="lt" value="(.*?)" \\/>/si', $loginResult, $loginMatches)) { //Get the lt value $lt = $loginMatches[1]; //Login again $post_data['lt'] = $lt; $post_data['_eventId'] = 'submit'; $post_string = http_build_query($post_data); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $post_string); $loginResult = curl_exec($curl_connection); $curlInfo = curl_getinfo($curl_connection); } //Issue a post request with the information about what to do with the holds $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/holds"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $holdUpdateParams); curl_setopt($curl_connection, CURLOPT_POST, true); $sResult = curl_exec($curl_connection); $hold_original_results = $this->parseHoldsPage($sResult); // TODO: Get Failure Messages //$holds = $this->parseHoldsPage($sResult); //At this stage, we get messages if there were any errors freezing holds. } //Go back to the hold page to check make sure our hold was cancelled // Don't believe the page reload is necessary. same output as above. plb 2-3-2015 $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/holds"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_HTTPGET, true); $sResult = curl_exec($curl_connection); $holds = $this->parseHoldsPage($sResult); curl_close($curl_connection); unlink($cookieJar); if ($hold_original_results != $holds) { //test if they are the same $logger->log('Original Hold Results are different from the second Round!', PEAR_LOG_WARNING); } $combined_holds = array_merge($holds['unavailable'], $holds['available']); // $numHoldsEnd = count($combined_holds); //Finally, check to see if the update was successful. if ($type == 'cancel' || $type == 'recall') { $failure_messages = array(); // $success_messages = array(); foreach ($xNum as $tmpXnumInfo) { list($tmpBib) = explode('~', $tmpXnumInfo); // $failed = false; foreach ($combined_holds as $hold) { if ($tmpBib == $hold['shortId']) { // this hold failed (item still on hold) $title = array_key_exists($tmpBib, $titles) && $titles[$tmpBib] != '' ? $titles[$tmpBib] : 'an item'; $failure_messages[$tmpXnumInfo] = "The hold for {$title} could not be cancelled. Please try again later or see your librarian."; // use original id as index so that javascript functions can pick out failed cancels // $failed = true; break; } } // Currently individual success messages not used. // if (!$failed){ // $success_messages[] = "The hold for {$titles[$tmpBib]} was successfully cancelled."; // } } $success = empty($failure_messages); if ($success) { $logger->log('Cancelled ok', PEAR_LOG_INFO); } } //Make sure to clear any cached data global $memCache; $memCache->delete("patron_dump_{$this->driver->_getBarcode()}"); usleep(250); //Clear holds for the patron unset($this->holds[$patronId]); $this->driver->clearPatronProfile(); // Return Results $plural = count($xNum) > 1; global $analytics; $title_list = is_array($titles) ? implode(', ', $titles) : $titles; if ($type == 'cancel' || $type == 'recall') { if ($success) { // All were successful $analytics->addEvent('ILS Integration', 'Hold Cancelled', $title_list); return array('title' => $titles, 'result' => true, 'message' => 'Your hold' . ($plural ? 's were' : ' was') . ' cancelled successfully.'); } else { // at least one failure $analytics->addEvent('ILS Integration', 'Hold Not Cancelled', $title_list); return array('title' => $titles, 'result' => false, 'message' => $failure_messages); } } else { $analytics->addEvent('ILS Integration', 'Hold(s) Updated', $title_list); return array('title' => $titles, 'result' => true, 'message' => 'Your hold' . ($plural ? 's were' : ' was') . ' updated successfully.'); } }
public function renewItem($itemId, $itemIndex) { global $logger; global $configArray; global $analytics; //Setup the call to Millennium $patronDump = $this->driver->_getPatronDump($this->driver->_getBarcode()); $extraGetInfo = array('currentsortorder' => 'current_checkout', 'renewsome' => 'YES', 'renew' . $itemIndex => $itemId); $renewItemParams = http_build_query($extraGetInfo); //Login to the patron's account $cookieJar = tempnam("/tmp", "CURLCOOKIE"); $curl_url = $configArray['Catalog']['url'] . "/patroninfo"; //$logger->log('Loading page ' . $curl_url, PEAR_LOG_INFO); $curl_connection = curl_init($curl_url); curl_setopt($curl_connection, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($curl_connection, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"); curl_setopt($curl_connection, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl_connection, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl_connection, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($curl_connection, CURLOPT_UNRESTRICTED_AUTH, true); curl_setopt($curl_connection, CURLOPT_COOKIEJAR, $cookieJar); curl_setopt($curl_connection, CURLOPT_COOKIESESSION, false); curl_setopt($curl_connection, CURLOPT_POST, true); $post_data = $this->driver->_getLoginFormValues(); $post_string = http_build_query($post_data); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $post_string); $loginResult = curl_exec($curl_connection); //When a library uses Encore, the initial login does a redirect and requires additional parameters. if (preg_match('/<input type="hidden" name="lt" value="(.*?)" \\/>/si', $loginResult, $loginMatches)) { //Get the lt value $lt = $loginMatches[1]; //Login again $post_data['lt'] = $lt; $post_data['_eventId'] = 'submit'; $post_string = http_build_query($post_data); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $post_string); $loginResult = curl_exec($curl_connection); $curlInfo = curl_getinfo($curl_connection); } //Go to the items page $scope = $this->driver->getDefaultScope(); $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/items"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_HTTPGET, true); curl_exec($curl_connection); //Post renewal information $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/items"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_POST, true); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $renewItemParams); $checkedOutPageText = curl_exec($curl_connection); //Parse the checked out titles into individual rows $message = 'Unable to load renewal information for this entry.'; $success = false; if (preg_match('/<h2>\\s*You cannot renew items because:\\s*<\\/h2><ul><li>(.*?)<\\/ul>/si', $checkedOutPageText, $matches)) { $success = false; $msg = ucfirst(strtolower(trim($matches[1]))); $message = "Unable to renew this item: {$msg}."; if ($analytics) { $analytics->addEvent('ILS Integration', 'Renew Failed', $msg); } } elseif (preg_match('/Your record is in use/si', $checkedOutPageText, $matches)) { $success = false; $message = 'Unable to renew this item now, your account is in use by the system. Please try again later.'; if ($analytics) { $analytics->addEvent('ILS Integration', 'Renew Failed', 'Account in Use'); } } elseif (preg_match('/<table border="0" class="patFunc">(.*?)<\\/table>/s', $checkedOutPageText, $matches)) { $checkedOutTitleTable = $matches[1]; //$logger->log("Found checked out titles table", PEAR_LOG_DEBUG); if (preg_match_all('/<tr class="patFuncEntry">(.*?)<\\/tr>/s', $checkedOutTitleTable, $rowMatches, PREG_SET_ORDER)) { //$logger->log("Checked out titles table has " . count($rowMatches) . "rows", PEAR_LOG_DEBUG); //$logger->log(print_r($rowMatches, true), PEAR_LOG_DEBUG); // for ($i = 0; $i < count($rowMatches); $i++) { foreach ($rowMatches as $i => $row) { $rowData = $row[1]; if (preg_match("/{$itemId}/", $rowData)) { //$logger->log("Found the row for this item", PEAR_LOG_DEBUG); //Extract the renewal message if (preg_match('/<td align="left" class="patFuncStatus">.*?<em><font color="red">(.*?)<\\/font><\\/em>.*?<\\/td>/s', $rowData, $statusMatches)) { $success = false; $msg = ucfirst(strtolower(trim($statusMatches[1]))); $title = $this->extract_title_from_row($rowData); $message = "Unable to renew {$title}: {$msg}."; // title needed for in renewSelectedItems to distinguish which item failed. } elseif (preg_match('/<td align="left" class="patFuncStatus">.*?<em>(.*?)<\\/em>.*?<\\/td>/s', $rowData, $statusMatches)) { $success = true; $message = 'Your item was successfully renewed'; } $logger->log("Renew success = {$success}, {$message}", PEAR_LOG_DEBUG); break; // found our item, get out of loop. } } } else { $logger->log("Did not find any rows for the table {$checkedOutTitleTable}", PEAR_LOG_DEBUG); } } else { $success = true; $message = 'Your item was successfully renewed'; if ($analytics) { $analytics->addEvent('ILS Integration', 'Renew Successful'); } } curl_close($curl_connection); unlink($cookieJar); return array('itemId' => $itemId, 'result' => $success, 'message' => $message); }
/** * Load status (holdings) for a record and filter them based on the logged in user information. * * Format of return array is: * key = {section#}{location}-### where ### is the holding iteration * * value = array ( * id = The id of the bib * number = The position of the holding within the original list of holdings * section = A description of the section * sectionId = a numeric id of the section for sorting * type = holding * status * statusfull * availability * holdable * nonHoldableReason * reserve * holdQueueLength * duedate * location * libraryDisplayName * locationLink * callnumber * link = array * linkText * isDownload * ) * * Includes both physical titles as well as titles on order * * @param string $id the id of the record * @return array A list of holdings for the record */ public function getStatus($id) { if (array_key_exists($id, MillenniumStatusLoader::$loadedStatus)) { return MillenniumStatusLoader::$loadedStatus[$id]; } global $library; global $user; global $timer; global $logger; global $configArray; $pType = $this->driver->getPType(); $scope = $this->driver->getMillenniumScope(); if (!$configArray['Catalog']['offline']) { //Get information about holdings, order information, and issue information $millenniumInfo = $this->driver->getMillenniumRecordInfo($id); //Get the number of holds if ($millenniumInfo->framesetInfo) { if (preg_match('/(\\d+) hold(s?) on .*? of \\d+ (copies|copy)/', $millenniumInfo->framesetInfo, $matches)) { $holdQueueLength = $matches[1]; } else { $holdQueueLength = 0; } } // Load Record Page $r = substr($millenniumInfo->holdingsInfo, stripos($millenniumInfo->holdingsInfo, 'bibItems')); $r = substr($r, strpos($r, ">") + 1); $r = substr($r, 0, stripos($r, "</table")); $rows = preg_split("/<tr([^>]*)>/", $r); } else { $rows = array(); $millenniumInfo = null; } //Load item information from marc record $matchItemsWithMarcItems = $configArray['Catalog']['matchItemsWithMarcItems']; if ($matchItemsWithMarcItems) { // Load the full marc record so we can get the iType for each record. $marcRecord = MarcLoader::loadMarcRecordByILSId($id); $marcItemField = isset($configArray['Reindex']['itemTag']) ? $configArray['Reindex']['itemTag'] : '989'; $itemFields = $marcRecord->getFields($marcItemField); $marcItemData = array(); //TODO: Don't hardcode item subfields $statusSubfield = $configArray['Reindex']['statusSubfield']; $dueDateSubfield = $configArray['Reindex']['dueDateSubfield']; $locationSubfield = $configArray['Reindex']['locationSubfield']; $iTypeSubfield = $configArray['Reindex']['iTypeSubfield']; $callNumberPrestampSubfield = $configArray['Reindex']['callNumberPrestampSubfield']; $callNumberSubfield = $configArray['Reindex']['callNumberSubfield']; $callNumberCutterSubfield = $configArray['Reindex']['callNumberCutterSubfield']; $callNumberPoststampSubfield = $configArray['Reindex']['callNumberPoststampSubfield']; $volumeSubfield = $configArray['Reindex']['volumeSubfield']; $lastCheckinDateSubfield = $configArray['Reindex']['lastCheckinDateSubfield']; // added plb 3-24-2015 foreach ($itemFields as $itemField) { /** @var $itemField File_MARC_Data_Field */ $fullCallNumber = $itemField->getSubfield($callNumberPrestampSubfield) != null ? $itemField->getSubfield($callNumberPrestampSubfield)->getData() . ' ' : ''; $fullCallNumber .= $itemField->getSubfield($callNumberSubfield) != null ? $itemField->getSubfield($callNumberSubfield)->getData() : ''; $fullCallNumber .= $itemField->getSubfield($callNumberCutterSubfield) != null ? ' ' . $itemField->getSubfield($callNumberCutterSubfield)->getData() : ''; $fullCallNumber .= $itemField->getSubfield($callNumberPoststampSubfield) != null ? ' ' . $itemField->getSubfield($callNumberPoststampSubfield)->getData() : ''; $fullCallNumber .= $itemField->getSubfield($volumeSubfield) != null ? ' ' . $itemField->getSubfield($volumeSubfield)->getData() : ''; $fullCallNumber = str_replace(' ', ' ', $fullCallNumber); $itemData['callnumber'] = $fullCallNumber; $itemData['location'] = $itemField->getSubfield($locationSubfield) != null ? trim($itemField->getSubfield($locationSubfield)->getData()) : '?????'; $itemData['iType'] = $itemField->getSubfield($iTypeSubfield) != null ? $itemField->getSubfield($iTypeSubfield)->getData() : '-1'; $itemData['matched'] = false; $itemData['status'] = $itemField->getSubfield($statusSubfield) != null ? $itemField->getSubfield($statusSubfield)->getData() : '-'; $itemData['dueDate'] = $itemField->getSubfield($dueDateSubfield) != null ? trim($itemField->getSubfield($dueDateSubfield)->getData()) : null; $lastCheckinDate = $itemField->getSubfield($lastCheckinDateSubfield); // added plb 3-24-2015 if ($lastCheckinDate) { // convert to timestamp for ease of display in template $lastCheckinDate = trim($lastCheckinDate->getData()); $lastCheckinDate = DateTime::createFromFormat('m-d-Y G:i', $lastCheckinDate); if ($lastCheckinDate) { $lastCheckinDate = $lastCheckinDate->getTimestamp(); } } $itemData['lastCheckinDate'] = $lastCheckinDate ? $lastCheckinDate : null; $marcItemData[] = $itemData; } } else { $marcItemData = array(); $marcRecord = null; } if (!$configArray['Catalog']['offline']) { //Process each row in the callnumber table. $ret = $this->parseHoldingRows($id, $rows); if (count($ret) == 0) { //Also check the frameset for links if (preg_match('/<div class="bibDisplayUrls">.*?<table.*?>(.*?)<\\/table>.*?<\\/div>/si', $millenniumInfo->framesetInfo, $displayUrlInfo)) { $linksTable = $displayUrlInfo[1]; preg_match_all('/<td.*?>.*?<a href="(.*?)".*?>(.*?)<\\/a>.*?<\\/td>/si', $linksTable, $linkData, PREG_SET_ORDER); for ($i = 0; $i < count($linkData); $i++) { $linkText = $linkData[$i][2]; if ($linkText != 'Latest Received') { $newHolding = array('type' => 'holding', 'link' => array(), 'status' => 'Online', 'location' => 'Online'); $newHolding['link'][] = array('link' => $linkData[$i][1], 'linkText' => $linkText, 'isDownload' => true); $ret[] = $newHolding; } } } } $timer->logTime('processed all holdings rows'); } else { $ret = null; } global $locationSingleton; /** @var $locationSingleton Location */ $physicalLocation = $locationSingleton->getPhysicalLocation(); if ($physicalLocation != null) { $physicalBranch = $physicalLocation->holdingBranchLabel; } else { $physicalBranch = ''; } $homeBranch = ''; $homeBranchId = 0; $nearbyBranch1 = ''; $nearbyBranch1Id = 0; $nearbyBranch2 = ''; $nearbyBranch2Id = 0; //Set location information based on the user login. This will override information based if (isset($user) && $user != false) { $homeBranchId = $user->homeLocationId; $nearbyBranch1Id = $user->myLocation1Id; $nearbyBranch2Id = $user->myLocation2Id; } else { //Check to see if the cookie for home location is set. if (isset($_COOKIE['home_location']) && is_numeric($_COOKIE['home_location'])) { $cookieLocation = new Location(); $locationId = $_COOKIE['home_location']; $cookieLocation->whereAdd("locationId = '{$locationId}'"); $cookieLocation->find(); if ($cookieLocation->N == 1) { $cookieLocation->fetch(); $homeBranchId = $cookieLocation->locationId; $nearbyBranch1Id = $cookieLocation->nearbyLocation1; $nearbyBranch2Id = $cookieLocation->nearbyLocation2; } } } //Load the holding label for the user's home location. $userLocation = new Location(); $userLocation->whereAdd("locationId = '{$homeBranchId}'"); $userLocation->find(); if ($userLocation->N == 1) { $userLocation->fetch(); $homeBranch = $userLocation->holdingBranchLabel; } //Load nearby branch 1 $nearbyLocation1 = new Location(); $nearbyLocation1->whereAdd("locationId = '{$nearbyBranch1Id}'"); $nearbyLocation1->find(); if ($nearbyLocation1->N == 1) { $nearbyLocation1->fetch(); $nearbyBranch1 = $nearbyLocation1->holdingBranchLabel; } //Load nearby branch 2 $nearbyLocation2 = new Location(); $nearbyLocation2->whereAdd(); $nearbyLocation2->whereAdd("locationId = '{$nearbyBranch2Id}'"); $nearbyLocation2->find(); if ($nearbyLocation2->N == 1) { $nearbyLocation2->fetch(); $nearbyBranch2 = $nearbyLocation2->holdingBranchLabel; } $sorted_array = array(); //Get a list of the display names for all locations based on holding label. $locationLabels = array(); $location = new Location(); $location->find(); $libraryLocationLabels = array(); $locationCodes = array(); $suppressedLocationCodes = array(); while ($location->fetch()) { if (strlen($location->holdingBranchLabel) > 0 && $location->holdingBranchLabel != '???') { if ($library && $library->libraryId == $location->libraryId) { $cleanLabel = str_replace('/', '\\/', $location->holdingBranchLabel); $libraryLocationLabels[] = str_replace('.', '\\.', $cleanLabel); } $locationLabels[$location->holdingBranchLabel] = $location->displayName; $locationCodes[$location->code] = $location->holdingBranchLabel; if ($location->suppressHoldings == 1) { $suppressedLocationCodes[$location->code] = $location->code; } } } if (count($libraryLocationLabels) > 0) { $libraryLocationLabels = '/^(' . join('|', $libraryLocationLabels) . ').*/i'; } else { $libraryLocationLabels = ''; } //Get the current Ptype for later usage. $timer->logTime('setup for additional holdings processing.'); //Now that we have the holdings, we need to filter and sort them according to scoping rules. if (!$configArray['Catalog']['offline']) { $i = 0; foreach ($ret as $holdingKey => $holding) { $holding['type'] = 'holding'; //Process holdings without call numbers - Need to show items without call numbers //because they may have links, etc. Also show if there is a status. Since some //In process items may not have a call number yet. if ((!isset($holding['callnumber']) || strlen($holding['callnumber']) == 0) && (!isset($holding['link']) || count($holding['link']) == 0) && !isset($holding['status'])) { continue; } //Determine if the holding is available or not. //First check the status if (preg_match('/^(' . $this->driver->availableStatiRegex . ')$/', $holding['status'])) { $holding['availability'] = 1; } else { $holding['availability'] = 0; } if (preg_match('/^(' . $this->driver->holdableStatiRegex . ')$/', $holding['status'])) { $holding['holdable'] = 1; } else { $holding['holdable'] = 0; $holding['nonHoldableReason'] = "This item is not currently available for Patron Holds"; } if (!isset($holding['libraryDisplayName'])) { $holding['libraryDisplayName'] = $holding['location']; } //Get the location id for this holding $holding['locationCode'] = '?????'; foreach ($locationCodes as $locationCode => $holdingLabel) { if (strlen($locationCode) > 0 && preg_match("~{$holdingLabel}~i", $holding['location'])) { $holding['locationCode'] = $locationCode; break; } } if ($holding['locationCode'] == '?????') { $logger->log("Did not find location code for " . $holding['location'] . " record {$id}", PEAR_LOG_DEBUG); } if (array_key_exists($holding['locationCode'], $suppressedLocationCodes)) { $logger->log("Location " . $holding['locationCode'] . " is suppressed", PEAR_LOG_DEBUG); continue; } //Now that we have the location code, try to match with the marc record $holding['iType'] = 0; if ($matchItemsWithMarcItems) { foreach ($marcItemData as $itemKey => $itemData) { if ($itemData['matched'] === false) { // ensure not checked already $locationMatched = strpos($itemData['location'], $holding['locationCode']) === 0; $itemCallNumber = isset($itemData['callnumber']) ? $itemData['callnumber'] : ''; $holdingCallNumber = isset($holding['callnumber']) ? $holding['callnumber'] : ''; if (strlen($itemCallNumber) == 0 || strlen($holding['callnumber']) == 0) { $callNumberMatched = strlen($itemCallNumber) == strlen($holdingCallNumber); } else { $callNumberMatched = strpos($itemCallNumber, $holdingCallNumber) >= 0; } if ($locationMatched && $callNumberMatched) { $holding['iType'] = $itemData['iType']; $holding['lastCheckinDate'] = $itemData['lastCheckinDate']; // if the marc record matches up, include the last checkin date in the holding info. //Get the more specific location code if (strlen($holding['locationCode']) < strlen($itemData['location'])) { $holding['locationCode'] = $itemData['location']; } $itemData['matched'] = true; $marcItemData[$itemKey] = $itemData; // add matched sub-array element back to original array being looped over. break; } } } //Check to see if this item can be held by the current patron. Only important when //we know what pType is in use and we are showing all items. if ($scope == $this->driver->getDefaultScope() && $pType >= 0) { //Never remove the title if it is owned by the current library (could be in library use only) if (isset($library) && strlen($library->ilsCode) > 0 && strpos($holding['locationCode'], $library->ilsCode) === 0) { //$logger->log("Cannot remove holding because it belongs to the active library", PEAR_LOG_DEBUG); } else { if (!$this->driver->isItemHoldableToPatron($holding['locationCode'], $holding['iType'], $pType)) { //$logger->log("Removing item $holdingKey because it is not usable by the current patronType $pType, iType is {$holding['iType']}, location is {$holding['locationCode']}", PEAR_LOG_DEBUG); //echo("Removing item $holdingKey because it is not usable by the current patronType $pType, iType is {$holding['iType']}, location is {$holding['locationCode']}"); unset($ret[$holdingKey]); continue; } } } else { if ($pType >= 0 && $holding['holdable'] == 1) { //We won't want to remove titles based on holdability, but we do want to check if it is holdable if (!$this->driver->isItemHoldableToPatron($holding['locationCode'], $holding['iType'], $pType)) { $holding['holdable'] = 0; } } } } //Set the hold queue length $holding['holdQueueLength'] = isset($holdQueueLength) ? $holdQueueLength : null; //Add the holding to the sorted array to determine $paddedNumber = str_pad($i, 3, '0', STR_PAD_LEFT); $sortString = $holding['location'] . '-' . $paddedNumber; //$sortString = $holding['location'] . $holding['callnumber']. $i; if (strlen($physicalBranch) > 0 && stripos($holding['location'], $physicalBranch) !== false) { //If the user is in a branch, those holdings come first. $holding['section'] = 'In this library'; $holding['sectionId'] = 1; $sorted_array['1' . $sortString] = $holding; } else { if (strlen($homeBranch) > 0 && stripos($holding['location'], $homeBranch) !== false) { //Next come the user's home branch if the user is logged in or has the home_branch cookie set. $holding['section'] = 'Your library'; $holding['sectionId'] = 2; $sorted_array['2' . $sortString] = $holding; } else { if (strlen($nearbyBranch1) > 0 && stripos($holding['location'], $nearbyBranch1) !== false) { //Next come nearby locations for the user $holding['section'] = 'Nearby Libraries'; $holding['sectionId'] = 3; $sorted_array['3' . $sortString] = $holding; } else { if (strlen($nearbyBranch2) > 0 && stripos($holding['location'], $nearbyBranch2) !== false) { //Next come nearby locations for the user $holding['section'] = 'Nearby Libraries'; $holding['sectionId'] = 4; $sorted_array['4' . $sortString] = $holding; } else { if (strlen($libraryLocationLabels) > 0 && preg_match($libraryLocationLabels, $holding['location'])) { //Next come any locations within the same system we are in. $holding['section'] = $library->displayName; $holding['sectionId'] = 5; $sorted_array['5' . $sortString] = $holding; } else { //Finally, all other holdings are shown sorted alphabetically. $holding['section'] = 'Other Locations'; $holding['sectionId'] = 6; $sorted_array['6' . $sortString] = $holding; } } } } } $i++; } } else { $i = 0; //Offline circ, process each item in the marc record foreach ($marcItemData as $marcData) { $i++; $holding = array(); $holding['type'] = 'holding'; $holding['locationCode'] = $marcData['location']; $holding['callnumber'] = $marcData['callnumber']; $holding['statusfull'] = $this->translateStatusCode($marcData['status'], $marcData['dueDate']); $holding['lastCheckinDate'] = $marcData['lastCheckinDate']; //Try to translate the location code at least to location $location = new Location(); $location->whereAdd("LOCATE(code, '{$marcData['location']}') = 1"); if ($location->find(true)) { $holding['location'] = $location->displayName; } else { $holding['location'] = $marcData['location']; } if (array_key_exists($holding['locationCode'], $suppressedLocationCodes)) { $logger->log("Location " . $holding['locationCode'] . " is suppressed", PEAR_LOG_DEBUG); continue; } $holding['iType'] = $marcData['iType']; if ($marcData['status'] == '-' && $marcData['dueDate'] == null) { $holding['availability'] = 1; } else { $holding['availability'] = 0; } //Check to see if this item can be held by the current patron. Only important when //we know what pType is in use and we are showing all items. if ($scope == $this->driver->getDefaultScope() && $pType > 0) { //Never remove the title if it is owned by the current library (could be in library use only) if (isset($library) && strlen($library->ilsCode) > 0 && strpos($holding['locationCode'], $library->ilsCode) === 0) { //$logger->log("Cannot remove holding because it belongs to the active library", PEAR_LOG_DEBUG); } else { if (!$this->driver->isItemHoldableToPatron($holding['locationCode'], $holding['iType'], $pType)) { //$logger->log("Removing item because it is not usable by the current patronType $pType, iType is {$holding['iType']}, location is {$holding['locationCode']}", PEAR_LOG_DEBUG); //echo("Removing item $holdingKey because it is not usable by the current patronType $pType, iType is {$holding['iType']}, location is {$holding['locationCode']}"); continue; } } } $paddedNumber = str_pad($i, 3, '0', STR_PAD_LEFT); $sortString = $holding['location'] . '-' . $paddedNumber; if (strlen($physicalBranch) > 0 && stripos($holding['location'], $physicalBranch) !== false) { //If the user is in a branch, those holdings come first. $holding['section'] = 'In this library'; $holding['sectionId'] = 1; $sorted_array['1' . $sortString] = $holding; } else { if (strlen($homeBranch) > 0 && stripos($holding['location'], $homeBranch) !== false) { //Next come the user's home branch if the user is logged in or has the home_branch cookie set. $holding['section'] = 'Your library'; $holding['sectionId'] = 2; $sorted_array['2' . $sortString] = $holding; } else { if (strlen($nearbyBranch1) > 0 && stripos($holding['location'], $nearbyBranch1) !== false) { //Next come nearby locations for the user $holding['section'] = 'Nearby Libraries'; $holding['sectionId'] = 3; $sorted_array['3' . $sortString] = $holding; } else { if (strlen($nearbyBranch2) > 0 && stripos($holding['location'], $nearbyBranch2) !== false) { //Next come nearby locations for the user $holding['section'] = 'Nearby Libraries'; $holding['sectionId'] = 4; $sorted_array['4' . $sortString] = $holding; } else { if (strlen($libraryLocationLabels) > 0 && preg_match($libraryLocationLabels, $holding['location'])) { //Next come any locations within the same system we are in. $holding['section'] = $library->displayName; $holding['sectionId'] = 5; $sorted_array['5' . $sortString] = $holding; } else { //Finally, all other holdings are shown sorted alphabetically. $holding['section'] = 'Other Locations'; $holding['sectionId'] = 6; $sorted_array['6' . $sortString] = $holding; } } } } } } } $timer->logTime('finished processing holdings'); //Check to see if the title is holdable $holdable = $this->driver->isRecordHoldable($marcRecord); foreach ($sorted_array as $key => $holding) { //Do not override holdability based on status if (isset($holding['holdable']) && $holding['holdable'] == 1) { $holding['holdable'] = $holdable ? 1 : 0; $sorted_array[$key] = $holding; } } if (!$configArray['Catalog']['offline']) { //Load order records, these only show in the full page view, not the item display $orderMatches = array(); if (preg_match_all('/<tr\\s+class="bibOrderEntry">.*?<td\\s*>(.*?)<\\/td>/s', $millenniumInfo->framesetInfo, $orderMatches)) { for ($i = 0; $i < count($orderMatches[1]); $i++) { $location = trim($orderMatches[1][$i]); $location = preg_replace('/\\sC\\d{3}\\w{0,2}[\\s\\.]/', '', $location); //Remove courier code if any $sorted_array['7' . $location . $i] = array('location' => $location, 'section' => 'On Order', 'sectionId' => 7, 'holdable' => 1); } } $timer->logTime('loaded order records'); } ksort($sorted_array); //Check to see if we can remove the sections. //We can if all section keys are the same. $removeSection = true; $lastKeyIndex = ''; foreach ($sorted_array as $key => $holding) { $currentKey = substr($key, 0, 1); if ($lastKeyIndex == '') { $lastKeyIndex = $currentKey; } else { if ($lastKeyIndex != $currentKey) { $removeSection = false; break; } } } foreach ($sorted_array as $key => $holding) { if ($removeSection == true) { $holding['section'] = ''; $sorted_array[$key] = $holding; } } if (!$configArray['Catalog']['offline']) { $issueSummaries = $this->driver->getIssueSummaries($millenniumInfo); } else { $issueSummaries = null; } $timer->logTime('loaded issue summaries'); if (!is_null($issueSummaries)) { krsort($sorted_array); //Group holdings under the issue issue summary that is related. foreach ($sorted_array as $key => $holding) { //Have issue summary = false $haveIssueSummary = false; $issueSummaryKey = null; foreach ($issueSummaries as $issueKey => $issueSummary) { if ($issueSummary['location'] == $holding['location']) { $haveIssueSummary = true; $issueSummaryKey = $issueKey; break; } } if ($haveIssueSummary) { $issueSummaries[$issueSummaryKey]['holdings'][strtolower($key)] = $holding; } else { //Need to automatically add a summary so we don't lose data $issueSummaries[$holding['location']] = array('location' => $holding['location'], 'type' => 'issue', 'holdings' => array(strtolower($key) => $holding)); } } foreach ($issueSummaries as $key => $issueSummary) { if (isset($issueSummary['holdings']) && is_array($issueSummary['holdings'])) { krsort($issueSummary['holdings']); $issueSummaries[$key] = $issueSummary; } } ksort($issueSummaries); $status = $issueSummaries; } else { $status = $sorted_array; } MillenniumStatusLoader::$loadedStatus[$id] = $status; return $status; }
/** * Update a hold that was previously placed in the system. * Can cancel the hold or update pickup locations. */ public function updateHoldDetailed($patronId, $type, $title, $xNum, $cancelId, $locationId, $freezeValue = 'off') { global $logger; global $configArray; $patronDump = $this->driver->_getPatronDump($this->driver->_getBarcode()); // Millennium has a "quirk" where you can still freeze and thaw a hold even if it is in the wrong status. // therefore we need to check the current status before we freeze or unfreeze. $scope = $this->driver->getDefaultScope(); //go to the holds page and get the number of holds on the account $holds = $this->getMyHolds(); $numHoldsStart = count($holds['available'] + $holds['unavailable']); if (!isset($xNum)) { $waitingHolds = isset($_REQUEST['waitingholdselected']) ? $_REQUEST['waitingholdselected'] : array(); $availableHolds = isset($_REQUEST['availableholdselected']) ? $_REQUEST['availableholdselected'] : array(); $xNum = array_merge($waitingHolds, $availableHolds); } $location = new Location(); if (isset($locationId) && is_numeric($locationId)) { $location->whereAdd("locationId = '{$locationId}'"); $location->find(); if ($location->N == 1) { $location->fetch(); $paddedLocation = str_pad(trim($location->code), 5, "+"); } } else { $paddedLocation = null; } $cancelValue = $type == 'cancel' || $type == 'recall' ? 'on' : 'off'; if (!is_array($xNum)) { $xNum = array($xNum); } $loadTitles = !isset($title) || strlen($title) == 0; $logger->log("Load titles = {$loadTitles}", PEAR_LOG_DEBUG); $extraGetInfo = array('updateholdssome' => 'YES', 'currentsortorder' => 'current_pickup'); foreach ($xNum as $tmpXnumInfo) { list($tmpBib, $tmpXnum) = explode("~", $tmpXnumInfo); if ($type == 'cancel') { $extraGetInfo['cancel' . $tmpBib . 'x' . $tmpXnum] = $cancelValue; } if ($type == 'update') { $holdForXNum = $this->getHoldByXNum($holds, $tmpXnum); $canUpdate = false; if ($holdForXNum != null) { if ($freezeValue == 'off') { if ($holdForXNum['frozen']) { $canUpdate = true; } } else { if ($freezeValue == 'on') { if ($holdForXNum['frozen'] == false) { if ($holdForXNum['freezeable'] == true) { $canUpdate = true; } } } else { if ($freezeValue == '' && isset($paddedLocation) && $holdForXNum['locationUpdateable']) { $canUpdate = true; } } } } if ($canUpdate) { if (isset($paddedLocation)) { $extraGetInfo['loc' . $tmpBib . 'x' . $tmpXnum] = $paddedLocation; } if (strlen($freezeValue) > 0) { $extraGetInfo['freeze' . $tmpBib . 'x' . $tmpXnum] = $freezeValue; } } } if ($loadTitles) { $resource = new Resource(); $resource->shortId = $tmpBib; if ($resource->find(true)) { if (strlen($title) > 0) { $title .= ", "; } $title .= $resource->title; } else { $logger->log("Did not find bib for = {$tmpBib}", PEAR_LOG_DEBUG); } } } $get_items = array(); foreach ($extraGetInfo as $key => $value) { $get_items[] = $key . '=' . urlencode($value); } $holdUpdateParams = implode('&', $get_items); //Login to the patron's account $cookieJar = tempnam("/tmp", "CURLCOOKIE"); $success = false; $curl_url = $configArray['Catalog']['url'] . "/patroninfo"; $logger->log('Loading page ' . $curl_url, PEAR_LOG_INFO); $curl_connection = curl_init($curl_url); $header = array(); $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,"; $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; $header[] = "Cache-Control: max-age=0"; $header[] = "Connection: keep-alive"; $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7"; $header[] = "Accept-Language: en-us,en;q=0.5"; curl_setopt($curl_connection, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($curl_connection, CURLOPT_HTTPHEADER, $header); curl_setopt($curl_connection, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"); curl_setopt($curl_connection, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl_connection, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl_connection, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($curl_connection, CURLOPT_UNRESTRICTED_AUTH, true); curl_setopt($curl_connection, CURLOPT_COOKIEJAR, $cookieJar); curl_setopt($curl_connection, CURLOPT_COOKIESESSION, false); curl_setopt($curl_connection, CURLOPT_POST, true); $post_data = $this->driver->_getLoginFormValues(); $post_items = array(); foreach ($post_data as $key => $value) { $post_items[] = $key . '=' . urlencode($value); } $post_string = implode('&', $post_items); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $post_string); //Load the page, but we don't need to do anything with the results. curl_exec($curl_connection); //Issue a post request with the information about what to do with the holds $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/holds"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $holdUpdateParams); curl_setopt($curl_connection, CURLOPT_POST, true); $sResult = curl_exec($curl_connection); //$holds = $this->parseHoldsPage($sResult); //At this stage, we get messages if there were any errors freezing holds. //Go back to the hold page to check make sure our hold was cancelled $curl_url = $configArray['Catalog']['url'] . "/patroninfo~S{$scope}/" . $patronDump['RECORD_#'] . "/holds"; curl_setopt($curl_connection, CURLOPT_URL, $curl_url); curl_setopt($curl_connection, CURLOPT_HTTPGET, true); $sResult = curl_exec($curl_connection); $holds = $this->parseHoldsPage($sResult); $numHoldsEnd = count($holds['available'] + $holds['unavailable']); curl_close($curl_connection); unlink($cookieJar); //Finally, check to see if the update was successful. if ($type == 'cancel' || $type == 'recall') { if ($numHoldsEnd != $numHoldsStart) { $logger->log('Cancelled ok', PEAR_LOG_INFO); $success = true; } } //Make sure to clear any cached data global $memCache; $memCache->delete("patron_dump_{$this->driver->_getBarcode()}"); usleep(250); //Clear holds for the patron unset($this->holds[$patronId]); global $analytics; if ($type == 'cancel' || $type == 'recall') { if ($success) { $analytics->addEvent('ILS Integration', 'Hold Cancelled', $title); return array('title' => $title, 'result' => true, 'message' => 'Your hold was cancelled successfully.'); } else { $analytics->addEvent('ILS Integration', 'Hold Not Cancelled', $title); return array('title' => $title, 'result' => false, 'message' => 'Your hold could not be cancelled. Please try again later or see your librarian.'); } } else { $analytics->addEvent('ILS Integration', 'Hold(s) Updated', $title); return array('title' => $title, 'result' => true, 'message' => 'Your hold was updated successfully.'); } }