1<?php
2/**
3 * Horde_ActiveSync_Message_Task::
4 *
5 * Portions of this class were ported from the Z-Push project:
6 *   File      :   wbxml.php
7 *   Project   :   Z-Push
8 *   Descr     :   WBXML mapping file
9 *
10 *   Created   :   01.10.2007
11 *
12 *   � Zarafa Deutschland GmbH, www.zarafaserver.de
13 *   This file is distributed under GPL-2.0.
14 *   Consult COPYING file for details
15 *
16 * @license   http://www.horde.org/licenses/gpl GPLv2
17 *
18 * @copyright 2010-2020 Horde LLC (http://www.horde.org)
19 * @author    Michael J Rubinsky <mrubinsk@horde.org>
20 * @package   ActiveSync
21 */
22/**
23 * Horde_ActiveSync_Message_Task::
24 *
25 * @license   http://www.horde.org/licenses/gpl GPLv2
26 *
27 * @copyright 2010-2020 Horde LLC (http://www.horde.org)
28 * @author    Michael J Rubinsky <mrubinsk@horde.org>
29 * @package   ActiveSync
30 *
31 * @property boolean   $complete           Completion flag
32 * @property Horde_Date   $datecompleted   The date the task was completed, in UTC.
33 * @property Horde_Date   $utcduedate      The date this task is due, in UTC.
34 * @property integer   $importance         The importance flag.
35 * @property Horde_ActiveSync_Message_TaskRecurrence   $recurrence
36 *                                      The recurrence object.
37 * @property integer   $sensitivity        The sensitivity flag.
38 * @property Horde_Date   $utcstartdate    The date this task starts, in UTC.
39 * @property string   $subject             The task subject.
40 * @property array   $categories           An array of categories.
41 * @property string   $body                The task body (EAS Version < 12.0)
42 * @property boolean   $bodytruncated      Truncation flag (EAS Version < 12.0)
43 * @property Horde_ActiveSync_Message_AirSyncBaseBody   $airsyncbasebody
44 *                                      The task body (EAS Version >= 12.0)
45 */
46class Horde_ActiveSync_Message_Task extends Horde_ActiveSync_Message_Base
47{
48    /* POOMTASKS */
49    const POOMTASKS_BODY           = 'POOMTASKS:Body';
50    const POOMTASKS_BODYSIZE       = 'POOMTASKS:BodySize';
51    const POOMTASKS_BODYTRUNCATED  = 'POOMTASKS:BodyTruncated';
52    const POOMTASKS_CATEGORIES     = 'POOMTASKS:Categories';
53    const POOMTASKS_CATEGORY       = 'POOMTASKS:Category';
54    const POOMTASKS_COMPLETE       = 'POOMTASKS:Complete';
55    const POOMTASKS_DATECOMPLETED  = 'POOMTASKS:DateCompleted';
56    const POOMTASKS_DUEDATE        = 'POOMTASKS:DueDate';
57    const POOMTASKS_UTCDUEDATE     = 'POOMTASKS:UtcDueDate';
58    const POOMTASKS_IMPORTANCE     = 'POOMTASKS:Importance';
59    const POOMTASKS_RECURRENCE     = 'POOMTASKS:Recurrence';
60    const POOMTASKS_TYPE           = 'POOMTASKS:Type';
61    const POOMTASKS_START          = 'POOMTASKS:Start';
62    const POOMTASKS_UNTIL          = 'POOMTASKS:Until';
63    const POOMTASKS_OCCURRENCES    = 'POOMTASKS:Occurrences';
64    const POOMTASKS_INTERVAL       = 'POOMTASKS:Interval';
65    const POOMTASKS_DAYOFWEEK      = 'POOMTASKS:DayOfWeek';
66    const POOMTASKS_DAYOFMONTH     = 'POOMTASKS:DayOfMonth';
67    const POOMTASKS_WEEKOFMONTH    = 'POOMTASKS:WeekOfMonth';
68    const POOMTASKS_MONTHOFYEAR    = 'POOMTASKS:MonthOfYear';
69    const POOMTASKS_REGENERATE     = 'POOMTASKS:Regenerate';
70    const POOMTASKS_DEADOCCUR      = 'POOMTASKS:DeadOccur';
71    const POOMTASKS_REMINDERSET    = 'POOMTASKS:ReminderSet';
72    const POOMTASKS_REMINDERTIME   = 'POOMTASKS:ReminderTime';
73    const POOMTASKS_SENSITIVITY    = 'POOMTASKS:Sensitivity';
74    const POOMTASKS_STARTDATE      = 'POOMTASKS:StartDate';
75    const POOMTASKS_UTCSTARTDATE   = 'POOMTASKS:UtcStartDate';
76    const POOMTASKS_SUBJECT        = 'POOMTASKS:Subject';
77    const POOMTASKS_RTF            = 'POOMTASKS:Rtf';
78
79    // EAS 12.0
80    const POOMTASKS_ORDINALDATE    = 'POOMTASKS:OrdinalDate';
81    const POOMTASKS_SUBORDINALDATE = 'POOMTASKS:SubOrdinalDate';
82
83    // EAS 14
84    const POOMTASKS_CALENDARTYPE   = 'POOMTASKS:CalendarType';
85    const POOMTASKS_ISLEAPMONTH    = 'POOMTASKS:IsLeapMonth';
86    const POOMTASKS_FIRSTDAYOFWEEK = 'POOMTASKS:FirstDayOfWeek';
87
88    const TASK_COMPLETE_TRUE      = 1;
89    const TASK_COMPLETE_FALSE     = 0;
90
91    const IMPORTANCE_LOW          = 0;
92    const IMPORTANCE_NORMAL       = 1;
93    const IMPORTANCE_HIGH         = 2;
94
95    const REMINDER_SET_FALSE      = 0;
96    const REMINDER_SET_TRUE       = 1;
97
98    /**
99     * DOW mapping for DATE to MASK
100     *
101     * @var array
102     */
103    protected $_dayOfWeekMap = array(
104        Horde_Date::DATE_SUNDAY    => Horde_Date::MASK_SUNDAY,
105        Horde_Date::DATE_MONDAY    => Horde_Date::MASK_MONDAY,
106        Horde_Date::DATE_TUESDAY   => Horde_Date::MASK_TUESDAY,
107        Horde_Date::DATE_WEDNESDAY => Horde_Date::MASK_WEDNESDAY,
108        Horde_Date::DATE_THURSDAY  => Horde_Date::MASK_THURSDAY,
109        Horde_Date::DATE_FRIDAY    => Horde_Date::MASK_FRIDAY,
110        Horde_Date::DATE_SATURDAY  => Horde_Date::MASK_SATURDAY,
111    );
112
113    /**
114     * Property mapping
115     *
116     * @var array
117     */
118    protected $_mapping = array (
119        self::POOMTASKS_COMPLETE      => array (self::KEY_ATTRIBUTE => 'complete'),
120        self::POOMTASKS_DATECOMPLETED => array (self::KEY_ATTRIBUTE => 'datecompleted', self::KEY_TYPE => self::TYPE_DATE_DASHES),
121        self::POOMTASKS_DUEDATE       => array (self::KEY_ATTRIBUTE => 'duedate', self::KEY_TYPE => self::TYPE_DATE_LOCAL),
122        self::POOMTASKS_UTCDUEDATE    => array (self::KEY_ATTRIBUTE => 'utcduedate', self::KEY_TYPE => self::TYPE_DATE_DASHES),
123        self::POOMTASKS_IMPORTANCE    => array (self::KEY_ATTRIBUTE => 'importance'),
124        self::POOMTASKS_RECURRENCE    => array (self::KEY_ATTRIBUTE => 'recurrence', self::KEY_TYPE => 'Horde_ActiveSync_Message_TaskRecurrence'),
125        self::POOMTASKS_REMINDERSET   => array (self::KEY_ATTRIBUTE => 'reminderset'),
126        self::POOMTASKS_REMINDERTIME  => array (self::KEY_ATTRIBUTE => 'remindertime', self::KEY_TYPE => self::TYPE_DATE_DASHES),
127        self::POOMTASKS_SENSITIVITY   => array (self::KEY_ATTRIBUTE => 'sensitiviy'),
128        self::POOMTASKS_STARTDATE     => array (self::KEY_ATTRIBUTE => 'startdate', self::KEY_TYPE => self::TYPE_DATE_LOCAL),
129        self::POOMTASKS_UTCSTARTDATE  => array (self::KEY_ATTRIBUTE => 'utcstartdate', self::KEY_TYPE => self::TYPE_DATE_DASHES),
130        self::POOMTASKS_SUBJECT       => array (self::KEY_ATTRIBUTE => 'subject'),
131        self::POOMTASKS_CATEGORIES    => array (self::KEY_ATTRIBUTE => 'categories', self::KEY_VALUES => self::POOMTASKS_CATEGORY),
132    );
133
134    /**
135     * Property values.
136     *
137     * @var array
138     */
139    protected $_properties = array(
140        'subject'       => false,
141        'importance'    => false,
142        'categories'    => array(),
143        'startdate'     => false,
144        'duedate'       => false,
145        'utcduedate'    => false,
146        'complete'      => false,
147        'datecompleted' => false,
148        'remindertime'  => false,
149        'sensitiviy'    => false,
150        'reminderset'   => false,
151        'deadoccur'     => false,
152        'recurrence'    => false,
153        'regenerate'    => false,
154        'sensitiviy'    => false,
155        'utcstartdate'  => false,
156    );
157
158    /**
159     * Const'r
160     *
161     * @see Horde_ActiveSync_Message_Base::__construct()
162     */
163    public function __construct(array $options = array())
164    {
165        parent::__construct($options);
166        if ($this->_version < Horde_ActiveSync::VERSION_TWELVE) {
167            $this->_mapping += array(
168                self::POOMTASKS_BODY          => array(self::KEY_ATTRIBUTE => 'body'),
169                self::POOMTASKS_RTF           => array(self::KEY_ATTRIBUTE => 'rtf'),
170                self::POOMTASKS_BODYTRUNCATED => array(self::KEY_ATTRIBUTE => 'bodytruncated')
171            );
172
173            $this->_properties += array(
174                'body' => false,
175                'rtf'  => false,
176                'bodytruncated' => 0,
177            );
178        } else {
179            $this->_mapping += array(
180                Horde_ActiveSync::AIRSYNCBASE_BODY => array(self::KEY_ATTRIBUTE => 'airsyncbasebody', self::KEY_TYPE=> 'Horde_ActiveSync_Message_AirSyncBaseBody'),
181            );
182
183            $this->_properties += array(
184                'airsyncbasebody' => false,
185            );
186        }
187    }
188
189    /**
190     * Set the importance
191     *
192     * @param integer $importance  A IMPORTANCE_* flag
193     */
194    public function setImportance($importance)
195    {
196        if (is_null($importance)) {
197            $importance = self::IMPORTANCE_NORMAL;
198        }
199
200        $this->_properties['importance'] = $importance;
201    }
202
203    /**
204     * Get the task importance level
205     *
206     * @return integer  A IMPORTANCE_* constant
207     */
208    public function getImportance()
209    {
210        return $this->_getAttribute('importance', self::IMPORTANCE_NORMAL);
211    }
212
213    /**
214     * Set the reminder datetime
215     *
216     * @param Horde_Date $datetime  The time to trigger the alarm in local tz.
217     */
218    public function setReminder(Horde_Date $datetime)
219    {
220        $this->_properties['remindertime'] = $datetime;
221        $this->_properties['reminderset'] = self::REMINDER_SET_TRUE;
222    }
223
224    /**
225     * Get the reminder time.
226     *
227     * @return Horde_Date  in local tz
228     */
229    public function getReminder()
230    {
231        if (!$this->_getAttribute('reminderset')) {
232            return false;
233        }
234        return $this->_getAttribute('remindertime');
235    }
236
237    /**
238     * Set recurrence information for this task
239     *
240     * @param Horde_Date_Recurrence $recurrence
241     */
242    public function setRecurrence(Horde_Date_Recurrence $recurrence)
243    {
244        $r = Horde_ActiveSync::messageFactory('TaskRecurrence');
245
246        // Map the type fields
247        switch ($recurrence->recurType) {
248        case Horde_Date_Recurrence::RECUR_DAILY:
249            $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_DAILY;
250            break;
251        case Horde_Date_Recurrence::RECUR_WEEKLY:
252            $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_WEEKLY;
253            $r->dayofweek = $recurrence->getRecurOnDays();
254            break;
255        case Horde_Date_Recurrence::RECUR_MONTHLY_DATE:
256            $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY;
257            $r->dayofmonth = $recurrence->start->mday;
258            break;
259        case Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY;
260            $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY_NTH;
261            $r->weekofmonth = ceil($recurrence->start->mday / 7);
262            $r->dayofweek = $this->_dayOfWeekMap[$recurrence->start->dayOfWeek()];
263            break;
264        case Horde_Date_Recurrence::RECUR_YEARLY_DATE:
265            $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_YEARLY;
266            break;
267        case Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY:
268            $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_YEARLYNTH;
269            $r->dayofweek = $this->_dayOfWeekMap[$recurrence->start->dayOfWeek()];
270            $r->weekofmonth = ceil($recurrence->start->mday / 7);
271            $r->monthofyear = $recurrence->start->month;
272            break;
273        }
274        if (!empty($recurrence->recurInterval)) {
275            $r->interval = $recurrence->recurInterval;
276        }
277
278        // AS messages can only have one or the other (or none), not both
279        if ($recurrence->hasRecurCount()) {
280            $r->occurrences = $recurrence->getRecurCount();
281        } elseif ($recurrence->hasRecurEnd()) {
282            $r->until = $recurrence->getRecurEnd();
283        }
284
285        // Set the start of the recurrence series.
286        $r->start = clone $this->duedate;
287
288        $this->_properties['recurrence'] = $r;
289    }
290
291    /**
292     * Obtain a recurrence object. Note this returns a Horde_Date_Recurrence
293     * object, not Horde_ActiveSync_Message_Recurrence.
294     *
295     * @return Horde_Date_Recurrence
296     */
297    public function getRecurrence()
298    {
299        if (!$recurrence = $this->_getAttribute('recurrence')) {
300            return false;
301        }
302
303        $d = clone($this->getDueDate());
304        //  $d->setTimezone($this->getTimezone());
305
306        $rrule = new Horde_Date_Recurrence($d);
307
308        /* Map MS AS type field to Horde_Date_Recurrence types */
309        switch ($recurrence->type) {
310        case Horde_ActiveSync_Message_Recurrence::TYPE_DAILY:
311            $rrule->setRecurType(Horde_Date_Recurrence::RECUR_DAILY);
312             break;
313        case Horde_ActiveSync_Message_Recurrence::TYPE_WEEKLY:
314            $rrule->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY);
315            $rrule->setRecurOnDay($recurrence->dayofweek);
316            break;
317        case Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY:
318            $rrule->setRecurType(Horde_Date_Recurrence::RECUR_MONTHLY_DATE);
319            break;
320        case Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY_NTH:
321            $rrule->setRecurType(Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY);
322            $rrule->setRecurOnDay($recurrence->dayofweek);
323            break;
324        case Horde_ActiveSync_Message_Recurrence::TYPE_YEARLY:
325            $rrule->setRecurType(Horde_Date_Recurrence::RECUR_YEARLY_DATE);
326            break;
327        case Horde_ActiveSync_Message_Recurrence::TYPE_YEARLYNTH:
328            $rrule->setRecurType(Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY);
329            $rrule->setRecurOnDay($recurrence->dayofweek);
330            break;
331        }
332
333        if ($rcnt = $recurrence->occurrences) {
334            $rrule->setRecurCount($rcnt);
335        }
336        if ($runtil = $recurrence->until) {
337            $rrule->setRecurEnd(new Horde_Date($runtil));
338        }
339        if ($interval = $recurrence->interval) {
340            $rrule->setRecurInterval($interval);
341        }
342
343        return $rrule;
344    }
345
346    /**
347     * Return this object's folder class
348     *
349     * @return string
350     */
351    public function getClass()
352    {
353        return 'Tasks';
354    }
355
356    /**
357     * Check if a field should be sent to the device even if it is empty.
358     *
359     * @param string $tag  The field tag.
360     *
361     * @return boolean
362     */
363    protected function _checkSendEmpty($tag)
364    {
365        if ($tag == self::POOMTASKS_BODYTRUNCATED && $this->bodysize > 0) {
366            return true;
367        }
368
369        return false;
370    }
371
372}
373