diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index fcff4f68..3703068e 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -1168,6 +1168,11 @@ "default": 2, "description": "Valid numbers are 1 and 2, changes the style of the login page and some secondary pages." }, + "showModernUIToggle": { + "type": "boolean", + "default": false, + "description": "When set to true, the user will be able to toggle between the modern and classic UI." + }, "title": { "type": "string", "default": "MeshCentral", diff --git a/public/styles/style-bootstrap.css b/public/styles/style-bootstrap.css index 3d30e5fe..bfd44b95 100644 --- a/public/styles/style-bootstrap.css +++ b/public/styles/style-bootstrap.css @@ -267,6 +267,16 @@ body { right: 3px; } +.textnewui { + color: white; + font-weight: bold; + padding-top: 5px; + cursor: pointer; + position: absolute; + right: 0; + margin-right: 10px; +} + .LogoffLinkColor { color:white; } diff --git a/public/styles/style.css b/public/styles/style.css index 4d8796cb..4df3ac40 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -266,7 +266,11 @@ body { .textnewui { color: white; font-weight: bold; - padding: 2px; + padding-top: 5px; + cursor: pointer; + position: absolute; + right: 0; + margin-right: 10px; } .LogoffLinkColor { diff --git a/sample-config-advanced.json b/sample-config-advanced.json index 936b0875..13c53654 100644 --- a/sample-config-advanced.json +++ b/sample-config-advanced.json @@ -186,6 +186,7 @@ "domains": { "": { "_siteStyle": 2, + "_showModernUIToggle": true, "title": "MyServer", "title2": "Servername", "_titlePicture": "title-sample.png", diff --git a/views/default.handlebars b/views/default.handlebars index e98b51d2..04b6f485 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -164,15 +164,8 @@

-
- - - - - -
- -
+
+ Try the new MeshCentral UI
@@ -1584,7 +1577,6 @@ var deskAspectRatio = 0; try { deskAspectRatio = parseInt(getstore('deskAspectRatio', '0')); } catch (ex) { } var uiMode = parseInt(getstore('uiMode', 1)); - // var uiViewMode = parseInt(getstore('uiViewMode', 0)); var webPageStackMenu = false; var webPageFullScreen = true; var nightMode = setNightMode(); @@ -1735,6 +1727,9 @@ }); } + // Show the modern ui switcher + QV('textnewui', ((features2 & 0x40000000) == 0) ? false : true); + // Connect to the mesh server meshserver = MeshServerCreateControl(domainUrl); meshserver.onStateChanged = onStateChanged; @@ -2196,16 +2191,19 @@ } function saveUserInterfaceMode() { - var nUiViewMode = 0; - if (Q('ui1').checked) { nUiViewMode = 1; } - putstore('uiViewMode', nUiViewMode); + var nUiViewMode = 2; + if (Q('ui1').checked) { nUiViewMode = 3; } + if (getstore('uiViewMode', 2) != nUiViewMode) { + putstore('uiViewMode', nUiViewMode); + reload(); + } } function toggleBootstrapUIMode() { if (xxdialogMode) return; - var uiViewMode = getstore('uiViewMode', '0'); - var x = '
'; - x += '
'; + var uiViewMode = getstore('uiViewMode', 2); + var x = '
'; + x += '
'; setDialogMode(2, "User Interface", 3, saveUserInterfaceMode, x); QV('uiMenu', false); } diff --git a/views/default3.handlebars b/views/default3.handlebars index bc949b72..58eb2cb5 100644 --- a/views/default3.handlebars +++ b/views/default3.handlebars @@ -153,17 +153,9 @@ - -
- - - - - -
- -
-
+
+ Try the new MeshCentral UI +
@@ -2229,6 +2221,9 @@ }); } + // Show the modern ui switcher + QV('textnewui', ((features2 & 0x40000000) == 0) ? false : true); + // Connect to the mesh server meshserver = MeshServerCreateControl(domainUrl); meshserver.onStateChanged = onStateChanged; @@ -2703,17 +2698,26 @@ } function saveUserInterfaceMode() { - var nUiViewMode = 0; - if (Q('ui1').checked) { nUiViewMode = 1; } - putstore('uiViewMode', nUiViewMode); + var nUiViewMode = 3; + if (Q('ui0').checked) { nUiViewMode = 2; } + if (getstore('uiViewMode', 3) != nUiViewMode) { + putstore('uiViewMode', nUiViewMode); + // Check if the URL includes 'sitestyle=' and remove it + var url = new URL(window.location.href); + if (url.searchParams.has('sitestyle')) { + url.searchParams.delete('sitestyle'); + window.history.replaceState({}, document.title, url.toString()); + } + reload(); + } } function toggleBootstrapUIMode() { if (xxdialogMode) return; - var uiViewMode = getstore('uiViewMode', '0'); - var x = '
'; - x += '
'; - setModalContent('xxAddAgent', 'User Interface', x); + var uiViewMode = getstore('uiViewMode', 3); + var x = '
'; + x += '
'; + setModalContent('xxAddAgent', "User Interface", x); showModal('xxAddAgentModal', 'idx_dlgOkButton', saveUserInterfaceMode); QV('uiMenu', false); } diff --git a/webserver.js b/webserver.js index bd6887c5..0e2d49eb 100644 --- a/webserver.js +++ b/webserver.js @@ -3152,8 +3152,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF if (obj.parent.config.settings && obj.parent.config.settings.webrtcconfig && (typeof obj.parent.config.settings.webrtcconfig == 'object')) { webRtcConfig = encodeURIComponent(JSON.stringify(obj.parent.config.settings.webrtcconfig)).replace(/'/g, '%27'); } else if (args.webrtcconfig && (typeof args.webrtcconfig == 'object')) { webRtcConfig = encodeURIComponent(JSON.stringify(args.webrtcconfig)).replace(/'/g, '%27'); } + // Load default page style or new modern ui + var uiViewMode = 'default'; + var webstateJSON = JSON.parse(webstate); + if (webstateJSON && webstateJSON.uiViewMode == 3) { uiViewMode = 'default3'; } + if (domain.sitestyle == 3) { uiViewMode = 'default3'; } + if (req.query.sitestyle == 3) { uiViewMode = 'default3'; } // Refresh the session - render(dbGetFunc.req, dbGetFunc.res, getRenderPage(((domain.sitestyle == 3) || (req.query.sitestyle == 3) ? 'default3' : 'default'), dbGetFunc.req, domain), getRenderArgs({ + render(dbGetFunc.req, dbGetFunc.res, getRenderPage(uiViewMode, dbGetFunc.req, domain), getRenderArgs({ authCookie: authCookie, authRelayCookie: authRelayCookie, viewmode: viewmode, @@ -3303,6 +3309,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF if (domain.scrolltotop == true) { features2 += 0x08000000; } // Show the "Scroll to top" button if (domain.devicesearchbargroupname === true) { features2 += 0x10000000; } // Search bar will find by group name too if (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.duo2factor != false)) && (typeof domain.duo2factor == 'object') && (typeof domain.duo2factor.integrationkey == 'string') && (typeof domain.duo2factor.secretkey == 'string') && (typeof domain.duo2factor.apihostname == 'string')) { features2 += 0x20000000; } // using Duo for 2FA is allowed + if (domain.showmodernuitoggle == true) { features2 += 0x40000000; } // Indicates that the new UI should be shown return { features: features, features2: features2 }; } @@ -9009,7 +9016,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF } // Filter the user web site and only output state that we need to keep - const acceptableUserWebStateStrings = ['webPageStackMenu', 'notifications', 'deviceView', 'nightMode', 'webPageFullScreen', 'search', 'showRealNames', 'sort', 'deskAspectRatio', 'viewsize', 'DeskControl', 'uiMode', 'footerBar','loctag','theme','lastThemes']; + const acceptableUserWebStateStrings = ['webPageStackMenu', 'notifications', 'deviceView', 'nightMode', 'webPageFullScreen', 'search', 'showRealNames', 'sort', 'deskAspectRatio', 'viewsize', 'DeskControl', 'uiMode', 'footerBar','loctag','theme','lastThemes','uiViewMode']; const acceptableUserWebStateDesktopStrings = ['encoding', 'showfocus', 'showmouse', 'showcad', 'limitFrameRate', 'noMouseRotate', 'quality', 'scaling', 'agentencoding'] obj.filterUserWebState = function (state) { if (typeof state == 'string') { try { state = JSON.parse(state); } catch (ex) { return null; } }