6 * @subpackage Controller
7 * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
8 * @author Michael Spahn <m.spahn@metaways.de>
9 * @copyright Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
13 * Tinebase Import Controller
16 * @subpackage Controller
18 class Tinebase_Controller_ScheduledImport extends Tinebase_Controller_Record_Abstract
21 * holds the instance of the singleton
23 * @var Tinebase_Controller_ScheduledImport
25 private static $instance = null;
27 const MAXFAILCOUNT = 5;
32 * don't use the constructor. use the singleton
34 private function __construct()
36 $this->_applicationName = 'Tinebase';
37 $this->_modelName = 'Tinebase_Model_Import';
38 $this->_backend = new Tinebase_Backend_Sql(array(
39 'modelName' => $this->_modelName,
40 'tableName' => 'import',
41 'modlogActive' => true,
43 $this->_purgeRecords = false;
44 // activate this if you want to use containers
45 $this->_doContainerACLChecks = false;
46 $this->_resolveCustomFields = false;
50 * the singleton pattern
52 * @return Tinebase_Controller_ScheduledImport
54 public static function getInstance()
56 if (self::$instance === null) {
57 self::$instance = new self();
59 return self::$instance;
63 * Search and executed the next scheduled import
67 public function runNextScheduledImport()
69 if ($record = $this->_getNextScheduledImport()) {
70 return $this->_doScheduledImport($record)->toArray();
77 * @return Tinebase_Model_ImportFilter
79 public function getScheduledImportFilter()
81 $timestampBefore = null;
83 $now = new Tinebase_DateTime();
85 $anHourAgo = clone $now;
86 $anHourAgo->subHour(1);
88 $aDayAgo = clone $now;
91 $aWeekAgo = clone $now;
92 $aWeekAgo->subWeek(1);
94 $filter = new Tinebase_Model_ImportFilter(array(array(
95 array('field' => 'failcount', 'operator' => 'greater', 'value' => 5),
98 'condition' => 'OR', 'filters' => array(
99 array('field' => 'timestamp', 'operator' => 'isnull', 'value' => null),
100 array('condition' => 'AND', 'filters' => array(
101 array('field' => 'interval', 'operator' => 'equals', 'value' => Tinebase_Model_Import::INTERVAL_HOURLY),
102 array('field' => 'timestamp', 'operator' => 'before', 'value' => $anHourAgo),
104 array('condition' => 'AND', 'filters' => array(
105 array('field' => 'interval', 'operator' => 'equals', 'value' => Tinebase_Model_Import::INTERVAL_DAILY),
106 array('field' => 'timestamp', 'operator' => 'before', 'value' => $aDayAgo),
108 array('condition' => 'AND', 'filters' => array(
109 array('field' => 'interval', 'operator' => 'equals', 'value' => Tinebase_Model_Import::INTERVAL_WEEKLY),
110 array('field' => 'timestamp', 'operator' => 'before', 'value' => $aWeekAgo),
115 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
116 Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' . __LINE__ . ' Filter used: ' . print_r($filter->toArray(), true));
123 * Get the next scheduled import
127 * @return Tinebase_Model_Import|null
129 protected function _getNextScheduledImport()
131 $filter = $this->getScheduledImportFilter();
133 // Always sort by timestamp to ensure first in first out
134 $pagination = new Tinebase_Model_Pagination(array(
136 'sort' => 'timestamp',
140 $records = $this->search($filter, $pagination);
142 foreach ($records as $record) {
143 // TODO add failcount to filter in getScheduledImportFilter as
144 // no more valid imports are run if we have 50+ failing imports
145 if ($record->failcount < self::MAXFAILCOUNT) {
148 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
149 Tinebase_Core::getLogger()->info(__METHOD__ . ' ' . __LINE__ . ' Too many failures, skipping import');
154 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
155 Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__ . ' No valid ScheduledImport could be found.');
162 * Downloads file to memory
164 * @param string $source
165 * @return string|null
167 protected function _getFileToImport($source)
169 if (strpos($source, 'http') === 0) {
171 $client = new Zend_Http_Client($source);
172 $requestBody = $client->request()->getBody();
173 } catch (Exception $e) {
174 Tinebase_Exception::log($e);
179 return file_get_contents($source);
184 * Execute scheduled import
185 * @param Tinebase_Model_Import $record
186 * @return Tinebase_Model_Import
188 protected function _doScheduledImport(Tinebase_Model_Import $record)
190 $currentUser = Tinebase_Core::getUser();
191 // set user who created the import job
192 $importUser = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $record->user_id, 'Tinebase_Model_FullUser');
193 Tinebase_Core::set(Tinebase_Core::USER, $importUser);
195 $importer = $record->getOption('plugin');
198 'container_id' => $record->container_id,
200 'importContainerId' => $record->container_id,
203 if ($record->getOption('importFileByScheduler') === true) {
204 $toImport = $this->_getFileToImport($record->source);
207 'remoteUrl' => $record->source,
208 'container_id' => $record->container_id,
209 'options' => $record->options
215 $importer = new $importer($options);
216 $importer->import($toImport);
217 $record->failcount = 0;
218 } catch (Exception $e) {
219 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
220 Tinebase_Core::getLogger()->notice(__METHOD__ . ' ' . __LINE__
221 . ' Import failed.');
223 Tinebase_Exception::log($e);
225 $record->lastfail = $e->getMessage();
226 $record->failcount = $record->failcount + 1;
229 if ($record->interval === Tinebase_Model_Import::INTERVAL_ONCE || !$record->timestamp instanceof Tinebase_DateTime) {
230 $record->timestamp = Tinebase_DateTime::now();
233 switch ($record->interval) {
234 case Tinebase_Model_Import::INTERVAL_DAILY:
235 $record->timestamp->addDay(1);
237 case Tinebase_Model_Import::INTERVAL_WEEKLY:
238 $record->timestamp->addWeek(1);
240 case Tinebase_Model_Import::INTERVAL_HOURLY:
241 $record->timestamp->addHour(1);
245 $record = $this->update($record);
248 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
249 Tinebase_Core::getLogger()->notice(__METHOD__ . ' ' . __LINE__ . ' The source could not be loaded: "' . $record->source . '"');
253 Tinebase_Core::set(Tinebase_Core::USER, $currentUser);
259 * Creates a remote import for events
261 * @param string $remoteUrl
262 * @param string $interval
263 * @param array $importOptions
264 * @throws Calendar_Exception_InvalidUrl
265 * @return Tinebase_Record_Interface
267 public function createRemoteImportEvent($data)
269 $possibleIntervals = array(
270 Tinebase_Model_Import::INTERVAL_DAILY,
271 Tinebase_Model_Import::INTERVAL_HOURLY,
272 Tinebase_Model_Import::INTERVAL_ONCE,
273 Tinebase_Model_Import::INTERVAL_WEEKLY
276 if (! in_array($data['interval'], $possibleIntervals)) {
277 $data['interval'] = Tinebase_Model_Import::INTERVAL_ONCE;
280 // find container or create a new one
281 $containerId = $data['options']['container_id'];
284 $container = Tinebase_Container::getInstance()->getContainerById($containerId);
285 } catch (Tinebase_Exception_InvalidArgument $e) {
286 $container = new Tinebase_Model_Container(array(
287 'name' => $data['options']['container_id'],
288 'type' => Tinebase_Model_Container::TYPE_PERSONAL,
289 'backend' => Tinebase_User::SQL,
290 'color' => '#ffffff',
291 'application_id' => $data['application_id'],
292 'owner_id' => $data['user_id'],
293 'model' => $data['model'],
296 $container = Tinebase_Container::getInstance()->addContainer($container);
299 $data['options'] = json_encode(array_replace(array(
300 'forceUpdateExisting' => TRUE,
301 'import_defintion' => NULL,
302 ), $data['options']));
304 $record = new Tinebase_Model_Import(array_replace(array(
305 'id' => Tinebase_Record_Abstract::generateUID(),
306 'user_id' => Tinebase_Core::getUser()->getId(),
307 'sourcetype' => Tinebase_Model_Import::SOURCETYPE_REMOTE,
308 'container_id' => $container->getId(),
311 return $this->create($record);