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
+383
View File
@@ -0,0 +1,383 @@
<?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
// Check whether the calendar navigation bar has wrapped, and if so add a class of
// 'wrapped' so that CSS can be used to change its styling.
?>
var checkNavWrapping = function() {
var navMainCalendar = $('nav.main_calendar');
var wrapped = false;
var lastTop;
<?php
// Remove the wrapped class before we start, because the wrapped class gives the
// element a flex-basis of 100%, which would force wrapping anyway. (We need the
// flex-basis of 100% to ensure that it takes up the whole line when wrapped and we
// don't get the next element on the same line.)
?>
navMainCalendar.removeClass('wrapped');
navMainCalendar.first().children().each(function() {
var thisTop = $(this).offset().top;
<?php
// Allow 5px of tolerance on the calculation of the top to allow for padding, border
// and margin.
?>
if ((typeof lastTop !== 'undefined') && (Math.abs(thisTop - lastTop) > 5))
{
wrapped = true;
return false;
}
lastTop = thisTop;
});
if (wrapped)
{
navMainCalendar.addClass('wrapped');
}
};
<?php
// Only show the bottom nav bar if no part of the top one is visible.
?>
var checkNav = function() {
var nav = $('nav.main_calendar');
if (nav.eq(0).visible(true))
{
nav.eq(1).hide();
}
else
{
nav.eq(1).show();
}
};
<?php
// Replace the body element with the body in response, for the page href.
?>
var replaceBody = function(response, href) {
<?php
// We get the entire page HTML returned, but we are only interested in the <body> element.
// That's because if we replace the whole HTML the browser will re-load the JavaScript and
// CSS files which is unnecessary and will also cause problems if the CSS is not loaded in
// time.
//
// Unfortunately, we can't use jQuery.replaceWith() on the body object as that doesn't work
// properly. So we have to replace the body HTML and then update the attributes for the body
// tag afterwards.
?>
var matches = response.match(/(<body[^>]*>)([^<]*(?:(?!<\/?body)<[^<]*)*)<\/body\s*>/i);
var body = $('body');
body.html(matches[2]);
$('<div' + matches[1].substring(5) + '</div>').each(function() {
$.each(this.attributes, function() {
<?php
// this.attributes is not a plain object, but an array
// of attribute nodes, which contain both the name and value
?>
if(this.specified)
{
if (this.name.substring(0, 5).toLowerCase() === 'data-')
{
<?php
// Data attributes have to be updated differently from other attributes because
// they are cached by jQuery.
?>
var value = this.value;
<?php // If the attribute looks like a JSON array, then turn it back into an array. ?>
if (value.charAt(0) === '[')
{
try {
value = JSON.parse(value);
}
catch (e) {
value = this.value;
}
}
<?php // If it looks like it should be a boolean then turn it back into one. ?>
else if (value === 'true')
{
value = true;
}
else if (value === 'false')
{
value = false;
}
body.data(this.name.substring(5), value);
}
else
{
body.attr(this.name, this.value);
}
}
});
});
<?php
// Trigger a page_ready event, because the normal document ready event
// won't be triggered when we are just replacing the html.
?>
$(document).trigger('page_ready');
<?php // and tell the server we've moved to a new page, so that it can update its records ?>
var data = {csrf_token: getCSRFToken(), page: href};
if(args.site)
{
data.site = args.site;
}
$.post('ajax/update_page.php', data);
<?php // change the URL in the address bar ?>
history.pushState(null, '', href);
};
<?php
// Update the <body> element either via an Ajax call or using a pre-fetched response,
// in order to avoid flickering of the screen as we move between pages in the calendar view.
//
// 'event' can either be an event object if the function is called from an 'on'
// handler, or else it as an href string (eg when called from flatpickr).
?>
var updateBody = function(event) {
var href;
if (typeof event === 'object')
{
href = $(this).attr('href');
event.preventDefault();
}
else
{
href = event;
}
<?php // Add a "Loading ..." message ?>
$('h2.date').text('<?php echo get_js_vocab('loading')?>')
.addClass('loading');
if (updateBody.prefetched && updateBody.prefetched[href])
{
replaceBody(updateBody.prefetched[href], href);
}
else
{
<?php
// Keep track of the last Ajax request, because it's only that one that we're
// interested in: if the server is slow and the user clicks on a succession
// of dates, we only want to show the data for the last date.
?>
updateBody.lastRequest = href;
<?php
// We don't want a refresh to happen while we're waiting for the next date.
?>
refreshPage.disabled = true;
$.get({url: href, dataType: 'html'})
.done(function(response) {
<?php // Only process this response if it corresponds to the last request ?>
if (href === updateBody.lastRequest)
{
updateBody.lastRequest = null;
refreshPage.disabled = false;
replaceBody(response, href);
}
});
}
};
<?php
// Pre-fetch the prev and next pages to improve performance. They are probably
// the two most likely pages to be required.
?>
var prefetch = function() {
<?php
// Don't pre-fetch if it's been disabled in the config
if (empty($prefetch_refresh_rate))
{
?>
return;
<?php
}
// Don't pre-fetch if we're in the process of moving to a different date (no point)
// or if we're on a metered connection (would waste bandwidth).
?>
if (updateBody.lastRequest || isMeteredConnection())
{
return;
}
var activeConnections = 0;
var delay = <?php echo $prefetch_refresh_rate?> * 1000;
var hrefs = [];
$('a.prefetch').each(function() {
var a = $(this);
<?php
// Don't waste time prefetching data for links that aren't visible, which
// they won't be if we are in kiosk mode.
?>
if (a.is(':visible'))
{
var href = a.attr('href');
if (typeof href !== 'undefined')
{
hrefs.push(href);
}
}
});
<?php // Clear any existing pre-fetched data and any timeout ?>
updateBody.prefetched = {};
clearTimeout(prefetch.timeoutId);
<?php
// Don't pre-fetch if the page is hidden. Just set another timeout
?>
if (isHidden())
{
prefetch.timeoutId = setTimeout(prefetch, delay);
return;
}
hrefs.forEach(function(href) {
activeConnections++;
$.get({url: href, dataType: 'html'})
.fail(function() {
<?php // Don't do anything if the request failed ?>
})
.done(function(response) {
updateBody.prefetched[href] = response;
activeConnections--;
<?php // Once we've got all the responses back set off another timeout ?>
if (activeConnections === 0)
{
<?php
// Only set another timeout if all the requests were successful. There's no
// point in doing so if one failed: it will probably fail again and just fill
// up the PHP error log.
?>
prefetch.timeoutId = setTimeout(prefetch, delay);
}
});
});
};
$(document).on('page_ready', function() {
<?php
// Turn the room and area selects into fancy select boxes and then
// show the location menu (it's hidden to avoid screen jiggling).
?>
$('.room_area_select').mrbsSelect();
$('nav.location').removeClass('js_hidden');
<?php
// Check the wrapping on the calendar navigation so that we can change the
// styling if it has.
?>
checkNavWrapping();
$(window).on('resize', checkNavWrapping);
<?php
// The bottom navigation was hidden while the Select2 boxes were formed
// so that the correct widths could be established. It is then shown if
// the top navigation is not visible.
?>
$('nav.main_calendar').removeClass('js_hidden');
checkNav();
$(window).on('scroll', checkNav);
$(window).on('resize', checkNav);
<?php
// Only reveal the color key once the bottom navigation has been determined,
// in order to avoid jiggling.
?>
$('.color_key').removeClass('js_hidden');
<?php
// Replace the navigation links with Ajax calls in order to eliminate flickering
// as we move between pages.
?>
$('nav.arrow a, nav.view a').on('click', updateBody);
<?php
// In kiosk mode intercept all mouse and keyboard events as (a) we don't want to
// allow links to be clicked and (b) that's the way a user exits kiosk mode
?>
if (args.kiosk)
{
<?php // We need to use a jQuery UI dialog because we can't time out a confirm box ?>
var dialog = $('<div id="dialog_exit_kiosk"></div>');
var timeout;
$(document.body).on('click keypress', function(e) {
function dialogClose() {
dialog.dialog('close');
}
e.preventDefault();
if (!dialog.dialog('instance'))
{
dialog.dialog({
buttons: [
{text: "<?php echo get_js_vocab('ok')?>",
click: function() {
var href = 'kiosk.php?kiosk=' + encodeURIComponent(args.kiosk);
href += '&area=' + encodeURIComponent(args.area);
href += '&room=' + encodeURIComponent(args.room);
if (args.site)
{
href += '&site=' + encodeURIComponent(args.site);
}
$.redirect(href, {'csrf_token': getCSRFToken()});
}
},
{text: "<?php echo get_js_vocab('cancel')?>",
click: function() {
dialog.dialog('close');
}
}
],
close: function(event, ui) {
<?php // Clear the timeout so that a dialog being reopened gets a new timeout ?>
clearTimeout(timeout);
},
closeText: "<?php echo get_js_vocab('close')?>",
modal: true,
open: function(event, ui){
timeout = setTimeout(dialogClose, <?php echo $kiosk_exit_dialog_timeout; ?> *1000);
},
title: "<?php echo get_js_vocab('exit_kiosk_mode_confirm')?>"
});
}
<?php // Don't open a dialog if the event came from one ?>
else if (!dialog.dialog('isOpen') && !$(e.target).parents('[role="dialog"]').length)
{
dialog.dialog('open');
}
return false;
});
}
<?php
// Pre-fetch some pages to improve performance
?>
prefetch();
});