summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan de Groot <groot@kde.org>2019-05-10 15:07:20 -0400
committerAdriaan de Groot <groot@kde.org>2019-05-10 15:07:20 -0400
commitc3754126d0c200b26c1b55d7afa95c16e2cab265 (patch)
tree218215f79ec225e5f229e64a6bf429589a77e3e6
parentd194670625d09b5f0e62b9b8202d2bf06a5a568b (diff)
parentf18f9dcd14c043b95abfbd4e4319611b66aa9223 (diff)
Merge branch 'shuffle-geoip'
-rw-r--r--CHANGES5
-rw-r--r--src/libcalamares/CMakeLists.txt84
-rw-r--r--src/libcalamares/geoip/GeoIPJSON.cpp (renamed from src/modules/locale/GeoIPJSON.cpp)29
-rw-r--r--src/libcalamares/geoip/GeoIPJSON.h (renamed from src/modules/locale/GeoIPJSON.h)18
-rw-r--r--src/libcalamares/geoip/GeoIPTests.cpp (renamed from src/modules/locale/GeoIPTests.cpp)35
-rw-r--r--src/libcalamares/geoip/GeoIPTests.h (renamed from src/modules/locale/GeoIPTests.h)0
-rw-r--r--src/libcalamares/geoip/GeoIPXML.cpp (renamed from src/modules/locale/GeoIPXML.cpp)54
-rw-r--r--src/libcalamares/geoip/GeoIPXML.h (renamed from src/modules/locale/GeoIPXML.h)18
-rw-r--r--src/libcalamares/geoip/Handler.cpp183
-rw-r--r--src/libcalamares/geoip/Handler.h91
-rw-r--r--src/libcalamares/geoip/Interface.cpp (renamed from src/modules/locale/GeoIP.cpp)19
-rw-r--r--src/libcalamares/geoip/Interface.h98
-rw-r--r--src/libcalamares/geoip/test_geoip.cpp (renamed from src/modules/locale/test_geoip.cpp)7
-rw-r--r--src/libcalamares/locale/Label.cpp (renamed from src/libcalamares/utils/LocaleLabel.cpp)16
-rw-r--r--src/libcalamares/locale/Label.h (renamed from src/libcalamares/utils/LocaleLabel.h)29
-rw-r--r--src/libcalamares/locale/LabelModel.cpp (renamed from src/modules/welcome/LocaleModel.cpp)55
-rw-r--r--src/libcalamares/locale/LabelModel.h (renamed from src/modules/welcome/LocaleModel.h)49
-rw-r--r--src/libcalamares/locale/Lookup.cpp8
-rw-r--r--src/libcalamares/locale/Lookup.h9
-rw-r--r--src/modules/locale/CMakeLists.txt30
-rw-r--r--src/modules/locale/GeoIP.h70
-rw-r--r--src/modules/locale/LocalePage.cpp8
-rw-r--r--src/modules/locale/LocaleViewStep.cpp111
-rw-r--r--src/modules/locale/LocaleViewStep.h12
-rw-r--r--src/modules/locale/locale.conf82
-rw-r--r--src/modules/welcome/CMakeLists.txt1
-rw-r--r--src/modules/welcome/WelcomePage.cpp24
-rw-r--r--src/modules/welcome/WelcomePage.h20
-rw-r--r--src/modules/welcome/WelcomeViewStep.cpp69
-rw-r--r--src/modules/welcome/WelcomeViewStep.h7
-rw-r--r--src/modules/welcome/welcome.conf13
31 files changed, 874 insertions, 380 deletions
diff --git a/CHANGES b/CHANGES
index b038e111c..cb363364f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -35,6 +35,11 @@ This release contains contributions from (alphabetically by first name):
- *finished* has a new mechanism for configuring the behavior of the
*restart now* button. The old-style boolean configuration is still
supported but generates a warning. #1138
+ - *locale* module GeoIP configuration has a new preferred format.
+ See `locale.conf` for details. The old configuration is still
+ supported but will be phased out before 3.3.0 -- in particular,
+ support for "legacy" format will be removed, since that was a
+ crutch for the disappearance of one GeoIP provider in 2018.
- *oemid* is a new module for configuring OEM phase-0 (image pre-mastering,
or pre-deployment) things. It has limited functionality at the moment,
writing only a single batch-identifier file. #943
diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt
index c65fb6d39..959b0a9db 100644
--- a/src/libcalamares/CMakeLists.txt
+++ b/src/libcalamares/CMakeLists.txt
@@ -13,6 +13,9 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/CalamaresConfig.h.in
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/../calamares/CalamaresVersion.h.in
${CMAKE_CURRENT_BINARY_DIR}/CalamaresVersion.h )
+set( OPTIONAL_PRIVATE_LIBRARIES "" )
+set( OPTIONAL_PUBLIC_LIBRARIES "" )
+
set( libSources
CppJob.cpp
GlobalStorage.cpp
@@ -21,10 +24,17 @@ set( libSources
JobQueue.cpp
ProcessJob.cpp
Settings.cpp
-
+
+ # GeoIP services
+ geoip/Interface.cpp
+ geoip/GeoIPJSON.cpp
+ geoip/Handler.cpp
+
# Locale-data service
+ locale/Label.cpp
+ locale/LabelModel.cpp
locale/Lookup.cpp
-
+
# Partition service
partition/PartitionSize.cpp
@@ -32,7 +42,6 @@ set( libSources
utils/CalamaresUtilsSystem.cpp
utils/CommandList.cpp
utils/Dirs.cpp
- utils/LocaleLabel.cpp
utils/Logger.cpp
utils/PluginFactory.cpp
utils/Retranslator.cpp
@@ -54,9 +63,11 @@ include_directories(
${YAMLCPP_INCLUDE_DIR}
)
+### OPTIONAL Python support
+#
+#
if( WITH_PYTHON )
- set( libSources
- ${libSources}
+ list( APPEND libSources
PythonHelper.cpp
PythonJob.cpp
PythonJobApi.cpp
@@ -71,13 +82,24 @@ if( WITH_PYTHON )
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS})
- set( OPTIONAL_PRIVATE_LIBRARIES
- ${OPTIONAL_PRIVATE_LIBRARIES}
+ list( APPEND OPTIONAL_PRIVATE_LIBRARIES
${PYTHON_LIBRARIES}
${Boost_LIBRARIES}
)
endif()
+### OPTIONAL GeoIP XML support
+#
+#
+find_package(Qt5 COMPONENTS Xml)
+if( Qt5Xml_FOUND )
+ list( APPEND libSources geoip/GeoIPXML.cpp )
+ list( APPEND OPTIONAL_PUBLIC_LIBRARIES Qt5::Network Qt5::Xml )
+endif()
+
+### LIBRARY
+#
+#
add_library( calamares SHARED ${libSources} ${kdsagSources} )
set_target_properties( calamares
PROPERTIES
@@ -92,6 +114,7 @@ target_link_libraries( calamares
LINK_PUBLIC
${YAMLCPP_LIBRARY}
Qt5::Core
+ ${OPTIONAL_PUBLIC_LIBRARIES}
)
install( TARGETS calamares
@@ -101,19 +124,6 @@ install( TARGETS calamares
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-if ( ECM_FOUND AND BUILD_TESTING )
- ecm_add_test(
- Tests.cpp
- TEST_NAME
- libcalamarestest
- LINK_LIBRARIES
- calamares
- Qt5::Core
- Qt5::Test
- )
- calamares_automoc( libcalamarestest )
-endif()
-
# Make symlink lib/calamares/libcalamares.so to lib/libcalamares.so.VERSION so
# lib/calamares can be used as module path for the Python interpreter.
install( CODE "
@@ -130,3 +140,37 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/CalamaresConfig.h DESTINATION include
install( FILES ${rootHeaders} DESTINATION include/libcalamares )
install( FILES ${kdsingleapplicationguardHeaders} DESTINATION include/libcalamares/kdsingleapplicationguard )
install( FILES ${utilsHeaders} DESTINATION include/libcalamares/utils )
+
+### TESTING
+#
+#
+if ( ECM_FOUND AND BUILD_TESTING )
+ ecm_add_test(
+ Tests.cpp
+ TEST_NAME
+ libcalamarestest
+ LINK_LIBRARIES
+ calamares
+ Qt5::Core
+ Qt5::Test
+ )
+ calamares_automoc( libcalamarestest )
+
+ ecm_add_test(
+ geoip/GeoIPTests.cpp
+ ${geoip_src}
+ TEST_NAME
+ geoiptest
+ LINK_LIBRARIES
+ calamares
+ Qt5::Test
+ ${YAMLCPP_LIBRARY}
+ )
+ calamares_automoc( geoiptest )
+endif()
+
+if( BUILD_TESTING )
+ add_executable( test_geoip geoip/test_geoip.cpp ${geoip_src} )
+ target_link_libraries( test_geoip calamares Qt5::Network ${YAMLCPP_LIBRARY} )
+ calamares_automoc( test_geoip )
+endif()
diff --git a/src/modules/locale/GeoIPJSON.cpp b/src/libcalamares/geoip/GeoIPJSON.cpp
index 8e5cc2e5c..61b9fd8d6 100644
--- a/src/modules/locale/GeoIPJSON.cpp
+++ b/src/libcalamares/geoip/GeoIPJSON.cpp
@@ -25,11 +25,20 @@
#include <QByteArray>
+namespace CalamaresUtils::GeoIP
+{
+
GeoIPJSON::GeoIPJSON(const QString& attribute)
- : GeoIP( attribute.isEmpty() ? QStringLiteral( "time_zone" ) : attribute )
+ : Interface( attribute.isEmpty() ? QStringLiteral( "time_zone" ) : attribute )
{
}
+/** @brief Indexes into a map @m by selectors @p l
+ *
+ * Each element of @p l is an index into map @m or a sub-map thereof,
+ * so that "foo.bar.baz" looks up "baz" in the sub-map "bar" of sub-map
+ * "foo" of @p m, like a regular JSON lookup would.
+ */
static QString
selectMap( const QVariantMap& m, const QStringList& l, int index)
{
@@ -48,8 +57,8 @@ selectMap( const QVariantMap& m, const QStringList& l, int index)
}
}
-GeoIP::RegionZonePair
-GeoIPJSON::processReply( const QByteArray& data )
+QString
+GeoIPJSON::rawReply( const QByteArray& data )
{
try
{
@@ -60,7 +69,7 @@ GeoIPJSON::processReply( const QByteArray& data )
var.isValid() &&
var.type() == QVariant::Map )
{
- return splitTZString( selectMap( var.toMap(), m_element.split('.'), 0 ) );
+ return selectMap( var.toMap(), m_element.split('.'), 0 );
}
else
cWarning() << "Invalid YAML data for GeoIPJSON";
@@ -70,5 +79,15 @@ GeoIPJSON::processReply( const QByteArray& data )
CalamaresUtils::explainYamlException( e, data, "GeoIP data");
}
- return qMakePair( QString(), QString() );
+ return QString();
+}
+
+GeoIP::RegionZonePair
+GeoIPJSON::processReply( const QByteArray& data )
+{
+ return splitTZString( rawReply( data ) );
}
+
+
+
+} // namespace
diff --git a/src/modules/locale/GeoIPJSON.h b/src/libcalamares/geoip/GeoIPJSON.h
index 3c08f577b..584825d70 100644
--- a/src/modules/locale/GeoIPJSON.h
+++ b/src/libcalamares/geoip/GeoIPJSON.h
@@ -1,6 +1,6 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
- * Copyright 2018, Adriaan de Groot <groot@kde.org>
+ * Copyright 2018-2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,19 +16,23 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GEOIPJSON_H
-#define GEOIPJSON_H
+#ifndef GEOIP_GEOIPJSON_H
+#define GEOIP_GEOIPJSON_H
-#include "GeoIP.h"
+#include "Interface.h"
+namespace CalamaresUtils::GeoIP
+{
/** @brief GeoIP lookup for services that return JSON.
*
* This is the original implementation of GeoIP lookup,
* (e.g. using the FreeGeoIP.net service), or similar.
*
* The data is assumed to be in JSON format with a time_zone attribute.
+ *
+ * @note This class is an implementation detail.
*/
-class GeoIPJSON : public GeoIP
+class GeoIPJSON : public Interface
{
public:
/** @brief Configure the attribute name which is selected.
@@ -38,7 +42,9 @@ public:
*/
explicit GeoIPJSON( const QString& attribute = QString() );
- virtual RegionZonePair processReply( const QByteArray& );
+ virtual RegionZonePair processReply( const QByteArray& ) override;
+ virtual QString rawReply(const QByteArray & ) override;
} ;
+} // namespace
#endif
diff --git a/src/modules/locale/GeoIPTests.cpp b/src/libcalamares/geoip/GeoIPTests.cpp
index af114611e..ec7511370 100644
--- a/src/modules/locale/GeoIPTests.cpp
+++ b/src/libcalamares/geoip/GeoIPTests.cpp
@@ -19,9 +19,10 @@
#include "GeoIPTests.h"
#include "GeoIPJSON.h"
-#ifdef HAVE_XML
+#ifdef QT_XML_LIB
#include "GeoIPXML.h"
#endif
+#include "Handler.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
@@ -31,6 +32,8 @@
QTEST_GUILESS_MAIN( GeoIPTests )
+using namespace CalamaresUtils::GeoIP;
+
GeoIPTests::GeoIPTests()
{
}
@@ -118,7 +121,7 @@ static const char xml_data_ubiquity[] =
void
GeoIPTests::testXML()
{
-#ifdef HAVE_XML
+#ifdef QT_XML_LIB
GeoIPXML handler;
auto tz = handler.processReply( xml_data_ubiquity );
@@ -133,7 +136,7 @@ GeoIPTests::testXML2()
static const char data[] =
"<Response><TimeZone>America/North Dakota/Beulah</TimeZone></Response>"; // With a space!
-#ifdef HAVE_XML
+#ifdef QT_XML_LIB
GeoIPXML handler;
auto tz = handler.processReply( data );
@@ -145,7 +148,7 @@ GeoIPTests::testXML2()
void GeoIPTests::testXMLalt()
{
-#ifdef HAVE_XML
+#ifdef QT_XML_LIB
GeoIPXML handler( "ZT" );
auto tz = handler.processReply( "<A><B/><C><ZT>Moon/Dark_side</ZT></C></A>" );
@@ -157,7 +160,7 @@ void GeoIPTests::testXMLalt()
void
GeoIPTests::testXMLbad()
{
-#ifdef HAVE_XML
+#ifdef QT_XML_LIB
GeoIPXML handler;
auto tz = handler.processReply( "{time_zone: \"Europe/Paris\"}" );
QCOMPARE( tz.first, QString() );
@@ -172,24 +175,25 @@ GeoIPTests::testXMLbad()
void GeoIPTests::testSplitTZ()
{
- auto tz = GeoIP::splitTZString( QStringLiteral("Moon/Dark_side") );
+ using namespace CalamaresUtils::GeoIP;
+ auto tz = splitTZString( QStringLiteral("Moon/Dark_side") );
QCOMPARE( tz.first, QStringLiteral("Moon") );
QCOMPARE( tz.second, QStringLiteral("Dark_side") );
// Some providers return weirdly escaped data
- tz = GeoIP::splitTZString( QStringLiteral("America\\/NewYork") );
+ tz = splitTZString( QStringLiteral("America\\/NewYork") );
QCOMPARE( tz.first, QStringLiteral("America") );
QCOMPARE( tz.second, QStringLiteral("NewYork") ); // That's not actually the zone name
// Check that bogus data fails
- tz = GeoIP::splitTZString( QString() );
+ tz = splitTZString( QString() );
QCOMPARE( tz.first, QString() );
- tz = GeoIP::splitTZString( QStringLiteral("America.NewYork") );
+ tz = splitTZString( QStringLiteral("America.NewYork") );
QCOMPARE( tz.first, QString() );
// Check that three-level is split properly and space is replaced
- tz = GeoIP::splitTZString( QStringLiteral("America/North Dakota/Beulah") );
+ tz = splitTZString( QStringLiteral("America/North Dakota/Beulah") );
QCOMPARE( tz.first, QStringLiteral("America") );
QCOMPARE( tz.second, QStringLiteral("North_Dakota/Beulah") );
}
@@ -216,14 +220,18 @@ synchronous_get( const char* urlstring )
#define CHECK_GET(t, selector, url) \
{ \
auto tz = GeoIP##t( selector ).processReply( synchronous_get( url ) ); \
+ qDebug() << tz; \
QCOMPARE( default_tz, tz ); \
+ auto tz2 = CalamaresUtils::GeoIP::Handler( ""#t, url, selector ).get(); \
+ qDebug() << tz2; \
+ QCOMPARE( default_tz, tz2 ); \
}
void GeoIPTests::testGet()
{
if ( !QProcessEnvironment::systemEnvironment().contains( QStringLiteral("TEST_HTTP_GET") ) )
{
- qDebug() << "Skipping HTTP GET tests";
+ qDebug() << "Skipping HTTP GET tests, set TEST_HTTP_GET environment variable to enable";
return;
}
@@ -241,15 +249,12 @@ void GeoIPTests::testGet()
// the TZ data is the same as the default_tz; this is fragile if the
// services don't agree on the location of where the test is run.
CHECK_GET( JSON, QString(), "https://geoip.kde.org/v1/calamares" ) // Check it's consistent
- CHECK_GET( JSON, QString(), "http://freegeoip.net/json/" ) // Original FreeGeoIP service
CHECK_GET( JSON, QStringLiteral("timezone"), "https://ipapi.co/json" ) // Different JSON
CHECK_GET( JSON, QStringLiteral("timezone"), "http://ip-api.com/json" )
- CHECK_GET( JSON, QStringLiteral("location.time_zone"), "http://geoip.nekudo.com/api/" ) // 2-level JSON
-
CHECK_GET( JSON, QStringLiteral("Location.TimeZone"), "https://geoip.kde.org/debug" ) // 2-level JSON
-#ifdef HAVE_XML
+#ifdef QT_XML_LIB
CHECK_GET( XML, QString(), "http://geoip.ubuntu.com/lookup" ) // Ubiquity's XML format
CHECK_GET( XML, QString(), "https://geoip.kde.org/v1/ubiquity" ) // Temporary KDE service
#endif
diff --git a/src/modules/locale/GeoIPTests.h b/src/libcalamares/geoip/GeoIPTests.h
index a320e3263..a320e3263 100644
--- a/src/modules/locale/GeoIPTests.h
+++ b/src/libcalamares/geoip/GeoIPTests.h
diff --git a/src/modules/locale/GeoIPXML.cpp b/src/libcalamares/geoip/GeoIPXML.cpp
index bd675c2ef..a4b9bb146 100644
--- a/src/modules/locale/GeoIPXML.cpp
+++ b/src/libcalamares/geoip/GeoIPXML.cpp
@@ -23,38 +23,68 @@
#include <QNetworkReply>
#include <QtXml/QDomDocument>
+namespace CalamaresUtils::GeoIP
+{
+
GeoIPXML::GeoIPXML( const QString& element )
- : GeoIP( element.isEmpty() ? QStringLiteral( "TimeZone" ) : element )
+ : Interface( element.isEmpty() ? QStringLiteral( "TimeZone" ) : element )
{
}
-GeoIP::RegionZonePair
-GeoIPXML::processReply( const QByteArray& data )
+static QStringList
+getElementTexts( const QByteArray& data, const QString& tag )
{
+ QStringList elements;
+
QString domError;
int errorLine, errorColumn;
QDomDocument doc;
if ( doc.setContent( data, false, &domError, &errorLine, &errorColumn ) )
{
- const auto tzElements = doc.elementsByTagName( m_element );
+ const auto tzElements = doc.elementsByTagName( tag );
cDebug() << "GeoIP found" << tzElements.length() << "elements";
for ( int it = 0; it < tzElements.length(); ++it )
{
auto e = tzElements.at(it).toElement();
- auto tz = splitTZString( e.text() );
- if ( !tz.first.isEmpty() )
- return tz;
+ auto e_text = e.text();
+ if ( !e_text.isEmpty() )
+ elements.append( e_text );
}
-
- // None of them valid
- cWarning() << "GeopIP XML had no recognizable timezone";
- return qMakePair( QString(), QString() );
}
else
{
cWarning() << "GeoIP XML data error:" << domError << "(line" << errorLine << errorColumn << ')';
}
- return qMakePair( QString(), QString() );
+ if ( elements.count() < 1 )
+ cWarning() << "GeopIP XML had no non-empty elements" << tag;
+
+ return elements;
}
+
+
+QString
+GeoIPXML::rawReply( const QByteArray& data )
+{
+ for ( const auto& e : getElementTexts( data, m_element ) )
+ if ( !e.isEmpty() )
+ return e;
+
+ return QString();
+}
+
+GeoIP::RegionZonePair
+GeoIPXML::processReply( const QByteArray& data )
+{
+ for ( const auto& e : getElementTexts( data, m_element ) )
+ {
+ auto tz = splitTZString( e );
+ if ( !tz.first.isEmpty() )
+ return tz;
+ }
+
+ return RegionZonePair();
+}
+
+} // namespace
diff --git a/src/modules/locale/GeoIPXML.h b/src/libcalamares/geoip/GeoIPXML.h
index bc3f23bec..7dee2ecbe 100644
--- a/src/modules/locale/GeoIPXML.h
+++ b/src/libcalamares/geoip/GeoIPXML.h
@@ -1,6 +1,6 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
- * Copyright 2018, Adriaan de Groot <groot@kde.org>
+ * Copyright 2018-2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,19 +16,23 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GEOIPXML_H
-#define GEOIPXML_H
+#ifndef GEOIP_GEOIPXML_H
+#define GEOIP_GEOIPXML_H
-#include "GeoIP.h"
+#include "Interface.h"
+namespace CalamaresUtils::GeoIP
+{
/** @brief GeoIP lookup with XML data
*
* The data is assumed to be in XML format with a
* <Response><TimeZone></TimeZone></Response>
* element, which contains the text (string) for the region/zone. This
* format is expected by, e.g. the Ubiquity installer.
+ *
+ * @note This class is an implementation detail.
*/
-class GeoIPXML : public GeoIP
+class GeoIPXML : public Interface
{
public:
/** @brief Configure the element tag which is selected.
@@ -38,7 +42,9 @@ public:
*/
explicit GeoIPXML( const QString& element = QString() );
- virtual RegionZonePair processReply( const QByteArray& );
+ virtual RegionZonePair processReply( const QByteArray& ) override;
+ virtual QString rawReply(const QByteArray & ) override;
} ;
+} // namespace
#endif
diff --git a/src/libcalamares/geoip/Handler.cpp b/src/libcalamares/geoip/Handler.cpp
new file mode 100644
index 000000000..1e8b03b26
--- /dev/null
+++ b/src/libcalamares/geoip/Handler.cpp
@@ -0,0 +1,183 @@
+/* === This file is part of Calamares - <http://github.com/calamares> ===
+ *
+ * Copyright 2019, Adriaan de Groot <groot@kde.org>
+ *
+ * Calamares 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Calamares is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Handler.h"
+
+#include "GeoIPJSON.h"
+#if defined(QT_XML_LIB)
+#include "GeoIPXML.h"
+#endif
+
+#include "utils/Logger.h"
+#include "utils/NamedEnum.h"
+#include "utils/Variant.h"
+
+#include <QEventLoop>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+
+#include <memory>
+
+static const NamedEnumTable< CalamaresUtils::GeoIP::Handler::Type >&
+handlerTypes()
+{
+ using Type = CalamaresUtils::GeoIP::Handler::Type;
+
+ static const NamedEnumTable<Type> names{
+ { QStringLiteral( "none" ), Type::None},
+ { QStringLiteral( "json" ), Type::JSON},
+ { QStringLiteral( "xml" ), Type::XML}
+ };
+
+ return names;
+}
+
+namespace CalamaresUtils::GeoIP
+{
+
+Handler::Handler()
+ : m_type( Type::None )
+{
+}
+
+Handler::Handler( const QString& implementation, const QString& url, const QString& selector )
+ : m_type( Type::None )
+ , m_url( url )
+ , m_selector( selector )
+{
+ bool ok = false;
+ m_type = handlerTypes().find( implementation, ok );
+#if !defined(QT_XML_LIB)
+ if ( m_type == Type::XML )
+ {
+ m_type = Type::None;
+ cWarning() << "GeoIP style XML is not supported in this version of Calamares.";
+ }
+#endif
+ if ( !ok )
+ {
+ cWarning() << "GeoIP Style" << implementation << "is not recognized.";
+ }
+}
+
+Handler::~Handler()
+{
+}
+
+static QByteArray
+synchronous_get( const QString& urlstring )
+{
+ QUrl url( urlstring );
+ QNetworkAccessManager manager;
+ QEventLoop loop;
+
+ QObject::connect( &manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit );
+
+ QNetworkRequest request( url );
+ QNetworkReply* reply = manager.get( request );
+ loop.exec();
+ reply->deleteLater();
+ return reply->readAll();
+}
+
+static std::unique_ptr< Interface >
+create_interface( Handler::Type t, const QString& selector )
+{
+ switch( t )
+ {
+ case Handler::Type::None:
+ return nullptr;
+ case Handler::Type::JSON:
+ return std::make_unique< GeoIPJSON >( selector );
+ case Handler::Type::XML:
+#if defined(QT_XML_LIB)
+ return std::make_unique< GeoIPXML >( selector );
+#else
+ return nullptr;
+#endif
+ default: // there are no others
+ return nullptr;
+ }
+}
+
+static RegionZonePair
+do_query( Handler::Type type, const QString& url, const QString& selector )
+{
+ const auto interface = create_interface( type, selector );
+ if ( !interface )
+ return RegionZonePair();
+
+ return interface->processReply( synchronous_get( url ) );
+}
+
+static QString
+do_raw_query( Handler::Type type, const QString& url, const QString& selector )
+{
+ const auto interface = create_interface( type, selector );
+ if ( !interface )
+ return QString();
+
+ return interface->rawReply( synchronous_get( url ) );
+}
+
+RegionZonePair
+Handler::get() const
+{
+ if ( !isValid() )
+ return RegionZonePair();
+ return do_query( m_type, m_url, m_selector );
+}
+
+
+QFuture< RegionZonePair >
+Handler::query() const
+{
+ Handler::Type type = m_type;
+ QString url = m_url;
+ QString selector = m_selector;
+
+ return QtConcurrent::run( [=]
+ {
+ return do_query( type, url, selector );
+ } );
+}
+
+QString
+Handler::getRaw() const
+{
+ if ( !isValid() )
+ return QString();
+ return do_raw_query( m_type, m_url, m_selector );
+}
+
+
+QFuture< QString >
+Handler::queryRaw() const
+{
+ Handler::Type type = m_type;
+ QString url = m_url;
+ QString selector = m_selector;
+
+ return QtConcurrent::run( [=]
+ {
+ return do_raw_query( type, url, selector );
+ } );
+}
+
+
+} // namespace
diff --git a/src/libcalamares/geoip/Handler.h b/src/libcalamares/geoip/Handler.h
new file mode 100644
index 000000000..92e5f326e
--- /dev/null
+++ b/src/libcalamares/geoip/Handler.h
@@ -0,0 +1,91 @@
+/* === This file is part of Calamares - <http://github.com/calamares> ===
+ *
+ * Copyright 2019, Adriaan de Groot <groot@kde.org>
+ *
+ * Calamares 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Calamares is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GEOIP_HANDLER_H
+#define GEOIP_HANDLER_H
+
+#include "Interface.h"
+
+#include <QtConcurrent/QtConcurrentRun>
+#include <QString>
+#include <QVariantMap>
+
+namespace CalamaresUtils {}
+namespace CalamaresUtils::GeoIP
+{
+
+/** @brief Handle one complete GeoIP lookup.
+ *
+ * This class handles one complete GeoIP lookup. Create it with
+ * suitable configuration values, then call get(). This is a
+ * synchronous API and will return an invalid zone pair on
+ * error or if the configuration is not understood. For an
+ * async API, use query().
+ */
+class DLLEXPORT Handler
+{
+public:
+ enum class Type
+ {
+ None,
+ JSON,
+ XML
+ } ;
+
+ /** @brief An unconfigured handler; this always returns errors. */
+ Handler();
+ /** @brief A handler for a specific GeoIP source.
+ *
+ * The @p implementation name selects an implementation; currently JSON and XML
+ * are supported. The @p url is retrieved by query() and then the @p selector
+ * is used to select something from the data returned by the @url.
+ */
+ Handler( const QString& implementation, const QString& url, const QString& selector );
+
+ ~Handler();
+
+ /** @brief Synchronously get the GeoIP result.
+ *
+ * If the Handler is valid, then do the actual fetching and interpretation
+ * of data and return the result. An invalid Handler will return an
+ * invalid (empty) result.
+ */
+ RegionZonePair get() const;
+ /// @brief Like get, but don't interpret the contents
+ QString getRaw() const;
+
+ /** @brief Asynchronously get the GeoIP result.
+ *
+ * See get() for the return value.
+ */
+ QFuture< RegionZonePair > query() const;
+ /// @brief Like query, but don't interpret the contents
+ QFuture< QString > queryRaw() const;
+
+ bool isValid() const { return m_type != Type::None; }
+ Type type() const { return m_type; }
+
+private:
+ Type m_type;
+ const QString m_url;
+ const QString m_selector;
+};
+
+} // namespace
+#endif
+
diff --git a/src/modules/locale/GeoIP.cpp b/src/libcalamares/geoip/Interface.cpp
index 4c031f286..50aa04683 100644
--- a/src/modules/locale/GeoIP.cpp
+++ b/src/libcalamares/geoip/Interface.cpp
@@ -16,21 +16,24 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "GeoIP.h"
+#include "Interface.h"
#include "utils/Logger.h"
-GeoIP::GeoIP(const QString& e)
+namespace CalamaresUtils::GeoIP
+{
+
+Interface::Interface(const QString& e)
: m_element( e )
{
}
-GeoIP::~GeoIP()
+Interface::~Interface()
{
}
-GeoIP::RegionZonePair
-GeoIP::splitTZString( const QString& tz )
+RegionZonePair
+splitTZString( const QString& tz )
{
QString timezoneString( tz );
timezoneString.remove( '\\' );
@@ -42,8 +45,10 @@ GeoIP::splitTZString( const QString& tz )
cDebug() << "GeoIP reporting" << timezoneString;
QString region = tzParts.takeFirst();
QString zone = tzParts.join( '/' );
- return qMakePair( region, zone );
+ return RegionZonePair( region, zone );
}
- return qMakePair( QString(), QString() );
+ return RegionZonePair( QString(), QString() );
}
+
+} // namespace
diff --git a/src/libcalamares/geoip/Interface.h b/src/libcalamares/geoip/Interface.h
new file mode 100644
index 000000000..4b2ff3a5a
--- /dev/null
+++ b/src/libcalamares/geoip/Interface.h
@@ -0,0 +1,98 @@
+/* === This file is part of Calamares - <http://github.com/calamares> ===
+ *
+ * Copyright 2018-2019, Adriaan de Groot <groot@kde.org>
+ *
+ * Calamares 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Calamares is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GEOIP_INTERFACE_H
+#define GEOIP_INTERFACE_H
+
+#include "DllMacro.h"
+
+#include <QPair>
+#include <QString>
+#include <QUrl>
+
+class QByteArray;
+
+namespace CalamaresUtils {}
+namespace CalamaresUtils::GeoIP
+{
+/** @brief A Region, Zone pair of strings
+ *
+ * A GeoIP lookup returns a timezone, which is represented as a Region,
+ * Zone pair of strings (e.g. "Europe" and "Amsterdam"). Generally,
+ * pasting the strings back together with a "/" is the right thing to
+ * do. The Zone **may** contain a "/" (e.g. "Kentucky/Monticello").
+ */
+class DLLEXPORT RegionZonePair : public QPair<QString, QString>
+{
+public:
+ /** @brief Construct from an existing pair. */
+ explicit RegionZonePair( const QPair& p ) : QPair(p) { }
+ /** @brief Construct from two strings, like qMakePair(). */
+ RegionZonePair( const QString& region, const QString& zone ) : QPair( region, zone ) { }
+ /** @brief An invalid zone pair (empty strings). */
+ RegionZonePair() : QPair( QString(), QString() ) { }
+
+ bool isValid() const { return !first.isEmpty(); }
+} ;
+
+/** @brief Splits a region/zone string into a pair.
+ *
+ * Cleans up the string by removing backslashes (\\)
+ * since some providers return silly-escaped names. Replaces
+ * spaces with _ since some providers return human-readable names.
+ * Splits on the first / in the resulting string, or returns a
+ * pair of empty QStrings if it can't. (e.g. America/North Dakota/Beulah
+ * will return "America", "North_Dakota/Beulah").
+ */
+DLLEXPORT RegionZonePair
+splitTZString( const QString& s );
+
+/**
+ * @brief Interface for GeoIP retrievers.
+ *
+ * A GeoIP retriever takes a configured URL (from the config file)
+ * and can handle the data returned from its interpretation of that
+ * configured URL, returning a region and zone.
+ */
+class DLLEXPORT Interface
+{
+public:
+ virtual ~Interface();
+
+ /** @brief Handle a (successful) request by interpreting the data.
+ *
+ * Should return a ( <zone>, <region> ) pair, e.g.
+ * ( "Europe", "Amsterdam" ). This is called **only** if the
+ * request to the fullUrl was successful; the handler
+ * is free to read as much, or as little, data as it
+ * likes. On error, returns a RegionZonePair with empty
+ * strings (e.g. ( "", "" ) ).
+ */
+ virtual RegionZonePair processReply( const QByteArray& ) = 0;
+
+ /** @brief Get the raw reply data. */
+ virtual QString rawReply( const QByteArray& ) = 0;
+
+protected:
+ Interface( const QString& e = QString() );
+
+ QString m_element; // string for selecting from data
+} ;
+
+} // namespace
+#endif
diff --git a/src/modules/locale/test_geoip.cpp b/src/libcalamares/geoip/test_geoip.cpp
index 89c1b6030..5f7ab935c 100644
--- a/src/modules/locale/test_geoip.cpp
+++ b/src/libcalamares/geoip/test_geoip.cpp
@@ -23,11 +23,12 @@
#include <iostream>
#include "GeoIPJSON.h"
-#ifdef HAVE_XML
+#ifdef QT_XML_LIB
#include "GeoIPXML.h"
#endif
using std::cerr;
+using namespace CalamaresUtils::GeoIP;
int main(int argc, char** argv)
{
@@ -37,10 +38,10 @@ int main(int argc, char** argv)
return 1;
}
- GeoIP* handler = nullptr;
+ Interface* handler = nullptr;
if ( QStringLiteral( "json" ) == argv[1] )
handler = new GeoIPJSON;
-#ifdef HAVE_XML
+#ifdef QT_XML_LIB
else if ( QStringLiteral( "xml" ) == argv[1] )
handler = new GeoIPXML;
#endif
diff --git a/src/libcalamares/utils/LocaleLabel.cpp b/src/libcalamares/locale/Label.cpp
index 26480ef14..ca528dc75 100644
--- a/src/libcalamares/utils/LocaleLabel.cpp
+++ b/src/libcalamares/locale/Label.cpp
@@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
- * Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
+ * Copyright 2017-2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,12 +17,12 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "LocaleLabel.h"
+#include "Label.h"
-namespace CalamaresUtils
+namespace CalamaresUtils::Locale
{
-LocaleLabel::LocaleLabel()
+Label::Label()
: m_locale( QLocale() )
{
m_localeId = m_locale.name();
@@ -30,15 +30,15 @@ LocaleLabel::LocaleLabel()
setLabels( QString(), LabelFormat::IfNeededWithCountry );
}
-LocaleLabel::LocaleLabel( const QString& locale, LabelFormat format )
- : m_locale( LocaleLabel::getLocale( locale ) )
+Label::Label( const QString& locale, LabelFormat format )
+ : m_locale( Label::getLocale( locale ) )
, m_localeId( locale )
{
setLabels( locale, format );
}
void
-LocaleLabel::setLabels( const QString& locale, LabelFormat format )
+Label::setLabels( const QString& locale, LabelFormat format )
{
//: language[name] (country[name])
QString longFormat = QObject::tr( "%1 (%2)" );
@@ -59,7 +59,7 @@ LocaleLabel::setLabels( const QString& locale, LabelFormat format )
m_englishLabel = needsCountryName ? longFormat.arg( englishName, QLocale::countryToString( m_locale.country() ) ) : englishName;
}
-QLocale LocaleLabel::getLocale( const QString& localeName )
+QLocale Label::getLocale( const QString& localeName )
{
if ( localeName.contains( "@latin" ) )
{
diff --git a/src/libcalamares/utils/LocaleLabel.h b/src/libcalamares/locale/Label.h
index b56b29f33..65befc6b4 100644
--- a/src/libcalamares/utils/LocaleLabel.h
+++ b/src/libcalamares/locale/Label.h
@@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
- * Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
+ * Copyright 2017-2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,13 +17,14 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UTILS_LOCALELABEL_H
-#define UTILS_LOCALELABEL_H
+#ifndef LOCALE_LABEL_H
+#define LOCALE_LABEL_H
#include <QLocale>
#include <QString>
-namespace CalamaresUtils
+namespace CalamaresUtils {}
+namespace CalamaresUtils::Locale
{
/**
@@ -33,14 +34,14 @@ namespace CalamaresUtils
* translation system) into QLocales, and also into consistent
* human-readable text labels.
*/
-class LocaleLabel
+class Label
{
public:
/** @brief Formatting option for label -- add (country) to label. */
enum class LabelFormat { AlwaysWithCountry, IfNeededWithCountry } ;
/** @brief Empty locale. This uses the system-default locale. */
- LocaleLabel();
+ Label();
/** @brief Construct from a locale name.
*
@@ -48,13 +49,13 @@ public:
* The @p format determines whether the country name is always present
* in the label (human-readable form) or only if needed for disambiguation.
*/
- LocaleLabel( const QString& localeName, LabelFormat format = LabelFormat::IfNeededWithCountry );
+ Label( const QString& localeName, LabelFormat format = LabelFormat::IfNeededWithCountry );
/** @brief Define a sorting order.
*
* English (@see isEnglish() -- it means en_US) is sorted at the top.
*/
- bool operator <( const LocaleLabel& other ) const
+ bool operator <( const Label& other ) const
{
return m_localeId < other.m_localeId;
}
@@ -91,6 +92,18 @@ public:
return m_locale.name();
}
+ /// @brief Convenience accessor to the language part of the locale
+ QLocale::Language language() const
+ {
+ return m_locale.language();
+ }
+
+ /// @brief Convenience accessor to the country part (if any) of the locale
+ QLocale::Country country() const
+ {
+ return m_locale.country();
+ }
+
/** @brief Get a Qt locale for the given @p localeName
*
* This special-cases `sr@latin`, which is used as a translation
diff --git a/src/modules/welcome/LocaleModel.cpp b/src/libcalamares/locale/LabelModel.cpp
index 0ecf0fd1c..543417212 100644
--- a/src/modules/welcome/LocaleModel.cpp
+++ b/src/libcalamares/locale/LabelModel.cpp
@@ -16,30 +16,37 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "LocaleModel.h"
+#include "LabelModel.h"
-LocaleModel::LocaleModel( const QStringList& locales, QObject* parent )
+#include "Lookup.h"
+
+#include "CalamaresVersion.h" // For the list of translations
+
+namespace CalamaresUtils::Locale
+{
+
+LabelModel::LabelModel( const QStringList& locales, QObject* parent )
: QAbstractListModel( parent )
{
Q_ASSERT( locales.count() > 0 );
m_locales.reserve( locales.count() );
for ( const auto& l : locales )
- m_locales.push_back( CalamaresUtils::LocaleLabel( l ) );
+ m_locales.push_back( Label( l ) );
}
-LocaleModel::~LocaleModel()
+LabelModel::~LabelModel()
{
}
int
-LocaleModel::rowCount( const QModelIndex& ) const
+LabelModel::rowCount( const QModelIndex& ) const
{
return m_locales.count();
}
QVariant
-LocaleModel::data( const QModelIndex& index, int role ) const
+LabelModel::data( const QModelIndex& index, int role ) const
{
if ( ( role != LabelRole ) && ( role != EnglishLabelRole ) )
return QVariant();
@@ -59,8 +66,8 @@ LocaleModel::data( const QModelIndex& index, int role ) const
}
}
-const CalamaresUtils::LocaleLabel&
-LocaleModel::locale( int row )
+const Label&
+LabelModel::locale( int row ) const
{
if ( ( row < 0 ) || ( row >= m_locales.count() ) )
{
@@ -73,7 +80,7 @@ LocaleModel::locale( int row )
}
int
-LocaleModel::find( std::function<bool ( const LocaleLabel& )> predicate ) const
+LabelModel::find( std::function<bool ( const Label& )> predicate ) const
{
for ( int row = 0; row < m_locales.count() ; ++row )
{
@@ -84,26 +91,40 @@ LocaleModel::find( std::function<bool ( const LocaleLabel& )> predicate ) const
}
int
-LocaleModel::find( std::function<bool ( const QLocale& )> predicate ) const
+LabelModel::find( std::function<bool ( const QLocale& )> predicate ) const
{
- return find( [&]( const LocaleLabel& l )
+ return find( [&]( const Label& l )
{
return predicate( l.locale() );
} );
}
int
-LocaleModel::find( const QLocale& locale ) const
+LabelModel::find( const QLocale& locale ) const
{
- return find( [&]( const LocaleLabel& l )
+ return find( [&]( const Label& l )
{
return locale == l.locale();
} );
}
-void
-LocaleTwoColumnDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
+int
+LabelModel::find( const QString& countryCode ) const
+{
+ if ( countryCode.length() != 2 )
+ return -1;
+
+ auto c_l = countryData( countryCode );
+ int r = find( [&]( const Label& l ){ return ( l.language() == c_l.second ) && ( l.country() == c_l.first ); } );
+ if ( r >= 0 )
+ return r;
+ return find( [&]( const Label& l ){ return l.language() == c_l.second; } );
+}
+
+LabelModel* const availableTranslations()
{
- QStyledItemDelegate::paint( painter, option, index );
- option.widget->style()->drawItemText( painter, option.rect, Qt::AlignRight | Qt::AlignVCenter, option.palette, false, index.data( LocaleModel::EnglishLabelRole ).toString() );
+ static LabelModel model( QString( CALAMARES_TRANSLATION_LANGUAGES ).split( ';') );
+ return &model;
}
+
+} // namespace
diff --git a/src/modules/welcome/LocaleModel.h b/src/libcalamares/locale/LabelModel.h
index b1566d336..178f76343 100644
--- a/src/modules/welcome/LocaleModel.h
+++ b/src/libcalamares/locale/LabelModel.h
@@ -16,28 +16,31 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef WELCOME_LOCALEMODEL_H
-#define WELCOME_LOCALEMODEL_H
+#ifndef LOCALE_LABELMODEL_H
+#define LOCALE_LABELMODEL_H
+
+#include "DllMacro.h"
+#include "Label.h"
#include <QAbstractListModel>
-#include <QStyledItemDelegate>
#include <QVector>
-#include "utils/LocaleLabel.h"
-class LocaleModel : public QAbstractListModel
+namespace CalamaresUtils {}
+namespace CalamaresUtils::Locale
{
-public:
- using LocaleLabel = CalamaresUtils::LocaleLabel;
+class DLLEXPORT LabelModel : public QAbstractListModel
+{
+public:
enum
{
LabelRole = Qt::DisplayRole,
EnglishLabelRole = Qt::UserRole + 1
};
- LocaleModel( const QStringList& locales, QObject* parent = nullptr );
- virtual ~LocaleModel() override;
+ LabelModel( const QStringList& locales, QObject* parent = nullptr );
+ virtual ~LabelModel() override;
int rowCount( const QModelIndex& parent ) const override;
@@ -48,26 +51,34 @@ public:
* This is the backing data for the model; if @p row is out-of-range,
* returns a reference to en_US.
*/
- const LocaleLabel& locale( int row );
+ const Label& locale( int row ) const;
/** @brief Searches for an item that matches @p predicate
*
* Returns the row number of the first match, or -1 if there isn't one.
*/
int find( std::function<bool( const QLocale& )> predicate ) const;
- int find( std::function<bool( const LocaleLabel& )> predicate ) const;
+ int find( std::function<bool( const Label& )> predicate ) const;
+ /// @brief Looks for an item using the same locale, -1 if there isn't one
int find( const QLocale& ) const;
+ /// @brief Looks for an item that best matches the 2-letter country code
+ int find( const QString& countryCode ) const;
private:
- QVector< LocaleLabel > m_locales;
+ QVector< Label > m_locales;
} ;
-class LocaleTwoColumnDelegate : public QStyledItemDelegate
-{
-public:
- using QStyledItemDelegate::QStyledItemDelegate;
-
- void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const override;
-} ;
+/** @brief Returns a model with all available translations.
+ *
+ * The translations are set when Calamares is compiled; the list
+ * is provided by CMake via the CALAMARES_TRANSLATION_LANGUAGES
+ * #define.
+ *
+ * This model is a singleton and can be shared.
+ *
+ * NOTE: While the model is not typed const, it should be. Do not modify.
+ */
+DLLEXPORT LabelModel* const availableTranslations();
+} // namespace
#endif
diff --git a/src/libcalamares/locale/Lookup.cpp b/src/libcalamares/locale/Lookup.cpp
index d38d417d4..a096bc679 100644
--- a/src/libcalamares/locale/Lookup.cpp
+++ b/src/libcalamares/locale/Lookup.cpp
@@ -20,7 +20,7 @@
#include "CountryData_p.cpp"
-namespace Calamares
+namespace CalamaresUtils::Locale
{
struct TwoChar
@@ -35,7 +35,7 @@ struct TwoChar
cc2 = code[1].toLatin1();
}
}
-
+
char cc1;
char cc2;
};
@@ -44,7 +44,7 @@ static const CountryData* lookup( TwoChar c )
{
if ( !c.cc1 )
return nullptr;
-
+
const CountryData* p = std::find_if(country_data_table, country_data_table + country_data_size,
[c=c]( const CountryData& d ){ return (d.cc1 == c.cc1) && (d.cc2 == c.cc2); }
);
@@ -52,7 +52,7 @@ static const CountryData* lookup( TwoChar c )
return nullptr;
return p;
}
-
+
QLocale::Country countryForCode(const QString& code)
{
const CountryData* p = lookup( TwoChar( code ) );
diff --git a/src/libcalamares/locale/Lookup.h b/src/libcalamares/locale/Lookup.h
index 976c4dc21..5712a1120 100644
--- a/src/libcalamares/locale/Lookup.h
+++ b/src/libcalamares/locale/Lookup.h
@@ -24,25 +24,26 @@
#include <QLocale>
#include <QPair>
-namespace Calamares
+namespace CalamaresUtils {}
+namespace CalamaresUtils::Locale
{
/* All the functions in this file do lookups of locale data
* based on CLDR tables; these are lookups that you can't (easily)
* do with just QLocale (e.g. from 2-letter country code to a likely
* locale).
*/
-
+
/// @brief Map a 2-letter code to a Country, or AnyCountry if not found
DLLEXPORT QLocale::Country countryForCode( const QString& code );
/** @brief Map a Country to a Language, or AnyLanguage if not found
- *
+ *
* This is a *likely* language for the given country, based on the
* CLDR tables. For instance, this maps Belgium to Dutch.
*/
DLLEXPORT QLocale::Language languageForCountry( QLocale::Country country );
/// @brief Map a 2-letter code to a Language, or AnyLanguage if not found
DLLEXPORT QLocale::Language languageForCountry( const QString& code );
-
+
/// @brief Get both Country and Language for a 2-letter code
DLLEXPORT QPair< QLocale::Country, QLocale::Language > countryData( const QString& code );
/// @brief Get a likely locale for a 2-letter country code
diff --git a/src/modules/locale/CMakeLists.txt b/src/modules/locale/CMakeLists.txt
index affaa3753..768a67543 100644
--- a/src/modules/locale/CMakeLists.txt
+++ b/src/modules/locale/CMakeLists.txt
@@ -8,16 +8,6 @@ endif()
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui )
-set( geoip_src GeoIP.cpp GeoIPJSON.cpp )
-set( geoip_libs )
-
-find_package(Qt5 COMPONENTS Xml)
-if( Qt5Xml_FOUND )
- list( APPEND geoip_src GeoIPXML.cpp )
- list( APPEND geoip_libs Qt5::Xml )
- add_definitions( -DHAVE_XML )
-endif()
-
calamares_add_plugin( locale
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
@@ -43,20 +33,6 @@ calamares_add_plugin( locale
if( ECM_FOUND AND BUILD_TESTING )
ecm_add_test(
- GeoIPTests.cpp
- ${geoip_src}
- TEST_NAME
- geoiptest
- LINK_LIBRARIES
- calamaresui
- Qt5::Network
- Qt5::Test
- ${geoip_libs}
- ${YAMLCPP_LIBRARY}
- )
- calamares_automoc( geoiptest )
-
- ecm_add_test(
Tests.cpp
LocaleConfiguration.cpp
TEST_NAME
@@ -67,9 +43,3 @@ if( ECM_FOUND AND BUILD_TESTING )
)
calamares_automoc( localetest )
endif()
-
-if( BUILD_TESTING )
- add_executable( test_geoip test_geoip.cpp ${geoip_src} )
- target_link_libraries( test_geoip calamaresui Qt5::Network ${geoip_libs} ${YAMLCPP_LIBRARY} )
- calamares_automoc( test_geoip )
-endif()
diff --git a/src/modules/locale/GeoIP.h b/src/modules/locale/GeoIP.h
deleted file mode 100644
index 41abd2042..000000000
--- a/src/modules/locale/GeoIP.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* === This file is part of Calamares - <http://github.com/calamares> ===
- *
- * Copyright 2018, Adriaan de Groot <groot@kde.org>
- *
- * Calamares 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 3 of the License, or
- * (at your option) any later version.
- *
- * Calamares is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef GEOIP_H
-#define GEOIP_H
-
-#include <QPair>
-#include <QString>
-#include <QUrl>
-
-class QByteArray;
-
-/**
- * @brief Interface for GeoIP retrievers.
- *
- * A GeoIP retriever takes a configured URL (from the config file)
- * and can handle the data returned from its interpretation of that
- * configured URL, returning a region and zone.
- */
-class GeoIP
-{
-public:
- using RegionZonePair = QPair<QString, QString>;
-
- virtual ~GeoIP();
-
- /** @brief Handle a (successful) request by interpreting the data.
- *
- * Should return a ( <zone>, <region> ) pair, e.g.
- * ( "Europe", "Amsterdam" ). This is called **only** if the
- * request to the fullUrl was successful; the handler
- * is free to read as much, or as little, data as it
- * likes. On error, returns a RegionZonePair with empty
- * strings (e.g. ( "", "" ) ).
- */
- virtual RegionZonePair processReply( const QByteArray& ) = 0;
-
- /** @brief Splits a region/zone string into a pair.
- *
- * Cleans up the string by removing backslashes (\\)
- * since some providers return silly-escaped names. Replaces
- * spaces with _ since some providers return human-readable names.
- * Splits on the first / in the resulting string, or returns a
- * pair of empty QStrings if it can't. (e.g. America/North Dakota/Beulah
- * will return "America", "North_Dakota/Beulah").
- */
- static RegionZonePair splitTZString( const QString& s );
-
-protected:
- GeoIP( const QString& e = QString() );
-
- QString m_element; // string for selecting from data
-} ;
-
-#endif
diff --git a/src/modules/locale/LocalePage.cpp b/src/modules/locale/LocalePage.cpp
index c8076866e..f6cdba436 100644
--- a/src/modules/locale/LocalePage.cpp
+++ b/src/modules/locale/LocalePage.cpp
@@ -27,8 +27,8 @@
#include "LCLocaleDialog.h"
#include "Settings.h"
+#include "locale/Label.h"
#include "utils/CalamaresUtilsGui.h"
-#include "utils/LocaleLabel.h"
#include "utils/Logger.h"
#include "utils/Retranslator.h"
@@ -387,10 +387,10 @@ LocalePage::init( const QString& initialRegion,
std::pair< QString, QString > LocalePage::prettyLocaleStatus( const LocaleConfiguration& lc ) const
{
- using CalamaresUtils::LocaleLabel;
+ using CalamaresUtils::Locale::Label;
- LocaleLabel lang( lc.language(), LocaleLabel::LabelFormat::AlwaysWithCountry );
- LocaleLabel num( lc.lc_numeric, LocaleLabel::LabelFormat::AlwaysWithCountry );
+ Label lang( lc.language(), Label::LabelFormat::AlwaysWithCountry );
+ Label num( lc.lc_numeric, Label::LabelFormat::AlwaysWithCountry );
return std::make_pair< QString, QString >(
tr( "The system language will be set to %1." ).arg( lang.label() ),
diff --git a/src/modules/locale/LocaleViewStep.cpp b/src/modules/locale/LocaleViewStep.cpp
index d321b8501..29006ec33 100644
--- a/src/modules/locale/LocaleViewStep.cpp
+++ b/src/modules/locale/LocaleViewStep.cpp
@@ -19,18 +19,15 @@
#include "LocaleViewStep.h"
-#include "GeoIP.h"
-#include "GeoIPJSON.h"
-#ifdef HAVE_XML
-#include "GeoIPXML.h"
-#endif
-#include "GlobalStorage.h"
-#include "JobQueue.h"
#include "LocalePage.h"
-
#include "timezonewidget/localeglobal.h"
#include "widgets/WaitingWidget.h"
+#include "GlobalStorage.h"
+#include "JobQueue.h"
+
+#include "geoip/Handler.h"
+
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
@@ -116,54 +113,16 @@ LocaleViewStep::setUpPage()
void
LocaleViewStep::fetchGeoIpTimezone()
{
- QString actualUrl( m_geoipUrl );
- GeoIP *handler = nullptr;
-
- if ( m_geoipStyle.isEmpty() || m_geoipStyle == "legacy" )
- {
- actualUrl.append( "/json/" );
- handler = new GeoIPJSON( m_geoipSelector );
- }
- else if ( m_geoipStyle == "json" )
+ CalamaresUtils::GeoIP::Handler h( m_geoipStyle, m_geoipUrl, m_geoipSelector );
+ if ( h.isValid() )
{
- handler = new GeoIPJSON( m_geoipSelector );
+ m_startingTimezone = h.get();
+ if ( !m_startingTimezone.isValid() )
+ cWarning() << "GeoIP lookup at" << m_geoipUrl << "failed.";
}
-#if defined(HAVE_XML)
- else if ( m_geoipStyle == "xml" )
- {
- handler = new GeoIPXML( m_geoipSelector );
- }
-#endif
else
- {
cWarning() << "GeoIP Style" << m_geoipStyle << "is not recognized.";
- setUpPage();
- return;
- }
- cDebug() << "Fetching GeoIP data from" << actualUrl;
-
- QNetworkAccessManager *manager = new QNetworkAccessManager( this );
- connect( manager, &QNetworkAccessManager::finished,
- [=]( QNetworkReply* reply )
- {
- if ( reply->error() == QNetworkReply::NoError )
- {
- auto tz = handler->processReply( reply->readAll() );
- if ( !tz.first.isEmpty() )
- m_startingTimezone = tz;
- else
- cWarning() << "GeoIP lookup at" << reply->url() << "failed.";
- }
- delete handler;
- reply->deleteLater();
- manager->deleteLater();
- setUpPage();
- } );
-
- QNetworkRequest request;
- request.setUrl( QUrl::fromUserInput( actualUrl ) );
- request.setAttribute( QNetworkRequest::FollowRedirectsAttribute, true );
- manager->get( request );
+ setUpPage();
}
@@ -251,35 +210,43 @@ LocaleViewStep::onLeave()
void
LocaleViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
- if ( configurationMap.contains( "region" ) &&
- configurationMap.value( "region" ).type() == QVariant::String &&
- !configurationMap.value( "region" ).toString().isEmpty() &&
- configurationMap.contains( "zone" ) &&
- configurationMap.value( "zone" ).type() == QVariant::String &&
- !configurationMap.value( "zone" ).toString().isEmpty() )
+ QString region = CalamaresUtils::getString( configurationMap, "region" );
+ QString zone = CalamaresUtils::getString( configurationMap, "zone" );
+ if ( !region.isEmpty() && !zone.isEmpty() )
{
- m_startingTimezone = qMakePair( configurationMap.value( "region" ).toString(),
- configurationMap.value( "zone" ).toString() );
+ m_startingTimezone = CalamaresUtils::GeoIP::RegionZonePair( region, zone );
}
else
{
- m_startingTimezone = qMakePair( QStringLiteral( "America" ),
- QStringLiteral( "New_York" ) );
+ m_startingTimezone = CalamaresUtils::GeoIP::RegionZonePair( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
}
- if ( configurationMap.contains( "localeGenPath" ) &&
- configurationMap.value( "localeGenPath" ).type() == QVariant::String &&
- !configurationMap.value( "localeGenPath" ).toString().isEmpty() )
+ m_localeGenPath = CalamaresUtils::getString( configurationMap, "localeGenPath" );
+ if ( m_localeGenPath.isEmpty() )
+ m_localeGenPath = QStringLiteral( "/etc/locale.gen" );
+
+ bool ok = false;
+ QVariantMap geoip = CalamaresUtils::getSubMap( configurationMap, "geoip", ok );
+ if ( ok )
{
- m_localeGenPath = configurationMap.value( "localeGenPath" ).toString();
+ m_geoipUrl = CalamaresUtils::getString( geoip, "url" );
+ m_geoipStyle = CalamaresUtils::getString( geoip, "style" );
+ m_geoipSelector = CalamaresUtils::getString( geoip, "selector" );
}
else
{
- m_localeGenPath = QStringLiteral( "/etc/locale.gen" );
- }
+ // Optional
+ m_geoipUrl = CalamaresUtils::getString( configurationMap, "geoipUrl" );
+ m_geoipStyle = CalamaresUtils::getString( configurationMap, "geoipStyle" );
+ m_geoipSelector = CalamaresUtils::getString( configurationMap, "geoipSelector" );
- // Optional
- m_geoipUrl = CalamaresUtils::getString( configurationMap, "geoipUrl" );
- m_geoipStyle = CalamaresUtils::getString( configurationMap, "geoipStyle" );
- m_geoipSelector = CalamaresUtils::getString( configurationMap, "geoipSelector" );
+ if ( !m_geoipUrl.isEmpty() && ( m_geoipStyle.isEmpty() || m_geoipStyle == "legacy" ) )
+ {
+ m_geoipStyle = "json";
+ m_geoipUrl.append( "/json/" );
+ }
+
+ if ( !m_geoipUrl.isEmpty() )
+ cWarning() << "Legacy-style GeoIP configuration is deprecated. Use geoip: map.";
+ }
}
diff --git a/src/modules/locale/LocaleViewStep.h b/src/modules/locale/LocaleViewStep.h
index 3f5c91621..8ab50b75d 100644
--- a/src/modules/locale/LocaleViewStep.h
+++ b/src/modules/locale/LocaleViewStep.h
@@ -20,14 +20,14 @@
#ifndef LOCALEVIEWSTEP_H
#define LOCALEVIEWSTEP_H
-#include <QObject>
-
-#include <utils/PluginFactory.h>
-#include <viewpages/ViewStep.h>
+#include "geoip/Interface.h"
+#include "utils/PluginFactory.h"
+#include "viewpages/ViewStep.h"
-#include <PluginDllMacro.h>
+#include "PluginDllMacro.h"
#include <QFutureWatcher>
+#include <QObject>
class LocalePage;
class WaitingWidget;
@@ -71,7 +71,7 @@ private:
bool m_nextEnabled;
QString m_prettyStatus;
- QPair< QString, QString > m_startingTimezone;
+ CalamaresUtils::GeoIP::RegionZonePair m_startingTimezone;
QString m_localeGenPath;
QString m_geoipUrl; // The URL, depening on style might be modified on lookup
diff --git a/src/modules/locale/locale.conf b/src/modules/locale/locale.conf
index ddd0bc97e..7c2ec332c 100644
--- a/src/modules/locale/locale.conf
+++ b/src/modules/locale/locale.conf
@@ -25,21 +25,46 @@ zone: "New_York"
# custom path for locale.gen
#localeGenPath: "PATH_TO/locale.gen"
-# GeoIP based Language settings:
+# GeoIP based Language settings: Leave commented out to disable GeoIP.
#
-# GeoIP need an working Internet connection.
+# GeoIP needs a working Internet connection.
# This can be managed from `welcome.conf` by adding
# internet to the list of required conditions.
#
-# Leave commented out to disable GeoIP.
+# The configuration
+# is in three parts: a *style*, which can be "json" or "xml"
+# depending on the kind of data returned by the service, and
+# a *url* where the data is retrieved, and an optional *selector*
+# to pick the right field out of the returned data (e.g. field
+# name in JSON or element name in XML).
#
-# An HTTP request is made to *geoipUrl* -- depending on the geoipStyle,
-# the URL may be modified before use. The request should return
-# valid data in a suitable format, depending on geoipStyle;
+# The default selector (when the setting is blank) is picked to
+# work with existing JSON providers (which use "time_zone") and
+# Ubiquity's XML providers (which use "TimeZone").
+#
+# If the service configured via *url* uses
+# a different attribute name (e.g. "timezone") in JSON or a
+# different element tag (e.g. "<Time_Zone>") in XML, set this
+# string to the name or tag to be used.
+#
+# In JSON:
+# - if the string contains "." characters, this is used as a
+# multi-level selector, e.g. "a.b" will select the timezone
+# from data "{a: {b: "Europe/Amsterdam" } }".
+# - each part of the string split by "." characters is used as
+# a key into the JSON data.
+# In XML:
+# - all elements with the named tag (e.g. all TimeZone) elements
+# from the document are checked; the first one with non-empty
+# text value is used.
+#
+#
+# An HTTP(S) request is made to *url*. The request should return
+# valid data in a suitable format, depending on *style*;
# generally this includes a string value with the timezone
# in <region>/<zone> format. For services that return data which
# does not follow the conventions of "suitable data" described
-# below, *geoIPSelector* may be used to pick different data.
+# below, *selector* may be used to pick different data.
#
# Note that this example URL works, but the service is shutting
# down in June 2018.
@@ -58,40 +83,9 @@ zone: "New_York"
# - backslashes are removed
# - spaces are replaced with _
#
-#geoipUrl: "freegeoip.net"
-
-# GeoIP style. Leave commented out for the "legacy" interpretation.
-# This setting only makes sense if geoipUrl is set, enabliing geoIP.
-#
-# Possible values are:
-# unset same as "legacy"
-# blank same as "legacy"
-# "legacy" appends "/json" to geoipUrl, above, and uses JSON format
-# (which is what freegeoip.net provides there).
-# "json" URL is not modified, uses JSON format.
-# "xml" URL is not modified, uses XML format.
-#
-# The JSON format is provided by freegeoip.net, but that service is
-# shutting down in June 2018. There are other providers with the same
-# format. XML format is provided for Ubiquity.
-#geoipStyle: "legacy"
-
-# GeoIP selector. Leave commented out for the default selector
-# (which depends on the style: JSON uses "time_zone" and XML
-# uses TimeZone, for the FreeGeoIP-alike and the Ubiquity-alike
-# respectively). If the service configured via *geoipUrl* uses
-# a different attribute name (e.g. "timezone") in JSON or a
-# different element tag (e.g. "<Time_Zone>") in XML, set this
-# string to the name or tag to be used.
-#
-# In JSON:
-# - if the string contains "." characters, this is used as a
-# multi-level selector, e.g. "a.b" will select the timezone
-# from data "{a: {b: "Europe/Amsterdam" } }".
-# - each part of the string split by "." characters is used as
-# a key into the JSON data.
-# In XML:
-# - all elements with the named tag (e.g. all TimeZone) elements
-# from the document are checked; the first one with non-empty
-# text value is used.
-#geoipSelector: ""
+# Legacy settings "geoipStyle", "geoipUrl" and "geoipSelector"
+# in the top-level are still supported, but I'd advise against.
+geoip:
+ style: "json"
+ url: "https://geoip.kde.org/v1/calamares"
+ selector: "" # leave blank for the default
diff --git a/src/modules/welcome/CMakeLists.txt b/src/modules/welcome/CMakeLists.txt
index e6ddd2bd7..e25b7f5d0 100644
--- a/src/modules/welcome/CMakeLists.txt
+++ b/src/modules/welcome/CMakeLists.txt
@@ -27,7 +27,6 @@ calamares_add_plugin( welcome
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
${CHECKER_SOURCES}
- LocaleModel.cpp
WelcomeViewStep.cpp
WelcomePage.cpp
UI
diff --git a/src/modules/welcome/WelcomePage.cpp b/src/modules/welcome/WelcomePage.cpp
index 65a475310..ae78aa8ff 100644
--- a/src/modules/welcome/WelcomePage.cpp
+++ b/src/modules/welcome/WelcomePage.cpp
@@ -21,16 +21,16 @@
#include "WelcomePage.h"
#include "ui_WelcomePage.h"
-#include "LocaleModel.h"
#include "checker/CheckerContainer.h"
#include "Branding.h"
#include "CalamaresVersion.h"
#include "Settings.h"
#include "ViewManager.h"
+
+#include "locale/LabelModel.h"
#include "modulesystem/ModuleManager.h"
#include "utils/CalamaresUtilsGui.h"
-#include "utils/LocaleLabel.h"
#include "utils/Logger.h"
#include "utils/Retranslator.h"
@@ -131,7 +131,7 @@ WelcomePage::initLanguages()
ui->languageWidget->clear();
ui->languageWidget->setInsertPolicy( QComboBox::InsertAtBottom );
- m_languages = new LocaleModel( QString( CALAMARES_TRANSLATION_LANGUAGES ).split( ';') );
+ m_languages = CalamaresUtils::Locale::availableTranslations();
ui->languageWidget->setModel( m_languages );
ui->languageWidget->setItemDelegate( new LocaleTwoColumnDelegate( ui->languageWidget ) );
@@ -257,7 +257,23 @@ WelcomePage::focusInEvent( QFocusEvent* e )
e->accept();
}
-bool WelcomePage::verdict() const
+bool
+WelcomePage::verdict() const
{
return m_checkingWidget->verdict();
}
+
+void
+WelcomePage::externallySelectedLanguage( int row )
+{
+ if ( ( row >= 0 ) && ( row < ui->languageWidget->count() ) )
+ ui->languageWidget->setCurrentIndex( row );
+}
+
+
+void
+LocaleTwoColumnDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
+{
+ QStyledItemDelegate::paint( painter, option, index );
+ option.widget->style()->drawItemText( painter, option.rect, Qt::AlignRight | Qt::AlignVCenter, option.palette, false, index.data( CalamaresUtils::Locale::LabelModel::EnglishLabelRole ).toString() );
+}
diff --git a/src/modules/welcome/WelcomePage.h b/src/modules/welcome/WelcomePage.h
index ec689735b..f05426d38 100644
--- a/src/modules/welcome/WelcomePage.h
+++ b/src/modules/welcome/WelcomePage.h
@@ -20,6 +20,9 @@
#ifndef WELCOMEPAGE_H
#define WELCOMEPAGE_H
+#include "locale/LabelModel.h"
+
+#include <QStyledItemDelegate>
#include <QWidget>
namespace Ui
@@ -28,7 +31,6 @@ class WelcomePage;
}
class CheckerContainer;
-class LocaleModel;
class WelcomePage : public QWidget
{
@@ -44,6 +46,8 @@ public:
/// @brief Results of requirements checking
bool verdict() const;
+ /// @brief Change the language from an external source.
+ void externallySelectedLanguage( int row );
protected:
void focusInEvent( QFocusEvent* e ) override; //choose the child widget to focus
@@ -53,7 +57,19 @@ private:
Ui::WelcomePage* ui;
CheckerContainer* m_checkingWidget;
- LocaleModel *m_languages;
+ CalamaresUtils::Locale::LabelModel *m_languages;
};
+/** @brief Delegate to display language information in two columns.
+ *
+ * Displays the native language name and the English language name.
+ */
+class LocaleTwoColumnDelegate : public QStyledItemDelegate
+{
+public:
+ using QStyledItemDelegate::QStyledItemDelegate;
+
+ void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const override;
+} ;
+
#endif // WELCOMEPAGE_H
diff --git a/src/modules/welcome/WelcomeViewStep.cpp b/src/modules/welcome/WelcomeViewStep.cpp
index 88b5f6324..e115565b7 100644
--- a/src/modules/welcome/WelcomeViewStep.cpp
+++ b/src/modules/welcome/WelcomeViewStep.cpp
@@ -22,9 +22,13 @@
#include "WelcomePage.h"
#include "checker/GeneralRequirements.h"
+#include "geoip/Handler.h"
+#include "locale/Lookup.h"
#include "modulesystem/ModuleManager.h"
#include "utils/Logger.h"
+#include "utils/Variant.h"
+#include <QFutureWatcher>
#include <QVariant>
CALAMARES_PLUGIN_FACTORY_DEFINITION( WelcomeViewStepFactory, registerPlugin<WelcomeViewStep>(); )
@@ -97,18 +101,9 @@ WelcomeViewStep::jobs() const
void
WelcomeViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
- bool showSupportUrl =
- configurationMap.contains( "showSupportUrl" ) &&
- configurationMap.value( "showSupportUrl" ).type() == QVariant::Bool &&
- configurationMap.value( "showSupportUrl" ).toBool();
- bool showKnownIssuesUrl =
- configurationMap.contains( "showKnownIssuesUrl" ) &&
- configurationMap.value( "showKnownIssuesUrl" ).type() == QVariant::Bool &&
- configurationMap.value( "showKnownIssuesUrl" ).toBool();
- bool showReleaseNotesUrl =
- configurationMap.contains( "showReleaseNotesUrl" ) &&
- configurationMap.value( "showReleaseNotesUrl" ).type() == QVariant::Bool &&
- configurationMap.value( "showReleaseNotesUrl" ).toBool();
+ bool showSupportUrl = CalamaresUtils::getBool( configurationMap, "showSupportUrl", false );
+ bool showKnownIssuesUrl = CalamaresUtils::getBool( configurationMap, "showKnownIssuesUrl", false );
+ bool showReleaseNotesUrl = CalamaresUtils::getBool( configurationMap, "showReleaseNotesUrl", false );
m_widget->setUpLinks( showSupportUrl,
showKnownIssuesUrl,
@@ -120,9 +115,57 @@ WelcomeViewStep::setConfigurationMap( const QVariantMap& configurationMap )
else
cWarning() << "no valid requirements map found in welcome "
"module configuration.";
+
+ bool ok = false;
+ QVariantMap geoip = CalamaresUtils::getSubMap( configurationMap, "geoip", ok );
+ if ( ok )
+ {
+ using FWString = QFutureWatcher< QString >;
+
+ auto* handler = new CalamaresUtils::GeoIP::Handler(
+ CalamaresUtils::getString( geoip, "style" ),
+ CalamaresUtils::getString( geoip, "url" ),
+ CalamaresUtils::getString( geoip, "selector" ) );
+ auto* future = new FWString();
+ connect( future, &FWString::finished, [view=this, f=future, h=handler]()
+ {
+ QString countryResult = f->future().result();
+ cDebug() << "GeoIP result for welcome=" << countryResult;
+ view->setCountry( countryResult );
+ f->deleteLater();
+ delete h;
+ } );
+ future->setFuture( handler->queryRaw() );
+ }
}
-Calamares::RequirementsList WelcomeViewStep::checkRequirements()
+Calamares::RequirementsList
+WelcomeViewStep::checkRequirements()
{
return m_requirementsChecker->checkRequirements();
}
+
+void
+WelcomeViewStep::setCountry( const QString& countryCode )
+{
+ if ( countryCode.length() != 2 )
+ {
+ cDebug() << "Unusable country code" << countryCode;
+ return;
+ }
+
+ auto c_l = CalamaresUtils::Locale::countryData( countryCode );
+ if ( c_l.first == QLocale::Country::AnyCountry )
+ {
+ cDebug() << "Unusable country code" << countryCode;
+ return;
+ }
+ else
+ {
+ int r = CalamaresUtils::Locale::availableTranslations()->find( countryCode );
+ if ( r < 0 )
+ cDebug() << "Unusable country code" << countryCode << "(no suitable translation)";
+ if ( ( r >= 0 ) && m_widget )
+ m_widget->externallySelectedLanguage( r );
+ }
+}
diff --git a/src/modules/welcome/WelcomeViewStep.h b/src/modules/welcome/WelcomeViewStep.h
index 937cad246..7deed2167 100644
--- a/src/modules/welcome/WelcomeViewStep.h
+++ b/src/modules/welcome/WelcomeViewStep.h
@@ -54,6 +54,13 @@ public:
void setConfigurationMap( const QVariantMap& configurationMap ) override;
+ /** @brief Sets the country that Calamares is running in.
+ *
+ * This (ideally) sets up language and locale settings that are right for
+ * the given 2-letter country code.
+ */
+ void setCountry( const QString& );
+
Calamares::RequirementsList checkRequirements() override;
private:
diff --git a/src/modules/welcome/welcome.conf b/src/modules/welcome/welcome.conf
index 8b6ad3482..6d9a307fc 100644
--- a/src/modules/welcome/welcome.conf
+++ b/src/modules/welcome/welcome.conf
@@ -47,3 +47,16 @@ requirements:
# - storage
- ram
# - root
+
+# GeoIP checking
+#
+# This can be used to pre-select a language based on the country
+# the user is currently in. It *assumes* that there's internet
+# connectivity, though. Configuration is like in the locale module,
+# but remember to use a URL that returns full data **and** to
+# use a selector that will pick the country, not the timezone.
+
+geoip:
+ style: "xml"
+ url: "https://geoip.kde.org/v1/ubiquity" # extended XML format
+ selector: "CountryCode" # blank uses default, which is wrong