Files
人事系统开发 1ba6efd8ed MRBS 1.12.2 等保2.0二级整改完整提交
包含:登录失败锁定、90天密码有效期、30分钟会话超时、
强制改密、登录审计日志、屏幕水印、企业背景图、
备案信息固定底部、favicon、JS空集合保护、
会话过期体验优化(403 JSON)、display_errors 关闭、
固定 key 根治 Integrity check failed 等全部改动

注意:config.inc.php/.htaccess/.user.ini 含敏感信息,
通过 .gitignore 排除,勿推送到公开仓库。
2026-09-09 16:55:02 +08:00

1099 lines
32 KiB
PHP

<?php
declare(strict_types=1);
namespace MRBS;
use MRBS\Form\ElementFieldset;
use MRBS\Form\ElementInputSubmit;
use MRBS\Form\FieldDiv;
use MRBS\Form\FieldInputSubmit;
use MRBS\Form\FieldTextarea;
use MRBS\Form\Form;
use MRBS\ICalendar\Calendar;
require "defaultincludes.inc";
require_once "mrbs_sql.inc";
require_once "functions_view.inc";
function generate_registrant_table(array $row, ?string $previous_page=null) : void
{
global $auth;
echo "<div id=\"registrant_list\" class=\"datatable_container\">\n";
echo "<table id=\"registrants\" class=\"admin_table display\">\n";
echo "<thead>\n";
echo '<tr>';
echo '<th></th>';
echo '<th class="name">' . get_vocab('name') . '</th>';
echo '<th class="name">' . get_vocab('registered_by') . '</th>';
echo '<th>' . get_vocab('registered_on') . '</th>';
echo "</tr>\n";
echo "</thead>\n";
echo "<tbody>\n";
$mrbs_user = session()->getCurrentUser();
foreach ($row['registrants'] as $registrant)
{
echo '<tr>';
echo '<td>';
// A registration can be cancelled by the registrant or by
// the person who registered them or by the booking owner or
// if ordinary users are allowed to delete other users' registrations.
if (getWritable($registrant['username'], $row['room_id']) ||
getWritable($registrant['create_by'], $row['room_id']) ||
getWritable($row['create_by'], $row['room_id']) ||
(isset($mrbs_user) && $auth['users_can_delete_others_registrations']))
{
generate_cancel_registration_button(
$row,
$registrant,
get_vocab('delete'),
$previous_page
);
}
echo '</td>';
// Registrant and Registered by
foreach(['username', 'create_by'] as $key)
{
// Default values
$display_name = '';
$email = null;
// Only show a name if it's the registrant or the creator is someone other than the registrant
if (($key != 'create_by') || ($registrant['create_by'] !== $registrant['username']))
{
if (can_see_email_addresses())
{
$user = auth()->getUser($registrant[$key]);
if (isset($user))
{
$display_name = $user->display_name;
$email = $user->email;
}
else
{
$display_name = $registrant[$key];
}
}
else
{
// Can be faster sometimes if we don't need the email address
$display_name = auth()->getDisplayName($registrant[$key]);
}
}
$sort_name = get_sortable_name($display_name);
echo '<td data-order="' . escape_html($sort_name) . '">';
$display_name_html = escape_html($display_name);
// Add a mailto: link if we've got an email address
if (isset($email) && ($email !== ''))
{
$display_name_html = '<a href="mailto:' . escape_html($email) . '">' . $display_name_html . '</a>';
}
echo $display_name_html;
echo '</td>';
}
// Time of registration
$time = time_date_string($registrant['registered']);
echo '<td data-order="' . intval($registrant['registered']) . '">' . escape_html($time) . '</td>';
echo "</tr>\n";
}
echo "</tbody>\n";
echo "</table>\n";
echo "</div>\n";
}
function get_returl(?string $previous_page=null) : string
{
global $server;
static $returl=null;
if (!isset($returl))
{
// Add the previous_page (ie the one we were on before view_entry) to the query string
// so that it is preserved.
$returl = this_page();
$query_string = $server['QUERY_STRING'] ?? '';
parse_str($query_string, $query_string_parts);
if (isset($previous_page))
{
$query_string_parts['previous_page'] = $previous_page;
}
if (!empty($query_string_parts))
{
$returl .= '?' . http_build_query($query_string_parts, '', '&');
}
}
return $returl;
}
function generate_cancel_registration_button(array $row, array $registrant, string $label_text, ?string $previous_page=null, bool $as_field=false) : void
{
global $area, $room;
// Check that it is not too late for ordinary users to cancel
if (!getWritable($row['create_by'], $row['room_id']) && entry_registration_cancellation_has_closed($row))
{
return;
}
$form = new Form(Form::METHOD_POST);
$form->setAttributes(array('action' => multisite('registration_handler.php')));
if ($as_field)
{
$form->setAttribute('class', 'standard');
}
// Hidden inputs
$form->addHiddenInputs(array(
'action' => 'cancel',
'registration_id' => $registrant['id'],
'returl' => get_returl($previous_page),
'area' => $area,
'room' => $room
));
// Submit button
$button = new ElementInputSubmit();
$display_name = auth()->getDisplayName($registrant['username']);
$message = get_vocab("confirm_del_registrant", $display_name);
$button->setAttributes(array(
'value' => $label_text,
'onclick' => "return confirm('" . escape_js($message) . "');"
));
if ($as_field)
{
$fieldset = new ElementFieldset();
$field = new FieldDiv();
$field->addControl($button);
$fieldset->addElement($field);
$form->addElement($fieldset);
}
else
{
$form->addElement($button);
}
$form->render();
}
function generate_register_button(array $row, ?string $previous_page=null) : void
{
global $area, $room;
// Check that the user is an admin or else that the entry is open for registration
if (!getWritable($row['create_by'], $row['room_id']) && !entry_registration_is_open($row))
{
return;
}
$mrbs_user = session()->getCurrentUser();
$can_register_others = can_register_others($row['room_id']);
$form = new Form(Form::METHOD_POST);
$form->setAttributes(array('action' => multisite('registration_handler.php'),
'class' => 'standard'));
// Hidden inputs
$form->addHiddenInputs(array(
'action' => 'register',
'event_id' => $row['id'],
'returl' => get_returl($previous_page),
'area' => $area,
'room' => $room
));
if (!$can_register_others)
{
$form->addHiddenInput('username', $mrbs_user->username);
}
else
{
$fieldset = new ElementFieldset();
$params = array(
'value' => $mrbs_user->username,
'disabled' => false,
'required' => true,
'field' => 'participants.username',
'label' => get_vocab('name'),
'name' => 'username',
);
$fieldset->addElement(get_user_field($params, true));
$form->addElement($fieldset);
}
// Submit button
$fieldset = new ElementFieldset();
$field = new FieldDiv();
$element = new ElementInputSubmit();
$element->setAttribute('value', get_vocab('register'));
$field->addControl($element);
$fieldset->addElement($field);
$form->addElement($fieldset);
$form->render();
}
function generate_event_registration(array $row, ?string $previous_page=null) : void
{
global $auth;
if (empty($row['allow_registration']))
{
return;
}
$can_register_others = can_register_others($row['room_id']);
$can_see_others = $can_register_others || $auth['show_registrant_names'] || getWritable($row['create_by'], $row['room_id']);
$n_registered = count($row['registrants']);
echo '<h4>' . get_vocab('event_registration') . "</h4>\n";
echo "<div id=\"registration\">\n";
echo "<table class=\"list\">\n";
echo "<tbody>\n";
if ($row['registration_opens_enabled'])
{
echo '<tr>';
echo '<td>' . escape_html(get_vocab('registration_opens')) . '</td>';
echo '<td>' . escape_html(time_date_string(registration_opens_timestamp($row))) . '</td>';
echo "</tr>\n";
}
if ($row['registration_closes_enabled'])
{
echo '<tr>';
echo '<td>' . escape_html(get_vocab('registration_closes')) . '</td>';
echo '<td>' . escape_html(time_date_string(registration_closes_timestamp($row))) . '</td>';
echo "</tr>\n";
}
if (!empty($row['registrant_limit_enabled']))
{
echo '<tr>';
echo '<td>' . escape_html(get_vocab('registrant_limit')) . '</td>';
echo '<td>' . intval($row['registrant_limit']) . '</td>'; // Redundant escaping, just in case
echo "</tr>\n";
}
echo '<tr>';
echo '<td>' . escape_html(get_vocab('n_registered')) . '</td>';
echo '<td>' . intval($n_registered) . '</td>'; // Redundant escaping, just in case
echo "</tr>\n";
echo "</tbody>\n";
echo "</table>\n";
// Display the list of registrants, if the user is allowed to see it, which is if
// they have write access for this booking.
// Display it as a table because in the future we might want to (a) add more columns,
// eg date and time of registration and (b) use DataTables to make the list searchable
// and exportable.
if (($n_registered > 0) && $can_see_others)
{
generate_registrant_table($row, $previous_page);
}
// Display registration information and buttons for this user
$mrbs_user = session()->getCurrentUser();
// Doesn't make sense to have anonymous registration at the moment.
// You'd have to have a different kind of registration form - and
// also think about who is allowed to cancel.
if (isset($mrbs_user) && ($mrbs_user->username !== ''))
{
if (!$can_register_others && in_arrayi($mrbs_user->username,
array_column($row['registrants'], 'username')))
{
echo '<p>' . escape_html(get_vocab('already_registered')) . "</p>\n";
foreach ($row['registrants'] as $registrant)
{
if (compare_usernames($mrbs_user->username, $registrant['username']) === 0)
{
$this_registrant = $registrant;
break;
}
}
// If they can see others they'll have a delete button against their name. Otherwise
// we'll need to provide one for them.
if (!$can_see_others)
{
generate_cancel_registration_button(
$row,
$this_registrant,
get_vocab('cancel_registration'),
$previous_page,
true
);
}
}
else
{
if (empty($row['registrant_limit_enabled']) ||
($row['registrant_limit'] > $n_registered))
{
generate_register_button($row, $previous_page);
}
else
{
echo '<p>' . escape_html(get_vocab('event_full')) . "</p>\n";
}
}
}
echo "</div>\n";
}
// Generates a single button. Parameters in the array $params
//
// Mandatory parameters
// action The form action attribute
// value The value of the button
// inputs An array of hidden form inputs
//
// Optional parameters
// button_attributes An array of attributes to be used for the button.
function generate_button(array $params, array $button_attributes=array()) : void
{
// Note that until IE supports the form attribute on the button tag, we can't
// use a <button> here and have to use the <input type="submit"> to create the
// button. This unfortunately means that styling options on the button are limited.
$form = new Form(Form::METHOD_POST);
$attributes = array('action' => $params['action']);
$form->setAttributes($attributes);
// Hidden inputs
$form->addHiddenInputs($params['inputs']);
// Submit button
$element = new ElementInputSubmit();
$element->setAttribute('value', $params['value'])
->setAttributes($button_attributes);
$form->addElement($element);
$form->render();
}
function generate_copy_buttons(int $id, ?int $repeat_id, bool $series, string $returl, bool $repeats_allowed) : void
{
global $day, $month, $year;
echo "<div>\n";
if (!$series)
{
echo "<div>\n";
$params = [
'action' => multisite('edit_entry.php'),
'value' => get_vocab('copyentry'),
'inputs' => [
'id' => $id,
'copy' => true,
'returl' => $returl
]
];
generate_button($params);
echo "</div>\n";
}
if ((!empty($repeat_id) || $series) && $repeats_allowed)
{
echo "<div>\n";
$params = [
'action' => multisite("edit_entry.php?day=$day&month=$month&year=$year"),
'value' => get_vocab('copyseries'),
'inputs' => [
'id' => $id,
'edit_series' => true,
'copy' => true,
'returl' => $returl
]
];
generate_button($params);
echo "</div>\n";
}
echo "</div>\n";
}
function generate_edit_and_delete_buttons(array $row, int $id, ?int $repeat_id, bool $series, string $returl, bool $repeats_allowed) : void
{
global $day, $month, $year;
// Check whether the entry is deletable (and therefore editable). Also get the
// reason. We only get the first reason because it's easier just to display one
// reason in a tooltip, rather than a complete list. [Note: if the entry is
// deletable but the series is not, the series button will not be disabled. This
// is something that needs to be fixed in the future.]
$violations = mrbsCheckPolicy($row, null, null, true);
if (empty($violations['errors']))
{
$button_attributes = [];
}
else
{
$button_attributes = [
'disabled' => true,
'title' => $violations['errors'][0]
];
}
// Edit and Edit Series
echo "<div>\n";
if (!$series)
{
echo "<div>\n";
$params = [
'action' => multisite('edit_entry.php'),
'value' => get_vocab('editentry'),
'inputs' => ['id' => $id, 'returl' => $returl]
];
generate_button($params, $button_attributes);
echo "</div>\n";
}
if ((!empty($repeat_id) || $series) && $repeats_allowed)
{
echo "<div>\n";
$params = [
'action' => multisite("edit_entry.php?day=$day&month=$month&year=$year"),
'value' => get_vocab('editseries'),
'inputs' => [
'id' => $id,
'edit_series' => true,
'returl' => $returl
]
];
if (empty($button_attributes['disabled']) &&
isset($repeat_id) &&
series_has_registrants($repeat_id))
{
$button_attributes['onclick'] = "return confirm('" . get_js_vocab("confirm_edit_series") . "');";
}
generate_button($params, $button_attributes);
echo "</div>\n";
}
echo "</div>\n";
// Delete and Delete Series
echo "<div>\n";
// For the delete buttons, either the button is disabled and we show the reason why, or else
// we add a click event to confirm the deletion
unset($button_attributes['onclick']);
if (!$series)
{
echo "<div>\n";
if (empty($button_attributes['disabled']))
{
$button_attributes['onclick'] = "return confirm('" . get_js_vocab('confirmdel') . "');";
}
$params = [
'action' => multisite('del_entry.php'),
'value' => get_vocab('deleteentry'),
'inputs' => [
'id' => $id,
'series' => 0,
'returl' => $returl
]
];
generate_button($params, $button_attributes);
echo "</div>\n";
}
if ((!empty($repeat_id) || $series) && $repeats_allowed)
{
echo "<div>\n";
if (empty($button_attributes['disabled']))
{
$button_attributes['onclick'] = "return confirm('" . get_js_vocab('confirmdel_series') . "');";
}
$params = [
'action' => multisite("del_entry.php?day=$day&month=$month&year=$year"),
'value' => get_vocab('deleteseries'),
'inputs' => [
'id' => $id,
'series' => 1,
'returl' => $returl
]
];
generate_button($params, $button_attributes);
echo "</div>\n";
}
echo "</div>\n";
}
function generate_export_buttons(int $id, ?int $repeat_id, bool $series, string $returl) : void
{
global $day, $month, $year;
// The iCalendar information has the full booking details in it, so we will not allow
// it to be exported if it is private and the user is not authorised to see it.
echo "<div>\n";
if (!$series)
{
echo "<div>\n";
$params = [
'action' => multisite('view_entry.php'),
'value' => get_vocab('exportentry'),
'inputs' => [
'id' => $id,
'action' => 'export',
'returl' => $returl
]
];
generate_button($params);
echo "</div>\n";
}
if (!empty($repeat_id) || $series)
{
echo "<div>\n";
$params = [
'action' => multisite("view_entry.php?day=$day&month=$month&year=$year"),
'value' => get_vocab('exportseries'),
'inputs' => [
'id' => $repeat_id,
'action' => 'export',
'series' => 1,
'returl' => $returl
]
];
generate_button($params);
echo "</div>\n";
}
echo "</div>\n";
}
// Generates the Approve, Reject and More Info buttons
function generateApproveButtons(int $id, bool $series) : void
{
global $returl;
global $entry_info_time, $entry_info_user, $repeat_info_time, $repeat_info_user;
global $multisite, $site;
$info_time = ($series) ? $repeat_info_time : $entry_info_time;
$info_user = ($series) ? $repeat_info_user : $entry_info_user;
$this_page = this_page();
if (empty($info_time))
{
$info_title = get_vocab("no_request_yet");
}
else
{
$info_title = get_vocab("last_request") . ' ' . time_date_string($info_time);
if (!empty($info_user))
{
$info_title .= " " . get_vocab("by") . " $info_user";
}
}
$query_string = "id=$id&series=" . (($series) ? 1 : 0);
if ($multisite && isset($site) && ($site !== ''))
{
$query_string .= "&site=$site";
}
echo "<tr>\n";
echo "<td>" . ($series ? get_vocab("series") : get_vocab("entry")) . "</td>\n";
echo "<td>\n";
// Approve
$params = array('action' => multisite("approve_entry_handler.php?$query_string"),
'value' => get_vocab('approve'),
'inputs' => array('action' => 'approve',
'returl' => $returl)
);
generate_button($params);
// Reject
$params = array('action' => multisite("$this_page?$query_string"),
'value' => get_vocab('reject'),
'inputs' => array('action' => 'reject',
'returl' => $returl)
);
generate_button($params);
// More info
$params = array('action' => multisite("$this_page?$query_string"),
'value' => get_vocab('more_info'),
'inputs' => array('action' => 'more_info',
'returl' => $returl)
);
generate_button($params, array('title' => $info_title));
echo "</td>\n";
echo "</tr>\n";
}
function generateOwnerButtons(int $id, bool $series) : void
{
global $mrbs_username, $create_by, $awaiting_approval, $area;
global $reminders_enabled, $last_reminded, $reminder_interval;
global $multisite, $site;
// Remind button if you're the owner AND there's a booking awaiting
// approval AND sufficient time has passed since the last reminder
// AND we want reminders in the first place
if ($reminders_enabled &&
isset($mrbs_username) &&
(compare_usernames($mrbs_username, $create_by) === 0) &&
$awaiting_approval &&
(working_time_diff(time(), $last_reminded) >= $reminder_interval))
{
echo "<tr>\n";
echo "<td class=\"no_suffix\"></td>\n";
echo "<td>\n";
$this_page = this_page();
$returl = "$this_page?id=$id&area=$area";
$query_string = "id=$id&series=" . (($series) ? 1 : 0);
if ($multisite && isset($site) && ($site !== ''))
{
$query_string .= "&site=$site";
$returl .= "&site=$site";
}
$params = array('action' => multisite("approve_entry_handler.php?$query_string"),
'value' => get_vocab('remind_admin'),
'inputs' => array('action' => 'remind',
'returl' => "$this_page?id=$id&area=$area")
);
generate_button($params);
echo "</td>\n";
echo "</tr>\n";
}
}
function generateTextArea(string $form_action, int $id, bool $series, string $action_type, string $returl, string $submit_value, string $caption, string $value='') : void
{
echo "<tr><td id=\"caption\" colspan=\"2\">$caption</td></tr>\n";
echo "<tr>\n";
echo "<td id=\"note\" class=\"no_suffix\" colspan=\"2\">\n";
$form = new Form(Form::METHOD_POST);
$attributes = array('action' => $form_action);
$form->setAttributes($attributes);
// Hidden inputs
$hidden_inputs = array('id' => $id,
'series' => ($series) ? 1 : 0,
'returl' => $returl,
'action' => $action_type);
$form->addHiddenInputs($hidden_inputs);
// Visible fields
$fieldset = new ElementFieldset();
$fieldset->addLegend('');
$field = new FieldTextarea();
$field->setControlAttribute('name', 'note')
->setControlText($value);
$fieldset->addElement($field);
// The submit button
$field = new FieldInputSubmit();
$field->setControlAttribute('value', $submit_value);
$fieldset->addElement($field);
$form->addElement($fieldset);
$form->render();
echo "</td>\n";
echo "<tr>\n";
}
// Get non-standard form variables
//
// If $series is TRUE, it means that the $id is the id of an
// entry in the repeat table. Otherwise it's from the entry table.
$id = get_form_var('id', 'int');
$series = get_form_var('series', 'bool', false);
$action = get_form_var('action', 'string');
$returl = get_form_var('returl', 'url_local');
$error = get_form_var('error', 'string');
$previous_page = get_form_var('previous_page', 'url_local');
$referrer = session()->getReferrer();
if (!isset($previous_page) && isset($referrer))
{
$previous_page = $referrer;
}
// Need to tell all the links where to go back to after an edit or delete
if (!isset($returl))
{
// We need $referrer to contain an actual page, and not be a directory, ie end in '/'
if (isset($referrer) && (substr($referrer, -1) != '/'))
{
$parsed_url = parse_url($referrer);
if (isset($parsed_url['path']))
{
$returl = basename($parsed_url['path']);
}
}
// If we still haven't got a referrer (eg if we've come here from an email) then construct
// a sensible place to go to afterwards
if (!isset($returl))
{
$returl = 'index.php';
}
// Add on the query string
if (isset($parsed_url['query']))
{
$returl .= '?' . $parsed_url['query'];
}
else
{
$vars = array('view' => $default_view,
'year' => $year,
'month' => $month,
'day' => $day,
'area' => $area,
'room' => $room);
$returl .= '?' . http_build_query($vars, '', '&');
}
}
// Check the CSRF token if we're going to do something
if (isset($action))
{
Form::checkToken();
}
// Check the user is authorised for this page
checkAuthorised(this_page());
$mrbs_user = session()->getCurrentUser();
$mrbs_username = (isset($mrbs_user)) ? $mrbs_user->username : null;
// You're only allowed to make repeat bookings if you're an admin
// or else if $auth['only_admin_can_book_repeat'] is not set
$repeats_allowed = is_book_admin() || empty($auth['only_admin_can_book_repeat']);
$row = get_booking_info($id, $series);
$room = $row['room_id'];
$area = $row['area_id'];
// Get the area settings for the entry's area. In particular we want
// to know how to display private/public bookings in this area.
get_area_settings($row['area_id']);
$area_periods = Periods::getForArea($area);
// Work out whether the room or area is disabled
$room_disabled = $row['room_disabled'] || $row['area_disabled'];
// Get the approval status
$awaiting_approval = $row['awaiting_approval'];
// Work out whether this event should be kept private
$private = $row['private'];
// Get the creator
$create_by = $row['create_by'];
$writeable = getWritable($row['create_by'], $row['room_id']);
$keep_private = (is_private_event($private) && !$writeable);
// Work out when the last reminder was sent
$last_reminded = (empty($row['reminded'])) ? $row['last_updated'] : $row['reminded'];
if ($series)
{
$repeat_id = $id; // Save the repeat_id
// I also need to set $id to the value of a single entry as it is a
// single entry from a series that is used by del_entry.php and
// edit_entry.php
// So I will look for the first entry in the series where the entry is
// as per the original series settings
$sql = "SELECT id
FROM " . _tbl('entry') . "
WHERE repeat_id=? AND entry_type=" . ENTRY_RPT_ORIGINAL . "
ORDER BY start_time
LIMIT 1";
$id = db()->query1($sql, array($id));
if ($id < 1)
{
// if all entries in series have been modified then
// as a fallback position just select the first entry
// in the series
// hopefully this code will never be reached as
// this page will display the start time of the series
// but edit_entry.php will display the start time of the entry
$sql = "SELECT id
FROM " . _tbl('entry') . "
WHERE repeat_id=?
ORDER BY start_time
LIMIT 1";
$id = db()->query1($sql, array($id));
}
$repeat_info_time = $row['repeat_info_time'];
$repeat_info_user = $row['repeat_info_user'];
$repeat_info_text = $row['repeat_info_text'];
}
else
{
$repeat_id = $row['repeat_id'];
$entry_info_time = $row['entry_info_time'];
$entry_info_user = $row['entry_info_user'];
$entry_info_text = $row['entry_info_text'];
}
// PHASE 2 - EXPORTING ICALENDAR FILES
// -------------------------------------
if (isset($action) && ($action == "export"))
{
if ($keep_private || ($enable_periods && !$area_periods->hasTimes()))
{
// Should never normally be able to get here, but if we have then
// go somewhere safe.
location_header('index.php');
}
else
{
// Construct the SQL query
$sql_params = array();
$sql = "SELECT E.*, "
. db()->syntax_timestamp_to_unix("E.timestamp") . " AS last_updated, "
. "A.area_name, R.room_name, "
. "A.approval_enabled, A.confirmation_enabled, A.timezone";
if ($series)
{
// If it's a series we want the repeat information
$sql .= ", T.rep_type, T.end_date, T.rep_opt, T.rep_interval, T.month_absolute, T.month_relative";
}
$sql .= " FROM " . _tbl('area') . " A, " . _tbl('room') . " R, " . _tbl('entry') . " E";
if ($series)
{
$sql .= ", " . _tbl('repeat') . " T"
. " WHERE E.repeat_id=?"
. " AND E.repeat_id=T.id";
$sql_params[] = $repeat_id;
}
else
{
$sql .= " WHERE E.id=?";
$sql_params[] = $id;
}
$sql .= " AND E.room_id=R.id
AND R.area_id=A.id";
if ($series)
{
$sql .= " ORDER BY E.start_time";
}
$res = db()->query($sql, $sql_params);
// Export the calendar
$content_type = "application/ics; charset=" . Language::MRBS_CHARSET . "; name=\"" . $mail_settings['ics_filename'] . ".ics\"";
$content_disposition = "attachment; filename=\"" . $mail_settings['ics_filename'] . ".ics\"";
http_headers(array("Content-Type: $content_type",
"Content-Disposition: $content_disposition"));
$calendar = Calendar::createFromStatement($res, $keep_private);
echo $calendar->toString();
exit;
}
}
// PHASE 1 - VIEW THE ENTRY
// ------------------------
$context = array(
'view' => $view,
'view_all' => $view_all,
'year' => $year,
'month' => $month,
'day' => $day,
'area' => $area,
'room' => $room ?? null
);
print_header($context);
// Now that we know all the data we start drawing it
echo "<h3" . (($keep_private && $is_private_field['entry.name']) ? " class=\"private\"" : "") . ">\n";
echo ($keep_private && $is_private_field['entry.name']) ? get_vocab("unavailable") : escape_html($row['name']);
if (is_private_event($private) && $writeable)
{
echo ' ' . get_vocab("unavailable");
}
echo "</h3>\n";
generate_event_registration($row, $previous_page);
echo '<h4>' . get_vocab('event_details') . "</h4>\n";
echo "<table id=\"entry\" class=\"list\">\n";
// Output any error messages
if (!empty($error))
{
echo "<tr><td>&nbsp;</td><td class=\"error\">" . get_vocab($error) . "</td></tr>\n";
}
// If it's a registration event, then we only show the most important details, unless it's writable.
$major_details_only = $row['allow_registration'] && !getWritable($row['create_by'], $row['room_id'], false);
echo create_details_body($row, true, $keep_private, $room_disabled, $major_details_only);
// If bookings require approval, and the room is enabled, put the buttons
// to do with managing the bookings in the footer
if ($approval_enabled && !$room_disabled && $awaiting_approval)
{
echo "<tfoot id=\"approve_buttons\">\n";
// PHASE 2 - REJECT
if (isset($action) && ($action == "reject"))
{
// del_entry expects the id of a member of a series
// when deleting a series and not the repeat_id
generateTextArea(multisite('del_entry.php'), $id, $series,
"reject", $returl,
get_vocab("reject"),
get_vocab("reject_reason"));
}
// PHASE 2 - MORE INFO
elseif (isset($action) && ($action == "more_info"))
{
// but approve_entry_handler expects the id to be a repeat_id
// if $series is true (ie behaves like the rest of MRBS).
// Sometime this difference in behaviour should be rationalised
// because it is very confusing!
$target_id = ($series) ? $repeat_id : $id;
$info_time = ($series) ? $repeat_info_time : $entry_info_time;
$info_user = ($series) ? $repeat_info_user : $entry_info_user;
$info_text = ($series) ? $repeat_info_text : $entry_info_text;
if (empty($info_time))
{
$value = '';
}
else
{
$value = get_vocab("sent_at") . time_date_string($info_time);
if (!empty($info_user))
{
$value .= "\n" . get_vocab("by") . " $info_user";
}
$value .= "\n----\n";
$value .= $info_text;
}
generateTextArea(multisite('approve_entry_handler.php'), $target_id, $series,
"more_info", $returl,
get_vocab("send"),
get_vocab("request_more_info"),
$value);
}
// PHASE 1 - first time through this page
else
{
// Buttons for those who are allowed to approve this booking
if (is_book_admin($row['room_id']))
{
if (!$series)
{
generateApproveButtons($id, false);
}
if (!empty($repeat_id) || $series)
{
generateApproveButtons($repeat_id, true);
}
}
// Buttons for the owner of this booking
elseif (isset($mrbs_username) && compare_usernames($mrbs_username, $create_by) === 0)
{
generateOwnerButtons($id, $series);
}
// Others don't get any buttons
}
echo "</tfoot>\n";
}
echo "</table>\n";
echo "<div id=\"view_entry_nav\">\n";
// Set the return URL. If $previous_page is set and $returl is not 'index.php' then
// it means that we've come from the registration handler and the original return URL
// is held in $previous_page.
// TODO: simplify the code concerning return urls, target urls and the previous page
// TODO: throughout MRBS.
if (isset($previous_page) &&
(!(isset($returl) && (basename(parse_url($returl)['path']) == 'index.php'))))
{
$returl = $previous_page;
}
if (!$major_details_only)
{
// Only show the links for Edit and Delete if (a) the room is enabled, and (b) the user
// is allowed to use them. (We're allowed to view and copy existing bookings in disabled
// rooms, but not to modify or delete them.)
if (!$room_disabled && getWritable($create_by, $room))
{
generate_edit_and_delete_buttons($row, $id, $repeat_id, $series, $returl, $repeats_allowed);
}
// Copy and Copy Series
if (!$auth['only_admin_can_copy_others_entries'] || $writeable)
{
generate_copy_buttons($id, $repeat_id, $series, $returl, $repeats_allowed);
}
// Export and Export Series
if (!$keep_private && (!$enable_periods || ($enable_periods && $area_periods->hasTimes())))
{
generate_export_buttons($id, $repeat_id, $series, $returl);
}
}
echo "</div>\n"; // id="view_entry_nav"
// Don't display a link to the previous page if there isn't one (eg if we've got here
// from an email), or if it would just send the user to the same page for some reason.
if (isset($previous_page) &&
(basename(parse_url($previous_page)['path']) !== this_page()))
{
echo "<div id=\"returl\">\n";
echo '<a href="' . escape_html($previous_page) . '">' . get_vocab('returnprev') . "</a>\n";
echo "</div>\n";
}
print_footer();