PluginProbe
ShiftController Employee Shift Scheduling / 4.9.95
ShiftController Employee Shift Scheduling v4.9.95
4.9.97 4.9.96 4.9.95 4.9.74 4.9.75 4.9.76 4.9.77 4.9.78 4.9.84 4.9.85 4.9.87 4.9.91 4.9.92 trunk 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 3.2.4 All 38 releases
shiftcontroller / sh4 / schedule / html / view / week.php

week.php in ShiftController Employee Shift Scheduling 4.9.95, at sh4/schedule/html/view/week.php

1,204 lines 32.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php if (! defined('ABSPATH')) exit; // Exit if accessed directly
2 #[AllowDynamicProperties]
3 class SH4_Schedule_Html_View_Week
4 {
5 protected $borderColor = 'gray';
6 protected $allCombosShift = array();
7 protected $allCombosTimeoff = array();
8
9 public function __construct(
10 HC3_Hooks $hooks,
11 HC3_Ui $ui,
12 HC3_Request $request,
13 HC3_Time $t,
14 HC3_Settings $settings,
15
16 HC3_Uri $uri,
17
18 SH4_New_Acl $newAcl,
19
20 SH4_Shifts_Duration $shiftsDuration,
21 SH4_Shifts_View_Widget $widget,
22 SH4_Calendars_Query $calendarsQuery,
23 SH4_Calendars_Presenter $calendarsPresenter,
24 SH4_Employees_Presenter $employeesPresenter,
25 SH4_Schedule_Html_View_Common $common,
26 SH4_Shifts_View_Common $shiftsCommon
27 )
28 {
29 $this->self = $hooks->wrap($this);
30
31 $this->ui = $ui;
32
33 $this->uri = $uri;
34 $this->t = $t;
35 $this->request = $request;
36
37 $this->settings = $hooks->wrap( $settings );
38 $this->newAcl = $hooks->wrap( $newAcl );
39
40 $this->shiftsDuration = $hooks->wrap( $shiftsDuration );
41 $this->calendarsQuery = $hooks->wrap( $calendarsQuery );
42 $this->calendarsPresenter = $hooks->wrap( $calendarsPresenter );
43 $this->employeesPresenter = $hooks->wrap( $employeesPresenter );
44
45 $this->widget = $hooks->wrap( $widget );
46 $this->common = $hooks->wrap( $common );
47 $this->shiftsCommon = $hooks->wrap( $shiftsCommon );
48
49 $this->allCombosShift = $this->newAcl->findAllCombosShift();
50 $this->allCombosTimeoff = $this->newAcl->findAllCombosTimeoff();
51 }
52
53 public function renderDateLabel( $date )
54 {
55 $slug = $this->request->getSlug();
56 $today = $this->t->setNow()->formatDateDb();
57
58 $this->t->setDateDb( $date );
59
60 $weekdayView = $this->t->getWeekdayName();
61 $weekdayView = $this->ui->makeBlock( $weekdayView )->tag('muted')->tag('font-size', 1);
62
63 $ret = $this->t->formatDate();
64
65 $ret = $this->ui->makeList( array($weekdayView, $ret) )
66 ->gutter(0)
67 ;
68
69 $toDateParams = array();
70 $toDateParams['type'] = 'day';
71 $toDateParams['start'] = $date;
72 $toDate = array( $slug, $toDateParams );
73
74 $ret = $this->ui->makeAhref( $toDate, $ret )
75 ->tag('print')
76 ;
77
78 if( $date == $today ){
79 $ret = $this->ui->makeBlock( $ret )
80 ->tag('border')
81 ->tag('border-color', 'gray')
82 ;
83 }
84
85 return $ret;
86 }
87
88 public function render()
89 {
90 $slug = $this->request->getSlug();
91 $today = $this->t->setNow()->formatDateDb();
92
93 $shifts = $this->common->getShifts();
94
95 $params = $this->request->getParams();
96 $startDate = $params['start'];
97
98 $disabledWeekdays = $this->settings->get( 'skip_weekdays', TRUE );
99
100 $startDate = $this->t->setDateDb( $startDate )->setStartWeek()->formatDateDb();
101 $endDate = $this->t->modify('+1 week')->modify('-1 day')->formatDateDb();
102
103 $this->t->setDateDb( $startDate );
104 $dates = array();
105 $rexDate = $startDate;
106
107 while( $rexDate <= $endDate ){
108 if( $disabledWeekdays ){
109 $wkd = $this->t->getWeekday();
110 if( in_array($wkd, $disabledWeekdays) ){
111 $rexDate = $this->t->modify('+1 day')->formatDateDb();
112 continue;
113 }
114 }
115
116 $dates[] = $rexDate;
117 $rexDate = $this->t->modify('+1 day')->formatDateDb();
118 }
119
120 $viewShifts = array();
121 $iknow = array('date');
122
123 $allCalendars = $this->common->findAllCalendars();
124 if( count($allCalendars) <= 1 ) $iknow[] = 'calendar';
125
126 $this->shiftsDuration->reset();
127 foreach( $shifts as $shift ){
128 $this->shiftsDuration->add( $shift );
129 $start = $shift->getStart();
130 $end = $shift->getEnd();
131
132 $startDayTime = substr($start, 8, 4);
133 $endDayTime = substr($end, 8, 4);
134 $shiftAllDay = ( (0 == $startDayTime) && (0 == $endDayTime) ) ? TRUE : FALSE;
135
136 $this->t->setDateTimeDb( $end )->modify('-1 second');
137 $shiftEndDate = $this->t->formatDateDb();
138
139 $this->t->setDateTimeDb( $start );
140 $shiftStartDate = $this->t->formatDateDb();
141
142 $rexDate = $shiftStartDate;
143 while( $rexDate <= $shiftEndDate ){
144 if( in_array($rexDate, $dates) ){
145 if( ! array_key_exists($rexDate, $viewShifts) ){
146 $viewShifts[$rexDate] = array();
147 }
148 $viewShifts[$rexDate][] = $shift;
149 }
150
151 if( ! $shiftAllDay ){
152 break;
153 }
154 $this->t->modify( '+1 day' );
155 $rexDate = $this->t->formatDateDb();
156 }
157 }
158
159 $rows = array();
160
161 $row = array();
162 foreach( $dates as $date ){
163 if( ! $date ){
164 $row[] = NULL;
165 continue;
166 }
167
168 $cell = array();
169
170 $dateView = $this->self->renderDateLabel( $date );
171
172 // SHIFTS DURATION
173 $counted = 0;
174 $this->shiftsDuration->reset();
175
176 if( isset($viewShifts[$date]) ){
177 $dateStart = $this->t->setDateDb( $date )
178 ->formatDateTimeDb()
179 ;
180 $dateEnd = $this->t->modify('+1 day')
181 ->formatDateTimeDb()
182 ;
183 foreach( $viewShifts[$date] as $shift ){
184 if( $shift->isMultiDay() ){
185 $durationShift = clone $shift;
186 if( $dateStart > $durationShift->getStart() ){
187 $durationShift->setStart( $dateStart );
188 }
189 if( $dateEnd < $durationShift->getEnd() ){
190 $durationShift->setEnd( $dateEnd );
191 }
192 $this->shiftsDuration->add( $durationShift );
193 }
194 else {
195 $this->shiftsDuration->add( $shift );
196 }
197 $counted++;
198 }
199 }
200
201 if( $counted ){
202 $outReport = $this->common->renderReport( $this->shiftsDuration, FALSE );
203 $outReport = $this->ui->makeSpan( $outReport )
204 ->tag('font-size', 2)
205 ;
206 $dateView = $this->ui->makeList( array($dateView, $outReport) )
207 ->gutter(1)
208 ;
209 }
210
211 $dateView = $this->ui->makeBlock( $dateView )
212 ->tag('align', 'center')
213 ;
214
215 $cell[] = $dateView;
216
217 if( isset($viewShifts[$date]) ){
218 $shiftsView = $this->self->renderDay( $viewShifts[$date], $iknow );
219 $cell[] = $shiftsView;
220 }
221
222 $to = 'new';
223 $toParams = array(
224 'date' => $date
225 );
226 if( array_key_exists('employee', $params) && (count($params['employee']) == 1) ){
227 if( ! in_array(-1, $params['employee']) ){
228 $toParams['employee'] = $params['employee'][0];
229 }
230 }
231
232 $links = array();
233
234 if( $this->allCombosShift ){
235 $label = '+' . ' ' . '__Shift__';
236 $thisTo = 'new/shift';
237 $thisTo = array( $thisTo, $toParams );
238 $link = $this->ui->makeAhref( $thisTo, $label )
239 ->tag('tab-link')
240 ->tag('align', 'center')
241 // ->addAttr('title', '__Add New__')
242 ;
243 if( $today > $date ){
244 $link->tag('muted', 3);
245 }
246 $links[] = $link;
247 }
248
249 if( $this->allCombosTimeoff ){
250 $label = '+' . ' ' . '__Time Off__';
251 $thisTo = 'new/timeoff';
252 $thisTo = array( $thisTo, $toParams );
253 $link = $this->ui->makeAhref( $thisTo, $label )
254 ->tag('tab-link')
255 ->tag('align', 'center')
256 // ->addAttr('title', '__Add New__')
257 ;
258 if( $today > $date ){
259 $link->tag('muted', 3);
260 }
261 $links[] = $link;
262 }
263
264 $links = $this->ui->makeList( $links )->gutter(0);
265
266 $cell[] = $links;
267
268 $cell = $this->ui->makeList( $cell )
269 ->gutter(1)
270 ->tag('margin', 1)
271 ;
272
273 $row[] = $cell;
274 }
275
276 $rows[] = $row;
277
278 $out = $this->ui->makeTable( NULL, $rows, FALSE )
279 ->gutter(0)
280 ->setBordered( $this->borderColor )
281 // ->setSegments( $weekSegments )
282 ;
283
284 return $out;
285 }
286
287 public function renderByCalendar()
288 {
289 $slug = $this->request->getSlug();
290 $today = $this->t->setNow()->formatDateDb();
291
292 $calendars = $this->common->getCalendars();
293 $employees = $this->common->getEmployees();
294 $shifts = $this->common->getShifts();
295
296 // finalize calendars to include those that employee was removed from but still have shifts in
297 $listCalendarId = array_keys( $calendars );
298 $dictCalendarId = array_combine( $listCalendarId, $listCalendarId );
299 foreach( $shifts as $shift ){
300 $calendar = $shift->getCalendar();
301 if( ! $calendar ) continue;
302 $calendarId = $calendar->getId();
303 $dictCalendarId[ $calendarId ] = $calendarId;
304 }
305 if( count($dictCalendarId) > count($calendars) ){
306 $allCalendars = $this->common->findAllCalendars();
307 $calendars = array_intersect_key( $allCalendars, $dictCalendarId );
308 }
309
310 // we can probably have archived calendars so if such calendars have no shifts then skip them
311 $removeArchivedCalendars = array();
312 foreach( $calendars as $calendar ){
313 if( $calendar->isArchived() ){
314 $removeArchivedCalendars[ $calendar->getId() ] = $calendar->getId();
315 }
316 }
317 if( $removeArchivedCalendars ){
318 foreach( $shifts as $shift ){
319 $calendar = $shift->getCalendar();
320 $id = $calendar ? $calendar->getId() : 0;
321 if( isset($removeArchivedCalendars[$id]) ){
322 unset( $removeArchivedCalendars[$id] );
323 }
324 if( ! $removeArchivedCalendars ){
325 break;
326 }
327 }
328 foreach( $removeArchivedCalendars as $id ){
329 unset( $calendars[$id] );
330 }
331 reset( $shifts );
332 }
333
334 $disabledWeekdays = $this->settings->get( 'skip_weekdays', TRUE );
335
336 $params = $this->request->getParams();
337 $startDate = $params['start'];
338
339 $this->t->setDateDb( $startDate )->setStartWeek();
340 $dates = array();
341
342 for( $ii = 0; $ii <= 6; $ii++ ){
343 if( $disabledWeekdays ){
344 $wkd = $this->t->getWeekday();
345 if( in_array($wkd, $disabledWeekdays) ){
346 $this->t->modify('+1 day');
347 continue;
348 }
349 }
350 $dates[] = $this->t->formatDateDb();
351 $this->t->modify('+1 day');
352 }
353
354 $view_calendars = array();
355 foreach( $calendars as $calendar ){
356 $calendarId = $calendar->getId();
357
358 $label = $this->calendarsPresenter->presentTitle( $calendar );
359 $title = htmlspecialchars( $calendar->getTitle() );
360 $description = $calendar->getDescription();
361 if( strlen($description) ){
362 $description = htmlspecialchars( $description );
363 $title .= "\n" . $description;
364 }
365
366 $label = $this->ui->makeSpan( $label )
367 ->addAttr( 'title', $title )
368 ;
369
370 $view_calendars[ $calendarId ] = $label;
371 }
372
373 $view_shifts = array();
374
375 $iknow = array('calendar', 'date');
376 $hori = FALSE;
377
378 $allEmployees = $this->common->findAllEmployees();
379 if( count($allEmployees) < 2 ){
380 $iknow[] = 'employee';
381 }
382
383 foreach( $shifts as $shift ){
384 $calendar = $shift->getCalendar();
385 $id = $calendar->getId();
386
387 if( ! array_key_exists($id, $view_shifts) ){
388 $view_shifts[$id] = array();
389 }
390
391 $start = $shift->getStart();
392 $end = $shift->getEnd();
393
394 $startDayTime = substr($start, 8, 4);
395 $endDayTime = substr($end, 8, 4);
396 $shiftAllDay = ( (0 == $startDayTime) && (0 == $endDayTime) ) ? TRUE : FALSE;
397
398 $this->t->setDateTimeDb( $end )->modify('-1 second');
399 $shiftEndDate = $this->t->formatDateDb();
400
401 $this->t->setDateTimeDb( $start );
402 $shiftStartDate = $this->t->formatDateDb();
403
404 $rexDate = $shiftStartDate;
405 while( $rexDate <= $shiftEndDate ){
406 if( in_array($rexDate, $dates) ){
407 if( ! array_key_exists($rexDate, $view_shifts[$id]) ){
408 $view_shifts[$id][$rexDate] = array();
409 }
410 $view_shifts[$id][$rexDate][] = $shift;
411 }
412
413 if( ! $shiftAllDay ){
414 break;
415 }
416 $this->t->modify( '+1 day' );
417 $rexDate = $this->t->formatDateDb();
418 }
419 }
420
421 $header = array();
422 $header[] = null;
423
424 foreach( $dates as $date ){
425 $this->t->setDateDb( $date );
426 $dateView = $this->self->renderDateLabel( $date );
427
428 // SHIFTS DURATION
429 $counted = 0;
430 $this->shiftsDuration->reset();
431
432 reset( $view_shifts );
433 foreach( $view_shifts as $id => $shifts2 ){
434 if( isset($shifts2[$date]) ){
435 $dateStart = $this->t->setDateDb( $date )
436 ->formatDateTimeDb()
437 ;
438 $dateEnd = $this->t->modify('+1 day')
439 ->formatDateTimeDb()
440 ;
441 foreach( $shifts2[$date] as $shift ){
442 if( $shift->isMultiDay() ){
443 $durationShift = clone $shift;
444 if( $dateStart > $durationShift->getStart() ){
445 $durationShift->setStart( $dateStart );
446 }
447 if( $dateEnd < $durationShift->getEnd() ){
448 $durationShift->setEnd( $dateEnd );
449 }
450 $this->shiftsDuration->add( $durationShift );
451 }
452 else {
453 $this->shiftsDuration->add( $shift );
454 }
455 $counted++;
456 }
457 }
458 }
459 if( $counted ){
460 $outReport = $this->common->renderReport( $this->shiftsDuration, FALSE );
461 $outReport = $this->ui->makeSpan( $outReport )
462 ->tag('font-size', 2)
463 ;
464 $dateView = $this->ui->makeList( array($dateView, $outReport) )
465 ->gutter(1)
466 ;
467 }
468
469 $dateView = $this->ui->makeBlock( $dateView )
470 ->tag('align', 'center')
471 ;
472
473 $header[] = $dateView;
474 }
475
476 $rows = array();
477
478 // link templates to speed things up
479 $toParams = array(
480 'calendar' => '{CID}',
481 'date' => '{DATE}'
482 );
483 $thisTo = 'new/calendar';
484 $thisTo = array( $thisTo, $toParams );
485 $thisHref = $this->uri->makeUrl( $thisTo );
486
487 $linkTemplateShift = '<a title="+ __Shift__" class="hc-block hc-theme-tab-link hc-align-center" href="' . $thisHref . '">+ __Shift__</a>';
488 $linkTemplateShiftMuted = '<a title="+ __Shift__" class="hc-block hc-theme-tab-link hc-align-center hc-muted3" href="' . $thisHref . '">+ __Shift__</a>';
489
490 $toParams = array(
491 'calendar' => '{CID}',
492 'date' => '{DATE}'
493 );
494 $thisTo = 'new/calendar';
495 $thisTo = array( $thisTo, $toParams );
496 $thisHref = $this->uri->makeUrl( $thisTo );
497
498 $linkTemplateTimeoff = '<a title="+ __Time Off__" class="hc-block hc-theme-tab-link hc-align-center" href="' . $thisHref . '">+ __Time Off__</a>';
499 $linkTemplateTimeoffMuted = '<a title="+ __Time Off__" class="hc-block hc-theme-tab-link hc-align-center hc-muted3" href="' . $thisHref . '">+ __Time Off__</a>';
500 $isPrintView = $this->request->isPrintView();
501
502 foreach( $view_calendars as $id => $calendarView ){
503 $row = array();
504
505 $thisCalendar = $calendars[$id];
506 $addOk = FALSE;
507 if( $thisCalendar->isTimeoff() ){
508 $label = '+ ' . '__Time Off__';
509 if( isset($toParams['employee']) ){
510 $testComboId = $id . '-' . $toParams['employee'];
511 if( isset($this->allCombosTimeoff[$testComboId]) ){
512 $addOk = TRUE;
513 }
514 }
515 else {
516 $testComboId = $id . '-';
517 reset( $this->allCombosTimeoff );
518 foreach( $this->allCombosTimeoff as $comboId ){
519 if( $testComboId == substr($comboId, 0, strlen($testComboId)) ){
520 $addOk = TRUE;
521 break;
522 }
523 }
524 }
525 }
526 else {
527 $label = '+ ' . '__Shift__';
528 if( isset($toParams['employee']) ){
529 $testComboId = $id . '-' . $toParams['employee'];
530 if( isset($this->allCombosShift[$testComboId]) ){
531 $addOk = TRUE;
532 }
533 }
534 else {
535 $testComboId = $id . '-';
536 reset( $this->allCombosShift );
537 foreach( $this->allCombosShift as $comboId ){
538 if( $testComboId == substr($comboId, 0, strlen($testComboId)) ){
539 $addOk = TRUE;
540 break;
541 }
542 }
543 }
544 }
545
546 if( $addOk ){
547 $to = 'new';
548 $toParams = array(
549 'calendar' => $id,
550 // 'date' => $date
551 );
552 if( array_key_exists('employee', $params) && (count($params['employee']) == 1) ){
553 if( ! in_array(-1, $params['employee']) ){
554 $toParams['employee'] = $params['employee'][0];
555 }
556 }
557
558 $to = array( $to, $toParams );
559
560 $link = $this->ui->makeAhref( $to, $label )
561 ->tag('tab-link')
562 ->tag('align', 'center')
563 ;
564 // if( $today > $date ){
565 // $link->tag('muted', 3);
566 // }
567
568 $calendarView = $this->ui->makeList( array($calendarView, $link) )
569 ->gutter(1)
570 ;
571 }
572
573 $this->shiftsDuration->reset();
574 if( isset($view_shifts[$id]) ){
575 foreach( $view_shifts[$id] as $date => $dateShifts ){
576 foreach( $dateShifts as $shift ){
577 $this->shiftsDuration->add( $shift );
578 }
579 }
580
581 $outReport = $this->common->renderReport( $this->shiftsDuration, FALSE );
582 $outReport = $this->ui->makeSpan( $outReport )
583 ->tag('font-size', 2)
584 ;
585 $calendarView = $this->ui->makeList( array($calendarView, $outReport) )
586 ->gutter(1)
587 ;
588 }
589
590 $calendarView = $this->ui->makeBlock( $calendarView )
591 ->tag('padding', 2)
592 ->tag('nowrap')
593 ;
594 $row[] = $calendarView;
595
596 foreach( $dates as $date ){
597 $cell = array();
598
599 if( isset($view_shifts[$id][$date]) ){
600 $shiftsView = $this->self->renderDay( $view_shifts[$id][$date], $iknow );
601 $cell[] = $shiftsView;
602 }
603
604 $thisCalendar = $calendars[$id];
605
606 $to = 'new';
607 $toParams = array(
608 'calendar' => $id,
609 'date' => $date
610 );
611 if( array_key_exists('employee', $params) && (count($params['employee']) == 1) ){
612 if( ! in_array(-1, $params['employee']) ){
613 $toParams['employee'] = $params['employee'][0];
614 }
615 }
616
617 if( $isPrintView ) $addOk = false;
618
619 if( $addOk ){
620 /*
621 $to = array( $to, $toParams );
622
623 $link = $this->ui->makeAhref( $to, $label )
624 ->tag('tab-link')
625 ->tag('align', 'center')
626 ;
627 if( $today > $date ){
628 $link->tag('muted', 3);
629 }
630 */
631 if( $today > $date ){
632 if( $thisCalendar->isTimeoff() ){
633 $link = str_replace( array('{CID}', '{DATE}'), array($id, $date), $linkTemplateTimeoffMuted );
634 }
635 else {
636 $link = str_replace( array('{CID}', '{DATE}'), array($id, $date), $linkTemplateShiftMuted );
637 }
638 }
639 else {
640 if( $thisCalendar->isTimeoff() ){
641 $link = str_replace( array('{CID}', '{DATE}'), array($id, $date), $linkTemplateTimeoff );
642 }
643 else {
644 $link = str_replace( array('{CID}', '{DATE}'), array($id, $date), $linkTemplateShift );
645 }
646 }
647
648 $cell[] = $link;
649 }
650
651 $cell = $this->ui->makeList( $cell )
652 ->gutter(1)
653 ->tag('margin', 1)
654 ;
655
656 $row[] = $cell;
657 }
658
659 $rows[] = $row;
660 }
661
662 $out = $this->ui->makeTable( $header, $rows, FALSE )
663 ->gutter(0)
664 ->setBordered( $this->borderColor )
665 // ->setSegments( $weekSegments )
666 ;
667
668 return $out;
669 }
670
671 public function renderByEmployee()
672 {
673 $slug = $this->request->getSlug();
674 $today = $this->t->setNow()->formatDateDb();
675
676 $calendars = $this->common->getCalendars();
677 $employees = $this->common->getEmployees();
678
679 $shifts = $this->common->getShifts();
680
681 // finalize employees to include those that employee was removed from but still have shifts in
682 $listEmployeeId = array_keys( $employees );
683 $dictEmployeeId = array_combine( $listEmployeeId, $listEmployeeId );
684 foreach( $shifts as $shift ){
685 $employee = $shift->getEmployee();
686 if( ! $employee ) continue;
687 $employeeId = $employee->getId();
688 $dictEmployeeId[ $employeeId ] = $employeeId;
689 }
690 if( count($dictEmployeeId) > count($employees) ){
691 $allEmployees = $this->common->findAllEmployees();
692 $employees = array_intersect_key( $allEmployees, $dictEmployeeId );
693 }
694
695 // we can probably have archived employees so if such employees have no shifts then skip them
696 $removeArchivedEmployees = array();
697 foreach( $employees as $employee ){
698 if( $employee->isArchived() ){
699 $removeArchivedEmployees[ $employee->getId() ] = $employee->getId();
700 }
701 }
702 if( $removeArchivedEmployees ){
703 foreach( $shifts as $shift ){
704 $employee = $shift->getEmployee();
705 $id = $employee ? $employee->getId() : 0;
706 if( isset($removeArchivedEmployees[$id]) ){
707 unset( $removeArchivedEmployees[$id] );
708 }
709 if( ! $removeArchivedEmployees ){
710 break;
711 }
712 }
713 foreach( $removeArchivedEmployees as $id ){
714 unset( $employees[$id] );
715 }
716 reset( $shifts );
717 }
718
719 $disabledWeekdays = $this->settings->get( 'skip_weekdays', TRUE );
720
721 $params = $this->request->getParams();
722 $startDate = $params['start'];
723
724 $this->t->setDateDb( $startDate )->setStartWeek();
725 $dates = array();
726
727 for( $ii = 0; $ii <= 6; $ii++ ){
728 if( $disabledWeekdays ){
729 $wkd = $this->t->getWeekday();
730 if( in_array($wkd, $disabledWeekdays) ){
731 $this->t->modify('+1 day');
732 continue;
733 }
734 }
735
736 $dates[] = $this->t->formatDateDb();
737 $this->t->modify('+1 day');
738 }
739
740 $view_employees = array();
741 foreach( $employees as $employee ){
742 $label = $this->employeesPresenter->presentTitle( $employee );
743 $title = htmlspecialchars( $label );
744
745 $description = $employee->getDescription();
746 if( strlen($description) ){
747 $imgpos = strpos( $description, '<img' );
748 if( false !== $imgpos ){
749 $label = $this->ui->makeList( array($label, $description) );
750 }
751 else {
752 $title .= "\n" . htmlspecialchars( $description );
753 }
754 }
755
756 $label = $this->ui->makeSpan( $label )
757 ->tag( 'font-size', 4 )
758 ->addAttr( 'title', $title )
759 ;
760
761 $view_employees[ $employee->getId() ] = $label;
762 }
763
764 $view_shifts = array();
765
766 $iknow = array('employee', 'date');
767
768 $allCalendars = $this->common->findAllCalendars();
769 if( count($allCalendars) <= 1 ) $iknow[] = 'calendar';
770
771 $hori = FALSE;
772
773 foreach( $shifts as $shift ){
774 $shiftId = $shift->getId();
775 $employee = $shift->getEmployee();
776 $id = $employee ? $employee->getId() : 0;
777
778 if( ! array_key_exists($id, $view_shifts) ){
779 $view_shifts[$id] = array();
780 }
781
782 $start = $shift->getStart();
783 $end = $shift->getEnd();
784
785 $startDayTime = substr($start, 8, 4);
786 $endDayTime = substr($end, 8, 4);
787 $shiftAllDay = ( (0 == $startDayTime) && (0 == $endDayTime) ) ? TRUE : FALSE;
788
789 $this->t->setDateTimeDb( $end )->modify('-1 second');
790 $shiftEndDate = $this->t->formatDateDb();
791
792 $this->t->setDateTimeDb( $start );
793 $shiftStartDate = $this->t->formatDateDb();
794
795 $rexDate = $shiftStartDate;
796 while( $rexDate <= $shiftEndDate ){
797 if( in_array($rexDate, $dates) ){
798 $shiftsByDate[ $rexDate ][ $shiftId ] = $shift;
799 if( ! array_key_exists($rexDate, $view_shifts[$id]) ){
800 $view_shifts[$id][$rexDate] = array();
801 }
802 $view_shifts[$id][$rexDate][] = $shift;
803 }
804
805 if( ! $shiftAllDay ){
806 break;
807 }
808 $this->t->modify( '+1 day' );
809 $rexDate = $this->t->formatDateDb();
810 }
811 }
812
813 $header = array();
814 $header[] = NULL;
815 foreach( $dates as $date ){
816 $this->t->setDateDb( $date );
817 $dateView = $this->self->renderDateLabel( $date );
818
819 // SHIFTS DURATION
820 $counted = 0;
821 $this->shiftsDuration->reset();
822
823 reset( $view_shifts );
824 foreach( $view_shifts as $id => $shifts2 ){
825 if( isset($shifts2[$date]) ){
826 $dateStart = $this->t->setDateDb( $date )
827 ->formatDateTimeDb()
828 ;
829 $dateEnd = $this->t->modify('+1 day')
830 ->formatDateTimeDb()
831 ;
832 foreach( $shifts2[$date] as $shift ){
833 if( $shift->isMultiDay() ){
834 $durationShift = clone $shift;
835 if( $dateStart > $durationShift->getStart() ){
836 $durationShift->setStart( $dateStart );
837 }
838 if( $dateEnd < $durationShift->getEnd() ){
839 $durationShift->setEnd( $dateEnd );
840 }
841 $this->shiftsDuration->add( $durationShift );
842 }
843 else {
844 $this->shiftsDuration->add( $shift );
845 }
846 $counted++;
847 }
848 }
849 }
850 if( $counted ){
851 $outReport = $this->common->renderReport( $this->shiftsDuration, FALSE );
852 $outReport = $this->ui->makeSpan( $outReport )
853 ->tag('font-size', 2)
854 ;
855 $dateView = $this->ui->makeList( array($dateView, $outReport) )
856 ->gutter(1)
857 ;
858 }
859
860 $dateView = $this->ui->makeBlock( $dateView )
861 ->tag('align', 'center')
862 ;
863
864 $header[] = $dateView;
865 }
866
867 $rows = array();
868
869 // link templates to speed things up
870 $toParams = array(
871 'employee' => '{EID}',
872 'date' => '{DATE}'
873 );
874 $thisTo = 'new/shift';
875 $thisTo = array( $thisTo, $toParams );
876 $thisHref = $this->uri->makeUrl( $thisTo );
877
878 $linkTemplateShift = '<a title="+ __Shift__" class="hc-block hc-theme-tab-link hc-align-center" href="' . $thisHref . '">+ __Shift__</a>';
879 $linkTemplateShiftMuted = '<a title="+ __Shift__" class="hc-block hc-theme-tab-link hc-align-center hc-muted3" href="' . $thisHref . '">+ __Shift__</a>';
880
881 $toParams = array(
882 'employee' => '{EID}',
883 'date' => '{DATE}'
884 );
885 $thisTo = 'new/timeoff';
886 $thisTo = array( $thisTo, $toParams );
887 $thisHref = $this->uri->makeUrl( $thisTo );
888
889 $linkTemplateTimeoff = '<a title="+ __Time Off__" class="hc-block hc-theme-tab-link hc-align-center" href="' . $thisHref . '">+ __Time Off__</a>';
890 $linkTemplateTimeoffMuted = '<a title="+ __Time Off__" class="hc-block hc-theme-tab-link hc-align-center hc-muted3" href="' . $thisHref . '">+ __Time Off__</a>';
891 $isPrintView = $this->request->isPrintView();
892
893 foreach( $view_employees as $id => $employeeView ){
894 $row = array();
895
896 if( isset($view_shifts[$id]) ){
897 $this->shiftsDuration->reset();
898 $counted = 0;
899 foreach( $view_shifts[$id] as $date => $dateShifts ){
900 foreach( $dateShifts as $shift ){
901 $shiftCalendar = $shift->getCalendar();
902 // if( ! $shiftCalendar->isShift() ){
903 // continue;
904 // }
905 $this->shiftsDuration->add( $shift );
906 $counted++;
907 }
908 }
909
910 if( $counted ){
911 $outReport = $this->common->renderReport( $this->shiftsDuration, FALSE );
912 $outReport = $this->ui->makeSpan( $outReport )
913 ->tag('font-size', 2)
914 ;
915 $employeeView = $this->ui->makeList( array($employeeView, $outReport) )
916 ->gutter(1)
917 ;
918 }
919 }
920
921 $thisShifts = isset( $view_shifts[$id] ) ? $view_shifts[$id] : array();
922 $employeeView = $this->common->employeeRowLabel( $employeeView, $id, 'week', $dates, $thisShifts );
923
924 $employeeView = $this->ui->makeBlock( $employeeView )
925 ->tag('padding', 2)
926 ->tag('nowrap')
927 ;
928 $row[] = $employeeView;
929
930 foreach( $dates as $date ){
931 $cell = array();
932
933 if( isset($view_shifts[$id][$date]) ){
934 $shiftsView = $this->self->renderDay( $view_shifts[$id][$date], $iknow );
935 $cell[] = $shiftsView;
936 }
937
938 $toParams = array(
939 'employee' => $id,
940 'date' => $date
941 );
942
943 $links = array();
944
945 $comboId = 0 . '-' . $id;
946 if( isset($this->allCombosShift[$comboId]) ){
947 /*
948 $label = '+' . ' ' . '__Shift__';
949 $thisTo = 'new/shift';
950 $thisTo = array( $thisTo, $toParams );
951 $link = $this->ui->makeAhref( $thisTo, $label )
952 ->tag('tab-link')
953 ->tag('align', 'center')
954 ;
955 if( $today > $date ){
956 $link->tag('muted', 3);
957 }
958 */
959 if( $today > $date ){
960 $link = str_replace( array('{EID}', '{DATE}'), array($id, $date), $linkTemplateShiftMuted );
961 }
962 else {
963 $link = str_replace( array('{EID}', '{DATE}'), array($id, $date), $linkTemplateShift );
964 }
965
966 if( ! $isPrintView ){
967 $cell[] = $link;
968 }
969 }
970
971 if( isset($this->allCombosTimeoff[$comboId]) ){
972 /*
973 $label = '+' . ' ' . '__Time Off__';
974 $thisTo = 'new/timeoff';
975 $thisTo = array( $thisTo, $toParams );
976 $link = $this->ui->makeAhref( $thisTo, $label )
977 ->tag('tab-link')
978 ->tag('align', 'center')
979 ;
980 if( $today > $date ){
981 $link->tag('muted', 3);
982 }
983 */
984
985 if( $today > $date ){
986 $link = str_replace( array('{EID}', '{DATE}'), array($id, $date), $linkTemplateTimeoffMuted );
987 }
988 else {
989 $link = str_replace( array('{EID}', '{DATE}'), array($id, $date), $linkTemplateTimeoff );
990 }
991
992 if( ! $isPrintView ){
993 $cell[] = $link;
994 }
995 }
996
997 if( ! $isPrintView ){
998 if( $links ){
999 $links = $this->ui->makeList( $links )->gutter(0);
1000 $cell[] = $links;
1001 }
1002 }
1003
1004 $cell = $this->ui->makeList( $cell )
1005 ->gutter(1)
1006 ->tag('margin', 1)
1007 ;
1008
1009 $row[] = $cell;
1010 }
1011
1012 $rows[] = $row;
1013 }
1014
1015 /* append calendars summary */
1016 $appendCalendars = $this->settings->get('datetime_summary_by_calendar');
1017 if( $appendCalendars && $shifts ){
1018 $calendarIds = array();
1019 foreach( $shifts as $shift ){
1020 $calendar = $shift->getCalendar();
1021 $calendarId = $calendar ? $calendar->getId() : 0;
1022 if( ! $calendarId ) continue;
1023 $calendarIds[ $calendarId ] = $calendarId;
1024 }
1025
1026 $calendars = $this->calendarsQuery->findManyById( $calendarIds );
1027
1028 $shiftsByCalendar = $shiftsByDate = array();
1029 foreach( $shifts as $shift ){
1030 $thisCalendar = $shift->getCalendar();
1031 if( ! $thisCalendar ) continue;
1032 $shiftId = $shift->getId();
1033 $thisCalendarId = $thisCalendar->getId();
1034 $shiftsByCalendar[ $thisCalendarId ][ $shiftId ] = $shift;
1035
1036 $start = $shift->getStart();
1037 $end = $shift->getEnd();
1038
1039 $startDayTime = substr($start, 8, 4);
1040 $endDayTime = substr($end, 8, 4);
1041 $shiftAllDay = ( (0 == $startDayTime) && (0 == $endDayTime) ) ? TRUE : FALSE;
1042
1043 $this->t->setDateTimeDb( $end )->modify('-1 second');
1044 $shiftEndDate = $this->t->formatDateDb();
1045
1046 $this->t->setDateTimeDb( $start );
1047 $shiftStartDate = $this->t->formatDateDb();
1048
1049 $rexDate = $shiftStartDate;
1050 while( $rexDate <= $shiftEndDate ){
1051 if( in_array($rexDate, $dates) ){
1052 $shiftsByDate[ $rexDate ][ $shiftId ] = $shift;
1053 }
1054
1055 if( ! $shiftAllDay ){
1056 break;
1057 }
1058 $this->t->modify( '+1 day' );
1059 $rexDate = $this->t->formatDateDb();
1060 }
1061 }
1062
1063 foreach( $calendars as $calendarId => $calendar ){
1064 $row = array();
1065
1066 $calendarView = $this->calendarsPresenter->presentTitle( $calendar );
1067 $title = htmlspecialchars( $calendar->getTitle() );
1068 $calendarView = $this->ui->makeSpan( $calendarView )
1069 ->addAttr( 'title', $title )
1070 ;
1071
1072 $this->shiftsDuration->reset();
1073 foreach( $shiftsByCalendar[$calendarId] as $shift ){
1074 $this->shiftsDuration->add( $shift );
1075 }
1076
1077 $outReport = $this->common->renderReport( $this->shiftsDuration, FALSE );
1078 $outReport = $this->ui->makeSpan( $outReport )
1079 ->tag('font-size', 2)
1080 ;
1081 $calendarView = $this->ui->makeList( array($calendarView, $outReport) )
1082 ->gutter(1)
1083 ;
1084
1085 $calendarView = $this->ui->makeBlock( $calendarView )
1086 ->tag('padding', 2)
1087 ->tag('nowrap')
1088 ;
1089 $row[] = $calendarView;
1090
1091 foreach( $dates as $date ){
1092 $cell = '';
1093 if( isset($shiftsByDate[$date]) ){
1094 $thisShifts = array_intersect_key( $shiftsByDate[$date], $shiftsByCalendar[$calendarId] );
1095 if( $thisShifts ){
1096 $this->shiftsDuration->reset();
1097 foreach( $thisShifts as $shift ){
1098 $this->shiftsDuration->add( $shift );
1099 }
1100 $outReport = $this->common->renderReport( $this->shiftsDuration, false, true );
1101 $outReport = $this->ui->makeSpan( $outReport )
1102 ->tag('font-size', 2)
1103 ;
1104 $outReport = $this->ui->makeBlock( $outReport )
1105 ->tag('align', 'center')
1106 ;
1107 $cell = $outReport;
1108 }
1109 }
1110 $row[] = $cell;
1111 }
1112 $rows[] = $row;
1113 }
1114 }
1115
1116 $out = $this->ui->makeTable( $header, $rows, FALSE )
1117 ->gutter(0)
1118 ->setBordered( $this->borderColor )
1119 // ->setSegments( $weekSegments )
1120 ;
1121
1122 return $out;
1123 }
1124
1125 public function renderDay( $shifts, $iknow )
1126 {
1127 $return = NULL;
1128 if( ! $shifts ){
1129 return $return;
1130 }
1131
1132 $params = $this->request->getParams();
1133
1134 $hideui = $params['hideui'];
1135 $noZoom = in_array('shiftdetails', $hideui);
1136
1137 if( array_key_exists('employee', $params) && (count($params['employee']) == 1) ){
1138 if( ! in_array(-1, $params['employee']) ){
1139 $iknow[] = 'employee';
1140 }
1141 }
1142 if( array_key_exists('calendar', $params) && (count($params['calendar']) == 1) ){
1143 $iknow[] = 'calendar';
1144 }
1145
1146 // groups?
1147 $groups = array();
1148 $groupedShifts = array();
1149 reset( $shifts );
1150 foreach( $shifts as $shift ){
1151 if( ! $shift->isOpen() ){
1152 continue;
1153 }
1154
1155 $groupId = $shift->getGroupingId();
1156
1157 if( ! isset($groups[$groupId]) ){
1158 $groups[ $groupId ] = $shift->getId();
1159 $groupedShifts[ $shift->getId() ] = array( $shift->getId() => $shift );
1160 }
1161 else {
1162 $mainShiftId = $groups[ $groupId ];
1163 $groupedShifts[ $mainShiftId ][ $shift->getId() ] = $shift;
1164 $groupedShifts[ $shift->getId() ] = 0;
1165 }
1166 }
1167
1168 $hori = FALSE;
1169 $return = array();
1170 reset( $shifts );
1171 foreach( $shifts as $shift ){
1172 $id = $shift->getId();
1173
1174 if( isset($groupedShifts[$id]) && (! $groupedShifts[$id]) ){
1175 continue;
1176 }
1177
1178 $groupedQty = NULL;
1179 if( isset($groupedShifts[$id]) && (count($groupedShifts[$id]) > 1) ){
1180 $groupedQty = count($groupedShifts[$id]);
1181 }
1182
1183 $thisView = $this->widget->render( $shift, $iknow, $hori, $noZoom, $groupedQty );
1184 $shiftId = $shift->getId();
1185
1186 $buildMenuFromShifts = isset($groupedShifts[$id]) ? $groupedShifts[$id] : array( $shift );
1187 $menu = $this->shiftsCommon->bulkMenu( $buildMenuFromShifts );
1188 if( $menu ){
1189 $thisView = $this->ui->makeCollection( array($thisView, $menu) );
1190 }
1191
1192 $thisView = $this->ui->makeBlock( $thisView )
1193 ->tag('block')
1194 ->addAttr('class', 'sh4-shift-widget')
1195 ;
1196
1197 $return[] = $thisView;
1198 }
1199
1200 $return = $this->ui->makeList( $return )->gutter(1);
1201
1202 return $return;
1203 }
1204 }