[merge] with upstream

This commit is contained in:
Philip Müller 2020-02-07 21:37:00 +01:00
commit 7333a925b4
51 changed files with 1955 additions and 393 deletions

View File

@ -11,3 +11,8 @@ trim_trailing_whitespace = true
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
insert_final_newline = true insert_final_newline = true
[*.sh]
indent_style = tab
insert_final_newline = true

View File

@ -1,5 +1,18 @@
#! /bin/sh #! /bin/sh
### LICENSE
# === This file is part of Calamares - <https://github.com/calamares> ===
#
# SPDX-License-Identifier: BSD-2-Clause
# SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot <groot@kde.org>
#
# This file is Free Software: you can redistribute it and/or modify
# it under the terms of the 2-clause BSD License.
#
### END LICENSE
### USAGE
#
# Does the translation tag (from a previous txpush) exist? # Does the translation tag (from a previous txpush) exist?
# This assumes that the release host has also locally done # This assumes that the release host has also locally done
# a translations push, which works for the current development # a translations push, which works for the current development
@ -7,6 +20,13 @@
# the typical txpush log messages instead of the tag. # the typical txpush log messages instead of the tag.
# #
# Use --cleanup as an argument to clean things up. # Use --cleanup as an argument to clean things up.
#
# Normal use:
# $ sh ci/txcheck.sh
# If there are differences, fix them and then clean up:
# $ sh ci/txcheck.sh --cleanup
#
### END USAGE
# The files that are translated; should match the contents of .tx/config # The files that are translated; should match the contents of .tx/config
TX_FILE_LIST="lang/calamares_en.ts lang/python.pot src/modules/dummypythonqt/lang/dummypythonqt.pot calamares.desktop" TX_FILE_LIST="lang/calamares_en.ts lang/python.pot src/modules/dummypythonqt/lang/dummypythonqt.pot calamares.desktop"
@ -17,15 +37,18 @@ TX_FILE_LIST="lang/calamares_en.ts lang/python.pot src/modules/dummypythonqt/lan
# normally used much later in the script. # normally used much later in the script.
tx_cleanup() tx_cleanup()
{ {
# Cleanup artifacs of checking # Cleanup artifacs of checking
git worktree remove --force build-txcheck-head git worktree remove --force build-txcheck-head
git worktree remove --force build-txcheck-prev git worktree remove --force build-txcheck-prev
git branch -D build-txcheck-head > /dev/null 2>&1 git branch -D build-txcheck-head > /dev/null 2>&1
} }
if test "x$1" = "x--cleanup" ; then if test "x$1" = "x--cleanup" ; then
tx_cleanup tx_cleanup
exit 0 exit 0
fi
if test "x$1" = "x--help" ; then
sed -e '1,/USAGE/d' -e '/END.USAGE/,$d' < "$0"
fi fi
test -z "$1" || { echo "! Usage: txcheck.sh [--cleanup]" ; exit 1 ; } test -z "$1" || { echo "! Usage: txcheck.sh [--cleanup]" ; exit 1 ; }
@ -36,22 +59,22 @@ test -z "$1" || { echo "! Usage: txcheck.sh [--cleanup]" ; exit 1 ; }
XMLLINT="" XMLLINT=""
for _xmllint in xmllint for _xmllint in xmllint
do do
$_xmllint --version > /dev/null 2>&1 && XMLLINT=$_xmllint $_xmllint --version > /dev/null 2>&1 && XMLLINT=$_xmllint
test -n "$XMLLINT" && break test -n "$XMLLINT" && break
done done
# Distinguish GNU date from BSD date # Distinguish GNU date from BSD date
if date +%s -d "1 week ago" > /dev/null 2>&1 ; then if date +%s -d "1 week ago" > /dev/null 2>&1 ; then
last_week() { date +%s -d "1 week ago" ; } last_week() { date +%s -d "1 week ago" ; }
else else
last_week() { date -v1w +%s; } last_week() { date -v1w +%s; }
fi fi
# Distinguish GNU SHA executables from BSD ones # Distinguish GNU SHA executables from BSD ones
if which sha256sum > /dev/null 2>&1 ; then if which sha256sum > /dev/null 2>&1 ; then
SHA256=sha256sum SHA256=sha256sum
else else
SHA256=sha256 SHA256=sha256
fi fi
### CHECK WORKING DIRECTORY ### CHECK WORKING DIRECTORY
@ -67,6 +90,8 @@ fi
if test `git describe` = `git describe --dirty` ; then if test `git describe` = `git describe --dirty` ; then
: :
else else
# Don't want any local changes, since those won't be
# reflected in the worktrees and we might miss a string change.
echo "! There are local changes." echo "! There are local changes."
exit 1 exit 1
fi fi
@ -75,13 +100,20 @@ DATE_PREV=$( git log -1 translation --date=unix | sed -e '/^Date:/s+.*:++p' -e d
DATE_HEAD=$( last_week ) DATE_HEAD=$( last_week )
test "$DATE_PREV" -le "$DATE_HEAD" || { echo "! Translation tag has not aged enough." ; git log -1 translation ; exit 1 ; } test "$DATE_PREV" -le "$DATE_HEAD" || { echo "! Translation tag has not aged enough." ; git log -1 translation ; exit 1 ; }
# Tag is good, do real work of checking strings: collect names of relevant files # Tag is good, check that necessary files exist. The list of
# files is hard-coded, but should match what is in the Transifex config.
test -f ".tx/config" || { echo "! No Transifex configuration is present." ; exit 1 ; } test -f ".tx/config" || { echo "! No Transifex configuration is present." ; exit 1 ; }
for f in $TX_FILE_LIST ; do for f in $TX_FILE_LIST ; do
test -f $f || { echo "! Translation file '$f' does not exist." ; exit 1 ; } test -f $f || { echo "! Translation file '$f' does not exist." ; exit 1 ; }
done done
# The state of translations ### COMPARE TRANSLATIONS
#
#
# The state of translations; assume that sha256 is enough
# to distinguish changed translations when we cat all the
# string sources together.
tx_sum() tx_sum()
{ {
CURDIR=`pwd` CURDIR=`pwd`

View File

@ -1,4 +1,18 @@
#!/bin/sh #!/bin/sh
### LICENSE
# === This file is part of Calamares - <https://github.com/calamares> ===
#
# SPDX-License-Identifier: BSD-2-Clause
# SPDX-FileCopyrightText: 2017-2020 Adriaan de Groot <groot@kde.org>
# SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac <teo@kde.org>
#
# This file is Free Software: you can redistribute it and/or modify
# it under the terms of the 2-clause BSD License.
#
### END LICENSE
### USAGE
# #
# Fetch the Transifex translations for Calamares and incorporate them # Fetch the Transifex translations for Calamares and incorporate them
# into the source tree, adding commits of the different files. # into the source tree, adding commits of the different files.
@ -6,6 +20,8 @@
# Run this (occasionally) at the top-level directory to get # Run this (occasionally) at the top-level directory to get
# new translations. See also CMakeLists.txt and ci/txstats.py # new translations. See also CMakeLists.txt and ci/txstats.py
# for update instructions. # for update instructions.
#
### END USAGE
### INITIAL SETUP ### INITIAL SETUP
# #
@ -32,8 +48,8 @@ test -f "calamares.desktop" || { echo "! Not at Calamares top-level" ; exit 1 ;
XMLLINT="" XMLLINT=""
for _xmllint in xmllint for _xmllint in xmllint
do do
$_xmllint --version > /dev/null 2>&1 && XMLLINT=$_xmllint $_xmllint --version > /dev/null 2>&1 && XMLLINT=$_xmllint
test -n "$XMLLINT" && break test -n "$XMLLINT" && break
done done
# XMLLINT is optional # XMLLINT is optional
@ -53,9 +69,9 @@ tx pull --force --source --all
# so clean them up after pulling. # so clean them up after pulling.
# #
drop_language() { drop_language() {
rm -rf lang/python/"$1" src/modules/dummypythonqt/lang/"$1" lang/calamares_"$1".ts rm -rf lang/python/"$1" src/modules/dummypythonqt/lang/"$1" lang/calamares_"$1".ts
grep -v "\\[$1]" calamares.desktop > calamares.desktop.new grep -v "\\[$1]" calamares.desktop > calamares.desktop.new
mv calamares.desktop.new calamares.desktop mv calamares.desktop.new calamares.desktop
} }
drop_language es_ES drop_language es_ES
@ -68,10 +84,10 @@ mv calamares.desktop.new calamares.desktop
# And fixup the XML files like in txpush.sh # And fixup the XML files like in txpush.sh
if test -n "$XMLLINT" ; then if test -n "$XMLLINT" ; then
for TS_FILE in lang/calamares_*.ts for TS_FILE in lang/calamares_*.ts
do do
$XMLLINT --c14n11 "$TS_FILE" | { echo "<!DOCTYPE TS>" ; cat - ; } | $XMLLINT --format --encode utf-8 -o "$TS_FILE".new - && mv "$TS_FILE".new "$TS_FILE" $XMLLINT --c14n11 "$TS_FILE" | { echo "<!DOCTYPE TS>" ; cat - ; } | $XMLLINT --format --encode utf-8 -o "$TS_FILE".new - && mv "$TS_FILE".new "$TS_FILE"
done done
fi fi
@ -108,24 +124,24 @@ git diff --numstat src/modules | awk '($1==1 && $2==1){print $3}' | xargs git ch
# Go through the Python modules; those with a lang/ subdir have their # Go through the Python modules; those with a lang/ subdir have their
# own complete gettext-based setup. # own complete gettext-based setup.
for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d) ; do for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d) ; do
FILES=$(find "$MODULE_DIR" -name "*.py" -a -type f) FILES=$(find "$MODULE_DIR" -name "*.py" -a -type f)
if test -n "$FILES" ; then if test -n "$FILES" ; then
MODULE_NAME=$(basename ${MODULE_DIR}) MODULE_NAME=$(basename ${MODULE_DIR})
if [ -d ${MODULE_DIR}/lang ]; then if [ -d ${MODULE_DIR}/lang ]; then
# Convert PO files to MO files # Convert PO files to MO files
for POFILE in $(find ${MODULE_DIR} -name "*.po") ; do for POFILE in $(find ${MODULE_DIR} -name "*.po") ; do
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' $POFILE sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' $POFILE
msgfmt -o ${POFILE%.po}.mo $POFILE msgfmt -o ${POFILE%.po}.mo $POFILE
done done
git add --verbose ${MODULE_DIR}/lang/* git add --verbose ${MODULE_DIR}/lang/*
git commit "$AUTHOR" --message="i18n: [${MODULE_NAME}] $BOILERPLATE" | true git commit "$AUTHOR" --message="i18n: [${MODULE_NAME}] $BOILERPLATE" | true
fi fi
fi fi
done done
for POFILE in $(find lang -name "python.po") ; do for POFILE in $(find lang -name "python.po") ; do
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' $POFILE sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' $POFILE
msgfmt -o ${POFILE%.po}.mo $POFILE msgfmt -o ${POFILE%.po}.mo $POFILE
done done
git add --verbose lang/python* git add --verbose lang/python*
git commit "$AUTHOR" --message="i18n: [python] $BOILERPLATE" | true git commit "$AUTHOR" --message="i18n: [python] $BOILERPLATE" | true

View File

@ -1,13 +1,33 @@
#!/bin/sh #!/bin/sh
### LICENSE
# === This file is part of Calamares - <https://github.com/calamares> ===
#
# SPDX-License-Identifier: BSD-2-Clause
# SPDX-FileCopyrightText: 2017-2020 Adriaan de Groot <groot@kde.org>
# SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac <teo@kde.org>
#
# This file is Free Software: you can redistribute it and/or modify
# it under the terms of the 2-clause BSD License.
#
### END LICENSE
### USAGE
# #
# Extract translations from Calamares source and send them # Extract translations from Calamares source and send them
# to Transifex. # to Transifex. Also (forcibly) updates the git "translation"
# tag to document that source texts were updated and sent;
# this is used by txcheck.sh to ensure that there's enough
# time between updates and releases, and that strings don't
# change between updates and releases.
# #
# Run this at the top-level. # Run this at the top-level.
# #
# Use the --no-tx option to do the extraction, but not the # Use the --no-tx option to do the extraction, but not the
# pushing-to-Transifex part. This can be useful to check for # pushing-to-Transifex part. This can be useful to check for
# new strings or when testing the tools themselves. # new strings or when testing the tools themselves.
#
### END USAGE
### INITIAL SETUP ### INITIAL SETUP
# #
@ -28,21 +48,21 @@ test -f ".tx/config" || { echo "! Not at Calamares top-level" ; exit 1 ; }
test -f "calamares.desktop" || { echo "! Not at Calamares top-level" ; exit 1 ; } test -f "calamares.desktop" || { echo "! Not at Calamares top-level" ; exit 1 ; }
if test "x$1" = "x--no-tx" ; then if test "x$1" = "x--no-tx" ; then
# tx is the transifex command -- eat its arguments and do nothing # tx is the transifex command -- eat its arguments and do nothing
tx() { tx() {
echo "Skipped tx $*" echo "Skipped tx $*"
} }
# txtag is used to tag in git to measure changes -- skip it too # txtag is used to tag in git to measure changes -- skip it too
txtag() { txtag() {
echo "Skipped tx tagging." echo "Skipped tx tagging."
} }
else else
# tx is the regular transifex command # tx is the regular transifex command
# txtag is used to tag in git to measure changes # txtag is used to tag in git to measure changes
txtag() { txtag() {
git tag -f translation git tag -f translation
git push --force origin translation git push --force origin translation
} }
fi fi
@ -52,18 +72,18 @@ fi
LUPDATE="" LUPDATE=""
for _lupdate in lupdate-qt5 lupdate for _lupdate in lupdate-qt5 lupdate
do do
export QT_SELECT=5 export QT_SELECT=5
$_lupdate -version > /dev/null 2>&1 || export QT_SELECT=qt5 $_lupdate -version > /dev/null 2>&1 || export QT_SELECT=qt5
$_lupdate -version > /dev/null 2>&1 && LUPDATE=$_lupdate $_lupdate -version > /dev/null 2>&1 && LUPDATE=$_lupdate
test -n "$LUPDATE" && break test -n "$LUPDATE" && break
done done
test -n "$LUPDATE" || { echo "! No working lupdate" ; lupdate -version ; exit 1 ; } test -n "$LUPDATE" || { echo "! No working lupdate" ; lupdate -version ; exit 1 ; }
XMLLINT="" XMLLINT=""
for _xmllint in xmllint for _xmllint in xmllint
do do
$_xmllint --version > /dev/null 2>&1 && XMLLINT=$_xmllint $_xmllint --version > /dev/null 2>&1 && XMLLINT=$_xmllint
test -n "$XMLLINT" && break test -n "$XMLLINT" && break
done done
# XMLLINT is optional # XMLLINT is optional
@ -82,8 +102,8 @@ $LUPDATE -no-obsolete $_srcdirs -ts lang/calamares_en.ts
# $LUPDATE -no-obsolete -extensions cxxtr src/libcalamares/locale -ts lang/tz_en.ts # $LUPDATE -no-obsolete -extensions cxxtr src/libcalamares/locale -ts lang/tz_en.ts
if test -n "$XMLLINT" ; then if test -n "$XMLLINT" ; then
TS_FILE="lang/calamares_en.ts" TS_FILE="lang/calamares_en.ts"
$XMLLINT --c14n11 "$TS_FILE" | { echo "<!DOCTYPE TS>" ; cat - ; } | $XMLLINT --format --encode utf-8 -o "$TS_FILE".new - && mv "$TS_FILE".new "$TS_FILE" $XMLLINT --c14n11 "$TS_FILE" | { echo "<!DOCTYPE TS>" ; cat - ; } | $XMLLINT --format --encode utf-8 -o "$TS_FILE".new - && mv "$TS_FILE".new "$TS_FILE"
fi fi
tx push --source --no-interactive -r calamares.calamares-master tx push --source --no-interactive -r calamares.calamares-master
@ -103,29 +123,29 @@ PYGETTEXT="xgettext --keyword=_n:1,2 -L python"
SHARED_PYTHON="" SHARED_PYTHON=""
for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d) ; do for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d) ; do
FILES=$(find "$MODULE_DIR" -name "*.py" -a -type f) FILES=$(find "$MODULE_DIR" -name "*.py" -a -type f)
if test -n "$FILES" ; then if test -n "$FILES" ; then
MODULE_NAME=$(basename ${MODULE_DIR}) MODULE_NAME=$(basename ${MODULE_DIR})
if [ -d ${MODULE_DIR}/lang ]; then if [ -d ${MODULE_DIR}/lang ]; then
${PYGETTEXT} -p ${MODULE_DIR}/lang -d ${MODULE_NAME} -o ${MODULE_NAME}.pot ${MODULE_DIR}/*.py ${PYGETTEXT} -p ${MODULE_DIR}/lang -d ${MODULE_NAME} -o ${MODULE_NAME}.pot ${MODULE_DIR}/*.py
POTFILE="${MODULE_DIR}/lang/${MODULE_NAME}.pot" POTFILE="${MODULE_DIR}/lang/${MODULE_NAME}.pot"
if [ -f "$POTFILE" ]; then if [ -f "$POTFILE" ]; then
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE" sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
tx set -r calamares.${MODULE_NAME} --source -l en "$POTFILE" tx set -r calamares.${MODULE_NAME} --source -l en "$POTFILE"
tx push --source --no-interactive -r calamares.${MODULE_NAME} tx push --source --no-interactive -r calamares.${MODULE_NAME}
fi fi
else else
SHARED_PYTHON="$SHARED_PYTHON $FILES" SHARED_PYTHON="$SHARED_PYTHON $FILES"
fi fi
fi fi
done done
if test -n "$SHARED_PYTHON" ; then if test -n "$SHARED_PYTHON" ; then
${PYGETTEXT} -p lang -d python -o python.pot $SHARED_PYTHON ${PYGETTEXT} -p lang -d python -o python.pot $SHARED_PYTHON
POTFILE="lang/python.pot" POTFILE="lang/python.pot"
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE" sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
tx set -r calamares.python --source -l en "$POTFILE" tx set -r calamares.python --source -l en "$POTFILE"
tx push --source --no-interactive -r calamares.python tx push --source --no-interactive -r calamares.python
fi fi
txtag txtag

View File

@ -69,11 +69,11 @@ windowPlacement: center
strings: strings:
productName: "@{NAME}" productName: "@{NAME}"
shortProductName: Generic shortProductName: Generic
version: 2017.8 LTS version: 2020.2 LTS
shortVersion: 2017.8 shortVersion: 2020.2
versionedName: Generic GNU/Linux 2017.8 LTS "Soapy Sousaphone" versionedName: Fancy GNU/Linux 2020.2 LTS "Turgid Tuba"
shortVersionedName: Generic 2017.8 shortVersionedName: FancyGL 2020.2
bootloaderEntryName: Generic bootloaderEntryName: FancyGL
productUrl: https://calamares.io/ productUrl: https://calamares.io/
supportUrl: https://github.com/calamares/calamares/issues supportUrl: https://github.com/calamares/calamares/issues
knownIssuesUrl: https://calamares.io/about/ knownIssuesUrl: https://calamares.io/about/

View File

@ -42,12 +42,17 @@
#include <QScreen> #include <QScreen>
#include <QTimer> #include <QTimer>
/// @brief Convenience for "are the settings in debug mode"
static bool
isDebug()
{
return Calamares::Settings::instance() && Calamares::Settings::instance()->debugMode();
}
CalamaresApplication::CalamaresApplication( int& argc, char* argv[] ) CalamaresApplication::CalamaresApplication( int& argc, char* argv[] )
: QApplication( argc, argv ) : QApplication( argc, argv )
, m_mainwindow( nullptr ) , m_mainwindow( nullptr )
, m_moduleManager( nullptr ) , m_moduleManager( nullptr )
, m_debugMode( false )
{ {
// Setting the organization name makes the default cache // Setting the organization name makes the default cache
// directory -- where Calamares stores logs, for instance -- // directory -- where Calamares stores logs, for instance --
@ -59,8 +64,6 @@ CalamaresApplication::CalamaresApplication( int& argc, char* argv[] )
setApplicationName( QStringLiteral( CALAMARES_APPLICATION_NAME ) ); setApplicationName( QStringLiteral( CALAMARES_APPLICATION_NAME ) );
setApplicationVersion( QStringLiteral( CALAMARES_VERSION ) ); setApplicationVersion( QStringLiteral( CALAMARES_VERSION ) );
CalamaresUtils::installTranslator( QLocale::system(), QString(), this );
QFont f = font(); QFont f = font();
CalamaresUtils::setDefaultFontSize( f.pointSize() ); CalamaresUtils::setDefaultFontSize( f.pointSize() );
} }
@ -73,15 +76,20 @@ CalamaresApplication::init()
cDebug() << "Calamares version:" << CALAMARES_VERSION; cDebug() << "Calamares version:" << CALAMARES_VERSION;
cDebug() << " languages:" << QString( CALAMARES_TRANSLATION_LANGUAGES ).replace( ";", ", " ); cDebug() << " languages:" << QString( CALAMARES_TRANSLATION_LANGUAGES ).replace( ";", ", " );
setQuitOnLastWindowClosed( false ); if ( !Calamares::Settings::instance() )
{
cError() << "Must create Calamares::Settings before the application.";
::exit( 1 );
}
initQmlPath(); initQmlPath();
initSettings();
initBranding(); initBranding();
CalamaresUtils::installTranslator( QLocale::system(), QString(), this );
setQuitOnLastWindowClosed( false );
setWindowIcon( QIcon( Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductIcon ) ) ); setWindowIcon( QIcon( Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductIcon ) ) );
cDebug() << "STARTUP: initQmlPath, initSettings, initBranding done"; cDebug() << "STARTUP: initSettings, initQmlPath, initBranding done";
initModuleManager(); //also shows main window initModuleManager(); //also shows main window
@ -103,20 +111,6 @@ CalamaresApplication::instance()
} }
void
CalamaresApplication::setDebug( bool enabled )
{
m_debugMode = enabled;
}
bool
CalamaresApplication::isDebug()
{
return m_debugMode;
}
CalamaresWindow* CalamaresWindow*
CalamaresApplication::mainWindow() CalamaresApplication::mainWindow()
{ {
@ -152,35 +146,6 @@ qmlDirCandidates( bool assumeBuilddir )
} }
static QStringList
settingsFileCandidates( bool assumeBuilddir )
{
static const char settings[] = "settings.conf";
QStringList settingsPaths;
if ( CalamaresUtils::isAppDataDirOverridden() )
{
settingsPaths << CalamaresUtils::appDataDir().absoluteFilePath( settings );
}
else
{
if ( assumeBuilddir )
{
settingsPaths << QDir::current().absoluteFilePath( settings );
}
if ( CalamaresUtils::haveExtraDirs() )
for ( auto s : CalamaresUtils::extraConfigDirs() )
{
settingsPaths << ( s + settings );
}
settingsPaths << CMAKE_INSTALL_FULL_SYSCONFDIR "/calamares/settings.conf"; // String concat
settingsPaths << CalamaresUtils::appDataDir().absoluteFilePath( settings );
}
return settingsPaths;
}
static QStringList static QStringList
brandingFileCandidates( bool assumeBuilddir, const QString& brandingFilename ) brandingFileCandidates( bool assumeBuilddir, const QString& brandingFilename )
{ {
@ -246,49 +211,6 @@ CalamaresApplication::initQmlPath()
} }
void
CalamaresApplication::initSettings()
{
QStringList settingsFileCandidatesByPriority = settingsFileCandidates( isDebug() );
QFileInfo settingsFile;
bool found = false;
foreach ( const QString& path, settingsFileCandidatesByPriority )
{
QFileInfo pathFi( path );
if ( pathFi.exists() && pathFi.isReadable() )
{
settingsFile = pathFi;
found = true;
break;
}
}
if ( !found || !settingsFile.exists() || !settingsFile.isReadable() )
{
cError() << "Cowardly refusing to continue startup without settings."
<< Logger::DebugList( settingsFileCandidatesByPriority );
if ( CalamaresUtils::isAppDataDirOverridden() )
{
cError() << "FATAL: explicitly configured application data directory is missing settings.conf";
}
else
{
cError() << "FATAL: none of the expected configuration file paths exist.";
}
::exit( EXIT_FAILURE );
}
auto* settings = new Calamares::Settings( settingsFile.absoluteFilePath(), isDebug(), this ); // Creates singleton
if ( settings->modulesSequence().count() < 1 )
{
cError() << "FATAL: no sequence set.";
::exit( EXIT_FAILURE );
}
}
void void
CalamaresApplication::initBranding() CalamaresApplication::initBranding()
{ {

View File

@ -49,16 +49,6 @@ public:
void init(); void init();
static CalamaresApplication* instance(); static CalamaresApplication* instance();
/**
* @brief setDebug controls whether debug mode is enabled
*/
void setDebug( bool enabled );
/**
* @brief isDebug returns true if running in debug mode, otherwise false.
*/
bool isDebug();
/** /**
* @brief mainWindow returns the Calamares application main window. * @brief mainWindow returns the Calamares application main window.
*/ */
@ -70,16 +60,14 @@ private slots:
void initFailed( const QStringList& l ); void initFailed( const QStringList& l );
private: private:
// Initialization steps happen in this order
void initQmlPath(); void initQmlPath();
void initSettings();
void initBranding(); void initBranding();
void initModuleManager(); void initModuleManager();
void initJobQueue(); void initJobQueue();
CalamaresWindow* m_mainwindow; CalamaresWindow* m_mainwindow;
Calamares::ModuleManager* m_moduleManager; Calamares::ModuleManager* m_moduleManager;
bool m_debugMode;
}; };
#endif // CALAMARESAPPLICATION_H #endif // CALAMARESAPPLICATION_H

View File

@ -20,9 +20,10 @@
#include "CalamaresApplication.h" #include "CalamaresApplication.h"
#include "CalamaresConfig.h" #include "Settings.h"
#include "utils/Dirs.h" #include "utils/Dirs.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Retranslator.h"
#include "3rdparty/kdsingleapplicationguard/kdsingleapplicationguard.h" #include "3rdparty/kdsingleapplicationguard/kdsingleapplicationguard.h"
@ -35,6 +36,21 @@
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
static unsigned int
debug_level( QCommandLineParser& parser, QCommandLineOption& levelOption )
{
bool ok = true;
int l = parser.value( levelOption ).toInt( &ok );
if ( !ok || ( l < 0 ) )
{
return Logger::LOGVERBOSE;
}
else
{
return static_cast< unsigned int >( l ); // l >= 0
}
}
static void static void
handle_args( CalamaresApplication& a ) handle_args( CalamaresApplication& a )
{ {
@ -42,6 +58,9 @@ handle_args( CalamaresApplication& a )
"Also look in current directory for configuration. Implies -D8." ); "Also look in current directory for configuration. Implies -D8." );
QCommandLineOption debugLevelOption( QCommandLineOption debugLevelOption(
QStringLiteral( "D" ), "Verbose output for debugging purposes (0-8).", "level" ); QStringLiteral( "D" ), "Verbose output for debugging purposes (0-8).", "level" );
QCommandLineOption debugTxOption( QStringList { "T", "debug-translation" },
"Also look in the current directory for translation." );
QCommandLineOption configOption( QCommandLineOption configOption(
QStringList { "c", "config" }, "Configuration directory to use, for testing purposes.", "config" ); QStringList { "c", "config" }, "Configuration directory to use, for testing purposes.", "config" );
QCommandLineOption xdgOption( QStringList { "X", "xdg-config" }, "Use XDG_{CONFIG,DATA}_DIRS as well." ); QCommandLineOption xdgOption( QStringList { "X", "xdg-config" }, "Use XDG_{CONFIG,DATA}_DIRS as well." );
@ -55,29 +74,11 @@ handle_args( CalamaresApplication& a )
parser.addOption( debugLevelOption ); parser.addOption( debugLevelOption );
parser.addOption( configOption ); parser.addOption( configOption );
parser.addOption( xdgOption ); parser.addOption( xdgOption );
parser.addOption( debugTxOption );
parser.process( a ); parser.process( a );
a.setDebug( parser.isSet( debugOption ) ); Logger::setupLogLevel( parser.isSet( debugOption ) ? Logger::LOGVERBOSE : debug_level( parser, debugLevelOption ) );
if ( parser.isSet( debugOption ) )
{
Logger::setupLogLevel( Logger::LOGVERBOSE );
}
else if ( parser.isSet( debugLevelOption ) )
{
bool ok = true;
int l = parser.value( debugLevelOption ).toInt( &ok );
unsigned int dlevel = 0;
if ( !ok || ( l < 0 ) )
{
dlevel = Logger::LOGVERBOSE;
}
else
{
dlevel = static_cast< unsigned int >( l ); // l >= 0
}
Logger::setupLogLevel( dlevel );
}
if ( parser.isSet( configOption ) ) if ( parser.isSet( configOption ) )
{ {
CalamaresUtils::setAppDataDir( QDir( parser.value( configOption ) ) ); CalamaresUtils::setAppDataDir( QDir( parser.value( configOption ) ) );
@ -86,6 +87,9 @@ handle_args( CalamaresApplication& a )
{ {
CalamaresUtils::setXdgDirs(); CalamaresUtils::setXdgDirs();
} }
CalamaresUtils::setAllowLocalTranslation( parser.isSet( debugOption ) || parser.isSet( debugTxOption ) );
Calamares::Settings::init( parser.isSet( debugOption ) );
a.init();
} }
int int
@ -113,14 +117,11 @@ main( int argc, char* argv[] )
// TODO: umount anything in /tmp/calamares-... as an emergency save function // TODO: umount anything in /tmp/calamares-... as an emergency save function
#endif #endif
handle_args( a );
KDSingleApplicationGuard guard( KDSingleApplicationGuard::AutoKillOtherInstances ); KDSingleApplicationGuard guard( KDSingleApplicationGuard::AutoKillOtherInstances );
int returnCode = 0;
if ( guard.isPrimaryInstance() ) if ( guard.isPrimaryInstance() )
{ {
a.init(); handle_args( a );
returnCode = a.exec(); return a.exec();
} }
else else
{ {
@ -135,7 +136,6 @@ main( int argc, char* argv[] )
{ {
qDebug() << " " << i.isValid() << i.pid() << i.arguments(); qDebug() << " " << i.isValid() << i.pid() << i.arguments();
} }
return 69; // EX_UNAVAILABLE on FreeBSD
} }
return returnCode;
} }

View File

@ -225,7 +225,7 @@ main( int argc, char* argv[] )
return 1; return 1;
} }
std::unique_ptr< Calamares::Settings > settings_p( new Calamares::Settings( QString(), true ) ); std::unique_ptr< Calamares::Settings > settings_p( Calamares::Settings::init( QString() ) );
std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) ); std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) );
QMainWindow* mw = nullptr; QMainWindow* mw = nullptr;

View File

@ -171,6 +171,18 @@ if ( ECM_FOUND AND BUILD_TESTING )
) )
calamares_automoc( libcalamarestest ) calamares_automoc( libcalamarestest )
ecm_add_test(
utils/TestPaths.cpp
TEST_NAME
libcalamarestestpaths
LINK_LIBRARIES
calamares
Qt5::Core
Qt5::Test
)
calamares_automoc( libcalamarestestpaths )
ecm_add_test( ecm_add_test(
geoip/GeoIPTests.cpp geoip/GeoIPTests.cpp
${geoip_src} ${geoip_src}

View File

@ -21,6 +21,7 @@
#include "Settings.h" #include "Settings.h"
#include "CalamaresConfig.h"
#include "utils/Dirs.h" #include "utils/Dirs.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Yaml.h" #include "utils/Yaml.h"
@ -193,8 +194,8 @@ interpretSequence( const YAML::Node& node, Settings::ModuleSequence& moduleSeque
} }
} }
Settings::Settings( const QString& settingsFilePath, bool debugMode, QObject* parent ) Settings::Settings( const QString& settingsFilePath, bool debugMode )
: QObject( parent ) : QObject()
, m_debug( debugMode ) , m_debug( debugMode )
, m_doChroot( true ) , m_doChroot( true )
, m_promptInstall( false ) , m_promptInstall( false )
@ -265,37 +266,93 @@ Settings::brandingComponentName() const
return m_brandingComponentName; return m_brandingComponentName;
} }
static QStringList
bool settingsFileCandidates( bool assumeBuilddir )
Settings::showPromptBeforeExecution() const
{ {
return m_promptInstall; static const char settings[] = "settings.conf";
QStringList settingsPaths;
if ( CalamaresUtils::isAppDataDirOverridden() )
{
settingsPaths << CalamaresUtils::appDataDir().absoluteFilePath( settings );
}
else
{
if ( assumeBuilddir )
{
settingsPaths << QDir::current().absoluteFilePath( settings );
}
if ( CalamaresUtils::haveExtraDirs() )
for ( auto s : CalamaresUtils::extraConfigDirs() )
{
settingsPaths << ( s + settings );
}
settingsPaths << CMAKE_INSTALL_FULL_SYSCONFDIR "/calamares/settings.conf"; // String concat
settingsPaths << CalamaresUtils::appDataDir().absoluteFilePath( settings );
}
return settingsPaths;
} }
Settings*
bool Settings::init( bool debugMode )
Settings::debugMode() const
{ {
return m_debug; if ( s_instance )
{
cWarning() << "Calamares::Settings already created";
return s_instance;
}
QStringList settingsFileCandidatesByPriority = settingsFileCandidates( debugMode );
QFileInfo settingsFile;
bool found = false;
foreach ( const QString& path, settingsFileCandidatesByPriority )
{
QFileInfo pathFi( path );
if ( pathFi.exists() && pathFi.isReadable() )
{
settingsFile = pathFi;
found = true;
break;
}
}
if ( !found || !settingsFile.exists() || !settingsFile.isReadable() )
{
cError() << "Cowardly refusing to continue startup without settings."
<< Logger::DebugList( settingsFileCandidatesByPriority );
if ( CalamaresUtils::isAppDataDirOverridden() )
{
cError() << "FATAL: explicitly configured application data directory is missing settings.conf";
}
else
{
cError() << "FATAL: none of the expected configuration file paths exist.";
}
::exit( EXIT_FAILURE );
}
auto* settings = new Calamares::Settings( settingsFile.absoluteFilePath(), debugMode ); // Creates singleton
if ( settings->modulesSequence().count() < 1 )
{
cError() << "FATAL: no sequence set.";
::exit( EXIT_FAILURE );
}
return settings;
} }
bool Settings*
Settings::doChroot() const Settings::init( const QString& path )
{ {
return m_doChroot; if ( s_instance )
{
cWarning() << "Calamares::Settings already created";
return s_instance;
}
return new Calamares::Settings( path, true );
} }
bool
Settings::disableCancel() const
{
return m_disableCancel;
}
bool
Settings::disableCancelDuringExec() const
{
return m_disableCancelDuringExec;
}
} // namespace Calamares } // namespace Calamares

View File

@ -35,11 +35,14 @@ namespace Calamares
class DLLEXPORT Settings : public QObject class DLLEXPORT Settings : public QObject
{ {
Q_OBJECT Q_OBJECT
explicit Settings( const QString& settingsFilePath, bool debugMode );
public: public:
explicit Settings( const QString& settingsFilePath, bool debugMode, QObject* parent = nullptr );
static Settings* instance(); static Settings* instance();
/// @brief Find a settings.conf, following @p debugMode
static Settings* init( bool debugMode );
/// @brief Explicif filename, debug is always true (for testing)
static Settings* init( const QString& filename );
QStringList modulesSearchPaths() const; QStringList modulesSearchPaths() const;
using InstanceDescription = QMap< QString, QString >; using InstanceDescription = QMap< QString, QString >;
@ -51,11 +54,31 @@ public:
QString brandingComponentName() const; QString brandingComponentName() const;
bool showPromptBeforeExecution() const; /** @brief Is this a debugging run?
*
* Returns true if Calamares is in debug mode. In debug mode,
* modules and settings are loaded from more locations, to help
* development and debugging.
*/
bool debugMode() const { return m_debug; }
bool debugMode() const; /** @brief Distinguish "install" from "OEM" modes.
*
* Returns true in "install" mode, which is where actions happen
* in a chroot -- the target system, which exists separately from
* the source system. In "OEM" mode, returns false and most actions
* apply to the *current* (host) system.
*/
bool doChroot() const { return m_doChroot; }
bool doChroot() const; /** @brief Global setting of prompt-before-install.
*
* Returns true when the configuration is such that the user
* should be prompted one-last-time before any action is taken
* that really affects the machine.
*/
bool showPromptBeforeExecution() const { return m_promptInstall; }
/** @brief Distinguish between "install" and "setup" modes. /** @brief Distinguish between "install" and "setup" modes.
* *
* This influences user-visible strings, for instance using the * This influences user-visible strings, for instance using the
@ -64,9 +87,9 @@ public:
bool isSetupMode() const { return m_isSetupMode; } bool isSetupMode() const { return m_isSetupMode; }
/** @brief Global setting of disable-cancel: can't cancel ever. */ /** @brief Global setting of disable-cancel: can't cancel ever. */
bool disableCancel() const; bool disableCancel() const { return m_disableCancel; }
/** @brief Temporary setting of disable-cancel: can't cancel during exec. */ /** @brief Temporary setting of disable-cancel: can't cancel during exec. */
bool disableCancelDuringExec() const; bool disableCancelDuringExec() const { return m_disableCancelDuringExec; }
private: private:
static Settings* s_instance; static Settings* s_instance;

View File

@ -19,6 +19,7 @@
#include "Tests.h" #include "Tests.h"
#include "Manager.h" #include "Manager.h"
#include "utils/Logger.h"
#include <QtTest/QtTest> #include <QtTest/QtTest>
@ -43,6 +44,9 @@ NetworkTests::testInstance()
void void
NetworkTests::testPing() NetworkTests::testPing()
{ {
auto& nam = CalamaresUtils::Network::Manager::instance(); using namespace CalamaresUtils::Network;
QVERIFY( nam.synchronousPing( QUrl( "https://www.kde.org" ) ) ); Logger::setupLogLevel( Logger::LOGVERBOSE );
auto& nam = Manager::instance();
auto r = nam.synchronousPing( QUrl( "https://www.kde.org" ), RequestOptions( RequestOptions::FollowRedirect ) );
QVERIFY( r );
} }

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2014, Teo Mrnjavac <teo@kde.org> * Copyright 2014, Teo Mrnjavac <teo@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org> * Copyright 2017-2018, 2020, Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -263,11 +263,16 @@ System::runCommand( System::RunLocation location,
return ProcessResult( r, output ); return ProcessResult( r, output );
} }
/// @brief Cheap check if a path is absolute.
static inline bool
isAbsolutePath( const QString& path )
{
return path.startsWith( '/' );
}
QString QString
System::targetPath( const QString& path ) const System::targetPath( const QString& path ) const
{ {
QString completePath;
if ( doChroot() ) if ( doChroot() )
{ {
Calamares::GlobalStorage* gs Calamares::GlobalStorage* gs
@ -275,18 +280,17 @@ System::targetPath( const QString& path ) const
if ( !gs || !gs->contains( "rootMountPoint" ) ) if ( !gs || !gs->contains( "rootMountPoint" ) )
{ {
cWarning() << "No rootMountPoint in global storage, cannot create target file" << path; cWarning() << "No rootMountPoint in global storage, cannot name target file" << path;
return QString(); return QString();
} }
completePath = gs->value( "rootMountPoint" ).toString() + '/' + path; QString root = gs->value( "rootMountPoint" ).toString();
return isAbsolutePath( path ) ? ( root + path ) : ( root + '/' + path );
} }
else else
{ {
completePath = QStringLiteral( "/" ) + path; return isAbsolutePath( path ) ? path : ( QStringLiteral( "/" ) + path );
} }
return completePath;
} }
QString QString
@ -327,6 +331,59 @@ System::createTargetFile( const QString& path, const QByteArray& contents ) cons
return QFileInfo( f ).canonicalFilePath(); return QFileInfo( f ).canonicalFilePath();
} }
void
System::removeTargetFile( const QString& path ) const
{
if ( !isAbsolutePath( path ) )
{
cWarning() << "Will not remove non-absolute path" << path;
return;
}
QString target = targetPath( path );
if ( !target.isEmpty() )
{
QFile::remove( target );
}
// If it was empty, a warning was already printed
}
bool
System::createTargetDirs( const QString& path ) const
{
if ( !isAbsolutePath( path ) )
{
cWarning() << "Will not create basedirs for non-absolute path" << path;
return false;
}
QString target = targetPath( path );
if ( target.isEmpty() )
{
// If it was empty, a warning was already printed
return false;
}
QString root = Calamares::JobQueue::instance()->globalStorage()->value( "rootMountPoint" ).toString();
if ( root.isEmpty() )
{
return false;
}
QDir d( root );
if ( !d.exists() )
{
cWarning() << "Root mountpoint" << root << "does not exist.";
return false;
}
return d.mkpath( target ); // This re-does everything starting from the **host** /
}
bool
System::createTargetParentDirs( const QString& filePath ) const
{
return createTargetDirs( QFileInfo( filePath ).dir().path() );
}
QPair< quint64, float > QPair< quint64, float >
System::getTotalMemoryB() const System::getTotalMemoryB() const

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2014, Teo Mrnjavac <teo@kde.org> * Copyright 2014, Teo Mrnjavac <teo@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org> * Copyright 2017-2018, 2020, Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -246,6 +246,33 @@ public:
*/ */
DLLEXPORT QString createTargetFile( const QString& path, const QByteArray& contents ) const; DLLEXPORT QString createTargetFile( const QString& path, const QByteArray& contents ) const;
/** @brief Remove a file from the target system.
*
* @param path Path to the file; this is interpreted from the root
* of the target system (@see targetPath()).
*
* Does no error checking to see if the target file was really removed.
*/
DLLEXPORT void removeTargetFile( const QString& path ) const;
/** @brief Ensure that the directory @p path exists
*
* @param path a full pathname to a desired directory.
*
* All the directory components including the last path component are
* created, as needed. Returns true on success.
*
* @see QDir::mkpath
*/
DLLEXPORT bool createTargetDirs( const QString& path ) const;
/** @brief Convenience to create parent directories of a file path.
*
* Creates all the parent directories until the last
* component of @p filePath . @see createTargetDirs()
*/
DLLEXPORT bool createTargetParentDirs( const QString& filePath ) const;
/** /**
* @brief getTotalMemoryB returns the total main memory, in bytes. * @brief getTotalMemoryB returns the total main memory, in bytes.
* *

View File

@ -27,6 +27,8 @@
#include <QEvent> #include <QEvent>
#include <QTranslator> #include <QTranslator>
static bool s_allowLocalTranslations = false;
/** @brief Helper class for loading translations /** @brief Helper class for loading translations
* *
* This is used by the loadSingletonTranslator() function to hand off * This is used by the loadSingletonTranslator() function to hand off
@ -131,7 +133,7 @@ static bool
tryLoad( QTranslator* translator, const QString& prefix, const QString& localeName ) tryLoad( QTranslator* translator, const QString& prefix, const QString& localeName )
{ {
// In debug-mode, try loading from the current directory // In debug-mode, try loading from the current directory
if ( Calamares::Settings::instance() && Calamares::Settings::instance()->debugMode() && translator->load( prefix + localeName ) ) if ( s_allowLocalTranslations && translator->load( prefix + localeName ) )
{ {
cDebug() << Logger::SubEntry << "Loaded local translation" << prefix << localeName; cDebug() << Logger::SubEntry << "Loaded local translation" << prefix << localeName;
return true; return true;
@ -139,14 +141,15 @@ tryLoad( QTranslator* translator, const QString& prefix, const QString& localeNa
// Or load from appDataDir -- often /usr/share/calamares -- subdirectory land/ // Or load from appDataDir -- often /usr/share/calamares -- subdirectory land/
QDir localeData( CalamaresUtils::appDataDir() ); QDir localeData( CalamaresUtils::appDataDir() );
if ( localeData.exists() && translator->load( localeData.absolutePath() + QStringLiteral("/lang/") + prefix + localeName) ) if ( localeData.exists()
&& translator->load( localeData.absolutePath() + QStringLiteral( "/lang/" ) + prefix + localeName ) )
{ {
cDebug() << Logger::SubEntry << "Loaded appdata translation" << prefix << localeName; cDebug() << Logger::SubEntry << "Loaded appdata translation" << prefix << localeName;
return true; return true;
} }
// Or from QRC (most common) // Or from QRC (most common)
if ( translator->load( QStringLiteral( ":/lang/") + prefix + localeName ) ) if ( translator->load( QStringLiteral( ":/lang/" ) + prefix + localeName ) )
{ {
cDebug() << Logger::SubEntry << "Loaded QRC translation" << prefix << localeName; cDebug() << Logger::SubEntry << "Loaded QRC translation" << prefix << localeName;
return true; return true;
@ -260,5 +263,11 @@ Retranslator::eventFilter( QObject* obj, QEvent* e )
return QObject::eventFilter( obj, e ); return QObject::eventFilter( obj, e );
} }
void
setAllowLocalTranslation( bool allow )
{
s_allowLocalTranslations = allow;
}
} // namespace CalamaresUtils } // namespace CalamaresUtils

View File

@ -42,6 +42,15 @@ DLLEXPORT void installTranslator( const QLocale& locale, const QString& branding
DLLEXPORT QString translatorLocaleName(); DLLEXPORT QString translatorLocaleName();
/** @brief Set @p allow to true to load translations from current dir.
*
* If false, (or never called) the translations are loaded only from
* system locations (the AppData dir) and from QRC (compiled in).
* Enable local translations to test translations stored in the
* current directory.
*/
DLLEXPORT void setAllowLocalTranslation( bool allow );
class Retranslator : public QObject class Retranslator : public QObject
{ {
Q_OBJECT Q_OBJECT

View File

@ -0,0 +1,165 @@
/* === This file is part of Calamares - <https://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/>.
*/
#include "CalamaresUtilsSystem.h"
#include "Entropy.h"
#include "Logger.h"
#include "UMask.h"
#include "Yaml.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include <QDir>
// #include <QTemporaryFile>
#include <QtTest/QtTest>
// #include <fcntl.h>
// #include <sys/stat.h>
// #include <unistd.h>
class TestPaths : public QObject
{
Q_OBJECT
public:
TestPaths() {};
virtual ~TestPaths() {};
private Q_SLOTS:
void initTestCase();
void init();
void cleanupTestCase();
void testTargetPath();
void testCreateTarget();
void testCreateTargetBasedirs();
private:
CalamaresUtils::System* m_system = nullptr; // Points to singleton instance, not owned
Calamares::GlobalStorage* m_gs = nullptr;
};
static const char testFile[] = "/calamares-testcreate";
static const char absFile[] = "/tmp/calamares-testcreate"; // With rootMountPoint prepended
void
TestPaths::initTestCase()
{
Logger::setupLogLevel( Logger::LOGDEBUG );
// Ensure we have a system object, expect it to be a "bogus" one
CalamaresUtils::System* system = CalamaresUtils::System::instance();
QVERIFY( system );
QVERIFY( system->doChroot() );
// Ensure we have a system-wide GlobalStorage with /tmp as root
if ( !Calamares::JobQueue::instance() )
{
cDebug() << "Creating new JobQueue";
(void)new Calamares::JobQueue();
}
Calamares::GlobalStorage* gs
= Calamares::JobQueue::instance() ? Calamares::JobQueue::instance()->globalStorage() : nullptr;
QVERIFY( gs );
m_system = system;
m_gs = gs;
}
void
TestPaths::cleanupTestCase()
{
QFile::remove( absFile );
}
void
TestPaths::init()
{
cDebug() << "Setting rootMountPoint";
m_gs->insert( "rootMountPoint", "/tmp" );
}
void
TestPaths::testTargetPath()
{
// Paths mapped normally
QCOMPARE( m_system->targetPath( "/etc/calamares" ), QStringLiteral( "/tmp/etc/calamares" ) );
QCOMPARE( m_system->targetPath( "//etc//calamares" ),
QStringLiteral( "/tmp//etc//calamares" ) ); // extra / are not cleaned up
QCOMPARE( m_system->targetPath( "etc/calamares" ), QStringLiteral( "/tmp/etc/calamares" ) ); // relative to root
// Weird Paths
QCOMPARE( m_system->targetPath( QString() ), QStringLiteral( "/tmp/" ) );
// Now break GS
m_gs->remove( "rootMountPoint" );
QCOMPARE( m_system->targetPath( QString() ), QString() ); // Without root, no path
}
void
TestPaths::testCreateTarget()
{
QCOMPARE( m_system->createTargetFile( testFile, "Hello" ), QString( absFile ) ); // Success
QFileInfo fi( absFile );
QVERIFY( fi.exists() );
QCOMPARE( fi.size(), 5 );
m_system->removeTargetFile( testFile );
QFileInfo fi2( absFile ); // fi caches information
QVERIFY( !fi2.exists() );
}
struct DirRemover
{
DirRemover( const QString& base, const QString& dir )
: m_base( base )
, m_dir( dir )
{
}
~DirRemover() { QDir( m_base ).rmpath( m_dir ); }
bool exists() const { return QDir( m_base ).exists( m_dir ); }
QString m_base, m_dir;
};
void
TestPaths::testCreateTargetBasedirs()
{
{
DirRemover dirrm( "/tmp", "var/lib/dbus" );
QVERIFY( m_system->createTargetDirs( "/" ) );
QVERIFY( m_system->createTargetDirs( "/var/lib/dbus" ) );
QVERIFY( QFile( "/tmp/var/lib/dbus" ).exists() );
QVERIFY( dirrm.exists() );
}
QVERIFY( !QFile( "/tmp/var/lib/dbus" ).exists() );
// QFileInfo.dir() behaves even when things don't exist
QCOMPARE( QFileInfo( "/tmp/var/lib/dbus/bogus" ).dir().path(), QStringLiteral( "/tmp/var/lib/dbus" ) );
}
QTEST_GUILESS_MAIN( TestPaths )
#include "utils/moc-warnings.h"
#include "TestPaths.moc"

View File

@ -24,6 +24,9 @@
#include "UMask.h" #include "UMask.h"
#include "Yaml.h" #include "Yaml.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QtTest/QtTest> #include <QtTest/QtTest>

View File

@ -18,6 +18,8 @@ set( calamaresui_SOURCES
utils/Paste.cpp utils/Paste.cpp
viewpages/BlankViewStep.cpp viewpages/BlankViewStep.cpp
viewpages/ExecutionViewStep.cpp
viewpages/QmlViewStep.cpp
viewpages/ViewStep.cpp viewpages/ViewStep.cpp
widgets/ClickableLabel.cpp widgets/ClickableLabel.cpp
@ -25,7 +27,6 @@ set( calamaresui_SOURCES
widgets/WaitingWidget.cpp widgets/WaitingWidget.cpp
${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp ${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp
ExecutionViewStep.cpp
Branding.cpp Branding.cpp
ViewManager.cpp ViewManager.cpp
) )

View File

@ -21,17 +21,16 @@
#include "ViewManager.h" #include "ViewManager.h"
#include "viewpages/BlankViewStep.h"
#include "viewpages/ViewStep.h"
#include "Branding.h" #include "Branding.h"
#include "ExecutionViewStep.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "Settings.h" #include "Settings.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Paste.h" #include "utils/Paste.h"
#include "utils/Retranslator.h" #include "utils/Retranslator.h"
#include "viewpages/BlankViewStep.h"
#include "viewpages/ViewStep.h"
#include "viewpages/ExecutionViewStep.h"
#include <QApplication> #include <QApplication>
#include <QBoxLayout> #include <QBoxLayout>
@ -159,13 +158,6 @@ void
ViewManager::insertViewStep( int before, ViewStep* step ) ViewManager::insertViewStep( int before, ViewStep* step )
{ {
m_steps.insert( before, step ); m_steps.insert( before, step );
QLayout* layout = step->widget()->layout();
if ( layout )
{
layout->setContentsMargins( 0, 0, 0, 0 );
}
m_stack->insertWidget( before, step->widget() );
connect( step, &ViewStep::enlarge, this, &ViewManager::enlarge ); connect( step, &ViewStep::enlarge, this, &ViewManager::enlarge );
connect( step, &ViewStep::nextStatusChanged, this, [this]( bool status ) { connect( step, &ViewStep::nextStatusChanged, this, [this]( bool status ) {
ViewStep* vs = qobject_cast< ViewStep* >( sender() ); ViewStep* vs = qobject_cast< ViewStep* >( sender() );
@ -178,6 +170,17 @@ ViewManager::insertViewStep( int before, ViewStep* step )
} }
} ); } );
if ( !step->widget() )
{
cError() << "ViewStep" << step->moduleInstanceKey() << "has no widget.";
}
QLayout* layout = step->widget()->layout();
if ( layout )
{
layout->setContentsMargins( 0, 0, 0, 0 );
}
m_stack->insertWidget( before, step->widget() );
m_stack->setCurrentIndex( 0 ); m_stack->setCurrentIndex( 0 );
step->widget()->setFocus(); step->widget()->setFocus();
} }

View File

@ -19,7 +19,6 @@
#include "ModuleManager.h" #include "ModuleManager.h"
#include "ExecutionViewStep.h"
#include "Module.h" #include "Module.h"
#include "RequirementsChecker.h" #include "RequirementsChecker.h"
#include "Settings.h" #include "Settings.h"
@ -27,6 +26,7 @@
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Yaml.h" #include "utils/Yaml.h"
#include "viewpages/ExecutionViewStep.h"
#include <QApplication> #include <QApplication>
#include <QDir> #include <QDir>
@ -129,8 +129,9 @@ ModuleManager::doInit()
} }
// At this point m_availableDescriptorsByModuleName is filled with // At this point m_availableDescriptorsByModuleName is filled with
// the modules that were found in the search paths. // the modules that were found in the search paths.
cDebug() << "Found" << m_availableDescriptorsByModuleName.count() << "modules" cDebug() << "Found"
<< m_moduleDirectoriesByModuleName.count() << "names"; << m_availableDescriptorsByModuleName.count() << "modules"
<< m_moduleDirectoriesByModuleName.count() << "names";
emit initDone(); emit initDone();
} }

View File

@ -19,12 +19,7 @@
#ifndef BLANKVIEWSTEP_H #ifndef BLANKVIEWSTEP_H
#define BLANKVIEWSTEP_H #define BLANKVIEWSTEP_H
#include <QObject> #include "viewpages/ViewStep.h"
#include <utils/PluginFactory.h>
#include <viewpages/ViewStep.h>
class QWidget;
namespace Calamares namespace Calamares
{ {

View File

@ -18,16 +18,16 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <ExecutionViewStep.h> #include "ExecutionViewStep.h"
#include "Branding.h" #include "Branding.h"
#include "Job.h" #include "Job.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "Settings.h" #include "Settings.h"
#include "ViewManager.h" #include "ViewManager.h"
#include "modulesystem/Module.h" #include "modulesystem/Module.h"
#include "modulesystem/ModuleManager.h" #include "modulesystem/ModuleManager.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "utils/Dirs.h" #include "utils/Dirs.h"
#include "utils/Logger.h" #include "utils/Logger.h"

View File

@ -0,0 +1,277 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2020, 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 "QmlViewStep.h"
#include "Branding.h"
#include "utils/Dirs.h"
#include "utils/Logger.h"
#include "utils/NamedEnum.h"
#include "utils/Variant.h"
#include "widgets/WaitingWidget.h"
#include <QQmlComponent>
#include <QQmlEngine>
#include <QQuickItem>
#include <QQuickWidget>
#include <QVBoxLayout>
#include <QWidget>
static const NamedEnumTable< Calamares::QmlViewStep::QmlSearch >&
searchNames()
{
using Search = Calamares::QmlViewStep::QmlSearch;
// *INDENT-OFF*
// clang-format off
static NamedEnumTable< Search > names {
{ QStringLiteral( "both" ), Search::Both },
{ QStringLiteral( "qrc" ), Search::QrcOnly },
{ QStringLiteral( "branding" ), Search::BrandingOnly }
};
// *INDENT-ON*
// clang-format on
return names;
}
namespace Calamares
{
QmlViewStep::QmlViewStep( const QString& name, QObject* parent )
: ViewStep( parent )
, m_name( name )
, m_widget( new QWidget )
, m_spinner( new WaitingWidget( tr( "Loading ..." ) ) )
, m_qmlWidget( new QQuickWidget )
{
QVBoxLayout* layout = new QVBoxLayout( m_widget );
layout->addWidget( m_spinner );
m_qmlWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
m_qmlWidget->setResizeMode( QQuickWidget::SizeRootObjectToView );
m_qmlWidget->engine()->addImportPath( CalamaresUtils::qmlModulesDir().absolutePath() );
// QML Loading starts when the configuration for the module is set.
}
QmlViewStep::~QmlViewStep() {}
QString
QmlViewStep::prettyName() const
{
// TODO: query the QML itself
return tr( "QML Step <i>%1</i>." ).arg( m_name );
}
} // namespace Calamares
bool
Calamares::QmlViewStep::isAtBeginning() const
{
return true;
}
bool
Calamares::QmlViewStep::isAtEnd() const
{
return true;
}
bool
Calamares::QmlViewStep::isBackEnabled() const
{
return true;
}
bool
Calamares::QmlViewStep::isNextEnabled() const
{
return true;
}
Calamares::JobList
Calamares::QmlViewStep::jobs() const
{
return JobList();
}
void
Calamares::QmlViewStep::onActivate()
{
// TODO: call into QML
}
void
Calamares::QmlViewStep::onLeave()
{
// TODO: call into QML
}
QWidget*
Calamares::QmlViewStep::widget()
{
return m_widget;
}
void
Calamares::QmlViewStep::loadComplete()
{
cDebug() << "QML component" << m_qmlFileName << m_qmlComponent->status();
if ( m_qmlComponent->status() == QQmlComponent::Error )
{
showFailedQml();
}
if ( m_qmlComponent->isReady() && !m_qmlObject )
{
cDebug() << "QML component complete" << m_qmlFileName;
// Don't do this again
disconnect( m_qmlComponent, &QQmlComponent::statusChanged, this, &QmlViewStep::loadComplete );
QObject* o = m_qmlComponent->create();
m_qmlObject = qobject_cast< QQuickItem* >( o );
if ( !m_qmlObject )
{
cError() << Logger::SubEntry << "Could not create QML from" << m_qmlFileName;
delete o;
}
else
{
// setContent() is public API, but not documented publicly.
// It is marked \internal in the Qt sources, but does exactly
// what is needed: sets up visual parent by replacing the root
// item, and handling resizes.
m_qmlWidget->setContent( QUrl( m_qmlFileName ), m_qmlComponent, m_qmlObject );
showQml();
}
}
}
void
Calamares::QmlViewStep::showQml()
{
if ( !m_qmlWidget || !m_qmlObject )
{
cDebug() << "showQml() called but no QML object";
return;
}
if ( m_spinner )
{
m_widget->layout()->removeWidget( m_spinner );
m_widget->layout()->addWidget( m_qmlWidget );
delete m_spinner;
m_spinner = nullptr;
}
else
{
cDebug() << "showQml() called twice";
}
}
/** @brief Find a suitable QML file, given the search method and name hints
*
* Returns QString() if nothing is found (which would mean the module
* is badly configured).
*/
QString
searchQmlFile( Calamares::QmlViewStep::QmlSearch method, const QString& configuredName, const QString& moduleName )
{
using QmlSearch = Calamares::QmlViewStep::QmlSearch;
cDebug() << "Looking for QML for" << moduleName;
QStringList candidates;
if ( configuredName.startsWith( '/' ) )
{
candidates << configuredName;
}
if ( ( method == QmlSearch::Both ) || ( method == QmlSearch::BrandingOnly ) )
{
QString brandDir = Calamares::Branding::instance()->componentDirectory();
candidates << ( configuredName.isEmpty() ? QString()
: QStringLiteral( "%1/%2.qml" ).arg( brandDir, configuredName ) )
<< ( moduleName.isEmpty() ? QString() : QStringLiteral( "%1/%2.qml" ).arg( brandDir, moduleName ) );
}
if ( ( method == QmlSearch::Both ) || ( method == QmlSearch::QrcOnly ) )
{
candidates << ( configuredName.isEmpty() ? QString() : QStringLiteral( ":/%1.qml" ).arg( configuredName ) )
<< ( moduleName.isEmpty() ? QString() : QStringLiteral( ":/%1.qml" ).arg( moduleName ) );
}
for ( const QString& candidate : candidates )
{
if ( candidate.isEmpty() )
{
continue;
}
cDebug() << Logger::SubEntry << "Looking at QML file" << candidate;
if ( QFile::exists( candidate ) )
{
if ( candidate.startsWith( ':' ) )
{
// Inconsistency: QFile only sees the file with :,
// but QML needs an explicit scheme (of qrc:)
return QStringLiteral( "qrc" ) + candidate;
}
return candidate;
}
}
cDebug() << Logger::SubEntry << "None found.";
return QString();
}
void
Calamares::QmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
bool ok = false;
m_searchMethod = searchNames().find( CalamaresUtils::getString( configurationMap, "search" ), ok );
if ( !ok )
{
cDebug() << "Bad QML search mode.";
}
QString qmlFile = CalamaresUtils::getString( configurationMap, "filename" );
if ( qmlFile.isEmpty() )
{
// TODO use the module instance
}
if ( !m_qmlComponent )
{
m_qmlFileName = searchQmlFile( m_searchMethod, qmlFile, m_name );
cDebug() << "QmlViewStep" << moduleInstanceKey() << "loading" << m_qmlFileName;
m_qmlComponent = new QQmlComponent(
m_qmlWidget->engine(), QUrl( m_qmlFileName ), QQmlComponent::CompilationMode::Asynchronous );
connect( m_qmlComponent, &QQmlComponent::statusChanged, this, &QmlViewStep::loadComplete );
if ( m_qmlComponent->status() == QQmlComponent::Error )
{
showFailedQml();
}
}
else
{
cWarning() << "QML configuration set after component has loaded.";
}
}
void
Calamares::QmlViewStep::showFailedQml()
{
cWarning() << "QmlViewStep" << moduleInstanceKey() << "loading failed.";
m_spinner->setText( prettyName() + ' ' + tr( "Loading failed." ) );
}

View File

@ -0,0 +1,102 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2020, 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 QMLVIEWSTEP_H
#define QMLVIEWSTEP_H
#include "viewpages/ViewStep.h"
class QQmlComponent;
class QQuickItem;
class QQuickWidget;
class WaitingWidget;
namespace Calamares
{
/** @brief A viewstep that uses QML for the UI
*
* This is generally a **base** class for other view steps, but
* it can be used stand-alone for viewsteps that don't really have
* any functionality.
*/
class QmlViewStep : public Calamares::ViewStep
{
Q_OBJECT
public:
enum class QmlSearch
{
QrcOnly,
BrandingOnly,
Both
};
/** @brief Creates a QML view step
*
* The name should not have an extension or schema or anything;
* just the plain name, which will be searched as "/<name>.qml" in
* QRC files, or "<name>.qml" in suitable branding paths.
* The search behavior depends on a QmlSearch value.
*/
QmlViewStep( const QString& name, QObject* parent = nullptr );
virtual ~QmlViewStep() override;
virtual QString prettyName() const override;
virtual QWidget* widget() override;
virtual bool isNextEnabled() const override;
virtual bool isBackEnabled() const override;
virtual bool isAtBeginning() const override;
virtual bool isAtEnd() const override;
virtual void onActivate() override;
virtual void onLeave() override;
/// @brief QML widgets don't produce jobs by default
virtual JobList jobs() const override;
/// @brief Configure search paths; subclasses should call this as well
virtual void setConfigurationMap( const QVariantMap& configurationMap ) override;
private Q_SLOTS:
void loadComplete();
private:
/// @brief Swap out the spinner for the QQuickWidget
void showQml();
/// @brief Show error message in spinner.
void showFailedQml();
/// @brief Controls where m_name is searched
QmlSearch m_searchMethod;
QString m_name;
QString m_qmlFileName;
QWidget* m_widget = nullptr;
WaitingWidget* m_spinner = nullptr;
QQuickWidget* m_qmlWidget = nullptr;
QQmlComponent* m_qmlComponent = nullptr;
QQuickItem* m_qmlObject = nullptr;
};
} // namespace Calamares
#endif

View File

@ -0,0 +1,11 @@
calamares_add_plugin( dummyqml
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
DummyQmlViewStep.cpp
RESOURCES
dummyqml.qrc
LINK_PRIVATE_LIBRARIES
calamaresui
SHARED_LIB
)

View File

@ -0,0 +1,52 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2020, Adriaan de Groot <groot@kde.org>
* Copyright 2020, Anke Boersma <demm@kaosx.us>
*
* 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 "DummyQmlViewStep.h"
#include <QVariant>
DummyQmlViewStep::DummyQmlViewStep( QObject* parent )
: Calamares::QmlViewStep( "dummyqml", parent )
{
}
DummyQmlViewStep::~DummyQmlViewStep() {}
QString
DummyQmlViewStep::prettyName() const
{
return m_notesName ? m_notesName->get() : tr( "Notes" );
}
void
DummyQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation
bool qmlLabel_ok = false;
auto qmlLabel = CalamaresUtils::getSubMap( configurationMap, "qmlLabel", qmlLabel_ok );
if ( qmlLabel.contains( "notes" ) )
{
m_notesName = new CalamaresUtils::Locale::TranslatedString( qmlLabel, "notes" );
}
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( DummyQmlViewStepFactory, registerPlugin< DummyQmlViewStep >(); )

View File

@ -0,0 +1,48 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2020, Adriaan de Groot <groot@kde.org>
* Copyright 2020, Anke Boersma <demm@kaosx.us>
*
* 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 DUMMYQMLVIEWSTEP_H
#define DUMMYQMLVIEWSTEP_H
#include "PluginDllMacro.h"
#include "locale/TranslatableConfiguration.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Variant.h"
#include "utils/PluginFactory.h"
#include "viewpages/QmlViewStep.h"
class PLUGINDLLEXPORT DummyQmlViewStep : public Calamares::QmlViewStep
{
Q_OBJECT
public:
DummyQmlViewStep( QObject* parent = nullptr );
virtual ~DummyQmlViewStep() override;
QString prettyName() const override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
private:
CalamaresUtils::Locale::TranslatedString* m_notesName; // As it appears in the sidebar
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( DummyQmlViewStepFactory )
#endif

View File

@ -0,0 +1,29 @@
# The dummy QML module just displays a QML page. It doesn't
# have much in the way of own configuration, only where
# the QML file is searched.
#
# QML modules can search for the QML inside the Qt resources
# (QRC) which are compiled into the module, or in the branding
# setup for Calamares, (or both of them, with branding taking
# precedence). This allows the module to ship a default UI and
# branding to optionally introduce a replacement file.
#
# Generally, leave the search method set to "both" because if
# you don't want to brand the UI, just don't ship a branding
# QML file for it.
#
# To support instanced QML modules, searches in the branding
# directory look for the full module@instanceid name as well.
---
# Search mode. Valid values are "both", "qrc" and "branding"
search: both
# Name of the QML file. If not set, uses the name of the instance
# of the module (e.g. if you list this module in `settings.conf`
# in the *instances* section, you get *id*, otherwise it would
# normally be "dummyqml").
# filename: dummyqml
qmlLabel:
notes: "Release Notes"
notes[nl]: "Opmerkingen"

View File

@ -0,0 +1,73 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2020, Anke Boersma <demm@kaosx.us>
*
* 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/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.1
Item {
width: 740
height: 420
Flickable {
id: flick
anchors.fill: parent
contentHeight: 800
ScrollBar.vertical: ScrollBar {
width: 10
policy: ScrollBar.AlwaysOn
}
TextArea {
id: intro
x: 1
y: 0
width: 720
font.pointSize: 14
textFormat: Text.RichText
antialiasing: true
activeFocusOnPress: false
wrapMode: Text.WordWrap
text: qsTr("<h3>Generic GNU/Linux 2017.8 LTS <quote>Soapy Sousaphone</quote></h3>
<p>This an example QML file, showing options in RichText with Flickable content.</p>
<p>QML with RichText can use HTML tags, Flickable content is useful for touchscreens.</p>
<p><b>This is bold text</b></p>
<p><i>This is italic text</i></p>
<p><u>This is underlined text</u></p>
<p><strike>This is strikethrough</strike></p>
<p>Code example:
<code>ls -l /home</code></p>
<p><b>Lists:</b></p>
<ul>
<li>Intel CPU systems</li>
<li>AMD CPU systems</li>
</ul>
<p>The vertical scrollbar is adjustable, current width set to 10.</p>")
}
}
}

View File

@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="dummyqml.qml">dummyqml.qml</file>
</qresource>
</RCC>

View File

@ -12,6 +12,7 @@ calamares_add_plugin( machineid
if ( ECM_FOUND AND BUILD_TESTING ) if ( ECM_FOUND AND BUILD_TESTING )
ecm_add_test( ecm_add_test(
Tests.cpp Tests.cpp
MachineIdJob.cpp
Workers.cpp Workers.cpp
TEST_NAME TEST_NAME
machineidtest machineidtest

View File

@ -3,7 +3,7 @@
* Copyright 2014, Kevin Kofler <kevin.kofler@chello.at> * Copyright 2014, Kevin Kofler <kevin.kofler@chello.at>
* Copyright 2016, Philip Müller <philm@manjaro.org> * Copyright 2016, Philip Müller <philm@manjaro.org>
* Copyright 2017, Alf Gaida <agaida@siduction.org> * Copyright 2017, Alf Gaida <agaida@siduction.org>
* Copyright 2019, Adriaan de Groot <groot@kde.org> * Copyright 2019-2020, Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -68,18 +68,20 @@ MachineIdJob::exec()
QString target_dbus_machineid_file = QStringLiteral( "/var/lib/dbus/machine-id" ); QString target_dbus_machineid_file = QStringLiteral( "/var/lib/dbus/machine-id" );
QString target_entropy_file = QStringLiteral( "/var/lib/urandom/random-seed" ); QString target_entropy_file = QStringLiteral( "/var/lib/urandom/random-seed" );
const CalamaresUtils::System* system = CalamaresUtils::System::instance();
// Clear existing files // Clear existing files
if ( m_entropy ) if ( m_entropy )
{ {
MachineId::removeFile( root, target_entropy_file ); system->removeTargetFile( target_entropy_file );
} }
if ( m_dbus ) if ( m_dbus )
{ {
MachineId::removeFile( root, target_dbus_machineid_file ); system->removeTargetFile( target_dbus_machineid_file );
} }
if ( m_systemd ) if ( m_systemd )
{ {
MachineId::removeFile( root, target_systemd_machineid_file ); system->removeTargetFile( target_systemd_machineid_file );
} }
//Create new files //Create new files
@ -104,6 +106,10 @@ MachineIdJob::exec()
} }
if ( m_dbus ) if ( m_dbus )
{ {
if ( !system->createTargetParentDirs( target_dbus_machineid_file ) )
{
cWarning() << "Could not create DBus data-directory.";
}
if ( m_dbus_symlink && QFile::exists( root + target_systemd_machineid_file ) ) if ( m_dbus_symlink && QFile::exists( root + target_systemd_machineid_file ) )
{ {
auto r = MachineId::createDBusLink( root, target_dbus_machineid_file, target_systemd_machineid_file ); auto r = MachineId::createDBusLink( root, target_dbus_machineid_file, target_systemd_machineid_file );

View File

@ -16,11 +16,14 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "MachineIdJob.h"
#include "Workers.h" #include "Workers.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QtTest/QtTest> #include <QtTest/QtTest>
@ -35,10 +38,11 @@ public:
private Q_SLOTS: private Q_SLOTS:
void initTestCase(); void initTestCase();
void testRemoveFile();
void testCopyFile(); void testCopyFile();
void testPoolSize(); void testPoolSize();
void testJob();
}; };
void void
@ -84,11 +88,6 @@ MachineIdTests::testCopyFile()
} }
} }
void
MachineIdTests::testRemoveFile()
{
}
void void
MachineIdTests::testPoolSize() MachineIdTests::testPoolSize()
{ {
@ -101,6 +100,62 @@ MachineIdTests::testPoolSize()
#endif #endif
} }
void
MachineIdTests::testJob()
{
Logger::setupLogLevel( Logger::LOGDEBUG );
// Ensure we have a system object, expect it to be a "bogus" one
CalamaresUtils::System* system = CalamaresUtils::System::instance();
QVERIFY( system );
QVERIFY( system->doChroot() );
// Ensure we have a system-wide GlobalStorage with /tmp as root
if ( !Calamares::JobQueue::instance() )
{
cDebug() << "Creating new JobQueue";
(void)new Calamares::JobQueue();
}
Calamares::GlobalStorage* gs
= Calamares::JobQueue::instance() ? Calamares::JobQueue::instance()->globalStorage() : nullptr;
QVERIFY( gs );
gs->insert( "rootMountPoint", "/tmp" );
// Prepare part of the target filesystem
QVERIFY( system->createTargetDirs("/etc") );
QVERIFY( !(system->createTargetFile( "/etc/machine-id", "Hello" ).isEmpty() ) );
MachineIdJob job( nullptr );
QVERIFY( !job.prettyName().isEmpty() );
QVariantMap config;
config.insert( "dbus", true );
job.setConfigurationMap( config );
{
auto r = job.exec();
QVERIFY( !r ); // It's supposed to fail, because no dbus-uuidgen executable exists
QVERIFY( QFile::exists( "/tmp/var/lib/dbus" ) ); // but the target dir exists
}
config.insert( "dbus-symlink", true );
job.setConfigurationMap( config );
{
auto r = job.exec();
QVERIFY( !r ); // It's supposed to fail, because no dbus-uuidgen executable exists
QVERIFY( QFile::exists( "/tmp/var/lib/dbus" ) ); // but the target dir exists
// These all (would) fail, because the chroot isn't viable
#if 0
QVERIFY( QFile::exists( "/tmp/var/lib/dbus/machine-id" ) );
QFileInfo fi( "/tmp/var/lib/dbus/machine-id" );
QVERIFY( fi.exists() );
QVERIFY( fi.isSymLink() );
QCOMPARE( fi.size(), 5);
#endif
}
}
QTEST_GUILESS_MAIN( MachineIdTests ) QTEST_GUILESS_MAIN( MachineIdTests )

View File

@ -36,17 +36,6 @@ isAbsolutePath( const QString& fileName )
return fileName.startsWith( '/' ); return fileName.startsWith( '/' );
} }
// might need to use a helper to remove the file
void
removeFile( const QString& rootMountPoint, const QString& fileName )
{
if ( isAbsolutePath( fileName ) )
{
QFile::remove( rootMountPoint + fileName );
}
// Otherwise, do nothing
}
Calamares::JobResult Calamares::JobResult
copyFile( const QString& rootMountPoint, const QString& fileName ) copyFile( const QString& rootMountPoint, const QString& fileName )
{ {
@ -192,7 +181,7 @@ Calamares::JobResult
createDBusLink( const QString& rootMountPoint, const QString& fileName, const QString& systemdFileName ) createDBusLink( const QString& rootMountPoint, const QString& fileName, const QString& systemdFileName )
{ {
Q_UNUSED( rootMountPoint ) Q_UNUSED( rootMountPoint )
return runCmd( QStringList { QStringLiteral( "ln" ), QStringLiteral( "-s" ), systemdFileName, fileName } ); return runCmd( QStringList { QStringLiteral( "ln" ), QStringLiteral( "-sf" ), systemdFileName, fileName } );
} }
} // namespace MachineId } // namespace MachineId

View File

@ -30,9 +30,6 @@ namespace MachineId
* for moving files around in the target system. * for moving files around in the target system.
*/ */
/// @brief Remove @p fileName from the target system at @p rootMountPoint
void removeFile( const QString& rootMountPoint, const QString& fileName );
/// @brief Copy @p fileName from host into target system at @p rootMountPoint /// @brief Copy @p fileName from host into target system at @p rootMountPoint
Calamares::JobResult copyFile( const QString& rootMountPoint, const QString& fileName ); Calamares::JobResult copyFile( const QString& rootMountPoint, const QString& fileName );

View File

@ -1,73 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# === This file is part of Calamares - <https://github.com/calamares> ===
#
# Copyright 2014, Kevin Kofler <kevin.kofler@chello.at>
# Copyright 2019, Philip Müller <philm@manjaro.org>
# Copyright 2017, Alf Gaida <agaida@siduction.org>
# 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/>.
import libcalamares
import os
from libcalamares.utils import check_target_env_call, debug
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Generate machine-id.")
def run():
"""
Generate machine-id using dbus and systemd.
:return:
"""
root_mount_point = libcalamares.globalstorage.value("rootMountPoint")
if root_mount_point is None:
libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point))
return (_("Configuration Error"),
_("No root mount point is given for <pre>{!s}</pre> to use." ).format("machineid"))
enable_systemd = libcalamares.job.configuration["systemd"]
enable_dbus = libcalamares.job.configuration["dbus"]
enable_symlink = libcalamares.job.configuration["symlink"]
target_systemd_machineid_file = root_mount_point + "/etc/machine-id"
target_dbus_machineid_file = root_mount_point + "/var/lib/dbus/machine-id"
if os.path.exists(target_dbus_machineid_file):
os.remove(target_dbus_machineid_file)
if enable_systemd:
if os.path.exists(target_systemd_machineid_file):
os.remove(target_systemd_machineid_file)
check_target_env_call("systemd-machine-id-setup")
if enable_dbus:
if enable_symlink and os.path.exists(target_systemd_machineid_file):
check_target_env_call(["ln", "-sf", "/etc/machine-id",
"/var/lib/dbus/machine-id"])
else:
check_target_env_call(["dbus-uuidgen", "--ensure"])
return None

View File

@ -176,7 +176,7 @@ script:
if ( !Calamares::JobQueue::instance() ) if ( !Calamares::JobQueue::instance() )
(void)new Calamares::JobQueue( nullptr ); (void)new Calamares::JobQueue( nullptr );
if ( !Calamares::Settings::instance() ) if ( !Calamares::Settings::instance() )
(void)new Calamares::Settings( QString(), true ); (void)Calamares::Settings::init( QString() );
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
QVERIFY( gs != nullptr ); QVERIFY( gs != nullptr );

View File

@ -23,13 +23,13 @@
#include "SummaryViewStep.h" #include "SummaryViewStep.h"
#include "Branding.h" #include "Branding.h"
#include "ExecutionViewStep.h"
#include "Settings.h" #include "Settings.h"
#include "ViewManager.h" #include "ViewManager.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Retranslator.h" #include "utils/Retranslator.h"
#include "viewpages/ExecutionViewStep.h"
#include <QBoxLayout> #include <QBoxLayout>
#include <QLabel> #include <QLabel>

View File

@ -40,3 +40,13 @@ calamares_add_plugin( welcome
Qt5::Network Qt5::Network
SHARED_LIB SHARED_LIB
) )
add_executable( welcomeqmltest qmlmain.cpp Config.cpp )
target_link_libraries( welcomeqmltest PRIVATE calamaresui Qt5::Core )
set_target_properties( welcomeqmltest
PROPERTIES
ENABLE_EXPORTS TRUE
RUNTIME_OUTPUT_NAME welcomeqmltest
)
calamares_automoc( welcomeqmltest )
calamares_autouic( welcomeqmltest )

View File

@ -0,0 +1,28 @@
/* === This file is part of Calamares - <https://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 "Config.h"
Config::Config()
: m_helpUrl( "https://www.kde.org/" )
{
}
Config::~Config()
{
}

View File

@ -0,0 +1,40 @@
/* === This file is part of Calamares - <https://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 WELCOME_CONFIG_H
#define WELCOME_CONFIG_H
#include <QObject>
#include <QUrl>
class Config : public QObject
{
Q_OBJECT
Q_PROPERTY( QUrl helpUrl READ helpUrl WRITE setHelpUrl CONSTANT )
public:
Config();
virtual ~Config();
QUrl helpUrl() const { return m_helpUrl; }
void setHelpUrl( const QUrl& url ) { m_helpUrl = url; }
private:
QUrl m_helpUrl;
};
#endif

View File

@ -0,0 +1,84 @@
/* Example executable showing a QML page and using the
* models from libcalamares for displaying a welcome.
*/
#include <memory>
#include <QApplication>
#include <QLabel>
#include <QMainWindow>
#include <QQmlEngine>
#include <QQuickWidget>
#include <QString>
#include <QTimer>
#include <QUrl>
#include <QVBoxLayout>
#include <QWidget>
#include <KAboutData>
#include "Branding.h"
#include "JobQueue.h"
#include "Settings.h"
#include "locale/LabelModel.h"
#include "utils/Logger.h"
#include "Config.h"
static Config* theConfig()
{
static Config* cnf = new Config();
return cnf;
}
int main(int argc, char **argv)
{
QApplication a( argc, argv );
KAboutData aboutData( "calamares",
"Calamares",
"0.1",
"Calamares QML Test Application",
KAboutLicense::GPL_V3,
QString(),
QString(),
"https://calamares.io",
"https://github.com/calamares/calamares/issues" );
KAboutData::setApplicationData( aboutData );
a.setApplicationDisplayName( QString() ); // To avoid putting an extra "Calamares/" into the log-file
Logger::setupLogLevel( Logger::LOGVERBOSE );
std::unique_ptr< Calamares::Settings > settings_p( Calamares::Settings::init( QString() ) );
std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) );
Calamares::Branding defaultBrand( "src/branding/default/branding.desc" );
cDebug() << "Branding @" << (void *)Calamares::Branding::instance();
QMainWindow mw;
QWidget background;
QVBoxLayout vl;
QLabel l( "Hello, world", &mw );
QQuickWidget qqw( &mw );
vl.addWidget( &qqw );
vl.addWidget( &l );
background.setLayout( &vl );
mw.setCentralWidget( &background );
mw.resize( QSize( 400, 400 ) );
mw.show();
Config cnf;
if ( argc > 1 )
{
cnf.setHelpUrl( QUrl( argv[1] ) );
}
// TODO: this should put the one config object in the context, rather than adding a factory function to share it everywhere
qmlRegisterSingletonType< Config >( "io.calamares.modules.welcome", 1, 0, "PotatoConfig", [](QQmlEngine*, QJSEngine*) -> QObject* { return theConfig(); });
qmlRegisterSingletonType< CalamaresUtils::Locale::LabelModel >( "io.calamares.locale", 1, 0, "LocaleModel", [](QQmlEngine*, QJSEngine*) -> QObject* { return CalamaresUtils::Locale::availableTranslations(); } );
qqw.setSource( QUrl::fromLocalFile("../src/modules/welcome/welcome.qml") );
return a.exec();
}

View File

@ -0,0 +1,29 @@
import QtQuick 2.0;
import QtQuick.Controls 2.3;
import io.calamares.modules.welcome 1.0;
import io.calamares.locale 1.0;
Rectangle {
width: 200;
height: 200;
color: "pink";
Label {
id: label;
anchors.centerIn: parent;
text: "Welcome to Calamares";
}
Button {
id: thebutton;
anchors.top: label.bottom;
text: PotatoConfig.helpUrl;
}
ListView {
anchors.fill: parent;
model: LocaleModel;
delegate: Label { text: display }
}
}

View File

@ -0,0 +1,44 @@
# This is a re-write of the welcome module using QML view steps
# instead of widgets.
set( _welcome ${CMAKE_CURRENT_SOURCE_DIR}/../welcome )
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ${_welcome} )
# DUPLICATED WITH WELCOME MODULE
find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED DBus Network )
find_package( LIBPARTED )
if ( LIBPARTED_FOUND )
set( PARTMAN_SRC ${_welcome}/checker/partman_devices.c )
set( CHECKER_LINK_LIBRARIES ${LIBPARTED_LIBRARY} )
else()
set( PARTMAN_SRC )
set( CHECKER_LINK_LIBRARIES )
add_definitions( -DWITHOUT_LIBPARTED )
endif()
set( CHECKER_SOURCES
${_welcome}/checker/CheckerContainer.cpp
${_welcome}/checker/GeneralRequirements.cpp
${_welcome}/checker/ResultWidget.cpp
${_welcome}/checker/ResultsListWidget.cpp
${PARTMAN_SRC}
)
calamares_add_plugin( welcomeq
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
${CHECKER_SOURCES}
WelcomeQmlViewStep.cpp
Config.cpp
RESOURCES
welcomeq.qrc
LINK_PRIVATE_LIBRARIES
calamaresui
${CHECKER_LINK_LIBRARIES}
Qt5::DBus
Qt5::Network
SHARED_LIB
)

View File

@ -0,0 +1,28 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019-2020, 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 "Config.h"
Config::Config()
: m_helpUrl( "https://www.kde.org/" )
{
}
Config::~Config()
{
}

View File

@ -0,0 +1,40 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019-2020, 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 WELCOME_CONFIG_H
#define WELCOME_CONFIG_H
#include <QObject>
#include <QUrl>
class Config : public QObject
{
Q_OBJECT
Q_PROPERTY( QUrl helpUrl READ helpUrl WRITE setHelpUrl CONSTANT )
public:
Config();
virtual ~Config();
QUrl helpUrl() const { return m_helpUrl; }
void setHelpUrl( const QUrl& url ) { m_helpUrl = url; }
private:
QUrl m_helpUrl;
};
#endif

View File

@ -0,0 +1,243 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2018,2020 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 "WelcomeQmlViewStep.h"
#include "checker/GeneralRequirements.h"
#include "geoip/Handler.h"
#include "locale/LabelModel.h"
#include "locale/Lookup.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
#include "Branding.h"
#include "modulesystem/ModuleManager.h"
#include <QFutureWatcher>
#include <QPixmap>
#include <QVariant>
CALAMARES_PLUGIN_FACTORY_DEFINITION( WelcomeQmlViewStepFactory, registerPlugin< WelcomeQmlViewStep >(); )
WelcomeQmlViewStep::WelcomeQmlViewStep( QObject* parent )
: Calamares::ViewStep( parent )
, m_requirementsChecker( new GeneralRequirements( this ) )
{
connect( Calamares::ModuleManager::instance(),
&Calamares::ModuleManager::requirementsComplete,
this,
&WelcomeQmlViewStep::nextStatusChanged );
}
WelcomeQmlViewStep::~WelcomeQmlViewStep()
{
}
QString
WelcomeQmlViewStep::prettyName() const
{
return tr( "Welcome" );
}
QWidget*
WelcomeQmlViewStep::widget()
{
return nullptr;
}
bool
WelcomeQmlViewStep::isNextEnabled() const
{
// TODO: should return true
return false;
}
bool
WelcomeQmlViewStep::isBackEnabled() const
{
// TODO: should return true (it's weird that you are not allowed to have welcome *after* anything
return false;
}
bool
WelcomeQmlViewStep::isAtBeginning() const
{
// TODO: adjust to "pages" in the QML
return true;
}
bool
WelcomeQmlViewStep::isAtEnd() const
{
// TODO: adjust to "pages" in the QML
return true;
}
Calamares::JobList
WelcomeQmlViewStep::jobs() const
{
return Calamares::JobList();
}
/** @brief Look up a URL for a button
*
* Looks up @p key in @p map; if it is a *boolean* value, then
* assume an old-style configuration, and fetch the string from
* the branding settings @p e. If it is a string, not a boolean,
* use it as-is. If not found, or a weird type, returns empty.
*
* This allows switching the showKnownIssuesUrl and similar settings
* in welcome.conf from a boolean (deferring to branding) to an
* actual string for immediate use. Empty strings, as well as
* "false" as a setting, will hide the buttons as before.
*/
static QString
jobOrBrandingSetting( Calamares::Branding::StringEntry e, const QVariantMap& map, const QString& key )
{
if ( !map.contains( key ) )
{
return QString();
}
auto v = map.value( key );
if ( v.type() == QVariant::Bool )
{
return v.toBool() ? ( *e ) : QString();
}
if ( v.type() == QVariant::String )
{
return v.toString();
}
return QString();
}
void
WelcomeQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
using Calamares::Branding;
m_config.setHelpUrl( jobOrBrandingSetting( Branding::SupportUrl, configurationMap, "showSupportUrl" ) );
// TODO: expand Config class and set the remaining fields
// TODO: figure out how the requirements (held by ModuleManager) should be accessible
// to QML as a odel.
if ( configurationMap.contains( "requirements" )
&& configurationMap.value( "requirements" ).type() == QVariant::Map )
{
m_requirementsChecker->setConfigurationMap( configurationMap.value( "requirements" ).toMap() );
}
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" ) );
if ( handler->type() != CalamaresUtils::GeoIP::Handler::Type::None )
{
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, h );
f->deleteLater();
delete h;
} );
future->setFuture( handler->queryRaw() );
}
else
{
// Would not produce useful country code anyway.
delete handler;
}
}
QString language = CalamaresUtils::getString( configurationMap, "languageIcon" );
if ( !language.isEmpty() )
{
auto icon = Calamares::Branding::instance()->image( language, QSize( 48, 48 ) );
if ( !icon.isNull() )
{
// TODO: figure out where to set this: Config?
}
}
}
Calamares::RequirementsList
WelcomeQmlViewStep::checkRequirements()
{
return m_requirementsChecker->checkRequirements();
}
static inline void
logGeoIPHandler( CalamaresUtils::GeoIP::Handler* handler )
{
if ( handler )
{
cDebug() << Logger::SubEntry << "Obtained from" << handler->url() << " ("
<< static_cast< int >( handler->type() ) << handler->selector() << ')';
}
}
void
WelcomeQmlViewStep::setCountry( const QString& countryCode, CalamaresUtils::GeoIP::Handler* handler )
{
if ( countryCode.length() != 2 )
{
cDebug() << "Unusable country code" << countryCode;
logGeoIPHandler( handler );
return;
}
auto c_l = CalamaresUtils::Locale::countryData( countryCode );
if ( c_l.first == QLocale::Country::AnyCountry )
{
cDebug() << "Unusable country code" << countryCode;
logGeoIPHandler( handler );
return;
}
else
{
int r = CalamaresUtils::Locale::availableTranslations()->find( countryCode );
if ( r < 0 )
{
cDebug() << "Unusable country code" << countryCode << "(no suitable translation)";
}
if ( ( r >= 0 ) )
{
// TODO: update Config to point to selected language
}
}
}

View File

@ -0,0 +1,95 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019-2020 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 WELCOME_QMLVIEWSTEP_H
#define WELCOME_QMLVIEWSTEP_H
#include "Config.h"
#include "modulesystem/Requirement.h"
#include "utils/PluginFactory.h"
#include "viewpages/ViewStep.h"
#include <PluginDllMacro.h>
#include <QObject>
#include <QVariantMap>
namespace CalamaresUtils
{
namespace GeoIP
{
class Handler;
}
} // namespace CalamaresUtils
class GeneralRequirements;
class QQmlComponent;
class QQuickItem;
class QQuickWidget;
// TODO: Needs a generic Calamares::QmlViewStep as base class
// TODO: refactor and move what makes sense to base class
class PLUGINDLLEXPORT WelcomeQmlViewStep : public Calamares::ViewStep
{
Q_OBJECT
public:
explicit WelcomeQmlViewStep( QObject* parent = nullptr );
virtual ~WelcomeQmlViewStep() override;
QString prettyName() const override;
QWidget* widget() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;
bool isAtBeginning() const override;
bool isAtEnd() const override;
Calamares::JobList jobs() const override;
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. Uses the handler's information (if
* given) for error reporting.
*/
void setCountry( const QString&, CalamaresUtils::GeoIP::Handler* handler );
Calamares::RequirementsList checkRequirements() override;
private:
// TODO: a generic QML viewstep should return a config object from a method
Config m_config;
GeneralRequirements* m_requirementsChecker;
// TODO: these need to be in the base class (also a base class of ExecutionViewStep)
QQuickWidget* m_qmlWidget;
QQmlComponent* m_qmlComponent;
QQuickItem* m_qmlItem;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( WelcomeQmlViewStepFactory )
#endif // WELCOME_QMLVIEWSTEP_H