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 排除,勿推送到公开仓库。
This commit is contained in:
@@ -0,0 +1,510 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace MRBS;
|
||||
|
||||
use MRBS\Intl\FormatterFlatpickr;use MRBS\Intl\IntlDatePatternConverter;
|
||||
|
||||
require "../defaultincludes.inc";
|
||||
|
||||
// We use ICU datetime patterns in the config file rather than flatpickr formatting
|
||||
// tokens so that we can switch to a different datepicker if necessary, without
|
||||
// having to change the config settings.
|
||||
function get_date_format() : string
|
||||
{
|
||||
global $datetime_formats;
|
||||
|
||||
if (isset($datetime_formats['datepicker']['pattern']))
|
||||
{
|
||||
try
|
||||
{
|
||||
$converter = new IntlDatePatternConverter(new FormatterFlatpickr());
|
||||
return $converter->convert($datetime_formats['datepicker']['pattern']);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Report the error and fall through to the 'custom' format
|
||||
trigger_error($e->getMessage(), E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
return 'custom';
|
||||
}
|
||||
|
||||
http_headers(array("Content-type: application/x-javascript"),
|
||||
60*30); // 30 minute expiry
|
||||
?>
|
||||
|
||||
'use strict';
|
||||
|
||||
<?php
|
||||
// Fix for iOS 13 where the User Agent string has been changed.
|
||||
// See https://github.com/flatpickr/flatpickr/issues/1992
|
||||
?>
|
||||
function iPadMobileFix() {
|
||||
return function(instance) {
|
||||
return {
|
||||
onParseConfig: function() {
|
||||
if (instance.isMobile)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (isIos())
|
||||
{
|
||||
instance.isMobile = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
<?php
|
||||
// Turn a JavaScript year, month, day (ie with Jan = 0) into an
|
||||
// ISO format YYYY-MM-DD date string. Can cope with months
|
||||
// outside the range 0..11, but days must be valid.
|
||||
?>
|
||||
function getISODate(year, month, day)
|
||||
{
|
||||
<?php // toISOString() converts to UTC, so make the date a UTC date ?>
|
||||
var date = new Date(Date.UTC(year, month, day));
|
||||
return date.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
|
||||
// Given a JavaScript Date object returns a date string in YYYY-MM-DD
|
||||
// format. (Note that toISOString() returns a date in UTC time).
|
||||
function getLocalISODateString(date)
|
||||
{
|
||||
var month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
var day = date.getDate().toString().padStart(2, '0');
|
||||
var year = date.getFullYear().toString();
|
||||
|
||||
return [year, month, day].join('-');
|
||||
}
|
||||
|
||||
|
||||
|
||||
<?php
|
||||
// Functions to find the start and end dates of a week and month given a
|
||||
// date in YYYY-MM-DD format.
|
||||
// weekStarts is the start day of the week (0 for Sunday, 1 for Monday etc.)
|
||||
// (Could be implemented by extending the Date class, but extends isn't
|
||||
// supported by IE11.)
|
||||
?>
|
||||
|
||||
function weekStart(date, weekStarts) {
|
||||
<?php
|
||||
// Need to add a time to make sure the date is interpreted as local rather than UTC.
|
||||
// "When the time zone offset is absent, date-only forms are interpreted as a UTC time and
|
||||
// date-time forms are interpreted as local time. This is due to a historical spec error
|
||||
// that was not consistent with ISO 8601 but could not be changed due to web compatibility."
|
||||
?>
|
||||
var d = new Date(date + "T00:00:00");
|
||||
var diff = d.getDay() - weekStarts;
|
||||
if (diff < 0)
|
||||
{
|
||||
diff += 7;
|
||||
}
|
||||
d.setDate(d.getDate() - diff);
|
||||
return getLocalISODateString(d);
|
||||
}
|
||||
|
||||
function weekEnd(date, weekStarts) {
|
||||
<?php // Need to add a time to make sure the date is interpreted as local rather than UTC. ?>
|
||||
var d = new Date(weekStart(date, weekStarts) + "T00:00:00");
|
||||
d.setDate(d.getDate() + 6);
|
||||
return getLocalISODateString(d);
|
||||
}
|
||||
|
||||
function monthStart(date) {
|
||||
<?php // Need to add a time to make sure the date is interpreted as local rather than UTC. ?>
|
||||
var d = new Date(date + "T00:00:00");
|
||||
d.setDate(1);
|
||||
return getLocalISODateString(d);
|
||||
}
|
||||
|
||||
function monthEnd(date) {
|
||||
<?php // Need to add a time to make sure the date is interpreted as local rather than UTC. ?>
|
||||
var d = new Date(date + "T00:00:00");
|
||||
<?php
|
||||
// Set the date to the first of the month, because otherwise we will
|
||||
// advance by two months when incrementing the month below if we're at the
|
||||
// end of the month and the next month has fewer days than this one.
|
||||
?>
|
||||
d.setDate(1);
|
||||
d.setMonth(d.getMonth() + 1);
|
||||
d.setDate(0);
|
||||
return getLocalISODateString(d);
|
||||
}
|
||||
|
||||
|
||||
// Returns an array of dates in the range startDate..endDate, optionally
|
||||
// excluding hidden days.
|
||||
function datesInRange(startDate, endDate, excludeHiddenDays) {
|
||||
var result=[];
|
||||
<?php // Need to add a time to make sure the date is interpreted as local rather than UTC. ?>
|
||||
var e=new Date(endDate + "T00:00:00");
|
||||
var hiddenDays = [<?php echo implode(',', $hidden_days)?>];
|
||||
|
||||
<?php // dates can be compared using > and < but not == or === ?>
|
||||
for (var d=new Date(startDate + "T00:00:00"); !(d>e); d.setDate(d.getDate()+1))
|
||||
{
|
||||
if (excludeHiddenDays && (hiddenDays.indexOf(d.getDay()) >= 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
result.push(getLocalISODateString(d));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
$(document).on('page_ready', function() {
|
||||
|
||||
const locales = args.langPrefs;
|
||||
|
||||
<?php
|
||||
// Set up datepickers. We convert all inputs of type 'date' into flatpickr
|
||||
// datepickers. Note that by default flatpickr will use the native datepickers
|
||||
// on mobile devices because they are generally better.
|
||||
|
||||
// Localise the flatpickr
|
||||
// Could use new URLSearchParams(document.currentScript.src); and get the lang parameter from the
|
||||
// file's query string, but document.currentScript is not supported by IE (though that probably
|
||||
// doesn't matter much anymore).
|
||||
if (null !== ($flatpickr_lang_path = Language::getInstance()->getFlatpickrLangPath()))
|
||||
{
|
||||
// Map the flatpickr lang file onto a flatpickr l10ns property and then localize
|
||||
echo 'flatpickr.localize(flatpickr.l10ns["' . Language::getFlatpickrProperty($flatpickr_lang_path) . '"]);';
|
||||
}
|
||||
|
||||
|
||||
// Custom date formatter. At the moment, only two format strings are supported:
|
||||
//
|
||||
// 'custom' The date is formatted in numeric form in the user's preferred locale,
|
||||
// as expressed by their browser preferences and subject to any
|
||||
// overriding config settings. Note that 'custom' is not supported on
|
||||
// IE10, as it requires Intl.DateTimeFormat(), and so IE10 users
|
||||
// are given a date in 'Y-m-d' format.
|
||||
//
|
||||
// everything All other format strings are treated as 'Y-m-d'.
|
||||
// else
|
||||
?>
|
||||
var formatDate = function(dateObj, formatStr) {
|
||||
<?php
|
||||
// If window.Intl is supported then we can format dates in the user's preferred
|
||||
// locale. Otherwise, in practice just IE10, they have to make do with ISO
|
||||
// (YYYY-MM-DD) dates.
|
||||
?>
|
||||
if (window.Intl && (formatStr === 'custom'))
|
||||
{
|
||||
<?php
|
||||
if ($language_debug)
|
||||
{
|
||||
?>
|
||||
console.debug("MRBS language preferences: " + JSON.stringify(locales));
|
||||
console.debug("MRBS: locales supported by Intl.DateTimeFormat: " + JSON.stringify(Intl.DateTimeFormat.supportedLocalesOf(locales)));
|
||||
<?php
|
||||
}
|
||||
// Check whether we can use our first choice locale for date and time formatting.
|
||||
?>
|
||||
if (!Intl.DateTimeFormat.supportedLocalesOf(locales[0]).length)
|
||||
{
|
||||
console.warn("MRBS: Intl.DateTimeFormat in this browser does not support the '" + locales[0] + "' locale.");
|
||||
}
|
||||
return (typeof locales === 'undefined') ?
|
||||
new Intl.DateTimeFormat().format(dateObj) :
|
||||
new Intl.DateTimeFormat(locales).format(dateObj);
|
||||
}
|
||||
|
||||
return getISODate(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate());
|
||||
};
|
||||
|
||||
|
||||
var onDayCreate = function(dObj, dStr, fp, dayElem) {
|
||||
<?php
|
||||
// If the datepicker is used for navigation, and this is a hidden day, and the user is
|
||||
// a booking admin, then add a class to the day so that it can be styled differently.
|
||||
// If they're not a booking admin, and it's a navigation datepicker, the day will be
|
||||
// disabled - see later on in this file.
|
||||
?>
|
||||
const hiddenDays = [<?php echo implode(',', $hidden_days)?>];
|
||||
if (hiddenDays.length &&
|
||||
args.isBookAdmin &&
|
||||
fp.altInput.classList.contains('navigation') &&
|
||||
(hiddenDays.indexOf(dayElem.dateObj.getDay()) >= 0))
|
||||
{
|
||||
dayElem.classList.add('mrbs-hidden');
|
||||
}
|
||||
|
||||
<?php // And add a class if it's a weekend day ?>
|
||||
const weekDays = [<?php echo implode(',', $weekdays)?>];
|
||||
if (weekDays.indexOf(dayElem.dateObj.getDay()) < 0) {
|
||||
dayElem.classList.add('mrbs-weekend');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
<?php
|
||||
// Sync all the mini-calendars with this instance of one. In other words
|
||||
// make the mini-calendars show sequential months, aligning with this one.
|
||||
?>
|
||||
function syncCals(instance)
|
||||
{
|
||||
var thisId = instance.element.attributes.id.nodeValue,
|
||||
thisIndex = parseInt(thisId.substring(3), 10),
|
||||
currentMonth = parseInt(instance.currentMonth, 10),
|
||||
currentYear = parseInt(instance.currentYear, 10);
|
||||
|
||||
$.each(minicalendars, function(key, value) {
|
||||
if (value.element.attributes.id.nodeValue !== thisId)
|
||||
{
|
||||
var index = parseInt(value.element.attributes.id.nodeValue.substring(3), 10);
|
||||
value.jumpToDate(getISODate(currentYear, currentMonth + index - thisIndex, 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var onMonthChange = function(selectedDates, dateStr, instance) {
|
||||
syncCals(instance);
|
||||
};
|
||||
|
||||
var onYearChange = function(selectedDates, dateStr, instance) {
|
||||
syncCals(instance);
|
||||
};
|
||||
|
||||
var onMinicalChange = function(selectedDates, dateStr, instance) {
|
||||
<?php
|
||||
// The order of the query string parameters is important here. It needs to be the
|
||||
// same as the order in the Prev and Next navigation links so that the pre-fetched
|
||||
// pages can be used when possible.
|
||||
?>
|
||||
var href = 'index.php';
|
||||
href += '?view=' + args.view;
|
||||
href += '&page_date=' + dateStr;
|
||||
href += '&area=' + args.area;
|
||||
href += '&room=' + args.room;
|
||||
if (args.site)
|
||||
{
|
||||
href += '&site=' + encodeURIComponent(args.site);
|
||||
}
|
||||
<?php
|
||||
// Set the new date in the mini-calendar, in order to avoid the previous one
|
||||
// still showing as selected.
|
||||
// TODO: change the date in the other mini-calendar of the date appears there as well?
|
||||
?>
|
||||
instance.setDate([selectedDates[0], selectedDates[0]], false);
|
||||
updateBody(href); <?php // Update the body via an Ajax call to avoid flickering ?>
|
||||
};
|
||||
|
||||
var lastValidDate = {};
|
||||
|
||||
<?php
|
||||
$date_format = get_date_format();
|
||||
?>
|
||||
|
||||
var config = {
|
||||
plugins: [iPadMobileFix()],
|
||||
dateFormat: 'Y-m-d',
|
||||
altInput: true,
|
||||
altFormat: <?php echo "'" . get_date_format() . "'" ?>,
|
||||
<?php
|
||||
if ($date_format == 'custom')
|
||||
{
|
||||
echo "formatDate: formatDate,\n";
|
||||
}
|
||||
?>
|
||||
onDayCreate: onDayCreate,
|
||||
locale: {firstDayOfWeek: <?php echo $weekstarts ?>},
|
||||
onChange: function(selectedDates, dateStr, instance) {
|
||||
var element = $(instance.element);
|
||||
var submit = element.data('submit');
|
||||
var elementName = element.attr('name');
|
||||
<?php
|
||||
// Flatpickr allows the user to delete the date in the input field, even when allowInput
|
||||
// is false, resulting in an empty string, which then causes problems for edit_entry_handler.php.
|
||||
// See https://github.com/flatpickr/flatpickr/issues/936 and
|
||||
// https://github.com/flatpickr/flatpickr/pull/2252 . At the time of writing the PR has not been
|
||||
// merged. To get round this we substitute an empty string for the last known valid date,
|
||||
// but only for required inputs.
|
||||
?>
|
||||
if (dateStr) {
|
||||
lastValidDate[elementName] = dateStr;
|
||||
}
|
||||
else if (element.prop('required') && lastValidDate[elementName]) {
|
||||
instance.setDate(lastValidDate[elementName]);
|
||||
}
|
||||
<?php // Submit will be set for the datepicker in the banner ?>
|
||||
if (submit) {
|
||||
$('#' + submit).trigger('submit');
|
||||
}
|
||||
else {
|
||||
element.trigger('change');
|
||||
}
|
||||
},
|
||||
onReady: function(selectedDates, dateStr, instance) {
|
||||
if (instance.altInput.ariaLabel === null) {
|
||||
instance.altInput.ariaLabel = instance.input.ariaLabel;
|
||||
}
|
||||
if (dateStr) {
|
||||
lastValidDate[instance.element.getAttribute('name')] = dateStr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
<?php
|
||||
// Setting weekNumbers causes flatpickr not to use the native datepickers on mobile
|
||||
// devices. As these are generally better than flatpickr's, it's probably better
|
||||
// to have the native datepicker and do without the week numbers.
|
||||
?>
|
||||
if (isMobile())
|
||||
{
|
||||
$('input.flatpickr-input').width('auto');
|
||||
}
|
||||
else
|
||||
{
|
||||
<?php // Only display the week number if the MRBS week starts on the first day of the week ?>
|
||||
config.weekNumbers = <?php echo ($mincals_week_numbers && ($weekstarts == DateTime::firstDayOfWeek($timezone, Language::getInstance()->getWebLocale()))) ? 'true' : 'false' ?>;
|
||||
}
|
||||
|
||||
let fps = flatpickr('input[type="date"]:not(.navigation)', config);
|
||||
// flatpickr will either return a single instance or an array of instances,
|
||||
// so convert a single instance to an array so that the iteration works.
|
||||
fps = (Array.isArray(fps) ? fps : [fps]);
|
||||
fps.forEach(function(fp) {
|
||||
<?php
|
||||
// For dates that have a data-min attribute, set the minDate.
|
||||
?>
|
||||
let minDate = $(fp.input).data('min');
|
||||
if (minDate)
|
||||
{
|
||||
fp.set('minDate', minDate);
|
||||
// Note that although the flatpickr documentation says this should work on mobiles, it doesn't seem to.
|
||||
// So we manually set the 'min' attribute for mobiles.
|
||||
$('input[type="hidden"][data-min]').next('.flatpickr-input.flatpickr-mobile').attr('min', minDate);
|
||||
}
|
||||
});
|
||||
|
||||
<?php
|
||||
// For datepickers used for navigation we need to modify the config to take account
|
||||
// of hidden days.
|
||||
|
||||
// Disable hidden days, unless the user is a booking admin. (If they're a booking
|
||||
// admin then they'll still be able to select the date, but it will be given a different
|
||||
// class so that it can be styled differently - see code above in this file.)
|
||||
if (!empty($hidden_days))
|
||||
{
|
||||
?>
|
||||
if (!args.isBookAdmin)
|
||||
{
|
||||
config.disable = [
|
||||
function (date) {
|
||||
return ([<?php echo implode(',', $hidden_days); ?>].indexOf(date.getDay()) >= 0);
|
||||
}
|
||||
];
|
||||
}
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
flatpickr('input[type="date"].navigation', config);
|
||||
|
||||
<?php
|
||||
// Build the mini-calendars, if required.
|
||||
if (!empty($display_mincals))
|
||||
{
|
||||
?>
|
||||
if (!isMobile())
|
||||
{
|
||||
var div = $('.minicalendars');
|
||||
if (div.length > 0)
|
||||
{
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
<?php // Add the 'navigation' class so that the JavaScript knows it can use hidden days ?>
|
||||
div.append($('<span class="minicalendar navigation" id="cal' + i + '"></span>'));
|
||||
}
|
||||
config.inline = true;
|
||||
config.onMonthChange = onMonthChange;
|
||||
config.onYearChange = onYearChange;
|
||||
config.onChange = onMinicalChange;
|
||||
<?php
|
||||
// Setting a range only works if there are no hidden days: it does not make
|
||||
// sense to set a start date of a range on a disabled day.
|
||||
if (empty($hidden_days))
|
||||
{
|
||||
?>
|
||||
config.mode = 'range';
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
var minicalendars = flatpickr('span.minicalendar', config);
|
||||
|
||||
$.each(minicalendars, function (key, value) {
|
||||
var startDate, endDate;
|
||||
if (args.view === 'month')
|
||||
{
|
||||
startDate = monthStart(args.pageDate);
|
||||
endDate = monthEnd(args.pageDate);
|
||||
}
|
||||
else if (args.view === 'week')
|
||||
{
|
||||
startDate = weekStart(args.pageDate, <?php echo $weekstarts?>);
|
||||
endDate = weekEnd(args.pageDate, <?php echo $weekstarts?>);
|
||||
}
|
||||
else
|
||||
{
|
||||
startDate = args.pageDate;
|
||||
endDate = startDate;
|
||||
}
|
||||
<?php
|
||||
if (empty($hidden_days))
|
||||
{
|
||||
?>
|
||||
value.setDate([startDate, endDate]);
|
||||
<?php
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we've got hidden days then highlight in the mini-calendars those
|
||||
// days in the range that are not hidden.
|
||||
?>
|
||||
value.setDate(datesInRange(startDate, endDate, true));
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
value.changeMonth(key);
|
||||
});
|
||||
|
||||
<?php
|
||||
// Align the top of the mini-calendars with the top of the navigation bar
|
||||
?>
|
||||
div.css('margin-top', $('.view_container h2').outerHeight(true) + 'px');
|
||||
|
||||
<?php
|
||||
// Once the calendars are formed then we add the class 'formed' which will
|
||||
// bring into play CSS media queries. We need to do this because if we
|
||||
// form them when the media queries are operational then they won't get
|
||||
// formed if the result of the query is 'display: none', which means that if
|
||||
// the window is later widened, for example, they still won't appear when they
|
||||
// should do.
|
||||
?>
|
||||
div.addClass('formed');
|
||||
}
|
||||
}
|
||||
<?php
|
||||
}
|
||||
|
||||
// Only show the main table and navigation once the mini-calendars are in place
|
||||
// in order to avoid the screen jiggling about.
|
||||
?>
|
||||
$('.view_container').removeClass('js_hidden');
|
||||
|
||||
<?php
|
||||
// Show the datepicker in the banner, which has been hidden up until now
|
||||
?>
|
||||
$('#form_nav').removeClass('js_hidden');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user