0012024: remember popup window size in client state
[tine20] / tine20 / Tinebase / Alarm.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  Alarm
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  * @copyright   Copyright (c) 2009-2011 Metaways Infosystems GmbH (http://www.metaways.de)
10  * 
11  */
12
13 /**
14  * controller for alarms / reminder messages
15  *
16  * @package     Tinebase
17  * @subpackage  Alarm
18  */
19 class Tinebase_Alarm extends Tinebase_Controller_Record_Abstract
20 {
21     /**
22      * @var Tinebase_Backend_Sql
23      */
24     protected $_backend;
25     
26     /**
27      * Model name
28      *
29      * @var string
30      */
31     protected $_modelName = 'Tinebase_Model_Alarm';
32     
33     /**
34      * check for container ACLs?
35      *
36      * @var boolean
37      */
38     protected $_doContainerACLChecks = FALSE;
39     
40     /**
41      * holds the instance of the singleton
42      *
43      * @var Tinebase_Alarm
44      */
45     private static $instance = NULL;
46     
47     /**
48      * the constructor
49      *
50      */
51     private function __construct()
52     {
53         $this->_backend = new Tinebase_Backend_Sql(array(
54             'modelName' => $this->_modelName, 
55             'tableName' => 'alarm',
56         ));
57     }
58     
59     /**
60      * the singleton pattern
61      *
62      * @return Tinebase_Alarm
63      */
64     public static function getInstance() 
65     {
66         if (self::$instance === NULL) {
67             self::$instance = new Tinebase_Alarm();
68         }
69         return self::$instance;
70     }
71     
72     /**************************** public funcs *************************************/
73     
74     /**
75      * send pending alarms
76      *
77      * @param mixed $_eventName
78      * @return void
79      * 
80      * @todo sort alarms (by model/...)?
81      * @todo what to do about Tinebase_Model_Alarm::STATUS_FAILURE alarms?
82      */
83     public function sendPendingAlarms($_eventName)
84     {
85         $eventName = (is_array($_eventName)) ? $_eventName['eventName'] : $_eventName;
86         
87         $job = Tinebase_AsyncJob::getInstance()->startJob($eventName);
88         
89         if (! $job) {
90             return;
91         }
92         
93         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' No ' . $eventName . ' is running. Starting new one.');
94         
95         try {
96             // get all pending alarms
97             $filter = new Tinebase_Model_AlarmFilter(array(
98                 array(
99                     'field'     => 'alarm_time', 
100                     'operator'  => 'before', 
101                     'value'     => Tinebase_DateTime::now()->subMinute(1)->get(Tinebase_Record_Abstract::ISO8601LONG)
102                 ),
103                 array(
104                     'field'     => 'sent_status', 
105                     'operator'  => 'equals', 
106                     'value'     => Tinebase_Model_Alarm::STATUS_PENDING // STATUS_FAILURE?
107                 ),
108             ));
109             
110             $limit = Tinebase_Config::getInstance()->get(Tinebase_Config::ALARMS_EACH_JOB, 100);
111             $pagination = ($limit > 0) ? new Tinebase_Model_Pagination(array(
112                 'limit' => $limit
113             )) : null;
114             
115             $alarms = $this->_backend->search($filter, $pagination);
116             
117             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
118                 ' Sending ' . count($alarms) . ' alarms (limit: ' . $limit . ').');
119             
120             // loop alarms and call sendAlarm in controllers
121             foreach ($alarms as $alarm) {
122                 list($appName, $i, $itemName) = explode('_', $alarm->model);
123                 $appController = Tinebase_Core::getApplicationInstance($appName, $itemName);
124             
125                 if ($appController instanceof Tinebase_Controller_Alarm_Interface) {
126                 
127                     $alarm->sent_time = Tinebase_DateTime::now();
128                 
129                     try {
130                         // NOTE: we set the status here, so controller can adopt the status itself
131                         $alarm->sent_status = Tinebase_Model_Alarm::STATUS_SUCCESS;
132                         $appController->sendAlarm($alarm);
133                     
134                     } catch (Exception $e) {
135                         Tinebase_Exception::log($e);
136                         $alarm->sent_message = $e->getMessage();
137                         $alarm->sent_status = Tinebase_Model_Alarm::STATUS_FAILURE;
138                     } 
139                 
140                     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
141                         . ' Updating alarm status: ' . $alarm->sent_status);
142                 
143                     $this->update($alarm);
144                 }
145             }
146         
147             $job = Tinebase_AsyncJob::getInstance()->finishJob($job);
148         
149         } catch (Exception $e) {
150             // save new status 'failure'
151             $job = Tinebase_AsyncJob::getInstance()->finishJob($job, Tinebase_Model_AsyncJob::STATUS_FAILURE, $e->getMessage());
152             if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Job failed: ' . $e->getMessage());
153             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $e->getTraceAsString());
154         }
155         
156         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Job ' . $eventName . ' finished.');
157     }
158     
159     /**
160      * get all alarms of given record(s) / adds record_id index to result set
161      * 
162      * @param  string $_model model to get alarms for
163      * @param  string|array|Tinebase_Record_Interface|Tinebase_Record_RecordSet $_recordId record id(s) to get alarms for
164      * @param  boolean $_onlyIds
165      * @return Tinebase_Record_RecordSet|array of ids
166      */
167     public function getAlarmsOfRecord($_model, $_recordId, $_onlyIds = FALSE)
168     {
169         if ($_recordId instanceof Tinebase_Record_RecordSet) {
170             $recordId = $_recordId->getArrayOfIds();
171         } else if ($_recordId instanceof Tinebase_Record_Interface) {
172             $recordId = $_recordId->getId();
173         } else {
174             $recordId = $_recordId;
175         }
176         
177         //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . "  model: '$_model' id:" . print_r((array)$recordId, true));
178     
179         $filter = new Tinebase_Model_AlarmFilter(array(
180             array(
181                 'field'     => 'model', 
182                 'operator'  => 'equals', 
183                 'value'     => $_model
184             ),
185             array(
186                 'field'     => 'record_id', 
187                 'operator'  => 'in', 
188                 'value'     => (array)$recordId
189             ),
190         ));
191         $result = $this->_backend->search($filter, NULL, $_onlyIds);
192         
193         // NOTE: Adding indices to empty recordsets breaks empty tests
194         if (count($result) > 0 && $result instanceof Tinebase_Record_RecordSet) {
195             $result->addIndices(array('record_id'));
196         }
197         
198         return $result;
199     }
200     
201     /**
202      * set alarms of record
203      *
204      * @param Tinebase_Record_Abstract $_record
205      * @param string $_alarmsProperty
206      * @return void
207      */
208     public function setAlarmsOfRecord(Tinebase_Record_Abstract $_record, $_alarmsProperty = 'alarms')
209     {
210         $model = get_class($_record);
211         $alarms = $_record->{$_alarmsProperty};
212         
213         $currentAlarms = $this->getAlarmsOfRecord($model, $_record);
214         $diff = $currentAlarms->getMigration($alarms->getArrayOfIds());
215         $this->_backend->delete($diff['toDeleteIds']);
216         
217         // create / update alarms
218         foreach ($alarms as $alarm) {
219             $id = $alarm->getId();
220             
221             if ($id) {
222                 $alarm = $this->_backend->update($alarm);
223                 
224             } else {
225                 $alarm->record_id = $_record->getId();
226                 if (! $alarm->model) {
227                     $alarm->model = $model;
228                 }
229                 $alarm = $this->_backend->create($alarm);
230             }
231         }
232     }
233     
234     /**
235      * delete all alarms of a given record(s)
236      *
237      * @param string $_model
238      * @param string|array|Tinebase_Record_Interface|Tinebase_Record_RecordSet $_recordId
239      * @return void
240      */
241     public function deleteAlarmsOfRecord($_model, $_recordId)
242     {
243         $ids = $this->getAlarmsOfRecord($_model, $_recordId, TRUE);
244         $this->delete($ids);
245     }
246 }