MRBS 1.12.2 等保2.0二级整改完整提交
Docker image / push (push) Canceled after 0s

包含:登录失败锁定、90天密码有效期、30分钟会话超时、
强制改密、登录审计日志、屏幕水印、企业背景图、
备案信息固定底部、favicon、登录页JS修复等全部改动
This commit is contained in:
人事系统开发
2026-09-08 21:19:47 +08:00
commit 48092cab42
2221 changed files with 659586 additions and 0 deletions
+327
View File
@@ -0,0 +1,327 @@
<?php
declare(strict_types=1);
namespace MRBS;
require "../defaultincludes.inc";
http_headers(array("Content-type: application/x-javascript"),
60*30); // 30 minute expiry
?>
'use strict';
<?php
// Show or Hide the settings for Times and the note about Periods as
// appropriate. Also toggle the required property on the area_periods[]
// inputs: if they are left as required when they are hidden, then the
// browser will try and make you complete them, but throw an error because
// they cannot be brought in to focus. ?>
function toggleMode(speed)
{
if (typeof speed === 'undefined')
{
speed = 'slow';
}
if ($('input:radio[name=area_enable_periods]:checked').val() === '0')
{
$('#book_ahead_periods_note').hide(speed);
$('#time_settings').show(speed);
$('#period_settings').hide(speed);
$('input[name="area_periods[]"]').prop('required', false);
}
else
{
$('#book_ahead_periods_note').show(speed);
$('#time_settings').hide(speed);
$('#period_settings').show(speed);
$('input[name="area_periods[]"]').prop('required', true);
}
}
function getTimeString(time, twentyfourhour_format)
{
<?php
// Converts a time (in minutes since midnight) into a string
// of the form hh:mm if twentyfourhour_format is true,
// otherwise of the form hh:mm am/pm.
// This function doesn't do a great job of replicating the PHP
// internationalised format, but is probably sufficient for a
// rarely used admin page.
?>
var ap,
timeString,
minutes = time % 60;
time -= minutes;
var hour = time/60;
if (!twentyfourhour_format)
{
if (hour > 11)
{
ap = "<?php echo datetime_format(array('pattern' => 'a'), mktime(14, 0, 0)) ?>";
}
else
{
ap = "<?php echo datetime_format(array('pattern' => 'a'), mktime(10, 0, 0)) ?>";
}
if (hour > 12)
{
hour = hour - 12;
}
if (hour === 0)
{
hour = 12;
}
}
if (hour < 10)
{
hour = "0" + hour;
}
if (minutes < 10)
{
minutes = "0" + minutes;
}
timeString = hour + ':' + minutes;
if (!twentyfourhour_format)
{
timeString += ap;
}
return timeString;
} // function getTimeString()
<?php // Get the resolution in minutes ?>
function getResolutionMinutes()
{
return parseInt($('#area_res_mins').val(), 10);
}
<?php
// Converts a time string in the format 'hh:mm' to minutes.
// Returns null if the string is not properly formed.
?>
function hhmmToMins(hhmm)
{
if (hhmm === null)
{
return null;
}
if (!<?php echo REGEX_HHMM ?>.test(hhmm))
{
return null;
}
var array = hhmm.split(':');
return (parseInt(array[0], 10) * 60) + parseInt(array[1], 10);
}
<?php // Gets the start of the first slot in minutes past midnight ?>
function getStartFirstSlot()
{
return hhmmToMins($('input[name="area_start_first_slot"]').val());
}
<?php // Gets the start of the last slot in minutes past midnight ?>
function getStartLastSlot()
{
return hhmmToMins($('[name="area_start_last_slot"]').val());
}
function generateLastSlotSelect()
{
<?php
// Turn the last slot field into a select box that only contains permitted values
// given the first slot and resolution
?>
var resMins, tCorrected,
firstSlot, lastSlot,
minsPerDay = <?php echo MINUTES_PER_DAY ?>;
resMins = getResolutionMinutes();
if (isNaN(resMins) || (resMins === null) || (resMins === 0))
{
return; <?php // avoid endless loops and divide by zero errors ?>
}
firstSlot = getStartFirstSlot();
lastSlot = getStartLastSlot();
if (firstSlot === null)
{
return;
}
<?php
// Construct the <select> element.
// We allow the "day" to go all the way past midnight and up to the start of the
// next first slot.
?>
var lastPossible = minsPerDay + firstSlot - resMins;
var name = 'area_start_last_slot';
var element = $('[name="' + name + '"]');
var select = $('<select>').attr('name', name);
for (var t=firstSlot; t <= lastPossible; t += resMins)
{
tCorrected = t % minsPerDay; <?php // subtract one day if past midnight?>
<?php // Calculate the closest option to the old last slot ?>
if (Math.abs(lastSlot - tCorrected) <= resMins/2)
{
lastSlot = tCorrected;
}
select.append($('<option>')
.val(getTimeString(tCorrected, true))
.text(getTimeString(tCorrected, <?php echo (is_ampm() ? "false" : "true") ?>)));
}
<?php // and make the selected option the new last slot value ?>
select.val(getTimeString(lastSlot, true));
<?php // finally, replace the element with the new <select> ?>
element.replaceWith(select);
$('#last_slot').css('visibility', 'visible');
}
<?php
// Check to see if there's only one period name left and, if so,
// disable the delete button, to make sure there's always at least one
// period. (We could in theory have no period names, but it doesn't
// have a practical use. Besides, always having at least one makes the
// code a little simpler because there will always be something to clone.)
?>
function checkForLastPeriodName()
{
if ($('.period_name').length === 1)
{
$('.delete_period').hide();
}
}
$(document).on('page_ready', function() {
<?php
// We need to hide the sections of the form relating to times
// slots if the form is loaded with periods enabled. We hide
// the times sections instantly by setting speed = 0;
// Also show or hide the periods note as appropriate
// [This method works if there are no periods-specific settings.
// When we get those we will have to do something different]
?>
$('input:radio[name=area_enable_periods]').on('click', function() {
toggleMode('fast');
});
toggleMode(0);
<?php
// Work out if we can display the delete symbols, and then only
// after we have done that make them visible. (This stops the
// delete symbol appearing for a moment and then being removed).
?>
checkForLastPeriodName();
$('.delete_period')
.css('visibility', 'visible')
.on('click', function() {
<?php // Delete a period name input field ?>
$(this).parent().remove();
checkForLastPeriodName();
});
<?php
// When the Add Period button is clicked, duplicate the last period
// name input field, clearing its contents. Re-enable all the delete
// icons because there must be more than one having added one.
?>
$('#add_period').on('click', function() {
var lastPeriodName = $('#period_settings .period_name').last(),
clone = lastPeriodName.clone(true); <?php // duplicate data and events ?>
clone.find('input').val('');
clone.insertAfter(lastPeriodName).find('input').trigger('focus');
$('.delete_period').show();
});
<?php
// Show or hide the period times, depending on whether the use_period_times checkbox is checked.
// If use_period_times is unchecked, then disable the first input field so that the others are
// not lost, and disable any invalid fields so that they don't fail validation.
// If use_period_times is checked, then enable all the fields.
// TODO: Disabling just the first input is a temporary measure to avoid losing all the data until
// TODO: we start storing use_period_times in the database.
?>
$('#use_period_times').on('change', function() {
const checked = $(this).prop('checked');
$('.period_times').toggle(checked).find('input').each(function(index) {
const input = $(this).get(0);
if (checked) {
input.disabled = false;
}
else if ((index === 0) || !input.checkValidity()) {
input.disabled = true;
}
});
}).trigger('change');
<?php // Validate the period times. ?>
$('form#edit_area').on('submit', function(event) {
if (($('[name=area_enable_periods]:checked').val() !== '1') || !$('#use_period_times').prop('checked'))
{
return;
}
let lastEndVal;
$('div.period_name').each(function(index) {
<?php // Check that the start time isn't before the last end time ?>
const start = $(this).find('input[name="period_starts[]"]').get(0);
const startVal = start.value;
if ((index > 0) && (startVal < lastEndVal))
{
start.setCustomValidity('<?php echo get_js_vocab('period_start_before_previous_end') ?>');
start.reportValidity();
event.preventDefault(); <?php // Stop form submission ?>
}
<?php // Check that the end time is after the start time ?>
const end = $(this).find('input[name="period_ends[]"]').get(0);
const endVal = end.value;
if (endVal <= startVal) {
end.setCustomValidity('<?php echo get_js_vocab('period_end_must_be_after_start') ?>');
end.reportValidity();
event.preventDefault(); <?php // Stop form submission ?>
}
lastEndVal = endVal;
});
});
<?php // Ensure the validity is cleared when the user changes the value. ?>
$('.period_times input').on('input change', function(event) {
event.target.setCustomValidity('');
});
<?php // Disable the default duration if "All day" is checked. ?>
$('input[name="area_def_duration_all_day"]').on('change', function() {
$('#area_def_duration_mins').prop('disabled', $(this).prop('checked'));
}).trigger('change');
$('input[name="area_start_first_slot"], input[name="area_res_mins"]')
.on('change', function() {
generateLastSlotSelect();
});
generateLastSlotSelect();
});