51de0e94171d623a516c37f509d0c5a65692fbaf
[tine20] / tine20 / Tinebase / Controller / ScheduledImport.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Tinebase
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)
10  */
11
12 /**
13  * Tinebase Import Controller
14  * 
15  * @package Tinebase
16  * @subpackage  Controller
17  */
18 class Tinebase_Controller_ScheduledImport extends Tinebase_Controller_Record_Abstract
19 {
20     /**
21      * holds the instance of the singleton
22      *
23      * @var Tinebase_Controller_ScheduledImport
24      */
25     private static $instance = null;
26     
27     /**
28      * the constructor
29      *
30      * don't use the constructor. use the singleton 
31      */
32     private function __construct()
33     {
34         $this->_applicationName = 'Tinebase';
35         $this->_modelName = 'Tinebase_Model_Import';
36         $this->_backend = new Tinebase_Backend_Sql(array(
37             'modelName' => $this->_modelName, 
38             'tableName' => 'import',
39             'modlogActive' => true,
40         ));
41         $this->_purgeRecords = false;
42         // activate this if you want to use containers
43         $this->_doContainerACLChecks = false;
44         $this->_resolveCustomFields = false;
45     }
46     
47     /**
48      * the singleton pattern
49      *
50      * @return Tinebase_Controller_ScheduledImport
51      */
52     public static function getInstance() 
53     {
54         if (self::$instance === null) {
55             self::$instance = new self();
56         }
57         return self::$instance;
58     }
59     
60     /**
61      * Search and executed the next scheduled import
62      * 
63      * @return null|array
64      */
65     public function runNextScheduledImport()
66     {
67         if ($record = $this->_getNextScheduledImport()) {
68             return $this->_doScheduledImport($record)->toArray();
69         }
70         
71         return NULL;
72     }
73
74     public function getScheduledImportFilter()
75     {
76         $timestampBefore = null;
77
78         $now = new Tinebase_DateTime();
79
80         $anHourAgo = clone $now;
81         $anHourAgo->subHour(1);
82
83         $aDayAgo = clone $now;
84         $aDayAgo->subDay(1);
85
86         $aWeekAgo = clone $now;
87         $aWeekAgo->subWeek(1);
88
89         $filter = new Tinebase_Model_ImportFilter(array(array(
90             'condition' => 'OR', 'filters' => array(
91                 array('field' => 'timestamp', 'operator' => 'isnull', 'value' => null),
92                 array('condition' => 'AND', 'filters' => array(
93                     array('field' => 'interval', 'operator' => 'equals', 'value' => Tinebase_Model_Import::INTERVAL_HOURLY),
94                     array('field' => 'timestamp', 'operator' => 'before', 'value' => $anHourAgo),
95                 )),
96                 array('condition' => 'AND', 'filters' => array(
97                     array('field' => 'interval', 'operator' => 'equals', 'value' => Tinebase_Model_Import::INTERVAL_DAILY),
98                     array('field' => 'timestamp', 'operator' => 'before', 'value' => $aDayAgo),
99                 )),
100                 array('condition' => 'AND', 'filters' => array(
101                     array('field' => 'interval', 'operator' => 'equals', 'value' => Tinebase_Model_Import::INTERVAL_WEEKLY),
102                     array('field' => 'timestamp', 'operator' => 'before', 'value' => $aWeekAgo),
103                 )),
104             )
105         )));
106
107         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
108             Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' . __LINE__ . ' Filter used: ' . print_r($filter->toArray(), true));
109         }
110
111         return $filter;
112     }
113
114     /**
115      * Get the next scheduled import
116      * 
117      * @param interval
118      * @param recursive
119      * @return Object
120      */
121     protected function _getNextScheduledImport()
122     {
123         $filter = $this->getScheduledImportFilter();
124
125         // Always sort by timestamp to ensure first in first out
126         $pagination = new Tinebase_Model_Pagination(array(
127             'limit'     => 1,
128             'sort'      => 'timestamp',
129             'dir'       => 'ASC'
130         ));
131
132         $record = $this->search($filter, $pagination)->getFirstRecord();
133         
134         if (! $record) {
135             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
136                 Tinebase_Core::getLogger()->debug(__METHOD__ . ' ' . __LINE__ . ' No ScheduledImport could be found.');
137             }
138
139             return NULL;
140         }
141         return $record;
142     }
143     
144     /**
145      * Downloads file to memory
146      * 
147      * @param string $source
148      * @return string|null
149      */
150     protected function _getFileToImport($source)
151     {
152         if (strpos($source, 'http') === 0) {
153             try {
154                 $client = new Zend_Http_Client($source);
155                 $requestBody = $client->request()->getBody();
156             } catch (Exception $e) {
157                 Tinebase_Exception::log($e);
158                 $requestBody = null;
159             }
160             return $requestBody;
161         } else {
162             return file_get_contents($source);
163         }
164     }
165     
166     /**
167      * Execute scheduled import
168      * @param Tinebase_Model_Import $record
169      * @return Tinebase_Model_Import
170      */
171     protected function _doScheduledImport(Tinebase_Model_Import $record)
172     {
173         $currentUser = Tinebase_Core::getUser();
174         // set user who created the import job
175         $importUser = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $record->user_id, 'Tinebase_Model_FullUser');
176         Tinebase_Core::set(Tinebase_Core::USER, $importUser);
177         
178         $importer = $record->getOption('plugin');
179         
180         $options = array(
181             'container_id' => $record->container_id,
182             // legacy
183             'importContainerId' => $record->container_id,
184         );
185         
186         if ($record->getOption('importFileByScheduler') === true) {
187             $toImport = $this->_getFileToImport($record->source);
188         } else {
189             $toImport = array(
190                 'remoteUrl'    => $record->source,
191                 'container_id' => $record->container_id,
192                 'options'      => $record->options
193             );
194         }
195         
196         if ($toImport) {
197             try {
198                 $importer = new $importer($options);
199                 $importer->import($toImport);
200             } catch (Exception $e) {
201                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
202                     Tinebase_Core::getLogger()->notice(__METHOD__ . ' ' . __LINE__
203                         . ' Import failed.');
204                 }
205                 // TODO log failure message in import record
206                 Tinebase_Exception::log($e);
207             }
208
209             if ($record->interval === Tinebase_Model_Import::INTERVAL_ONCE || !$record->timestamp instanceof Tinebase_DateTime) {
210                 $record->timestamp = Tinebase_DateTime::now();
211             }
212
213             switch ($record->interval) {
214                 case Tinebase_Model_Import::INTERVAL_DAILY:
215                     $record->timestamp->addDay(1);
216                     break;
217                 case Tinebase_Model_Import::INTERVAL_WEEKLY:
218                     $record->timestamp->addWeek(1);
219                     break;
220                 case Tinebase_Model_Import::INTERVAL_HOURLY:
221                     $record->timestamp->addHour(1);
222                     break;
223             }
224
225             // update record
226             $record = $this->update($record);
227             
228         } else {
229             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
230                 Tinebase_Core::getLogger()->notice(__METHOD__ . ' ' . __LINE__ . ' The source could not be loaded: "' . $record->source . '"');
231             }
232         }
233         
234         Tinebase_Core::set(Tinebase_Core::USER, $currentUser);
235         
236         return $record;
237     }
238     
239     /**
240      * Creates a remote import for events
241      * 
242      * @param string $remoteUrl
243      * @param string $interval
244      * @param array $importOptions
245      * @throws Calendar_Exception_InvalidUrl
246      * @return Tinebase_Record_Interface
247      */
248     public function createRemoteImportEvent($data)
249     {
250         $possibleIntervals = array(
251             Tinebase_Model_Import::INTERVAL_DAILY,
252             Tinebase_Model_Import::INTERVAL_HOURLY,
253             Tinebase_Model_Import::INTERVAL_ONCE,
254             Tinebase_Model_Import::INTERVAL_WEEKLY
255         );
256         
257         if (! in_array($data['interval'], $possibleIntervals)) {
258             $data['interval'] = Tinebase_Model_Import::INTERVAL_ONCE;
259         }
260         
261         // find container or create a new one
262         $containerId = $data['options']['container_id'];
263         
264         try {
265             $container = Tinebase_Container::getInstance()->getContainerById($containerId);
266         } catch (Tinebase_Exception_InvalidArgument $e) {
267             $container = new Tinebase_Model_Container(array(
268                 'name'              => $data['options']['container_id'],
269                 'type'              => Tinebase_Model_Container::TYPE_PERSONAL,
270                 'backend'           => Tinebase_User::SQL,
271                 'color'             => '#ffffff',
272                 'application_id'    => $data['application_id'],
273                 'owner_id'          => $data['user_id'],
274                 'model'             => $data['model'],
275             ));
276
277             $container = Tinebase_Container::getInstance()->addContainer($container);
278         }
279         
280         $data['options'] = json_encode(array_replace(array(
281             'forceUpdateExisting' => TRUE,
282             'import_defintion' => NULL,
283         ), $data['options']));
284         
285         $record = new Tinebase_Model_Import(array_replace(array(
286             'id'                => Tinebase_Record_Abstract::generateUID(),
287             'user_id'           => Tinebase_Core::getUser()->getId(),
288             'sourcetype'        => Tinebase_Model_Import::SOURCETYPE_REMOTE,
289             'container_id'      => $container->getId(),
290         ), $data));
291         
292         return $this->create($record);
293     }
294 }