Merge branch '2014.11-develop' into 2015.07
[tine20] / tine20 / Calendar / Model / iMIP.php
1 <?php
2 /**
3  * @package     Calendar
4  * @subpackage  Model
5  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
6  * @author      Cornelius Weiss <c.weiss@metaways.de>
7  * @copyright   Copyright (c) 2011 Metaways Infosystems GmbH (http://www.metaways.de)
8  */
9
10 /**
11  * Model of an iMIP (RFC 6047) Message
12  * 
13  * @property    id               message <id>_<part> of iMIP mail part
14  * @property    ics              ical string in UTF8
15  * @property    event            iMIP message event
16  * @property    method           method of iMIP transaction
17  * @property    userAgent        userAgent origination iMIP transaction
18  * @property    originator       originator /sender of iMIP transaction
19  * @property    preconditions     array of checked processing preconditions
20  * @package     Calendar
21  * @subpackage  Model
22  */
23 class Calendar_Model_iMIP extends Tinebase_Record_Abstract
24 {
25     /**
26      * Used to publish an iCalendar object to one or more "Calendar Users".  
27      * There is no interactivity between the publisher and any  other 
28      * "Calendar User".
29      */
30     const METHOD_PUBLISH        = 'PUBLISH';
31     
32     /**
33      * Used to schedule an iCalendar object with other "Calendar Users".  
34      * Requests are interactive in that they require the receiver to 
35      * respond using the reply methods.  Meeting requests, busy-time 
36      * requests, and the assignment of tasks to other "Calendar Users" 
37      * are all examples.  Requests are also used by the Organizer to 
38      * update the status of an iCalendar object. 
39      */
40     const METHOD_REQUEST        = 'REQUEST';
41     
42     /**
43      * A reply is used in response to a request to convey Attendee 
44      * status to the Organizer. Replies are commonly used to respond 
45      * to meeting and task requests. 
46      */
47     const METHOD_REPLY          = 'REPLY';
48     
49     /**
50      * Add one or more new instances to an existing recurring iCalendar object.
51      */
52     const METHOD_ADD            = 'ADD';
53     
54     /**
55      * Cancel one or more instances of an existing iCalendar object.
56      */
57     const METHOD_CANCEL         = 'CANCEL';
58     
59     /**
60      * Used by an Attendee to request the latest version of an iCalendar object.
61      */
62     const METHOD_REFRESH        = 'REFRESH';
63     
64     /**
65      * Used by an Attendee to negotiate a change in an iCalendar object.
66      * Examples include the request to change a proposed event time or 
67      * change the due date for a task.
68      */
69     const METHOD_COUNTER        = 'COUNTER';
70     
71     /**
72      * Used by the Organizer to decline the proposedcounter proposal
73      */
74     const METHOD_DECLINECOUNTER = 'DECLINECOUNTER';
75     
76     /**
77      * precondition that originator of iMIP is also:
78      * 
79      * organizer for PUBLISH/REQUEST/ADD/CANCEL/DECLINECOUNTER
80      * attendee  for REPLY/REFRESH/COUNTER
81      */
82     const PRECONDITION_ORIGINATOR = 'ORIGINATOR';
83     
84     /**
85      * precondition iMIP message is more recent than event stored in calendar backend
86      */
87     const PRECONDITION_RECENT     = 'RECENT';
88     
89     /**
90      * precondition that current user is event attendee
91      * 
92      * for REQUEST/DECLINECOUNTER
93      */
94     const PRECONDITION_ATTENDEE   = 'ATTENDEE';
95     
96     /**
97      * precondition that iMIP message is not already processed
98      */
99     const PRECONDITION_TOPROCESS = 'TOPROCESS';
100     
101     /**
102      * precondition that event has an organizer
103      */
104     const PRECONDITION_ORGANIZER  = 'ORGANIZER';
105     
106     /**
107      * precondition that method is supported
108      */
109     const PRECONDITION_SUPPORTED  = 'SUPPORTED';
110     
111     /**
112      * precondition that event exists
113      */
114     const PRECONDITION_EVENTEXISTS  = 'EVENTEXISTS';
115     
116     /**
117      * precondition that event is not deleted
118      */
119     const PRECONDITION_NOTDELETED     = 'NOTDELETED';
120
121     /**
122      * precondition that event is not cancelled
123      */
124     const PRECONDITION_NOTCANCELLED     = 'NOTCANCELLED';
125
126     /**
127      * (non-PHPdoc)
128      * @see Tinebase_Record_Abstract::_identifier
129      */
130     protected $_identifier = 'id';
131     
132     /**
133      * @var Calendar_Convert_Event_VCalendar_Abstract
134      */
135     protected $_converter = NULL;
136     
137     /**
138      * (non-PHPdoc)
139      * @see Tinebase_Record_Abstract::_validators
140      */
141     protected $_validators = array(
142         'id'                   => array('allowEmpty' => true,         ), 
143         'ics'                  => array('allowEmpty' => true          ),
144         'method'               => array('allowEmpty' => true,         ),
145         'originator'           => array('allowEmpty' => false,        ), // email adddress
146         'userAgent'            => array('allowEmpty' => true,         ),
147         'event'                => array('allowEmpty' => true          ),
148         'existing_event'       => array('allowEmpty' => true          ),
149         'preconditions'        => array('allowEmpty' => true          ),
150         'preconditionsChecked' => array('allowEmpty' => true          ),
151     );
152     
153     /**
154      * (non-PHPdoc)
155      * @see Tinebase_Record_Abstract::__set()
156      */
157     public function __set($_name, $_value)
158     {
159         if ($_name == 'ics') unset($this->event);
160         if ($_name == 'method') $_value = trim(strtoupper($_value));
161         
162         return parent::__set($_name, $_value);
163     }
164     
165     /**
166      * (non-PHPdoc)
167      * @see Tinebase_Record_Abstract::__get()
168      */
169     public function __get($_name) {
170         if ($_name == 'method' && !$this->_properties['method'] && $this->_properties['ics']) {
171             $this->getEvent();
172         }
173         
174         return parent::__get($_name);
175     }
176     
177     /**
178      * get event record
179      * 
180      * @return Calendar_Model_Event
181      */
182     public function getEvent()
183     {
184         if (! $this->event instanceof Calendar_Model_Event) {
185             if (! $this->ics) {
186                 throw new Tinebase_Exception_Record_NotDefined('ics is needed to generate event');
187             }
188             
189             $this->event = $this->_getConverter()->toTine20Model($this->ics);
190             
191             if (! $this->_properties['method']) {
192                 $this->method = $this->_getConverter()->getMethod($this->ics);
193             }
194         }
195         
196         return $this->event;
197     }
198
199     /**
200      * merge ics data into given event
201      * 
202      * @param Calendar_Model_Event $_event
203      */
204     public function mergeEvent($_event)
205     {
206         return $this->_getConverter()->toTine20Model($this->ics, $_event);
207     }
208     
209     /**
210      * get ics
211      * 
212      * @return string UTF8 ics
213      */
214     public function getIcs()
215     {
216         if (! $this->event instanceof Calendar_Model_Event) {
217             throw new Tinebase_Exception_Record_NotDefined('event is needed to generate ics');
218         }
219         
220         return $this->_getConverter()->fromTine20Model($this->event);
221     }
222     
223     /**
224      * get ics converter
225      * 
226      * @return Calendar_Convert_Event_VCalendar_Abstract
227      */
228     protected function _getConverter()
229     {
230         if (! $this->_converter) {
231             list($backend, $version) = Calendar_Convert_Event_VCalendar_Factory::parseUserAgent($this->userAgent);
232             $this->_converter = Calendar_Convert_Event_VCalendar_Factory::factory($backend, $version);
233         }
234         
235         return $this->_converter;
236     }
237     
238     /**
239      * add failed precondtion check
240      * 
241      * @param string $_preconditionName
242      * @param string $_message
243      */
244     public function addFailedPrecondition($_preconditionName, $_message)
245     {
246         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ 
247             . " Preconditions check failed for " . $_preconditionName . ' with message: ' . $_message);
248         
249         $this->_addPrecondition($_preconditionName, FALSE, $_message);
250     }
251     
252     /**
253      * add failed precondtion check
254      * 
255      * @param string $_preconditionName
256      * @param string $_message
257      */
258     public function addSuccessfulPrecondition($_preconditionName)
259     {
260         $this->_addPrecondition($_preconditionName, TRUE);
261     }
262     
263     /**
264      * add precondition
265      * 
266      * @param string $_preconditionName
267      * @param boolean $_check
268      * @param string $_message
269      */
270     protected function _addPrecondition($_preconditionName, $_check, $_message = NULL)
271     {
272         $preconditions = (is_array($this->preconditions)) ? $this->preconditions : array();
273         
274         if (! isset($preconditions[$_preconditionName])) {
275             $preconditions[$_preconditionName] = array();
276         }
277         
278         $preconditions[$_preconditionName][] = array(
279             'check'     => $_check,
280             'message'    => $_message,
281         );
282         
283         $this->preconditions = $preconditions;
284     }
285 }