diff --git a/python/server/auto_generated/qgsserversettings.sip.in b/python/server/auto_generated/qgsserversettings.sip.in index c7863cc9ab73..27b8870cb449 100644 --- a/python/server/auto_generated/qgsserversettings.sip.in +++ b/python/server/auto_generated/qgsserversettings.sip.in @@ -39,7 +39,7 @@ Load settings according to current environment variables. %Docstring Load setting for a specific environment variable name. -:return: true if loading is successful, false in case of an invalid name. +:return: TRUE if loading is successful, FALSE in case of an invalid name. %End void logSummary() const; @@ -58,7 +58,7 @@ Returns the ini file loaded by QSetting. %Docstring Returns parallel rendering setting. -:return: true if parallel rendering is activated, false otherwise. +:return: TRUE if parallel rendering is activated, FALSE otherwise. %End int maxThreads() const; @@ -117,6 +117,24 @@ Returns the cache size. Returns the cache directory. :return: the directory. +%End + + QString overrideSystemLocale() const; +%Docstring +Overrides system locale + +:return: the optional override for system locale. + +.. versionadded:: 3.8 +%End + + bool showGroupSeparator() const; +%Docstring +Show group (thousand) separator + +:return: if group separator must be shown, default to FALSE. + +.. versionadded:: 3.8 %End }; diff --git a/src/server/qgsserver.cpp b/src/server/qgsserver.cpp index 1cfc37dfaefd..53fe2810f584 100644 --- a/src/server/qgsserver.cpp +++ b/src/server/qgsserver.cpp @@ -156,6 +156,26 @@ QString QgsServer::configPath( const QString &defaultConfigPath, const QString & return cfPath; } +void QgsServer::initLocale() +{ + // System locale override + if ( ! sSettings.overrideSystemLocale().isEmpty() ) + { + QLocale::setDefault( QLocale( sSettings.overrideSystemLocale() ) ); + } + // Number group separator settings + QLocale currentLocale; + if ( sSettings.showGroupSeparator() ) + { + currentLocale.setNumberOptions( currentLocale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator ); + } + else + { + currentLocale.setNumberOptions( currentLocale.numberOptions() |= QLocale::NumberOption::OmitGroupSeparator ); + } + QLocale::setDefault( currentLocale ); +} + bool QgsServer::init() { if ( sInitialized ) @@ -191,6 +211,9 @@ bool QgsServer::init() QgsServerLogger::instance()->setLogStderr(); } + // Configure locale + initLocale(); + // log settings currently used sSettings.logSummary(); diff --git a/src/server/qgsserver.h b/src/server/qgsserver.h index 77bb82c42135..791a53384a1d 100644 --- a/src/server/qgsserver.h +++ b/src/server/qgsserver.h @@ -144,5 +144,8 @@ class SERVER_EXPORT QgsServer //! cache QgsConfigCache *mConfigCache = nullptr; + + //! Initialize locale + static void initLocale(); }; #endif // QGSSERVER_H diff --git a/src/server/qgsserversettings.cpp b/src/server/qgsserversettings.cpp index e5590d26f74d..b0be9feb6a4e 100644 --- a/src/server/qgsserversettings.cpp +++ b/src/server/qgsserversettings.cpp @@ -139,6 +139,29 @@ void QgsServerSettings::initSettings() QVariant() }; mSettings[ sCacheSize.envVar ] = sCacheSize; + + // system locale override + const Setting sOverrideSystemLocale = { QgsServerSettingsEnv::QGIS_SERVER_OVERRIDE_SYSTEM_LOCALE, + QgsServerSettingsEnv::DEFAULT_VALUE, + QStringLiteral( "Override system locale" ), + QStringLiteral( "/locale/userLocale" ), + QVariant::String, + QVariant( "" ), + QVariant() + }; + mSettings[ sOverrideSystemLocale.envVar ] = sOverrideSystemLocale; + + // show group separator + const Setting sShowGroupSeparator = { QgsServerSettingsEnv::QGIS_SERVER_SHOW_GROUP_SEPARATOR, + QgsServerSettingsEnv::DEFAULT_VALUE, + QStringLiteral( "Show group (thousands) separator" ), + QStringLiteral( "/locale/showGroupSeparator" ), + QVariant::String, + QVariant( false ), + QVariant() + }; + mSettings[ sShowGroupSeparator.envVar ] = sShowGroupSeparator; + } void QgsServerSettings::load() @@ -319,3 +342,14 @@ QString QgsServerSettings::cacheDirectory() const { return value( QgsServerSettingsEnv::QGIS_SERVER_CACHE_DIRECTORY ).toString(); } + +QString QgsServerSettings::overrideSystemLocale() const +{ + return value( QgsServerSettingsEnv::QGIS_SERVER_OVERRIDE_SYSTEM_LOCALE ).toString(); +} + +bool QgsServerSettings::showGroupSeparator() const +{ + return value( QgsServerSettingsEnv::QGIS_SERVER_SHOW_GROUP_SEPARATOR ).toBool(); +} + diff --git a/src/server/qgsserversettings.h b/src/server/qgsserversettings.h index 6008938c31a6..fd9cfd9e2f77 100644 --- a/src/server/qgsserversettings.h +++ b/src/server/qgsserversettings.h @@ -60,7 +60,9 @@ class SERVER_EXPORT QgsServerSettingsEnv : public QObject QGIS_PROJECT_FILE, MAX_CACHE_LAYERS, QGIS_SERVER_CACHE_DIRECTORY, - QGIS_SERVER_CACHE_SIZE + QGIS_SERVER_CACHE_SIZE, + QGIS_SERVER_SHOW_GROUP_SEPARATOR, //! Show group (thousands) separator when formatting numeric values, defaults to FALSE (since QGIS 3.8) + QGIS_SERVER_OVERRIDE_SYSTEM_LOCALE, //! Override system locale (since QGIS 3.8) }; Q_ENUM( EnvVar ) }; @@ -88,41 +90,41 @@ class SERVER_EXPORT QgsServerSettings /** * Constructor. - */ + */ QgsServerSettings(); /** * Load settings according to current environment variables. - */ + */ void load(); /** * Load setting for a specific environment variable name. - * \returns true if loading is successful, false in case of an invalid name. - */ + * \returns TRUE if loading is successful, FALSE in case of an invalid name. + */ bool load( const QString &envVarName ); /** * Log a summary of settings currently loaded. - */ + */ void logSummary() const; /** * Returns the ini file loaded by QSetting. - * \returns the path of the ini file or an empty string if none is loaded. - */ + * \returns the path of the ini file or an empty string if none is loaded. + */ QString iniFile() const; /** * Returns parallel rendering setting. - * \returns true if parallel rendering is activated, false otherwise. - */ + * \returns TRUE if parallel rendering is activated, FALSE otherwise. + */ bool parallelRendering() const; /** * Returns the maximum number of threads to use. - * \returns the number of threads. - */ + * \returns the number of threads. + */ int maxThreads() const; /** @@ -133,20 +135,20 @@ class SERVER_EXPORT QgsServerSettings /** * Returns the log level. - * \returns the log level. - */ + * \returns the log level. + */ Qgis::MessageLevel logLevel() const; /** * Returns the QGS project file to use. - * \returns the path of the QGS project or an empty string if none is defined. - */ + * \returns the path of the QGS project or an empty string if none is defined. + */ QString projectFile() const; /** * Returns the log file. - * \returns the path of the log file or an empty string if none is defined. - */ + * \returns the path of the log file or an empty string if none is defined. + */ QString logFile() const; /** @@ -158,16 +160,30 @@ class SERVER_EXPORT QgsServerSettings /** * Returns the cache size. - * \returns the cache size. - */ + * \returns the cache size. + */ qint64 cacheSize() const; /** * Returns the cache directory. - * \returns the directory. - */ + * \returns the directory. + */ QString cacheDirectory() const; + /** + * Overrides system locale + * \returns the optional override for system locale. + * \since QGIS 3.8 + */ + QString overrideSystemLocale() const; + + /** + * Show group (thousand) separator + * \returns if group separator must be shown, default to FALSE. + * \since QGIS 3.8 + */ + bool showGroupSeparator() const; + private: void initSettings(); QVariant value( QgsServerSettingsEnv::EnvVar envVar ) const; diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index b5bede31d1bb..f00bb8a10863 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -295,6 +295,7 @@ IF (WITH_SERVER) ADD_PYTHON_TEST(PyQgsServerWMTS test_qgsserver_wmts.py) ADD_PYTHON_TEST(PyQgsServerWFS test_qgsserver_wfs.py) ADD_PYTHON_TEST(PyQgsServerWFST test_qgsserver_wfst.py) + ADD_PYTHON_TEST(PyQgsServerLocaleOverride test_qgsserver_locale_override.py) ADD_PYTHON_TEST(PyQgsOfflineEditingWFS test_offline_editing_wfs.py) ADD_PYTHON_TEST(PyQgsAuthManagerPasswordOWSTest test_authmanager_password_ows.py) ADD_PYTHON_TEST(PyQgsAuthManagerPKIOWSTest test_authmanager_pki_ows.py) diff --git a/tests/src/python/test_qgsserver_locale_override.py b/tests/src/python/test_qgsserver_locale_override.py new file mode 100644 index 000000000000..6d186459e232 --- /dev/null +++ b/tests/src/python/test_qgsserver_locale_override.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsServer Locale Override Options. + +From build dir, run: ctest -R PyQgsServerLocaleOverride -V + +.. note:: This test needs env vars to be set before the server is + configured for the first time, for this + reason it cannot run as a test case of another server + test. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +""" +__author__ = 'Alessandro Pasotti' +__date__ = '01/04/2019' +__copyright__ = 'Copyright 2019, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import os + +# Needed on Qt 5 so that the serialization of XML is consistent among all +# executions +os.environ['QT_HASH_SEED'] = '1' + +from utilities import ( + unitTestDataPath, +) +from qgis.testing import unittest + +from test_qgsserver_wms import TestQgsServerWMSTestBase +from qgis.core import QgsProject, QgsFontUtils +from qgis.server import QgsServer + + +class TestQgsServerWMSLocaleOverride(TestQgsServerWMSTestBase): + """QGIS Server WMS Tests for GetFeatureInfo request""" + + # Set to True to re-generate reference files for this class + regenerate_reference = False + + def setUp(self): + """Create the server instance""" + self.fontFamily = QgsFontUtils.standardTestFontFamily() + QgsFontUtils.loadStandardTestFonts(['All']) + + self.testdata_path = unitTestDataPath('qgis_server') + '/' + + d = unitTestDataPath('qgis_server') + '/' + self.projectPath = os.path.join(d, "project.qgs") + + # Clean env just to be sure + env_vars = ['QUERY_STRING', 'QGIS_PROJECT_FILE'] + for ev in env_vars: + try: + del os.environ[ev] + except KeyError: + pass + + os.environ['QGIS_SERVER_OVERRIDE_SYSTEM_LOCALE'] = 'EN_us' + os.environ['QGIS_SERVER_SHOW_GROUP_SEPARATOR'] = '0' + + self.server = QgsServer() + + def testGetFeatureInfoThousandSeparator(self): + + self.wms_request_compare('GetFeatureInfo', + '&layers=testlayer_thousands&styles=&' + + 'info_format=text%2Fxml&transparent=true&' + + 'width=600&height=400&srs=EPSG%3A3857&bbox=913190.6389747962%2C' + + '5606005.488876367%2C913235.426296057%2C5606035.347090538&' + + 'query_layers=testlayer_thousands&X=190&Y=320', + 'wms_getfeatureinfo-thousands-text-xml', + project='test_project_gfi_thousands.qgs',) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/testdata/qgis_server/test_project_gfi_thousands.qgs b/tests/testdata/qgis_server/test_project_gfi_thousands.qgs new file mode 100644 index 000000000000..9512d78666b2 --- /dev/null +++ b/tests/testdata/qgis_server/test_project_gfi_thousands.qgs @@ -0,0 +1,643 @@ + + + + QGIS Test Project + + + + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + testlayer_thousands_50dfd72a_7b73_4987_8bad_4de2936b2f54 + + + + + + + + + + degrees + + 17.92067782886099891 + 30.16118840528694989 + 18.04813119270758293 + 30.27189237230483343 + + 0 + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + 0 + + + + + + + + + + + + 8.20345930703634352 + 44.90139483904469131 + 8.20354699399348775 + 44.90148252600183554 + + testlayer_thousands_50dfd72a_7b73_4987_8bad_4de2936b2f54 + ./testlayer_thousands.shp|layername=testlayer_thousands + + + + testlayer_thousands + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + ogr + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "id" + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + "id" + + + + + + + + 90 + 4 + + + + Alessandro Pasotti + QGIS TestProject + + QGIS dev team + + + 255 + 255 + 255 + 0 + 255 + 255 + 255 + + + + false + + + + + + m2 + meters + + + + + + + + current_layer + + + 2 + + off + + 0 + + + + 8 + + true + + false + true + + false + + 1 + 3452 + +proj=longlat +datum=WGS84 +no_defs + EPSG:4326 + + + 2 + D + true + + + + + conditions unknown + + false + Some UTF8 text èòù + + 8.20315414376310059 + 44.901236559338642 + 8.204164917965862 + 44.90159838674664172 + + + 0 + true + true + 50 + false + false + 0 + false + 16 + 30 + + + + + testlayer20150528120452665 + + + testlayer20150528120452665 + + + testlayer20150528120452665 + + + + elpaso@itopen.it + + + testlayer_0b835118_a5d5_4255_b5dd_f42253c0a4a0 + + + + None + + testlayer20150528120452665 + + + + + true + 1 + + 255 + + + + WGS84 + + + false + + + + + + + + + + QGIS Test Project + + + + + + + + + + + + + 2000-01-01T00:00:00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/qgis_server/testlayer_thousands.dbf b/tests/testdata/qgis_server/testlayer_thousands.dbf new file mode 100644 index 000000000000..80a8419db0db Binary files /dev/null and b/tests/testdata/qgis_server/testlayer_thousands.dbf differ diff --git a/tests/testdata/qgis_server/testlayer_thousands.shp b/tests/testdata/qgis_server/testlayer_thousands.shp new file mode 100644 index 000000000000..3a2fd07db68c Binary files /dev/null and b/tests/testdata/qgis_server/testlayer_thousands.shp differ diff --git a/tests/testdata/qgis_server/testlayer_thousands.shx b/tests/testdata/qgis_server/testlayer_thousands.shx new file mode 100644 index 000000000000..4fcbb5877685 Binary files /dev/null and b/tests/testdata/qgis_server/testlayer_thousands.shx differ diff --git a/tests/testdata/qgis_server/wms_getfeatureinfo-thousands-text-xml.txt b/tests/testdata/qgis_server/wms_getfeatureinfo-thousands-text-xml.txt new file mode 100644 index 000000000000..321ae29337f9 --- /dev/null +++ b/tests/testdata/qgis_server/wms_getfeatureinfo-thousands-text-xml.txt @@ -0,0 +1,14 @@ +Content-Length: 366 +Content-Type: text/xml; charset=utf-8 + + + + + + + + + + + +