/** * Send scheduled alerts * * @return void */ public function send() { global $configArray; global $interface; global $translator; $iso8601 = 'Y-m-d\\TH:i:s\\Z'; ini_set('display_errors', true); $configArray = $mainConfig = readConfig(); $datasourceConfig = getExtraConfigArray('datasources'); $siteLocal = $configArray['Site']['local']; // Set up time zone. N.B. Don't use msg() or other functions requiring date before this. date_default_timezone_set($configArray['Site']['timezone']); $this->msg('Sending scheduled alerts'); // Setup Local Database Connection ConnectionManager::connectToDatabase(); // Initialize Mailer $mailer = new VuFindMailer(); // Find all scheduled alerts $sql = 'SELECT * FROM "search" WHERE "schedule" > 0 ORDER BY user_id'; $s = new SearchEntry(); $s->query($sql); $this->msg('Processing ' . $s->N . ' searches'); $user = false; $interface = false; $institution = false; $todayTime = new DateTime(); while ($s->fetch()) { $lastTime = new DateTime($s->last_executed); if ($s->schedule == 1) { // Daily if ($todayTime->format('Y-m-d') == $lastTime->format('Y-m-d')) { $this->msg('Bypassing search ' . $s->id . ': previous execution too recent (daily, ' . $lastTime->format($iso8601) . ')'); continue; } } elseif ($s->schedule == 2) { // Weekly $diff = $todayTime->diff($lastTime); if ($diff->days < 6) { $this->msg('Bypassing search ' . $s->id . ': previous execution too recent (weekly, ' . $lastTime->format($iso8601) . ')'); continue; } } else { $this->msg('Search ' . $s->id . ': unknown schedule: ' . $s->schedule); continue; } if ($user === false || $s->user_id != $user->id) { $user = User::staticGet($s->user_id); } if (!$user->email || trim($user->email) == '') { $this->msg('User ' . $user->username . ' does not have an email address, bypassing alert ' . $s->id); continue; } $userInstitution = reset(explode(':', $user->username, 2)); if (!$institution || $institution != $userInstitution) { $institution = $userInstitution; if (!isset($datasourceConfig[$institution])) { foreach ($datasourceConfig as $code => $values) { if (isset($values['institution']) && strcasecmp($values['institution'], $institution) == 0) { $institution = $code; break; } } } if (!($configArray = $this->readInstitutionConfig($institution))) { continue; } $configArray['Site']['url'] = $s->schedule_base_url; // Start Interface $interface = new UInterface($siteLocal); $validLanguages = array_keys($configArray['Languages']); $dateFormat = new VuFindDate(); } $language = $user->language; if (!in_array($user->language, $validLanguages)) { $language = $configArray['Site']['language']; } $translator = new I18N_Translator(array($configArray['Site']['local'] . '/lang', $configArray['Site']['local'] . '/lang_local'), $language, $configArray['System']['debug']); $interface->setLanguage($language); $minSO = unserialize($s->search_object); // Check minified search object type $searchObject = SearchObjectFactory::deminify($minSO); if (!$searchObject instanceof SearchObject_Solr) { $this->msg('Search ' . $s->id . ': search object type not supported'); continue; } // Create a new search object to avoid cached defaults for e.g. // hidden filters. $searchObject = SearchObjectFactory::initSearchObject(); $searchObject->deminify($minSO); $searchObject->setSort('first_indexed desc'); $searchTime = time(); $searchDate = gmdate($iso8601, time()); $searchObject->setLimit(50); $searchObject->disableLogging(); $results = $searchObject->processSearch(); if (PEAR::isError($results)) { $this->msg('Search ' . $s->id . ' failed: ' . $results->getMessage()); continue; } if ($searchObject->getResultTotal() < 1) { $this->msg('No results found for search ' . $s->id); continue; } $newestRecordDate = date($iso8601, strtotime($results['response']['docs'][0]['first_indexed'])); $lastExecutionDate = $lastTime->format($iso8601); if ($newestRecordDate < $lastExecutionDate) { $this->msg('No new results for search ' . $s->id . ": {$newestRecordDate} < {$lastExecutionDate}"); } else { $this->msg('New results for search ' . $s->id . ": {$newestRecordDate} >= {$lastExecutionDate}"); $interface->assign('summary', $searchObject->getResultSummary()); $interface->assign('searchDate', $dateFormat->convertToDisplayDate("U", floor($searchTime))); $interface->assign('lastSearchDate', $dateFormat->convertToDisplayDate("U", floor($lastTime->getTimestamp()))); $records = array(); foreach ($results['response']['docs'] as &$doc) { $docDate = date($iso8601, strtotime($doc['first_indexed'])); if ($docDate < $lastExecutionDate) { break; } $record = RecordDriverFactory::initRecordDriver($doc); $records[] = $interface->fetch($record->getSearchResult('email')); } $interface->assign('recordSet', $records); $key = $this->getSecret($user, $s->id); $params = array('id' => $s->id, 'type' => 'alert', 'key' => $key); $unsubscribeUrl = $configArray['Site']['url'] . '/MyResearch/Unsubscribe?' . http_build_query($params); $interface->assign('info', array('time' => $dateFormat->convertToDisplayDate("U", floor($searchObject->getStartTime())), 'url' => $searchObject->renderSearchUrl(), 'searchId' => $searchObject->getSearchId(), 'description' => $searchObject->displayQuery(), 'filters' => $searchObject->getFilterList(), 'hits' => $searchObject->getResultTotal(), 'speed' => round($searchObject->getQuerySpeed(), 2) . "s", 'schedule' => $s->schedule, 'last_executed' => $s->last_executed, 'recordCount' => count($records), 'unsubscribeUrl' => $unsubscribeUrl)); $searchObject->close(); // Load template $message = $interface->fetch('MyResearch/alert-email.tpl'); if (strstr($message, 'Warning: Smarty error:')) { $this->msg("Message template processing failed: {$message}"); continue; } $result = $mailer->send($user->email, $configArray['Site']['email'], translate('Scheduled Alert Results'), $message); if (PEAR::isError($result)) { $this->msg("Failed to send message to {$user->email}: " . $result->getMessage()); continue; } } // Update search date $s->changeLastExecuted($searchDate); } $this->reportErrors(); $this->msg('Scheduled alerts execution completed'); }