Merge branch 'master' into dockerrewrite

This commit is contained in:
DaanSelen 2025-05-19 11:32:18 +02:00 committed by GitHub
commit 6eacc9f312
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 124 additions and 32 deletions

View File

@ -2314,11 +2314,11 @@ function terminal_end()
function terminal_consent_ask(ws) {
ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = currentTranslation['terminalConsent'].replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username);
var consentMessage = currentTranslation['terminalConsent'].replaceAll('{0}', ws.httprequest.realname).replaceAll('{1}', ws.httprequest.username);
var consentTitle = 'MeshCentral';
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
if (ws.httprequest.soptions.consentMsgTerminal != null) { consentMessage = ws.httprequest.soptions.consentMsgTerminal.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
if (ws.httprequest.soptions.consentMsgTerminal != null) { consentMessage = ws.httprequest.soptions.consentMsgTerminal.replaceAll('{0}', ws.httprequest.realname).replaceAll('{1}', ws.httprequest.username); }
}
if (process.platform == 'win32') {
var enhanced = false;
@ -2417,12 +2417,12 @@ function terminal_promise_connection_resolved(term)
if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
{
// User Notifications is required
var notifyMessage = currentTranslation['terminalNotify'].replace('{0}', this.ws.httprequest.realname ? this.ws.httprequest.realname : this.ws.httprequest.username);
var notifyMessage = currentTranslation['terminalNotify'].replaceAll('{0}', this.ws.httprequest.realname ? this.ws.httprequest.realname : this.ws.httprequest.username);
var notifyTitle = "MeshCentral";
if (this.ws.httprequest.soptions != null)
{
if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replaceAll('{0}', this.ws.httprequest.realname).replaceAll('{1}', this.ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
}
@ -2646,7 +2646,7 @@ function tunnel_kvm_end()
this.httprequest.desktop.kvm.users.splice(i, 1);
this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
this.httprequest.desktop.kvm.connectionBar.close();
this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replaceAll('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replaceAll('{1}', this.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
this.httprequest.desktop.kvm.connectionBar.on('close', function ()
{
@ -2682,11 +2682,11 @@ function kvm_consent_ok(ws) {
if (ws.httprequest.consent && (ws.httprequest.consent & 1)){
// User Notifications is required
MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
var notifyMessage = currentTranslation['desktopNotify'].replace('{0}', ws.httprequest.realname);
var notifyMessage = currentTranslation['desktopNotify'].replaceAll('{0}', ws.httprequest.realname);
var notifyTitle = "MeshCentral";
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.notifyTitle != null) { notifyTitle = ws.httprequest.soptions.notifyTitle; }
if (ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
if (ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = ws.httprequest.soptions.notifyMsgDesktop.replaceAll('{0}', ws.httprequest.realname).replaceAll('{1}', ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage, ws.tsid); } catch (ex) { }
} else {
@ -2699,7 +2699,7 @@ function kvm_consent_ok(ws) {
ws.httprequest.desktop.kvm.connectionBar.close();
}
try {
ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(ws.httprequest.privacybartext.replace('{0}', ws.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', ws.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(ws.httprequest.privacybartext.replaceAll('{0}', ws.httprequest.desktop.kvm.rusers.join(', ')).replaceAll('{1}', ws.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
} catch (ex) {
MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or not Supported (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
@ -2733,11 +2733,11 @@ function kvm_consent_ok(ws) {
function kvm_consent_ask(ws){
// Send a console message back using the console channel, "\n" is supported.
ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = currentTranslation['desktopConsent'].replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username);
var consentMessage = currentTranslation['desktopConsent'].replaceAll('{0}', ws.httprequest.realname).replaceAll('{1}', ws.httprequest.username);
var consentTitle = 'MeshCentral';
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
if (ws.httprequest.soptions.consentMsgDesktop != null) { consentMessage = ws.httprequest.soptions.consentMsgDesktop.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
if (ws.httprequest.soptions.consentMsgDesktop != null) { consentMessage = ws.httprequest.soptions.consentMsgDesktop.replaceAll('{0}', ws.httprequest.realname).replaceAll('{1}', ws.httprequest.username); }
}
var pr;
if (process.platform == 'win32') {
@ -2794,12 +2794,12 @@ function kvm_consentpromise_resolved(always)
if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1))
{
// User Notifications is required
var notifyMessage = currentTranslation['desktopNotify'].replace('{0}', this.ws.httprequest.realname);
var notifyMessage = currentTranslation['desktopNotify'].replaceAll('{0}', this.ws.httprequest.realname);
var notifyTitle = "MeshCentral";
if (this.ws.httprequest.soptions != null)
{
if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replaceAll('{0}', this.ws.httprequest.realname).replaceAll('{1}', this.ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (ex) { }
}
@ -2813,7 +2813,7 @@ function kvm_consentpromise_resolved(always)
}
try
{
this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.ws.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replaceAll('{0}', this.ws.httprequest.desktop.kvm.rusers.join(', ')).replaceAll('{1}', this.ws.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
} catch (ex)
{
@ -2856,11 +2856,11 @@ function files_consent_ok(ws){
if (ws.httprequest.consent && (ws.httprequest.consent & 4)) {
// User Notifications is required
MeshServerLogEx(42, null, "Started remote files with toast notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
var notifyMessage = currentTranslation['fileNotify'].replace('{0}', ws.httprequest.realname);
var notifyMessage = currentTranslation['fileNotify'].replaceAll('{0}', ws.httprequest.realname);
var notifyTitle = "MeshCentral";
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.notifyTitle != null) { notifyTitle = ws.httprequest.soptions.notifyTitle; }
if (ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = ws.httprequest.soptions.notifyMsgFiles.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
if (ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = ws.httprequest.soptions.notifyMsgFiles.replaceAll('{0}', ws.httprequest.realname).replaceAll('{1}', ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
} else {
@ -2872,12 +2872,12 @@ function files_consent_ok(ws){
function files_consent_ask(ws){
// Send a console message back using the console channel, "\n" is supported.
ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = currentTranslation['fileConsent'].replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username);
var consentMessage = currentTranslation['fileConsent'].replaceAll('{0}', ws.httprequest.realname).replaceAll('{1}', ws.httprequest.username);
var consentTitle = 'MeshCentral';
if (ws.httprequest.soptions != null) {
if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
if (ws.httprequest.soptions.consentMsgFiles != null) { consentMessage = ws.httprequest.soptions.consentMsgFiles.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
if (ws.httprequest.soptions.consentMsgFiles != null) { consentMessage = ws.httprequest.soptions.consentMsgFiles.replaceAll('{0}', ws.httprequest.realname).replaceAll('{1}', ws.httprequest.username); }
}
var pr;
if (process.platform == 'win32') {
@ -2923,12 +2923,12 @@ function files_consentpromise_resolved(always)
if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4))
{
// User Notifications is required
var notifyMessage = currentTranslation['fileNotify'].replace('{0}', this.ws.httprequest.realname);
var notifyMessage = currentTranslation['fileNotify'].replaceAll('{0}', this.ws.httprequest.realname);
var notifyTitle = "MeshCentral";
if (this.ws.httprequest.soptions != null)
{
if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replaceAll('{0}', this.ws.httprequest.realname).replaceAll('{1}', this.ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
}

View File

@ -2354,7 +2354,8 @@ module.exports.CreateAmtManager = function (parent) {
const dev = stack.dev;
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
const domain = parent.config.domains[dev.domainid];
if ((responses['AMT_PublicKeyCertificate'].status != 200) || (responses['AMT_PublicKeyCertificate'].status != 200)) { func(dev); return; } // We can't get the certificate list, fail and carry on.
if ((responses['AMT_PublicKeyCertificate'].status != 200) || (responses['AMT_PublicPrivateKeyPair'].status != 200)) { func(dev); return; } // We can't get the certificate list, fail and carry on.
if ((responses['AMT_PublicKeyCertificate'].responses.length == 0) || (responses['AMT_PublicPrivateKeyPair'].responses.length == 0)) { func(dev); return; } // Empty certificate list, fail and carry on.
// Sort out the certificates
var xxCertificates = responses['AMT_PublicKeyCertificate'].responses;

View File

@ -1000,6 +1000,7 @@ module.exports.CertificateOperations = function (parent) {
var organization = null;
var forceWebCertGen = 0;
var forceMpsCertGen = 0;
var forceCodeCertGen = 0;
if (certargs != undefined) {
var xargs = certargs.split(',');
if (xargs.length > 0) { commonName = xargs[0]; }
@ -1025,6 +1026,7 @@ module.exports.CertificateOperations = function (parent) {
r.CommonName = obj.getCertificateCommonName(r.web.cert);
r.CommonNames = obj.getCertificateAltNames(r.web.cert);
r.RootName = obj.getCertificateCommonName(r.root.cert);
r.CodeCertName = obj.getCertificateCommonName(r.codesign.cert);
// If the "cert" name is not set, try to use the certificate CN instead (ok if the certificate is not wildcard).
if (commonName == 'un-configured') {
@ -1085,10 +1087,11 @@ module.exports.CertificateOperations = function (parent) {
// Check if we have correct certificates.
if (obj.compareCertificateNames(r.CommonNames, commonName) == false) { console.log("Error: " + commonName + " does not match name in TLS certificate: " + r.CommonNames.join(', ')); forceWebCertGen = 1; } else { r.CommonName = commonName; }
if (r.AmtMpsName != mpsCommonName) { forceMpsCertGen = 1; }
if (args.keepcerts == true) { forceWebCertGen = 0; forceMpsCertGen = 0; r.CommonName = commonName; }
if (r.CodeCertName.startsWith(commonName) === false) { forceCodeCertGen = 1; }
if (args.keepcerts == true) { forceWebCertGen = 0; forceMpsCertGen = 0; forceCodeCertGen = 0; r.CommonName = commonName; }
// If the certificates matches what we want, use them.
if ((forceWebCertGen == 0) && (forceMpsCertGen == 0)) {
if ((forceWebCertGen == 0) && (forceMpsCertGen == 0) && (forceCodeCertGen == 0)) {
if (func !== null) { func(r); }
return r;
}
@ -1185,7 +1188,7 @@ module.exports.CertificateOperations = function (parent) {
// If the code signing certificate does not exist, create one
var codesignCertAndKey, codesignCertificate, codesignPrivateKey;
if (r.codesign == null) {
if ((r.codesign == null) || (forceCodeCertGen === 1)) {
console.log("Generating code signing certificate...");
codesignCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, true, commonName, country, organization, { codeSign: true }, strongCertificate);
codesignCertificate = obj.pki.certificateToPem(codesignCertAndKey.cert);

View File

@ -34,6 +34,7 @@ RUN cd meshcentral \
&& npm uninstall html-minifier jsdom esprima
# cleanup for inter-container copying.
RUN rm -rf /opt/meshcentral/meshcentral/docker
RUN rm -rf /opt/meshcentral/meshcentral/node_modules
@ -167,6 +168,7 @@ RUN cd meshcentral && npm install
# Expose only 443 by default to reduce attack surface. (Only encrypted ports).
EXPOSE 443
# These volumes will be created by default even without any declaration, this allows default persistence in Docker/Podman.
VOLUME /opt/meshcentral/meshcentral-data
VOLUME /opt/meshcentral/meshcentral-files

View File

@ -4103,6 +4103,8 @@ function InstallModules(modules, args, func) {
// If the module is not installed, but we get the ERR_PACKAGE_PATH_NOT_EXPORTED error, try a second way.
if ((versionMatch == false) && (modulePath != null)) {
if (JSON.parse(require('fs').readFileSync(modulePath, 'utf8')).version != moduleVersion) { throw new Error(); }
} else if (versionMatch == false) {
throw new Error();
}
} else {
// For all other modules, do the check here.
@ -4292,12 +4294,12 @@ function mainStart() {
if (passport != null) { modules.push(...passport); }
if (captcha == true) { modules.push('svg-captcha@1.4.0'); }
if (sessionRecording == true) { modules.push('image-size@1.1.1'); } // Need to get the remote desktop JPEG sizes to index the recodring file.
if (sessionRecording == true) { modules.push('image-size@1.2.1'); } // Need to get the remote desktop JPEG sizes to index the recodring file.
if (config.letsencrypt != null) { modules.push('acme-client@4.2.5'); } // Add acme-client module. We need to force v4.2.4 or higher since olver versions using SHA-1 which is no longer supported by Let's Encrypt.
if (config.settings.mqtt != null) { modules.push('aedes@0.39.0'); } // Add MQTT Modules
if (config.settings.mysql != null) { modules.push('mysql2@3.11.4'); } // Add MySQL.
//if (config.settings.mysql != null) { modules.push('@mysql/xdevapi@8.0.33'); } // Add MySQL, official driver (https://dev.mysql.com/doc/dev/connector-nodejs/8.0/)
if (config.settings.mongodb != null) { modules.push('mongodb@4.13.0'); modules.push('saslprep@1.0.3'); } // Add MongoDB, official driver.
if (config.settings.mongodb != null) { modules.push('mongodb@4.17.2'); } // Add MongoDB, official driver. 4.17.0 and above now includes saslprep by default https://github.com/mongodb/node-mongodb-native/releases/tag/v4.17.0
if (config.settings.postgres != null) { modules.push('pg@8.14.1') } // Add Postgres, official driver.
if (config.settings.mariadb != null) { modules.push('mariadb@3.4.0'); } // Add MariaDB, official driver.
if (config.settings.acebase != null) { modules.push('acebase@1.29.5'); } // Add AceBase, official driver.
@ -4336,7 +4338,7 @@ function mainStart() {
}
// Desktop multiplexor support
if (config.settings.desktopmultiplex === true) { modules.push('image-size@1.1.1'); }
if (config.settings.desktopmultiplex === true) { modules.push('image-size@1.2.1'); }
// SMS support
if (config.sms != null) {

10
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "meshcentral",
"version": "1.1.42",
"version": "1.1.44",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "meshcentral",
"version": "1.1.42",
"version": "1.1.44",
"license": "Apache-2.0",
"dependencies": {
"@seald-io/nedb": "4.0.4",
@ -238,9 +238,9 @@
"license": "MIT"
},
"node_modules/bignumber.js": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz",
"integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==",
"license": "MIT",
"engines": {
"node": "*"

View File

@ -10260,8 +10260,27 @@
]
},
{
"bs": "Sve teme",
"cs": "Všechna témata",
"da": "Alle temaer",
"de": "Alle Themes",
"en": "All Themes",
"es": "Todos los temas",
"fi": "Kaikki teemat",
"fr": "Tous les thèmes",
"hi": "सभी थीम",
"it": "Tutti i temi",
"ja": "すべてのテーマ",
"ko": "모든 테마",
"nl": "Alle thema's",
"pl": "Wszystkie tematy",
"pt": "Todos os temas",
"pt-br": "Todos os temas",
"ru": "Все темы",
"sv": "Alla teman",
"tr": "Tüm Temalar",
"zh-chs": "所有主题",
"zh-cht": "所有主題",
"xloc": [
"default3.handlebars->35->2105"
]
@ -65288,7 +65307,27 @@
]
},
{
"bs": "Nedavne teme",
"cs": "Nedávná témata",
"da": "Seneste temaer",
"de": "Neueste Themes",
"en": "Recent Themes",
"es": "Temas recientes",
"fi": "Viimeaikaiset teemat",
"fr": "Thèmes récents",
"hi": "हाल के विषय",
"it": "Temi recenti",
"ja": "最近のテーマ",
"ko": "최근 테마",
"nl": "Recente thema's",
"pl": "Ostatnie tematy",
"pt": "Temas recentes",
"pt-br": "Temas recentes",
"ru": "Последние темы",
"sv": "Nya teman",
"tr": "Son temalar",
"zh-chs": "最近的主题",
"zh-cht": "最近的主題",
"xloc": [
"default3.handlebars->35->2104"
]
@ -73030,7 +73069,27 @@
]
},
{
"bs": "Odaberite temu",
"cs": "Vyberte téma",
"da": "Vælg et tema",
"de": "Wählen Sie ein Theme",
"en": "Select a theme",
"es": "Seleccione un tema",
"fi": "Valitse teema",
"fr": "Sélectionnez un thème",
"hi": "एक विषय का चयन करें",
"it": "Seleziona un tema",
"ja": "テーマを選択します",
"ko": "테마를 선택하십시오",
"nl": "Selecteer een thema",
"pl": "Wybierz motyw",
"pt": "Selecione um tema",
"pt-br": "Selecione um tema",
"ru": "Выберите тему",
"sv": "Välj ett tema",
"tr": "Bir Tema Seçin",
"zh-chs": "选择主题",
"zh-cht": "選擇主題",
"xloc": [
"default3.handlebars->35->2103"
]
@ -80869,7 +80928,30 @@
]
},
{
"bs": "Promijeni temu",
"ca": "Canvia el tema",
"cs": "Přepnout téma",
"da": "Skift tema",
"de": "Theme ändern",
"en": "Switch theme",
"es": "Cambiar tema",
"fi": "Vaihda teema",
"fr": "Changer le thème",
"hi": "थीम बदलें",
"hu": "Téma váltás",
"it": "Cambia tema",
"ja": "テーマを変更",
"ko": "테마 변경",
"nl": "Thema wijzigen",
"pl": "Zmień motyw",
"pt": "Mudar tema",
"pt-br": "Mudar tema",
"ru": "Сменить тему",
"sv": "Byt tema",
"tr": "Tema değiştir",
"uk": "Змінити тему",
"zh-chs": "切换主题",
"zh-cht": "切換主題",
"xloc": [
"default3.handlebars->35->2106",
"default3.handlebars->container->column_l->p2->p2info->p2AccountActions->3->19"

View File

@ -500,7 +500,7 @@
else if (navigator.userAgent.indexOf('Windows') >= 0) { openTab(null, 'wintab32'); }
else if (navigator.userAgent.indexOf('Linux') >= 0) { openTab(null, 'linuxtab'); }
else if (navigator.userAgent.indexOf('Macintosh') >= 0) { openTab(null, 'macostab'); }
else if (navigator.userAgent.indexOf('Android') >= 0) { openTab(null, 'mobiltab'); }
else if (navigator.userAgent.indexOf('Android') >= 0) { openTab(null, 'androtab'); }
else { openTab(null, 'wintab64'); }
}

View File

@ -9478,6 +9478,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
var fileOptions = obj.renderPages[domain.id][obj.path.basename(filename)];
if (fileOptions != null) {
for (var i in acceptLanguages) {
if (acceptLanguages[i] == 'zh-tw') { acceptLanguages[i] = 'zh-cht'; } // Change newer "zh-tw" to legacy "zh-cht" Chinese (Traditional) for now
if (acceptLanguages[i] == 'zh-cn') { acceptLanguages[i] = 'zh-chs'; } // Change newer "zh-ch" to legacy "zh-chs" Chinese (Simplified) for now
if ((acceptLanguages[i] == 'en') || (acceptLanguages[i].startsWith('en-'))) {
// English requested
args.lang = 'en';