1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4include_once('Services/Calendar/classes/class.ilDate.php');
5include_once('./Services/Calendar/interfaces/interface.ilDatePeriod.php');
6
7define('IL_CAL_TRANSLATION_NONE', 0);
8define('IL_CAL_TRANSLATION_SYSTEM', 1);
9
10
11/**
12* Model for a calendar entry.
13*
14* @author Stefan Meyer <meyer@leifos.com>
15* @version $Id$
16*
17*
18* @ingroup ServicesCalendar
19*/
20class ilCalendarEntry implements ilDatePeriod
21{
22    protected $log;
23    protected $db;
24
25
26    protected $entry_id;
27    protected $last_update;
28    protected $title;
29    protected $subtitle;
30    protected $description;
31    protected $location;
32    protected $further_informations;
33    protected $start = null;
34    protected $fullday;
35    protected $end = null;
36    protected $is_auto_generated = false;
37    protected $context_id = 0;
38    protected $context_info = '';
39    protected $translation_type = IL_CAL_TRANSLATION_NONE;
40    protected $is_milestone = false;
41    protected $completion = 0;
42
43    protected $notification = false;
44
45    /**
46     * Constructor
47     *
48     * @access public
49     * @param int cal_entry id
50     *
51     */
52    public function __construct($a_id = 0)
53    {
54        global $DIC;
55
56        $ilDB = $DIC['ilDB'];
57        $this->log = $DIC->logger()->cal();
58
59        $this->db = $ilDB;
60
61        if ($this->entry_id = $a_id) {
62            $this->read();
63        }
64    }
65
66    /**
67     * clone instance
68     */
69    public function __clone()
70    {
71        $this->entry_id = null;
72    }
73
74    /**
75     * delete entry
76     *
77     * @access public
78     * @static
79     *
80     */
81    public static function _delete($a_entry_id)
82    {
83        global $DIC;
84
85        $ilDB = $DIC['ilDB'];
86
87        include_once('./Services/Calendar/classes/class.ilCalendarRecurrence.php');
88        ilCalendarRecurrence::_delete($a_entry_id);
89
90        $query = "DELETE FROM cal_entries " .
91            "WHERE cal_id = " . $ilDB->quote($a_entry_id, 'integer') . " ";
92        $res = $ilDB->manipulate($query);
93
94        return true;
95    }
96
97    /**
98     * Set context info
99     * @param $a_info
100     */
101    public function setContextInfo($a_info)
102    {
103        $this->context_info = $a_info;
104    }
105
106    /**
107     * @return string
108     */
109    public function getContextInfo()
110    {
111        return $this->context_info;
112    }
113
114    /**
115     * get entry id
116     *
117     * @access public
118     *
119     */
120    public function getEntryId()
121    {
122        return $this->entry_id;
123    }
124
125    /**
126     * get last update
127     *
128     * @access public
129     * @param
130     * @return
131     */
132    public function getLastUpdate()
133    {
134        return $this->last_update ? $this->last_update : new ilDateTime(time(), IL_CAL_UNIX);
135    }
136
137    /**
138     * set last update
139     *
140     * @access public
141     * @param
142     * @return
143     */
144    public function setLastUpdate($a_date)
145    {
146        $this->last_update = $a_date;
147    }
148
149
150    /**
151     * get start
152     *
153     * @access public
154     * @return
155     */
156    public function getStart()
157    {
158        return $this->start;
159    }
160
161    /**
162     *
163     * @access public
164     * @param
165     * @return
166     */
167    public function setStart($a_start)
168    {
169        $this->start = $a_start;
170    }
171
172    /**
173     * get end
174     * @access public
175     * @return ilDateTime end
176     */
177    public function getEnd()
178    {
179        return $this->end;
180    }
181
182    /**
183     * set end
184     * @access public
185     * @param
186     */
187    public function setEnd($a_end)
188    {
189        $this->end = $a_end;
190    }
191
192    /**
193     * set title
194     *
195     * @access public
196     * @param string title
197     *
198     */
199    public function setTitle($a_title)
200    {
201        $this->title = $a_title;
202    }
203
204    /**
205     * get title
206     *
207     * @access public
208     *
209     */
210    public function getTitle()
211    {
212        return $this->title;
213    }
214
215    /**
216     * get title for presentation.
217     * Special handling for auto generated appointments
218     *
219     * @access public
220     * @return
221     */
222    public function getPresentationTitle($a_shorten = true)
223    {
224        global $DIC;
225
226        $lng = $DIC['lng'];
227
228        if ($this->getTranslationType() == IL_CAL_TRANSLATION_NONE) {
229            $title = $this->getTitle();
230        } elseif (strlen($this->getSubtitle())) {
231            // parse dynamic title?
232            if (preg_match("/#([a-z]+)#/", $this->getSubtitle(), $matches)) {
233                $subtitle = $this->parseDynamicTitle($matches[1]);
234            } else {
235                $subtitle = $lng->txt($this->getSubtitle());
236            }
237            $title = $this->getTitle() .
238                (strlen($subtitle)
239                ? ' (' . $subtitle . ')'
240                : '');
241        } else {
242            $title = $lng->txt($this->getTitle());
243        }
244
245        if ($a_shorten) {
246            return ilUtil::shortenText(ilUtil::shortenWords($title, 20), 40, true);
247        }
248        return $title;
249    }
250
251    public function getPresentationStyle()
252    {
253        // see parseDynamicTitle()
254        return $this->presentation_style;
255    }
256
257    protected function parseDynamicTitle($a_type)
258    {
259        global $DIC;
260
261        $lng = $DIC['lng'];
262
263        $title = $style = "";
264        switch ($a_type) {
265            case "consultationhour":
266                include_once 'Services/Booking/classes/class.ilBookingEntry.php';
267                $entry = new ilBookingEntry($this->getContextId());
268                if ($entry) {
269                    if ($entry->isOwner()) {
270                        $max = (int) $entry->getNumberOfBookings();
271                        $current = (int) $entry->getCurrentNumberOfBookings($this->getEntryId());
272                        if (!$current) {
273                            $style = ';border-left-width: 5px; border-left-style: solid; border-left-color: green';
274                            $title = $lng->txt('cal_book_free');
275                        } elseif ($current >= $max) {
276                            $style = ';border-left-width: 5px; border-left-style: solid; border-left-color: red';
277                            $title = $lng->txt('cal_booked_out');
278                        } else {
279                            $style = ';border-left-width: 5px; border-left-style: solid; border-left-color: yellow';
280                            $title = $current . '/' . $max;
281                        }
282                    } else {
283                        /*
284                         * if($entry->hasBooked($this->getEntryId()))
285                         */
286                        include_once 'Services/Calendar/classes/ConsultationHours/class.ilConsultationHourAppointments.php';
287                        $apps = ilConsultationHourAppointments::getAppointmentIds($entry->getObjId(), $this->getContextId(), $this->getStart());
288                        $orig_event = $apps[0];
289                        if ($entry->hasBooked($orig_event)) {
290                            $style = ';border-left-width: 5px; border-left-style: solid; border-left-color: green';
291                            $title = $lng->txt('cal_date_booked');
292                        }
293                    }
294                }
295                break;
296        }
297
298        if ($style) {
299            $this->presentation_style = $style;
300        }
301        return $title;
302    }
303
304    /**
305     * set subtitle
306     * Used for automatic generated appointments.
307     * Will be appended to the title.
308     *
309     * @access public
310     * @param string subtitle
311     * @return
312     */
313    public function setSubtitle($a_subtitle)
314    {
315        $this->subtitle = $a_subtitle;
316    }
317
318    /**
319     * get subtitle
320     *
321     * @access public
322     * @return
323     */
324    public function getSubtitle()
325    {
326        return $this->subtitle;
327    }
328
329    /**
330     * set description
331     *
332     * @access public
333     * @param string description
334     *
335     */
336    public function setDescription($a_description)
337    {
338        $this->description = $a_description;
339    }
340
341    /**
342     * get description
343     *
344     * @access public
345     */
346    public function getDescription()
347    {
348        return $this->description;
349    }
350
351    /**
352     * set location
353     *
354     * @access public
355     * @param string location
356     *
357     */
358    public function setLocation($a_location)
359    {
360        $this->location = $a_location;
361    }
362
363    /**
364     * get location
365     *
366     * @access public
367     */
368    public function getLocation()
369    {
370        return $this->location;
371    }
372
373    /**
374     * set further informations
375     *
376     * @access public
377     * @param string further informations
378     *
379     */
380    public function setFurtherInformations($a_informations)
381    {
382        $this->further_informations = $a_informations;
383    }
384
385    /**
386     * get further informations
387     *
388     * @access public
389     */
390    public function getFurtherInformations()
391    {
392        return $this->further_informations;
393    }
394
395    /**
396     * set fullday event
397     * Fullday events do not change their time in different timezones.
398     * It is possible to create fullday events with a duration of more than one day.
399     *
400     * @access public
401     * @param bool fullday
402     *
403     */
404    public function setFullday($a_fullday)
405    {
406        $this->fullday = (bool) $a_fullday;
407    }
408
409    /**
410     * is fullday
411     *
412     * @access public
413     */
414    public function isFullday()
415    {
416        return (bool) $this->fullday;
417    }
418
419    /**
420     * is auto generated
421     *
422     * @access public
423     * @param
424     * @return
425     */
426    public function isAutoGenerated()
427    {
428        return (bool) $this->is_auto_generated;
429    }
430
431    /**
432     * set auto generated
433     *
434     * @access public
435     * @param
436     * @return
437     */
438    public function setAutoGenerated($a_status)
439    {
440        $this->is_auto_generated = $a_status;
441    }
442
443    /**
444     * is milestone
445     *
446     * @access public
447     * @param
448     * @return
449     */
450    public function isMilestone()
451    {
452        return (bool) $this->is_milestone;
453    }
454
455    /**
456     * set milestone
457     *
458     * @access public
459     * @param
460     * @return
461     */
462    public function setMilestone($a_status)
463    {
464        $this->is_milestone = $a_status;
465    }
466
467    /**
468    * Set Completion.
469    *
470    * @param	int	$a_completion	Completion
471    */
472    public function setCompletion($a_completion)
473    {
474        $this->completion = $a_completion;
475    }
476
477    /**
478    * Get Completion.
479    *
480    * @return	int	Completion
481    */
482    public function getCompletion()
483    {
484        return $this->completion;
485    }
486
487    /**
488     * set context id
489     *
490     * @access public
491     * @param int context id
492     * @return
493     */
494    public function setContextId($a_context_id)
495    {
496        $this->context_id = $a_context_id;
497    }
498
499    /**
500     * get context id
501     *
502     * @access public
503     * @return
504     */
505    public function getContextId()
506    {
507        return $this->context_id;
508    }
509
510    /**
511     *
512     *
513     * @access public
514     * @param
515     * @return
516     */
517    public function setTranslationType($a_type)
518    {
519        $this->translation_type = $a_type;
520    }
521
522    /**
523     * get translation type
524     *
525     * @access public
526     * @return int translation type
527     */
528    public function getTranslationType()
529    {
530        return $this->translation_type;
531    }
532
533    /**
534     * Enable course group notification
535     * @param bool $a_status
536     */
537    public function enableNotification($a_status)
538    {
539        $this->notification = $a_status;
540    }
541
542    /**
543     * Check if course group notification is enabled
544     * @return bool
545     */
546    public function isNotificationEnabled()
547    {
548        return (bool) $this->notification;
549    }
550
551    /**
552     * update
553     *
554     * @access public
555     *
556     */
557    public function update()
558    {
559        global $DIC;
560
561        $ilDB = $DIC['ilDB'];
562
563        $now = new ilDateTime(time(), IL_CAL_UNIX);
564        $utc_timestamp = $now->get(IL_CAL_DATETIME, '', ilTimeZone::UTC);
565
566
567        $query = "UPDATE cal_entries " .
568            "SET title = " . $this->db->quote($this->getTitle(), 'text') . ", " .
569            "last_update = " . $ilDB->quote($utc_timestamp, 'timestamp') . ", " .
570            "subtitle = " . $this->db->quote($this->getSubtitle(), 'text') . ", " .
571            "description = " . $this->db->quote($this->getDescription(), 'text') . ", " .
572            "location = " . $this->db->quote($this->getLocation(), 'text') . ", " .
573            "fullday = " . $ilDB->quote($this->isFullday() ? 1 : 0, 'integer') . ", " .
574            "starta = " . $this->db->quote($this->getStart()->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') . ", " .
575            "enda = " . $this->db->quote($this->getEnd()->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') . ", " .
576            "informations = " . $this->db->quote($this->getFurtherInformations(), 'text') . ", " .
577            "auto_generated =  " . $this->db->quote($this->isAutoGenerated(), 'integer') . ", " .
578            "translation_type = " . $this->db->quote($this->getTranslationType(), 'integer') . ", " .
579            "context_id = " . $this->db->quote($this->getContextId(), 'integer') . ", " .
580            'context_info = ' . $this->db->quote($this->getContextInfo(), 'text') . ', ' .
581            "completion = " . $this->db->quote($this->getCompletion(), 'integer') . ", " .
582            "is_milestone = " . $this->db->quote($this->isMilestone() ? 1 : 0, 'integer') . ", " .
583            'notification = ' . $this->db->quote($this->isNotificationEnabled() ? 1 : 0, 'integer') . ' ' .
584            "WHERE cal_id = " . $this->db->quote($this->getEntryId(), 'integer') . " ";
585        $res = $ilDB->manipulate($query);
586
587        return true;
588    }
589
590    /**
591     * save one entry
592     *
593     * @access public
594     *
595     */
596    public function save()
597    {
598        global $DIC;
599
600        $ilDB = $DIC['ilDB'];
601
602        $next_id = $ilDB->nextId('cal_entries');
603        $now = new ilDateTime(time(), IL_CAL_UNIX);
604        $utc_timestamp = $now->get(IL_CAL_DATETIME, '', ilTimeZone::UTC);
605
606        $query = "INSERT INTO cal_entries (cal_id,title,last_update,subtitle,description,location,fullday,starta,enda, " .
607            "informations,auto_generated,context_id,context_info,translation_type, completion, is_milestone, notification) " .
608            "VALUES( " .
609            $ilDB->quote($next_id, 'integer') . ", " .
610            $this->db->quote($this->getTitle(), 'text') . ", " .
611            $ilDB->quote($utc_timestamp, 'timestamp') . ", " .
612            $this->db->quote($this->getSubtitle(), 'text') . ", " .
613            $this->db->quote($this->getDescription(), 'text') . ", " .
614            $this->db->quote($this->getLocation(), 'text') . ", " .
615            $ilDB->quote($this->isFullday() ? 1 : 0, 'integer') . ", " .
616            $this->db->quote($this->getStart()->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') . ", " .
617            $this->db->quote($this->getEnd()->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') . ", " .
618            $this->db->quote($this->getFurtherInformations(), 'text') . ", " .
619            $this->db->quote($this->isAutoGenerated(), 'integer') . ", " .
620            $this->db->quote($this->getContextId(), 'integer') . ", " .
621            $this->db->quote($this->getContextInfo(), 'text') . ', ' .
622            $this->db->quote($this->getTranslationType(), 'integer') . ", " .
623            $this->db->quote($this->getCompletion(), 'integer') . ", " .
624            $this->db->quote($this->isMilestone() ? 1 : 0, 'integer') . ", " .
625            $this->db->quote($this->isNotificationEnabled() ? 1 : 0, 'integer') . ' ' .
626            ")";
627        $res = $ilDB->manipulate($query);
628
629        $this->entry_id = $next_id;
630        return true;
631    }
632
633    /**
634     * delete
635     *
636     * @access public
637     * @return
638     */
639    public function delete()
640    {
641        global $DIC;
642
643        $ilDB = $DIC['ilDB'];
644
645        include_once('./Services/Calendar/classes/class.ilCalendarRecurrence.php');
646        ilCalendarRecurrence::_delete($this->getEntryId());
647
648        $query = "DELETE FROM cal_entries " .
649            "WHERE cal_id = " . $this->db->quote($this->getEntryId(), 'integer') . " ";
650        $res = $ilDB->manipulate($query);
651
652        include_once './Services/Calendar/classes/class.ilCalendarCategoryAssignments.php';
653        ilCalendarCategoryAssignments::_deleteByAppointmentId($this->getEntryId());
654
655        return true;
656    }
657
658    /**
659     * validate
660     *
661     * @access public
662     * @return
663     */
664    public function validate()
665    {
666        global $DIC;
667
668        $ilErr = $DIC['ilErr'];
669        $lng = $DIC['lng'];
670
671        $success = true;
672        $ilErr->setMessage('');
673        if (!strlen($this->getTitle())) {
674            $success = false;
675            $ilErr->appendMessage($lng->txt('err_missing_title'));
676        }
677        if (!$this->getStart() || !$this->getEnd()) {
678            $success = false;
679        } elseif (ilDateTime::_before($this->getEnd(), $this->getStart(), '')) {
680            $success = false;
681            $ilErr->appendMessage($lng->txt('err_end_before_start'));
682        }
683        return $success;
684    }
685
686
687
688    /**
689     * @access protected
690     * @param
691     *
692     */
693    protected function read()
694    {
695        global $DIC;
696
697        $ilDB = $DIC['ilDB'];
698
699        $query = "SELECT * FROM cal_entries WHERE cal_id = " . $this->db->quote($this->getEntryId(), 'integer') . " ";
700        $res = $this->db->query($query);
701        while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
702            $this->setLastUpdate(new ilDateTime($row->last_update, IL_CAL_DATETIME, 'UTC'));
703            $this->setTitle($row->title);
704            $this->setSubtitle($row->subtitle);
705            $this->setDescription($row->description);
706            $this->setLocation($row->location);
707            $this->setFurtherInformations($row->informations);
708            $this->setFullday((bool) $row->fullday);
709            $this->setAutoGenerated($row->auto_generated);
710            $this->setContextId($row->context_id);
711            $this->setContextInfo($row->context_info);
712            $this->setTranslationType($row->translation_type);
713            $this->setCompletion($row->completion);
714            $this->setMilestone($row->is_milestone);
715            $this->enableNotification((bool) $row->notification);
716
717            if ($this->isFullday()) {
718                $this->start = new ilDate($row->starta, IL_CAL_DATETIME);
719                $this->end = new ilDate($row->enda, IL_CAL_DATETIME);
720            } else {
721                $this->start = new ilDateTime($row->starta, IL_CAL_DATETIME, 'UTC');
722                $this->end = new ilDateTime($row->enda, IL_CAL_DATETIME, 'UTC');
723            }
724        }
725    }
726
727    /**
728     *
729     * @param ilLanguage $lng
730     * @return
731     */
732    public function appointmentToMailString($lng)
733    {
734        $body = $lng->txt('cal_details');
735        $body .= "\n\n";
736        $body .= $lng->txt('title') . ': ' . $this->getTitle() . "\n";
737
738        ilDatePresentation::setUseRelativeDates(false);
739        $body .= $lng->txt('date') . ': ' . ilDatePresentation::formatPeriod($this->getStart(), $this->getEnd()) . "\n";
740        ilDatePresentation::setUseRelativeDates(true);
741
742        if (strlen($this->getLocation())) {
743            $body .= $lng->txt('cal_where') . ': ' . $this->getLocation() . "\n";
744        }
745
746        if (strlen($this->getDescription())) {
747            $body .= $lng->txt('description') . ': ' . $this->getDescription() . "\n";
748        }
749        return $body;
750    }
751
752
753    /**
754    * Write users responsible for a milestone
755    */
756    public function writeResponsibleUsers($a_users)
757    {
758        global $DIC;
759
760        $ilDB = $DIC['ilDB'];
761
762        $ilDB->manipulateF(
763            "DELETE FROM cal_entry_responsible WHERE cal_id = %s",
764            array("integer"),
765            array($this->getEntryId())
766        );
767
768        if (is_array($a_users)) {
769            foreach ($a_users as $user_id) {
770                $ilDB->manipulateF(
771                    "INSERT INTO cal_entry_responsible (cal_id, user_id) " .
772                    " VALUES (%s,%s)",
773                    array("integer", "integer"),
774                    array($this->getEntryId(), $user_id)
775                );
776            }
777        }
778
779        $this->responsible_users = $a_users;
780    }
781
782    /**
783    * Read responsible users
784    */
785    public function readResponsibleUsers()
786    {
787        global $DIC;
788
789        $ilDB = $DIC['ilDB'];
790
791        $set = $ilDB->queryF(
792            "SELECT * FROM cal_entry_responsible WHERE cal_id = %s",
793            array("integer"),
794            array($this->getEntryId())
795        );
796
797        $return = array();
798        while ($rec = $ilDB->fetchAssoc($set)) {
799            $n = ilObjUser::_lookupName($rec["user_id"]);
800            $return[] = array_merge(
801                $n,
802                array("login" => ilObjUser::_lookupLogin($rec["user_id"]))
803            );
804        }
805
806        return $return;
807    }
808}
809