# shiftcontroller/4.9.95/sh4/schedule/html/view/month.php

ShiftController Employee Shift Scheduling, version 4.9.95. 1,013 lines.

- Page: https://pluginprobe.com/plugins/shiftcontroller/4.9.95/code/sh4/schedule/html/view/month.php
- Raw: https://pluginprobe.com/plugins/shiftcontroller/4.9.95/raw/sh4/schedule/html/view/month.php
- Modified: 2025-05-02T15:32:08+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/shiftcontroller/4.9.95/code/sh4/schedule/html/view/month.php#L10-L20`.

```php
<?php if (! defined('ABSPATH')) exit; // Exit if accessed directly
#[AllowDynamicProperties]
class SH4_Schedule_Html_View_Month
{
	protected $borderColor = 'gray';
	protected $allCombos = array();
	protected $allCombosShift = array();
	protected $allCombosTimeoff = array();
	protected $slimView = true;

	public function __construct(
		HC3_Hooks $hooks,
		HC3_Ui $ui,
		HC3_Request $request,
		HC3_Time $t,
		HC3_Settings $settings,

		HC3_Uri $uri,

		SH4_New_Acl $newAcl,

		SH4_Shifts_Duration $shiftsDuration,
		SH4_Schedule_Html_View_Week $viewWeek,
		SH4_Shifts_View_Widget $widget,
		SH4_Calendars_Query $calendarsQuery,
		SH4_Calendars_Presenter $calendarsPresenter,
		SH4_Employees_Presenter $employeesPresenter,
		SH4_Schedule_Html_View_Common $common,
		SH4_Shifts_View_Common $shiftsCommon
		)
	{
		$this->self = $hooks->wrap($this);

		$this->ui = $ui;

		$this->uri = $uri;
		$this->t = $t;
		$this->request = $request;

		$this->settings = $hooks->wrap( $settings );
		$this->newAcl = $hooks->wrap( $newAcl );

		$this->viewWeek = $hooks->wrap( $viewWeek );
		$this->shiftsDuration = $hooks->wrap( $shiftsDuration );
		$this->calendarsQuery = $hooks->wrap( $calendarsQuery );
		$this->calendarsPresenter = $hooks->wrap( $calendarsPresenter );
		$this->employeesPresenter = $hooks->wrap( $employeesPresenter );

		$this->widget = $hooks->wrap( $widget );
		$this->common = $hooks->wrap( $common );
		$this->shiftsCommon = $hooks->wrap( $shiftsCommon );

		$this->allCombos = $this->newAcl->findAllCombos();
		$this->allCombosShift = $this->newAcl->findAllCombosShift();
		$this->allCombosTimeoff = $this->newAcl->findAllCombosTimeoff();

		$this->slimView = $this->settings->get( 'datetime_month_slim_view' ) ? true : false;
	}

	public function render()
	{
		$slug = $this->request->getSlug();
		$today = $this->t->setNow()->formatDateDb();

		$disabledWeekdays = $this->settings->get( 'skip_weekdays', TRUE );

		$shifts = $this->common->getShifts();

		$params = $this->request->getParams();
		$startDate = $params['start'];

		$startDate = $this->t->setDateDb( $startDate )->setStartMonth()->formatDateDb();
		$endDate = $this->t->modify('+1 month')->modify('-1 day')->formatDateDb();

		$this->t->setDateDb( $startDate );
		$dates = array();
		$rexDate = $startDate;
		while( $rexDate <= $endDate ){
			if( $disabledWeekdays ){
				$wkd = $this->t->getWeekday();
				if( in_array($wkd, $disabledWeekdays) ){
					$rexDate = $this->t->modify('+1 day')->formatDateDb();
					continue;
				}
			}

			$dates[] = $rexDate;
			$rexDate = $this->t->modify('+1 day')->formatDateDb();
		}

		$viewShifts = array();
		$iknow = array('date');

		$allCalendars = $this->common->findAllCalendars();
		if( count($allCalendars) <= 1 ) $iknow[] = 'calendar';

		$this->shiftsDuration->reset();
		foreach( $shifts as $shift ){
			$this->shiftsDuration->add( $shift );
			$start = $shift->getStart();
			$end = $shift->getEnd();

			$startDayTime = substr($start, 8, 4);
			$endDayTime = substr($end, 8, 4);
			$shiftAllDay = ( (0 == $startDayTime) && (0 == $endDayTime) ) ? TRUE : FALSE;

			$this->t->setDateTimeDb( $end )->modify('-1 second');
			$shiftEndDate = $this->t->formatDateDb();

			$this->t->setDateTimeDb( $start );
			$shiftStartDate = $this->t->formatDateDb();

			$rexDate = $shiftStartDate;
			while( $rexDate <= $shiftEndDate ){
				if( in_array($rexDate, $dates) ){
					if( ! array_key_exists($rexDate, $viewShifts) ){
						$viewShifts[$rexDate] = array();
					}
					$viewShifts[$rexDate][] = $shift;
				}

				if( ! $shiftAllDay ){
					break;
				}
				$this->t->modify( '+1 day' );
				$rexDate = $this->t->formatDateDb();
			}
		}

        $daysInWeek = $this->settings->get('datetime_days_in_week');
        if (!$daysInWeek) $daysInWeek = 7;
        $this->t->setDateDb( $startDate );
        $monthMatrix = $this->t->getMonthMatrix($disabledWeekdays, $daysInWeek);

		$rows = array();
		foreach( $monthMatrix as $week => $days ){
			$row = array();

			foreach( $days as $date ){
				if( ! $date ){
					$row[] = NULL;
					continue;
				}

				$cell = array();

				$dateView = $this->common->renderDateLabel( $date );
				$cell[] = $dateView;

				if( isset($viewShifts[$date]) ){
					$shiftsView = $this->viewWeek->renderDay( $viewShifts[$date], $iknow );
					$cell[] = $shiftsView;
				}

				$to = 'new';
				$toParams = array(
					'date'	=> $date
					);
				if( array_key_exists('employee', $params) && (count($params['employee']) == 1) ){
					if( ! in_array(-1, $params['employee']) ){
						$toParams['employee'] = $params['employee'][0];
					}
				}

				$links = array();

				if( $this->allCombosShift ){
					$label = '+' . ' ' . '__Shift__';
					$thisTo = 'new/shift';
					$thisTo = array( $thisTo, $toParams );
					$link = $this->ui->makeAhref( $thisTo, $label )
						->tag('tab-link')
						->tag('align', 'center')
						// ->addAttr('title', '__Add New__')
						;
					if( $today > $date ){
						$link->tag('muted', 3);
					}
					$links[] = $link;
				}

				if( $this->allCombosTimeoff ){
					$label = '+' . ' ' . '__Time Off__';
					$thisTo = 'new/timeoff';
					$thisTo = array( $thisTo, $toParams );
					$link = $this->ui->makeAhref( $thisTo, $label )
						->tag('tab-link')
						->tag('align', 'center')
						// ->addAttr('title', '__Add New__')
						;
					if( $today > $date ){
						$link->tag('muted', 3);
					}
					$links[] = $link;
				}

				$links = $this->ui->makeList( $links )->gutter(0);

				$cell[] = $links;

				$cell = $this->ui->makeList( $cell )
					->gutter(1)
					->tag('margin', 1)
					;

				$row[] = $cell;
			}

			$rows[] = $row;
		}

		$out = $this->ui->makeTable( NULL, $rows, FALSE )
			->gutter(0)
			->setBordered( $this->borderColor )
			// ->setSegments( $weekSegments )
			;

		return $out;
	}

	public function renderByCalendar()
	{
		$slug = $this->request->getSlug();
		$today = $this->t->setNow()->formatDateDb();

		$disabledWeekdays = $this->settings->get( 'skip_weekdays', TRUE );

		$calendars = $this->common->getCalendars();
		$employees = $this->common->getEmployees();
		$shifts = $this->common->getShifts();

	// finalize calendars to include those that employee was removed from but still have shifts in
		$listCalendarId = array_keys( $calendars );
		$dictCalendarId = array_combine( $listCalendarId, $listCalendarId );
		foreach( $shifts as $shift ){
			$calendar = $shift->getCalendar();
			if( ! $calendar ) continue;
			$calendarId = $calendar->getId();
			$dictCalendarId[ $calendarId ] = $calendarId;
		}
		if( count($dictCalendarId) > count($calendars) ){
			$allCalendars = $this->common->findAllCalendars();
			$calendars = array_intersect_key( $allCalendars, $dictCalendarId );
		}

	// we can probably have archived calendars so if such calendars have no shifts then skip them
		$removeArchivedCalendars = array();
		foreach( $calendars as $calendar ){
			if( $calendar->isArchived() ){
				$removeArchivedCalendars[ $calendar->getId() ] = $calendar->getId();
			}
		}
		if( $removeArchivedCalendars ){
			foreach( $shifts as $shift ){
				$calendar = $shift->getCalendar();
				$id = $calendar ? $calendar->getId() : 0;
				if( isset($removeArchivedCalendars[$id]) ){
					unset( $removeArchivedCalendars[$id] );
				}
				if( ! $removeArchivedCalendars ){
					break;
				}
			}
			foreach( $removeArchivedCalendars as $id ){
				unset( $calendars[$id] );
			}
			reset( $shifts );
		}

		$params = $this->request->getParams();
		$startDate = $params['start'];

		$startDate = $this->t->setDateDb( $startDate )->setStartMonth()->formatDateDb();
		$endDate = $this->t->modify('+1 month')->modify('-1 day')->formatDateDb();

		$this->t->setDateDb( $startDate );
		$dates = array();
		$rexDate = $startDate;
		while( $rexDate <= $endDate ){
			if( $disabledWeekdays ){
				$wkd = $this->t->getWeekday();
				if( in_array($wkd, $disabledWeekdays) ){
					$rexDate = $this->t->modify('+1 day')->formatDateDb();
					continue;
				}
			}

			$dates[] = $rexDate;
			$rexDate = $this->t->modify('+1 day')->formatDateDb();
		}

		$viewCalendars = array();
		foreach( $calendars as $calendar ){
			$label = $this->calendarsPresenter->presentTitle( $calendar );
			$title = htmlspecialchars( $calendar->getTitle() );
			$description = $calendar->getDescription();
			if( strlen($description) ){
				$description = htmlspecialchars( $description );
				$title .= "\n" . $description;
			}

			$label = $this->ui->makeSpan( $label )
				->addAttr( 'title', $title )
				;

			$viewCalendars[ $calendar->getId() ] = $label;
		}

		$viewShifts = array();

		$iknow = array('calendar', 'date');
		foreach( $shifts as $shift ){
			$calendar = $shift->getCalendar();
			$id = $calendar ? $calendar->getId() : 0;

			if( ! array_key_exists($id, $viewShifts) ){
				$viewShifts[$id] = array();
			}

			$start = $shift->getStart();
			$end = $shift->getEnd();

			$startDayTime = substr($start, 8, 4);
			$endDayTime = substr($end, 8, 4);
			$shiftAllDay = ( (0 == $startDayTime) && (0 == $endDayTime) ) ? TRUE : FALSE;

			$this->t->setDateTimeDb( $end )->modify('-1 second');
			$shiftEndDate = $this->t->formatDateDb();

			$this->t->setDateTimeDb( $start );
			$shiftStartDate = $this->t->formatDateDb();

			$rexDate = $shiftStartDate;
			while( $rexDate <= $shiftEndDate ){
				if( in_array($rexDate, $dates) ){
					if( ! array_key_exists($rexDate, $viewShifts[$id]) ){
						$viewShifts[$id][$rexDate] = array();
					}
					$viewShifts[$id][$rexDate][] = $shift;
				}

				if( ! $shiftAllDay ){
					break;
				}
				$this->t->modify( '+1 day' );
				$rexDate = $this->t->formatDateDb();
			}
		}

		$header = array();
		$header[] = NULL;

		$weekStartsOn = $this->t->getWeekStartsOn();
		$weekSegments = array();

		$this->t->setDateDb( $startDate );
		$monthMatrix = $this->t->getMonthMatrix( $disabledWeekdays );

		foreach( $monthMatrix as $week => $days ){
			$wdi = 0;
			foreach( $days as $date ){
				if( ! $date ){
					continue;
				}

				$dateView = $this->common->renderDateLabelGrouped( $date );
				$header[] = $dateView;

				if( ! $wdi ){
					$weekSegments[] = count($header);
				}

				$wdi++;
			}
		}

		// while( count($header) < 32 ){
			// $header[] = NULL;
		// }

		$rows = array();

// link templates to speed things up
$toParams = array(
	'calendar' => '{CID}',
	'date' => '{DATE}'
);
$thisTo = 'new/calendar';
$thisTo = array( $thisTo, $toParams );
$thisHref = $this->uri->makeUrl( $thisTo );

$linkTemplateShift = '<a title="__Add New__" class="hc-block hc-theme-tab-link hc-align-center" href="' . $thisHref . '">+</a>';
$linkTemplateShiftMuted = '<a title="__Add New__" class="hc-block hc-theme-tab-link hc-align-center hc-muted3" href="' . $thisHref . '">+</a>';
$isPrintView = $this->request->isPrintView();

		foreach( $viewCalendars as $id => $calendarView ){
			$row = array();

			$this->shiftsDuration->reset();
			if( isset($viewShifts[$id]) ){
				foreach( $viewShifts[$id] as $date => $dateShifts ){
					foreach( $dateShifts as $shift ){
						$this->shiftsDuration->add( $shift );
					}
				}

				$outReport = $this->common->renderReport( $this->shiftsDuration, FALSE );
				$outReport = $this->ui->makeSpan( $outReport )
					->tag('font-size', 2)
					;
				$calendarView = $this->ui->makeList( array($calendarView, $outReport) )
					->gutter(1)
					;
			}

			$calendarView = $this->ui->makeBlock( $calendarView )
				->tag('padding', array('x1', 'y2'))
				->tag('nowrap')
				;
			$row[] = $calendarView;

			$countDates = count( $dates );
			foreach( $dates as $date ){
				$cell = array();

				if( isset($viewShifts[$id][$date]) ){
					if( $countDates <= 10 ){
						$shiftsView = $this->viewWeek->renderDay( $viewShifts[$id][$date], $iknow );
					}
					else {
						$shiftsView = $this->self->renderDay( $viewShifts[$id][$date], $iknow );
					}
					$cell[] = $shiftsView;
				}

				$label = '+';

				$to = 'new';
				$toParams = array(
					'date'	=> $date,
					'calendar'	=> $id,
					);
				if( array_key_exists('employee', $params) && (count($params['employee']) == 1) ){
					if( ! in_array(-1, $params['employee']) ){
						$toParams['employee'] = $params['employee'][0];
					}
				}
				$to = array( $to, $toParams );

				$addOk = FALSE;
				if( isset($toParams['employee']) ){
					$testComboId = $id . '-' . $toParams['employee'];
					if( isset($this->allCombos[$testComboId]) ){
						$addOk = TRUE;
					}
				}
				else {
					$testComboId = $id . '-';
					reset( $this->allCombos );
					foreach( $this->allCombos as $comboId ){
						if( $testComboId == substr($comboId, 0, strlen($testComboId)) ){
							$addOk = TRUE;
							break;
						}
					}
				}

				if( $isPrintView ) $addOk = false;

				if( $addOk ){
/*
					$link = $this->ui->makeAhref( $to, $label )
						->tag('tab-link')
						->tag('align', 'center')
						->addAttr('title', '__Add New__')
						;

					if( $today > $date ){
						$link->tag('muted', 3);
					}
*/

					if( $today > $date ){
						$link = str_replace( array('{CID}', '{DATE}'), array($id, $date), $linkTemplateShiftMuted );
					}
					else {
						$link = str_replace( array('{CID}', '{DATE}'), array($id, $date), $linkTemplateShift );
					}

					$cell[] = $link;
				}

				$cell = $this->ui->makeList( $cell )
					->gutter(1)
					->tag('margin', '05')
					;

				$row[] = $cell;
			}

			// while( count($row) < 32 ){
				// $row[] = NULL;
			// }
			$rows[] = $row;
		}

		$out = $this->ui->makeTable( $header, $rows, FALSE )
			->gutter(0)
			->setBordered( $this->borderColor )
			->setSegments( $weekSegments )
			->setLabelled( TRUE )
			;

		if( ! $this->slimView ){
			$out->forceColWidth( '6rem' );
			$out->forceRowHeaderWidth( '10rem' );
		}

		return $out;
	}

	public function renderByEmployee()
	{
		$slug = $this->request->getSlug();
		$today = $this->t->setNow()->formatDateDb();

		$disabledWeekdays = $this->settings->get( 'skip_weekdays', TRUE );

		$calendars = $this->common->getCalendars();
		$employees = $this->common->getEmployees();
		$shifts = $this->common->getShifts();

	// finalize employees to include those that employee was removed from but still have shifts in
		$listEmployeeId = array_keys( $employees );
		$dictEmployeeId = array_combine( $listEmployeeId, $listEmployeeId );
		foreach( $shifts as $shift ){
			$employee = $shift->getEmployee();
			if( ! $employee ) continue;
			$employeeId = $employee->getId();
			$dictEmployeeId[ $employeeId ] = $employeeId;
		}
		if( count($dictEmployeeId) > count($employees) ){
			$allEmployees = $this->common->findAllEmployees();
			$employees = array_intersect_key( $allEmployees, $dictEmployeeId );
		}

	// we can probably have archived employees so if such employees have no shifts then skip them
		$removeArchivedEmployees = array();
		foreach( $employees as $employee ){
			if( $employee->isArchived() ){
				$removeArchivedEmployees[ $employee->getId() ] = $employee->getId();
			}
		}
		if( $removeArchivedEmployees ){
			foreach( $shifts as $shift ){
				$employee = $shift->getEmployee();
				$id = $employee ? $employee->getId() : 0;
				if( isset($removeArchivedEmployees[$id]) ){
					unset( $removeArchivedEmployees[$id] );
				}
				if( ! $removeArchivedEmployees ){
					break;
				}
			}
			foreach( $removeArchivedEmployees as $id ){
				unset( $employees[$id] );
			}
			reset( $shifts );
		}

		$params = $this->request->getParams();
		$startDate = $params['start'];

		$startDate = $this->t->setDateDb( $startDate )->setStartMonth()->formatDateDb();
		$endDate = $this->t->modify('+1 month')->modify('-1 day')->formatDateDb();

		$this->t->setDateDb( $startDate );
		$dates = array();
		$rexDate = $startDate;
		while( $rexDate <= $endDate ){
			if( $disabledWeekdays ){
				$wkd = $this->t->getWeekday();
				if( in_array($wkd, $disabledWeekdays) ){
					$rexDate = $this->t->modify('+1 day')->formatDateDb();
					continue;
				}
			}

			$dates[] = $rexDate;
			$rexDate = $this->t->modify('+1 day')->formatDateDb();
		}

		$countDates = count( $dates );

		$viewEmployees = array();
		foreach( $employees as $employee ){
			$label = $this->employeesPresenter->presentTitle( $employee );
			$title = htmlspecialchars( $label );

			$description = $employee->getDescription();
			if( strlen($description) ){
				$imgpos = strpos( $description, '<img' );
				if( false !== $imgpos ){
					$label = $this->ui->makeList( array($label, $description) );
				}
				else {
					$title .= "\n" . htmlspecialchars( $description );
				}
			}

			$label = $this->ui->makeSpan( $label )
				->tag( 'font-size', 4 )
				->addAttr( 'title', $title )
				;

			$viewEmployees[ $employee->getId() ] = $label;
		}

		$viewShifts = array();

		$iknow = array('employee', 'date');

		$allCalendars = $this->common->findAllCalendars();
		if( count($allCalendars) <= 1 ) $iknow[] = 'calendar';

		foreach( $shifts as $shift ){
			$employee = $shift->getEmployee();
			$id = $employee ? $employee->getId() : 0;

			if( ! array_key_exists($id, $viewShifts) ){
				$viewShifts[$id] = array();
			}

			$start = $shift->getStart();
			$end = $shift->getEnd();

			$startDayTime = substr($start, 8, 4);
			$endDayTime = substr($end, 8, 4);
			$shiftAllDay = ( (0 == $startDayTime) && (0 == $endDayTime) ) ? TRUE : FALSE;

			$this->t->setDateTimeDb( $end )->modify('-1 second');
			$shiftEndDate = $this->t->formatDateDb();

			$this->t->setDateTimeDb( $start );
			$shiftStartDate = $this->t->formatDateDb();

			$rexDate = $shiftStartDate;
			while( $rexDate <= $shiftEndDate ){
				if( in_array($rexDate, $dates) ){
					if( ! array_key_exists($rexDate, $viewShifts[$id]) ){
						$viewShifts[$id][$rexDate] = array();
					}
					$viewShifts[$id][$rexDate][] = $shift;
				}

				if( ! $shiftAllDay ){
					break;
				}
				$this->t->modify( '+1 day' );
				$rexDate = $this->t->formatDateDb();
			}
		}

		$header = array();
		$header[] = null;

		$weekStartsOn = $this->t->getWeekStartsOn();
		$weekSegments = array();

		$this->t->setDateDb( $startDate );
		$monthMatrix = $this->t->getMonthMatrix( $disabledWeekdays );

		foreach( $monthMatrix as $week => $days ){
			$wdi = 0;
			foreach( $days as $date ){
				if( ! $date ){
					continue;
				}

				$dateView = $this->common->renderDateLabelGrouped( $date );
				$header[] = $dateView;

				if( ! $wdi ){
					$weekSegments[] = count($header);
				}

				$wdi++;
			}
		}

		// while( count($header) < 32 ){
			// $header[] = NULL;
		// }

		$rows = array();

// link templates to speed things up
$toParams = array(
	'employee' => '{EID}',
	'date' => '{DATE}'
);
$thisTo = 'new';
$thisTo = array( $thisTo, $toParams );
$thisHref = $this->uri->makeUrl( $thisTo );

$linkTemplateShift = '<a title="__Add New__" class="hc-block hc-theme-tab-link hc-align-center" href="' . $thisHref . '">+</a>';
$linkTemplateShiftMuted = '<a title="__Add New__" class="hc-block hc-theme-tab-link hc-align-center hc-muted3" href="' . $thisHref . '">+</a>';
$isPrintView = $this->request->isPrintView();

		foreach( $viewEmployees as $id => $employeeView ){
			$row = array();

			$this->shiftsDuration->reset();
			if( isset($viewShifts[$id]) ){
				$counted = 0;
				foreach( $viewShifts[$id] as $date => $dateShifts ){
					foreach( $dateShifts as $shift ){
						$shiftCalendar = $shift->getCalendar();
						// if( $shiftCalendar->isTimeoff() ){
							// continue;
						// }
						$this->shiftsDuration->add( $shift );
						$counted++;
					}
				}

				if( $counted ){
					$outReport = $this->common->renderReport( $this->shiftsDuration, FALSE );
					$outReport = $this->ui->makeSpan( $outReport )
						->tag('font-size', 2)
						;
					$employeeView = $this->ui->makeList( array($employeeView, $outReport) )
						->gutter(1)
						;
				}
			}

			$thisShifts = isset( $view_shifts[$id] ) ? $view_shifts[$id] : array();
			$employeeView = $this->common->employeeRowLabel( $employeeView, $id, 'month', $dates, $thisShifts );

			$employeeView = $this->ui->makeBlock( $employeeView )
				->tag('padding', array('x1', 'y2'))
				->tag('nowrap')
				;
			$row[] = $employeeView;

			foreach( $dates as $date ){
				$cell = array();

				if( isset($viewShifts[$id][$date]) ){
					if( $this->slimView ){
						$shiftsView = $this->self->renderDay( $viewShifts[$id][$date], $iknow );
					}
					else {
						$shiftsView = $this->viewWeek->renderDay( $viewShifts[$id][$date], $iknow );
					}
					$cell[] = $shiftsView;
				}

				$comboId = 0 . '-' . $id;
				if( isset($this->allCombos[$comboId]) ){
/*
					$label = '+';

					$to = 'new';
					$toParams = array(
						'date'		=> $date,
						'employee'	=> $id,
						);
					$to = array( $to, $toParams );

					$link = $this->ui->makeAhref( $to, $label )
						->tag('tab-link')
						->tag('align', 'center')
						->addAttr('title', '__Add New__')
						;

					if( $today > $date ){
						$link->tag('muted', 3);
					}
*/
					if( $today > $date ){
						$link = str_replace( array('{EID}', '{DATE}'), array($id, $date), $linkTemplateShiftMuted );
					}
					else {
						$link = str_replace( array('{EID}', '{DATE}'), array($id, $date), $linkTemplateShift );
					}

					if( ! $isPrintView ){
						$cell[] = $link;
					}
				}

				$cell = $this->ui->makeList( $cell )
					->gutter(1)
					->tag('margin', '05')
					;

				$row[] = $cell;
			}

			// while( count($row) < 32 ){
				// $row[] = NULL;
			// }
			$rows[] = $row;
		}

	/* append calendars summary */
		$appendCalendars = $this->settings->get('datetime_summary_by_calendar');
		if( $appendCalendars && $shifts ){
			$calendarIds = array();
			foreach( $shifts as $shift ){
				$calendar = $shift->getCalendar();
				$calendarId = $calendar ? $calendar->getId() : 0;
				if( ! $calendarId ) continue;
				$calendarIds[ $calendarId ] = $calendarId;
			}

			$calendars = $this->calendarsQuery->findManyById( $calendarIds );

			$shiftsByCalendar = $shiftsByDate = array();
			foreach( $shifts as $shift ){
				$thisCalendar = $shift->getCalendar();
				if( ! $thisCalendar ) continue;
				$shiftId = $shift->getId();
				$thisCalendarId = $thisCalendar->getId();
				$shiftsByCalendar[ $thisCalendarId ][ $shiftId ] = $shift;

				$start = $shift->getStart();
				$end = $shift->getEnd();

				$startDayTime = substr($start, 8, 4);
				$endDayTime = substr($end, 8, 4);
				$shiftAllDay = ( (0 == $startDayTime) && (0 == $endDayTime) ) ? TRUE : FALSE;

				$this->t->setDateTimeDb( $end )->modify('-1 second');
				$shiftEndDate = $this->t->formatDateDb();

				$this->t->setDateTimeDb( $start );
				$shiftStartDate = $this->t->formatDateDb();

				$rexDate = $shiftStartDate;
				while( $rexDate <= $shiftEndDate ){
					if( in_array($rexDate, $dates) ){
						$shiftsByDate[ $rexDate ][ $shiftId ] = $shift;
					}

					if( ! $shiftAllDay ){
						break;
					}
					$this->t->modify( '+1 day' );
					$rexDate = $this->t->formatDateDb();
				}
			}

			foreach( $calendars as $calendarId => $calendar ){
				$row = array();

				$calendarView = $this->calendarsPresenter->presentTitle( $calendar );
				$title = htmlspecialchars( $calendar->getTitle() );
				$calendarView = $this->ui->makeSpan( $calendarView )
					->addAttr( 'title', $title )
					;

				$this->shiftsDuration->reset();
				foreach( $shiftsByCalendar[$calendarId] as $shift ){
					$this->shiftsDuration->add( $shift );
				}

				$outReport = $this->common->renderReport( $this->shiftsDuration, false, true );
				$outReport = $this->ui->makeSpan( $outReport )
					->tag('font-size', 2)
					;
				$calendarView = $this->ui->makeList( array($calendarView, $outReport) )
					->gutter(1)
					;

				$calendarView = $this->ui->makeBlock( $calendarView )
					->tag('padding', 2)
					->tag('nowrap')
					;
				$row[] = $calendarView;

				foreach( $dates as $date ){
					$cell = '';
					if( isset($shiftsByDate[$date]) ){
						$thisShifts = array_intersect_key( $shiftsByDate[$date], $shiftsByCalendar[$calendarId] );
						if( $thisShifts ){
							$this->shiftsDuration->reset();
							foreach( $thisShifts as $shift ){
								$this->shiftsDuration->add( $shift );
							}
							$outReport = $this->common->renderReport( $this->shiftsDuration, FALSE );
							$outReport = $this->ui->makeSpan( $outReport )
								->tag('font-size', 2)
								;
							$outReport = $this->ui->makeBlock( $outReport )
								->tag('align', 'center')
								;
							$cell = $outReport;
						}
					}
					$row[] = $cell;
				}
				$rows[] = $row;
			}
		}

		$out = $this->ui->makeTable( $header, $rows, false )
			->gutter(0)
			->setBordered( $this->borderColor )
			->setSegments( $weekSegments )
			->setLabelled( true )
			;

		if( ! $this->slimView ){
			$out->forceColWidth( '6rem' );
			$out->forceRowHeaderWidth( '10rem' );
		}

		return $out;
	}

	public function renderDay( $shifts, $iknow )
	{
		$return = NULL;
		if( ! $shifts ){
			return $return;
		}

		$params = $this->request->getParams();

		$hideui = $params['hideui'];
		$noZoom = in_array('shiftdetails', $hideui);

		if( array_key_exists('employee', $params) && (count($params['employee']) == 1) ){
			if( ! in_array(-1, $params['employee']) ){
				$iknow[] = 'employee';
			}
		}
		if( array_key_exists('calendar', $params) && (count($params['calendar']) == 1) ){
			$iknow[] = 'calendar';
		}

	// groups?
		$groups = array();
		$groupedShifts = array();
		reset( $shifts );
		foreach( $shifts as $shift ){
			if( ! $shift->isOpen() ){
				continue;
			}

			$groupId = $shift->getGroupingId();

			if( ! isset($groups[$groupId]) ){
				$groups[ $groupId ] = $shift->getId();
				$groupedShifts[ $shift->getId() ] = array( $shift->getId() => $shift );
			}
			else {
				$mainShiftId = $groups[ $groupId ];
				$groupedShifts[ $mainShiftId ][ $shift->getId() ] = $shift;
				$groupedShifts[ $shift->getId() ] = 0;
			}
		}

		$return = array();
		reset( $shifts );
		foreach( $shifts as $shift ){
			$id = $shift->getId();

			if( isset($groupedShifts[$id]) && (! $groupedShifts[$id]) ){
				continue;
			}

			$groupedQty = NULL;
			if( isset($groupedShifts[$id]) && (count($groupedShifts[$id]) > 1) ){
				$groupedQty = count($groupedShifts[$id]);
			}

			$thisView = $this->widget->renderCompact( $shift, $iknow, $groupedQty );

			if( ! $noZoom ){
				$to = 'shifts/' . $id;
				$thisView = $this->ui->makeAhref( $to, $thisView )
					->tag('unstyled-link')
					->tag('block')
					->tag('print')
					->addAttr('class', 'sh4-widget-loader')
					;
			}

			$buildMenuFromShifts = isset($groupedShifts[$id]) ? $groupedShifts[$id] : array( $shift );
			$menu = $this->shiftsCommon->bulkMenu( $buildMenuFromShifts );
			if( $menu ){
				$thisView = $this->ui->makeCollection( array($thisView, $menu) );
			}

			$thisView = $this->ui->makeBlock( $thisView )
				->tag('block')
				->addAttr('class', 'sh4-shift-widget')
				;

			$return[] = $thisView;
		}

		$return = $this->ui->makeList( $return )
			->gutter(1)
			;

		return $return;
	}
}
```
