1<?php
2/* Copyright (c) 1998-2017 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4/**
5* Presentation day view
6* @author Stefan Meyer <smeyer.ilias@gmx.de>
7* @version $Id$
8*
9* @ilCtrl_Calls ilCalendarDayGUI: ilCalendarAppointmentGUI
10* @ilCtrl_Calls ilCalendarDayGUI: ilCalendarAppointmentPresentationGUI
11* @ingroup ServicesCalendar
12*/
13
14include_once('./Services/Calendar/classes/class.ilDate.php');
15include_once('./Services/Calendar/classes/class.ilCalendarUtil.php');
16include_once('./Services/Calendar/classes/class.ilCalendarHeaderNavigationGUI.php');
17include_once('./Services/Calendar/classes/class.ilCalendarUserSettings.php');
18include_once('./Services/Calendar/classes/class.ilCalendarAppointmentColors.php');
19include_once './Services/Calendar/classes/class.ilCalendarViewGUI.php';
20
21
22class ilCalendarDayGUI extends ilCalendarViewGUI
23{
24    protected $seed_info = array();
25    protected $user_settings = null;
26
27    /**
28     * @var ilLanguage
29     */
30    protected $lng;
31
32    /**
33     * @var ilCtrl
34     */
35    protected $ctrl;
36
37    /**
38     * @var ilTabsGUI
39     */
40    protected $tabs_gui;
41
42    /**
43     * @var ilTemplate
44     */
45    protected $tpl;
46
47    protected $num_appointments = 1;
48
49    protected $timezone = 'UTC';
50
51    /**
52     * @var ilObjUser
53     */
54    protected $user;
55
56    /**
57     * @var \ILIAS\DI\UIServices
58     */
59    protected $ui;
60
61    /**
62     * @var ilToolbarGUI
63     */
64    protected $toolbar;
65
66    /**
67     * Constructor
68     *
69     * @access public
70     * @param
71     *
72     * @todo make parent constructor (initialize) and init also seed and other common stuff
73     */
74    public function __construct(ilDate $seed_date)
75    {
76        parent::__construct($seed_date, ilCalendarViewGUI::CAL_PRESENTATION_DAY);
77
78        $this->seed_info = $this->seed->get(IL_CAL_FKT_GETDATE);
79
80        $this->user_settings = ilCalendarUserSettings::_getInstanceByUserId($this->user->getId());
81        $this->app_colors = new ilCalendarAppointmentColors($this->user->getId());
82
83        $this->timezone = $this->user->getTimeZone();
84    }
85
86    /**
87     * Execute command
88     *
89     * @access public
90     *
91     */
92    public function executeCommand()
93    {
94        $ilCtrl = $this->ctrl;
95        $tpl = $this->tpl;
96
97        $next_class = $ilCtrl->getNextClass();
98        switch ($next_class) {
99            case "ilcalendarappointmentpresentationgui":
100                $this->ctrl->setReturn($this, "");
101                include_once("./Services/Calendar/classes/class.ilCalendarAppointmentPresentationGUI.php");
102                $this->logger->debug("-ExecCommand - representation of ilDate: this->seed->get(IL_CAL_DATE) = " . $this->seed->get(IL_CAL_DATE));
103                $gui = ilCalendarAppointmentPresentationGUI::_getInstance($this->seed, $this->getCurrentApp());
104                $this->ctrl->forwardCommand($gui);
105                break;
106            case 'ilcalendarappointmentgui':
107                $this->ctrl->setReturn($this, '');
108                $this->tabs_gui->setSubTabActive($_SESSION['cal_last_tab']);
109
110                // initial date for new calendar appointments
111                $idate = new ilDate($_REQUEST['idate'], IL_CAL_DATE);
112
113                include_once('./Services/Calendar/classes/class.ilCalendarAppointmentGUI.php');
114                $app = new ilCalendarAppointmentGUI($this->seed, $idate, (int) $_GET['app_id']);
115                $this->ctrl->forwardCommand($app);
116                break;
117
118            default:
119                $cmd = $this->ctrl->getCmd("show");
120                $this->$cmd();
121                $tpl->setContent($this->tpl->get());
122                break;
123        }
124        return true;
125    }
126
127    /**
128     * fill data section
129     *
130     * @access protected
131     *
132     */
133    protected function show()
134    {
135        /**
136         * @var ILIAS\DI\Container $DIC
137         */
138        global $DIC;
139
140        $ui_factory = $DIC->ui()->factory();
141        $renderer = $DIC->ui()->renderer();
142
143        $lng = $this->lng;
144        $ilUser = $this->user;
145
146
147        // config
148        $raster = 15;
149        if ($this->user_settings->getDayStart()) {
150            // push starting point to last "slot" of hour BEFORE morning aggregation
151            $morning_aggr = ($this->user_settings->getDayStart() - 1) * 60 + (60 - $raster);
152        } else {
153            $morning_aggr = 0;
154        }
155        $evening_aggr = $this->user_settings->getDayEnd() * 60;
156
157
158        $this->tpl = new ilTemplate('tpl.day_view.html', true, true, 'Services/Calendar');
159
160        include_once('./Services/YUI/classes/class.ilYuiUtil.php');
161        ilYuiUtil::initDragDrop();
162
163        if (isset($_GET["bkid"])) {
164            $user_id = $_GET["bkid"];
165            $no_add = true;
166        } elseif ($ilUser->getId() == ANONYMOUS_USER_ID) {
167            $user_id = $ilUser->getId();
168            $no_add = true;
169        } else {
170            $user_id = $ilUser->getId();
171            $no_add = false;
172        }
173        include_once('Services/Calendar/classes/class.ilCalendarSchedule.php');
174        $this->scheduler = new ilCalendarSchedule($this->seed, ilCalendarSchedule::TYPE_DAY, $user_id);
175        $this->scheduler->addSubitemCalendars(true);
176        $this->scheduler->calculate();
177        $daily_apps = $this->scheduler->getByDay($this->seed, $this->timezone);
178
179        //display the download files button.
180        if (count($daily_apps)) {
181            $this->view_with_appointments = true;
182        }
183
184        $hours = $this->parseInfoIntoRaster(
185            $daily_apps,
186            $morning_aggr,
187            $evening_aggr,
188            $raster
189        );
190
191        $colspan = $this->calculateColspan($hours);
192
193        $navigation = new ilCalendarHeaderNavigationGUI($this, $this->seed, ilDateTime::DAY);
194        $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'seed', $this->seed->get(IL_CAL_DATE));
195
196        // add milestone link
197        include_once('Services/Calendar/classes/class.ilCalendarSettings.php');
198        $settings = ilCalendarSettings::_getInstance();
199
200        if (!$no_add) {
201            $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'seed', $this->seed->get(IL_CAL_DATE));
202            $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'idate', $this->seed->get(IL_CAL_DATE));
203            $new_app_url = $this->ctrl->getLinkTargetByClass('ilcalendarappointmentgui', 'add');
204
205            if ($settings->getEnableGroupMilestones()) {
206                $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'seed', $this->seed->get(IL_CAL_DATE));
207                $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'idate', $this->seed->get(IL_CAL_DATE));
208                $new_ms_url = $this->ctrl->getLinkTargetByClass('ilcalendarappointmentgui', 'addMilestone');
209
210                $this->tpl->setCurrentBlock("new_ms");
211                $this->tpl->setVariable('DD_ID', $this->seed->get(IL_CAL_UNIX));
212                $this->tpl->setVariable('DD_TRIGGER', $renderer->render($ui_factory->glyph()->add()));
213                $this->tpl->setVariable('URL_DD_NEW_APP', $new_app_url);
214                $this->tpl->setVariable('TXT_DD_NEW_APP', $this->lng->txt('cal_new_app'));
215                $this->tpl->setVariable('URL_DD_NEW_MS', $new_ms_url);
216                $this->tpl->setVariable('TXT_DD_NEW_MS', $this->lng->txt('cal_new_ms'));
217                $this->tpl->parseCurrentBlock();
218            } else {
219                $this->tpl->setCurrentBlock("new_app1");
220                $this->tpl->setVariable('H_NEW_APP_GLYPH', $renderer->render($ui_factory->glyph()->add($new_app_url)));
221                $this->tpl->parseCurrentBlock();
222            }
223
224            $this->ctrl->clearParametersByClass('ilcalendarappointmentgui');
225        }
226
227        $this->tpl->setVariable('NAVIGATION', $navigation->getHTML());
228
229        $this->tpl->setVariable('HEADER_DATE', $this->seed_info['mday'] . ' ' . ilCalendarUtil::_numericMonthToString($this->seed_info['mon'], false));
230        $this->tpl->setVariable('HEADER_DAY', ilCalendarUtil::_numericDayToString($this->seed_info['wday'], true));
231        $this->tpl->setVariable('HCOLSPAN', $colspan - 1);
232
233        $this->tpl->setVariable('TXT_TIME', $lng->txt("time"));
234
235        // show fullday events
236        foreach ($daily_apps as $event) {
237            if ($event['fullday']) {
238                $this->showFulldayAppointment($event);
239            }
240        }
241        $this->tpl->setCurrentBlock('fullday_apps');
242        $this->tpl->setVariable('TXT_F_DAY', $lng->txt("cal_all_day"));
243        $this->tpl->setVariable('COLSPAN', $colspan - 1);
244        $this->tpl->parseCurrentBlock();
245
246        // parse the hour rows
247        foreach ($hours as $numeric => $hour) {
248            if (!($numeric % 60) || ($numeric == $morning_aggr && $morning_aggr) ||
249                ($numeric == $evening_aggr && $evening_aggr)) {
250                if (!$no_add) {
251                    $this->tpl->setCurrentBlock("new_app2");
252                    $this->ctrl->clearParametersByClass('ilcalendarappointmentgui');
253                    $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'seed', $this->seed->get(IL_CAL_DATE));
254                    $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'idate', $this->seed->get(IL_CAL_DATE));
255                    $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'hour', floor($numeric / 60));
256                    $this->tpl->setVariable('NEW_APP_GLYPH', $renderer->render(
257                        $ui_factory->glyph()->add(
258                            $this->ctrl->getLinkTargetByClass('ilcalendarappointmentgui', 'add')
259                        )
260                    ));
261                    $this->tpl->parseCurrentBlock();
262                }
263
264                // aggregation rows
265                if (($numeric == $morning_aggr && $morning_aggr) ||
266                    ($numeric == $evening_aggr && $evening_aggr)) {
267                    $this->tpl->setVariable('TIME_ROWSPAN', 1);
268                }
269                // rastered hour
270                else {
271                    $this->tpl->setVariable('TIME_ROWSPAN', 60 / $raster);
272                }
273
274                $this->tpl->setCurrentBlock('time_txt');
275
276                $this->tpl->setVariable('TIME', $hour['txt']);
277                $this->tpl->parseCurrentBlock();
278            }
279
280            foreach ($hour['apps_start'] as $app) {
281                $this->showAppointment($app);
282            }
283
284            if ($ilUser->prefs["screen_reader_optimization"]) {
285                // see #0022492
286                //$this->tpl->touchBlock('scrd_app_cell');
287            }
288
289            for ($i = ($colspan - 1);$i > $hour['apps_num'];$i--) {
290                $this->tpl->setCurrentBlock('empty_cell');
291                $this->tpl->setVariable('EMPTY_WIDTH', (100 / (int) ($colspan - 1)) . '%');
292
293                // last "slot" of hour needs border
294                if ($numeric % 60 == 60 - $raster ||
295                    ($numeric == $morning_aggr && $morning_aggr) ||
296                    ($numeric == $evening_aggr && $evening_aggr)) {
297                    $this->tpl->setVariable('EMPTY_STYLE', ' calempty_border');
298                }
299
300                $this->tpl->parseCurrentBlock();
301            }
302
303            $this->tpl->touchBlock('time_row');
304        }
305    }
306
307    /**
308     * show fullday appointment
309     *
310     * @access protected
311     * @param array appointment
312     * @return
313     */
314    protected function showFulldayAppointment($a_app)
315    {
316        $f = $this->ui_factory;
317        $r = $this->ui_renderer;
318
319        $event_tpl = new ilTemplate('tpl.day_event_view.html', true, true, 'Services/Calendar');
320
321        // milestone icon
322        if ($a_app['event']->isMilestone()) {
323            $event_tpl->setCurrentBlock('fullday_ms_icon');
324            $event_tpl->setVariable('ALT_FD_MS', $this->lng->txt("cal_milestone"));
325            $event_tpl->setVariable('SRC_FD_MS', ilUtil::getImagePath("icon_ms.svg"));
326            $event_tpl->parseCurrentBlock();
327        }
328
329        $event_tpl->setCurrentBlock('fullday_app');
330
331        $compl = ($a_app['event']->isMilestone() && $a_app['event']->getCompletion() > 0)
332            ? " (" . $a_app['event']->getCompletion() . "%)"
333            : "";
334
335        $shy = $this->getAppointmentShyButton($a_app['event'], $a_app['dstart'], "");
336
337        //$title = ($new_title = $this->getContentByPlugins($a_app['event'], $a_app['dstart'], $shy))? $new_title : $shy;
338
339        $content = $shy . $compl;
340
341        $event_tpl->setVariable('EVENT_CONTENT', $content);
342
343        $color = $this->app_colors->getColorByAppointment($a_app['event']->getEntryId());
344        $event_tpl->setVariable('F_APP_BGCOLOR', $color);
345        $event_tpl->setVariable('F_APP_FONTCOLOR', ilCalendarUtil::calculateFontColor($color));
346
347        $this->ctrl->clearParametersByClass('ilcalendarappointmentgui');
348        $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'seed', $this->seed->get(IL_CAL_DATE));
349        $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'app_id', $a_app['event']->getEntryId());
350        $event_tpl->setVariable('F_APP_EDIT_LINK', $this->ctrl->getLinkTargetByClass('ilcalendarappointmentgui', 'edit'));
351
352        if ($event_html_by_plugin = $this->getContentByPlugins($a_app['event'], $a_app['dstart'], $content, $event_tpl)) {
353            $body_html = $event_html_by_plugin;
354        } else {
355            $event_tpl->parseCurrentBlock();
356            $body_html = $event_tpl->get();
357        }
358
359        $this->tpl->setCurrentBlock("content_fd");
360        $this->tpl->setVariable("CONTENT_EVENT", $body_html);
361        $this->tpl->parseCurrentBlock();
362
363        $this->num_appointments++;
364    }
365
366    /**
367     * show appointment
368     *
369     * @access protected
370     * @param array appointment
371     */
372    protected function showAppointment($a_app)
373    {
374        $ilUser = $this->user;
375
376        $event_tpl = new ilTemplate('tpl.day_event_view.html', true, true, 'Services/Calendar');
377
378        if (!$ilUser->prefs["screen_reader_optimization"]) {
379            $event_tpl->setCurrentBlock('app');
380        } else {
381            $event_tpl->setCurrentBlock('scrd_app');
382        }
383
384        $this->tpl->setVariable('APP_ROWSPAN', $a_app['rowspan']);
385        //$event_tpl->setVariable('APP_TITLE',$a_app['event']->getPresentationTitle(false));
386
387        switch ($this->user_settings->getTimeFormat()) {
388            case ilCalendarSettings::TIME_FORMAT_24:
389                $time = $a_app['event']->getStart()->get(IL_CAL_FKT_DATE, 'H:i', $this->timezone);
390                break;
391
392            case ilCalendarSettings::TIME_FORMAT_12:
393                $time = $a_app['event']->getStart()->get(IL_CAL_FKT_DATE, 'h:ia', $this->timezone);
394                break;
395        }
396
397        // add end time for screen readers
398        if ($ilUser->prefs["screen_reader_optimization"]) {
399            switch ($this->user_settings->getTimeFormat()) {
400                case ilCalendarSettings::TIME_FORMAT_24:
401                    $time .= "-" . $a_app['event']->getEnd()->get(IL_CAL_FKT_DATE, 'H:i', $this->timezone);
402                    break;
403
404                case ilCalendarSettings::TIME_FORMAT_12:
405                    $time .= "-" . $a_app['event']->getEnd()->get(IL_CAL_FKT_DATE, 'h:ia', $this->timezone);
406                    break;
407            }
408        }
409
410        $shy = $this->getAppointmentShyButton($a_app['event'], $a_app['dstart'], "");
411
412        $title = $shy;
413        $content = ($time != "")? $time . " " . $title : $title;
414
415        $event_tpl->setVariable('EVENT_CONTENT', $content);
416
417        $color = $this->app_colors->getColorByAppointment($a_app['event']->getEntryId());
418        $event_tpl->setVariable('APP_BGCOLOR', $color);
419        //$this->tpl->setVariable('APP_BGCOLOR',$color);
420        $event_tpl->setVariable('APP_COLOR', ilCalendarUtil::calculateFontColor($color));
421        //$this->tpl->setVariable('APP_COLOR',ilCalendarUtil::calculateFontColor($color));
422        $event_tpl->setVariable('APP_ADD_STYLES', $a_app['event']->getPresentationStyle());
423        //$this->tpl->setVariable('APP_ADD_STYLES',$a_app['event']->getPresentationStyle());
424
425        $this->ctrl->clearParametersByClass('ilcalendarappointmentgui');
426        $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'seed', $this->seed->get(IL_CAL_DATE));
427        $this->ctrl->setParameterByClass('ilcalendarappointmentgui', 'app_id', $a_app['event']->getEntryId());
428        $event_tpl->setVariable('APP_EDIT_LINK', $this->ctrl->getLinkTargetByClass('ilcalendarappointmentgui', 'edit'));
429
430        if ($event_html_by_plugin = $this->getContentByPlugins($a_app['event'], $a_app['dstart'], $content, $event_tpl)) {
431            $event_html = $event_html_by_plugin;
432        } else {
433            $event_tpl->parseCurrentBlock();
434            $event_html = $event_tpl->get();
435        }
436
437        $this->tpl->setCurrentBlock("event_nfd");
438        $this->tpl->setVariable("CONTENT_EVENT_NFD", $event_html);
439        $this->tpl->parseCurrentBlock();
440
441        $this->num_appointments++;
442    }
443
444    /**
445     * calculate overlapping hours
446     *
447     * @access protected
448     * @return array hours
449     */
450    protected function parseInfoIntoRaster($daily_apps, $morning_aggr, $evening_aggr, $raster)
451    {
452        $ilUser = $this->user;
453
454        $hours = array();
455        for ($i = $morning_aggr;$i <= $evening_aggr;$i += $raster) {
456            $hours[$i]['apps_start'] = array();
457            $hours[$i]['apps_num'] = 0;
458
459            switch ($this->user_settings->getTimeFormat()) {
460                case ilCalendarSettings::TIME_FORMAT_24:
461                    if ($morning_aggr > 0 && $i == $morning_aggr) {
462                        $hours[$i]['txt'] = sprintf('%02d:00', 0) . ' - ' .
463                            sprintf('%02d:00', ceil(($i + 1) / 60));
464                    } else {
465                        $hours[$i]['txt'] .= sprintf('%02d:%02d', floor($i / 60), $i % 60);
466                    }
467                    if ($evening_aggr < 23 * 60 && $i == $evening_aggr) {
468                        $hours[$i]['txt'] .= ' - ' . sprintf('%02d:00', 0);
469                    }
470                    break;
471
472                case ilCalendarSettings::TIME_FORMAT_12:
473
474                    $this->logger->notice('Morning: ' . $morning_aggr . ' and $i:' . $i);
475
476                    if ($morning_aggr > 0 && $i == $morning_aggr) {
477                        $hours[$i]['txt'] =
478                            date('h a', mktime(0, 0, 0, 1, 1, 2000)) . ' - ' .
479                            date('h a', mktime($this->user_settings->getDayStart(), 0, 0, 1, 1, 2000));
480                    } else {
481                        $hours[$i]['txt'] = date('h a', mktime(floor($i / 60), $i % 60, 0, 1, 1, 2000));
482                    }
483                    if ($evening_aggr < 23 * 60 && $i == $evening_aggr) {
484                        $hours[$i]['txt'] .= ' - ' . date('h a', mktime(0, 0, 0, 1, 1, 2000));
485                    }
486                    break;
487            }
488        }
489
490
491        foreach ($daily_apps as $app) {
492            // fullday appointment are not relavant
493            if ($app['fullday']) {
494                continue;
495            }
496            // start hour for this day
497            #21132 #21636
498            if ($app['start_info']['mday'] != $this->seed_info['mday']) {
499                $start = 0;
500            } else {
501                $start = $app['start_info']['hours'] * 60 + $app['start_info']['minutes'];
502            }
503            #21636
504            //$start = $app['start_info']['hours']*60+$app['start_info']['minutes'];
505
506            // end hour for this day
507            #21132
508            if ($app['end_info']['mday'] != $this->seed_info['mday']) {
509                $end = 23 * 60;
510            } elseif ($app['start_info']['hours'] == $app['end_info']['hours']) {
511                $end = $start + $raster;
512            } else {
513                $end = $app['end_info']['hours'] * 60 + $app['end_info']['minutes'];
514            }
515            //$end = $app['end_info']['hours']*60+$app['end_info']['minutes'];
516
517            // set end to next hour for screen readers
518            if ($ilUser->prefs["screen_reader_optimization"]) {
519                $end = $start + $raster;
520            }
521
522            if ($start < $morning_aggr) {
523                $start = $morning_aggr;
524            }
525            if ($end <= $morning_aggr) {
526                $end = $morning_aggr + $raster;
527            }
528            if ($start > $evening_aggr) {
529                $start = $evening_aggr;
530            }
531            if ($end > $evening_aggr + $raster) {
532                $end = $evening_aggr + $raster;
533            }
534            if ($end <= $start) {
535                $end = $start + $raster;
536            }
537
538            // map start and end to raster
539            $start = floor($start / $raster) * $raster;
540            $end = ceil($end / $raster) * $raster;
541
542            $first = true;
543            for ($i = $start;$i < $end;$i += $raster) {
544                if ($first) {
545                    if (!$ilUser->prefs["screen_reader_optimization"]) {
546                        $app['rowspan'] = ceil(($end - $start) / $raster);
547                    } else {  	// screen readers get always a rowspan of 1
548                        $app['rowspan'] = 1;
549                    }
550                    $hours[$i]['apps_start'][] = $app;
551                    $first = false;
552                }
553                $hours[$i]['apps_num']++;
554            }
555        }
556
557        return $hours;
558    }
559
560    /**
561     * calculate colspan
562     *
563     * @access protected
564     * @param
565     * @return
566     */
567    protected function calculateColspan($hours)
568    {
569        $ilUser = $this->user;
570
571
572        $colspan = 1;
573        foreach ($hours as $hour) {
574            $colspan = max($colspan, $hour['apps_num'] + 1);
575        }
576
577        // screen reader: always two cols (time and event col)
578        if ($ilUser->prefs["screen_reader_optimization"]) {
579            $colspan = 2;
580        }
581
582        return max($colspan, 2);
583    }
584}
585