room_id)) || !is_visible($this->room_id)) { // No rooms have been created yet, or else they are all disabled // Add an 'empty' data flag so that the JavaScript knows whether this is a real table or not return "

".get_vocab("no_rooms_for_area")."

"; } $html = ''; // Month view start time. This ignores morningstarts/eveningends because it // doesn't make sense to not show all entries for the day, and it messes // things up when entries cross midnight. $month_start = mktime(0, 0, 0, $this->month, 1, $this->year); // What column the month starts in: 0 means $weekstarts weekday. $weekday_start = (date("w", $month_start) - $weekstarts + DAYS_PER_WEEK) % DAYS_PER_WEEK; $last_day_of_month = (int) date("t", $month_start); $html .= $this->theadHTML(); // Main body $html .= "\n"; $html .= "\n"; // Skip days in week before the start of the month: for ($weekcol = 0; $weekcol < $weekday_start; $weekcol++) { $html .= $this->tdBlankDayHTML($weekcol); } $start_date = (new DateTime())->setTimestamp(get_start_first_slot($this->month, 1, $this->year)); $end_date = (new DateTime())->setTimestamp(get_end_last_slot($this->month, $last_day_of_month, $this->year)); // Get the data. It's much quicker to do a single SQL query getting all the // entries for the interval in one go, rather than doing a query for each day. $entries = get_entries_by_room($this->room_id, $start_date, $end_date); // Draw the days of the month: for ($d = 1, $date = clone $start_date; $d <= $last_day_of_month; $d++, $date->modify('+1 day')) { // Get the slot times $start_first_slot = get_start_first_slot($this->month, $d, $this->year); $end_last_slot = get_end_last_slot($this->month, $d, $this->year); // if we're at the start of the week (and it's not the first week), start a new row if (($weekcol == 0) && ($d > 1)) { $html .= "\n"; } // output the day cell if ($date->isHiddenDay()) { // These days are to be hidden in the display (as they are hidden, just give the // day of the week in the header row) $html .= "\n"; $html .= "
\n"; $html .= "
\n"; // first put in the day of the month $html .= "$d\n"; $html .= "
\n"; $html .= "
\n"; $html .= "\n"; } else { // Add classes for weekends and holidays $classes = $this->getDateClasses($date); $html .= '\n"; $html .= "
\n"; $html .= "
\n"; $vars = [ 'page_date' => $date->getISODate(), 'area' => $this->area_id, 'room' => $this->room_id ]; // If it's the first day of the week, show the week number if ($view_week_number && $date->isFirstDayOfWeek(Language::getInstance()->getWebLocale())) { $vars['view'] = 'week'; $query = http_build_query($vars, '', '&'); $html .= ''; $html .= $date->format('W'); $html .= "\n"; } // then put in the day of the month $vars['view'] = 'day'; $query = http_build_query($vars, '', '&'); $html .= '$d\n"; $html .= "
\n"; // Then the link to make a new booking. // Don't provide a link if the slot doesn't really exist or if the user is logged in, but not a booking admin, // and it's a holiday/weekend and bookings on holidays/weekends are not allowed. (We provide a link if they // are not logged in because they might want to click and login as an admin). if ((null !== session()->getCurrentUser()) && !is_book_admin($this->room_id) && (($prevent_booking_on_holidays && in_array('holiday', $classes)) || ($prevent_booking_on_weekends && in_array('weekend', $classes)))) { $html .= ''; } else { $vars['view'] = $this->view; if ($enable_periods) { $vars['period'] = 0; } else { $vars['hour'] = $morningstarts; $vars['minute'] = $morningstarts_minutes; } $query = http_build_query($vars, '', '&'); $html .= '\n"; if ($show_plus_link) { $html .= "\"New\"\n"; } $html .= "\n"; } // then any bookings for the day $html .= "
\n"; // Show the start/stop times, 1 or 2 per line, linked to view_entry. foreach ($entries as $entry) { // We are only interested in this day's entries if (($entry['start_time'] >= $end_last_slot) || ($entry['end_time'] <= $start_first_slot)) { continue; } $entry = Map::prepareEntry($entry); $classes = $this->getEntryClasses($entry); $classes[] = $monthly_view_entries_details; $html .= '
'; $vars = [ 'id' => $entry['id'], 'year' => $this->year, 'month' => $this->month, 'day' => $d ]; $query = http_build_query($vars, '', '&'); $booking_link = multisite("view_entry.php?$query"); $slot_text = $this->bookingSummaryHTML( $entry['start_time'], $entry['end_time'], $start_first_slot, $end_last_slot ); $description_text = mb_substr($entry['name'], 0, 255); $full_text = $slot_text . " " . $description_text; switch ($monthly_view_entries_details) { case "description": { $display_text = $description_text; break; } case "slot": { $display_text = $slot_text; break; } case "both": { $display_text = $full_text; break; } default: { $html .= "error: unknown parameter"; } } $title_text = $full_text; if (isset($entry['description']) && ($entry['description'] !== '')) { $title_text .= "\n\n" . $entry['description']; } $html .= ''; $html .= escape_html($display_text) . ''; $html .= "
\n"; } $html .= "
\n"; $html .= "
\n"; $html .= "\n"; } // increment the day of the week counter if (++$weekcol == DAYS_PER_WEEK) { $weekcol = 0; } } // end of for loop going through valid days of the month // Skip from end of month to end of week: if ($weekcol > 0) { for (; $weekcol < DAYS_PER_WEEK; $weekcol++) { $html .= $this->tdBlankDayHTML($weekcol); } } $html .= "\n"; $html .= "\n"; return $html; } // Describe the start and end time, accounting for "all day" // and for entries starting before/ending after today. // There are 9 cases, for start time < = or > midnight this morning, // and end time < = or > midnight tonight. private function bookingSummaryHTML(int $start, int $end, int $day_start, int $day_end) : string { global $enable_periods, $area; // Use ~ (not -) to separate the start and stop times, because MSIE // will incorrectly line break after a -. $separator = '~'; $after_today = "==>"; $before_today = "<=="; $midnight = "24:00"; // need to fix this so it works with AM/PM configurations (and for that matter 24h) $all_day = get_vocab('all_day'); if ($enable_periods) { $start_str = escape_html(period_time_string($start, $area)); $end_str = escape_html(period_time_string($end, $area, true)); } else { $start_str = escape_html(datetime_format(hour_min_format(), $start)); $end_str = escape_html(datetime_format(hour_min_format(), $end)); } switch (self::cmp3($start, $day_start) . self::cmp3($end, $day_end + 1)) { case "> < ": // Starts after midnight, ends before midnight case "= < ": // Starts at midnight, ends before midnight $result = $start_str; // Don't bother showing the end if it's the same as the start period if ($end_str !== $start_str) { $result .= $separator . $end_str; } break; case "> = ": // Starts after midnight, ends at midnight $result = $start_str . $separator . $midnight; break; case "> > ": // Starts after midnight, continues tomorrow $result = $start_str . $separator . $after_today; break; case "= = ": // Starts at midnight, ends at midnight $result = $all_day; break; case "= > ": // Starts at midnight, continues tomorrow $result = $all_day . $after_today; break; case "< < ": // Starts before today, ends before midnight $result = $before_today . $separator . $end_str; break; case "< = ": // Starts before today, ends at midnight $result = $before_today . $all_day; break; case "< > ": // Starts before today, continues tomorrow $result = $before_today . $all_day . $after_today; break; } return $result; } // 3-value compare: Returns result of compare as "< " "= " or "> ". private static function cmp3(int $a, int $b) : string { if ($a < $b) { return "< "; } if ($a == $b) { return "= "; } return "> "; } private function tdBlankDayHTML(int $col) : string { global $weekstarts; $td_class = (is_hidden_day(($col + $weekstarts) % DAYS_PER_WEEK)) ? 'hidden_day' : 'invalid'; return "
 
\n"; } // Gets the table head for the single room month view private function theadHTML() : string { global $weekstarts; $html = ''; // Weekday name header row: $html .= "\n"; $html .= "\n"; for ($i = 0; $i< DAYS_PER_WEEK; $i++) { $classes = []; $dow = ($i + $weekstarts) % DAYS_PER_WEEK; if (is_hidden_day($dow)) { $classes[] = 'hidden_day'; } if (is_weekend($dow)) { $classes[] = 'weekend'; } $html .= ''; } $html .= "\n\n"; $html .= "\n"; return $html; } }