diff --git a/CHANGES-3.2 b/CHANGES-3.2 index 068b68aae..973945730 100644 --- a/CHANGES-3.2 +++ b/CHANGES-3.2 @@ -8,6 +8,31 @@ contributors are listed. Note that Calamares does not have a historical changelog -- this log starts with version 3.2.0. The release notes on the website will have to do for older versions. +# 3.2.59 (unreleased) # + +This release contains contributions from (alphabetically by first name): + - Arjen Balfoort + +## Core ## + - No core changes yet + +## Modules ## + - *fstab* can now be configured to put `/tmp` on a *tmpfs*, and this can + depend on it being on an SSD or not. Options applicable to `/tmp` can + be configured separately as well. #1818 (Thanks Arjen) + - *partition* now has some support for re-using LUKS partitions. + (Thanks Arjen) + - *partition* will cycle out a LUKS key if all the key slots are in use + and a new key is added, rather than crashing the installer. (Thanks Arjen) + +# 3.2.58.2 (2022-05-24) + +This is a extra-quick release for an issue that shows up when using a +swap **file** on a btrfs filesystem; the installation would fail with +a Python error, raised from btrfs-progs. Reported by Evan James, Erik +Dubois, TechXero. + + # 3.2.58.1 (2022-05-20) This is a hot-fix release for a regression in the *partition* module where diff --git a/CMakeLists.txt b/CMakeLists.txt index 34f5f71f5..4dcfb7632 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,11 +41,11 @@ # TODO:3.3: Require CMake 3.12 cmake_minimum_required( VERSION 3.3 FATAL_ERROR ) project( CALAMARES - VERSION 3.2.58.1 + VERSION 3.2.59 LANGUAGES C CXX ) -set( CALAMARES_VERSION_RC 0 ) # Set to 0 during release cycle, 1 during development +set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development if( CALAMARES_VERSION_RC EQUAL 1 AND CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR ) message( FATAL_ERROR "Do not build development versions in the source-directory." ) endif() diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index 2cf0342ec..95081bb5e 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -24,6 +24,7 @@ set( OPTIONAL_PRIVATE_LIBRARIES "" ) set( OPTIONAL_PUBLIC_LIBRARIES "" ) set( libSources + CalamaresAbout.cpp CppJob.cpp GlobalStorage.cpp Job.cpp diff --git a/src/libcalamares/CalamaresAbout.cpp b/src/libcalamares/CalamaresAbout.cpp new file mode 100644 index 000000000..08a70968b --- /dev/null +++ b/src/libcalamares/CalamaresAbout.cpp @@ -0,0 +1,81 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2022 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "CalamaresAbout.h" + +#include "CalamaresVersion.h" + +#include + +static const char s_header[] + = QT_TRANSLATE_NOOP( "AboutData", "

%1


%2
for %3


" ); + +static const char s_footer[] + = QT_TRANSLATE_NOOP( "AboutData", + "Thanks to the Calamares team " + "and the Calamares " + "translators team.

" + "Calamares " + "development is sponsored by
" + "Blue Systems - " + "Liberating Software." ); + +struct Maintainer +{ + unsigned int start; + unsigned int end; + const char* name; + const char* email; + QString text() const + { + //: Copyright year-year Name + return QCoreApplication::translate( "AboutData", "Copyright %1-%2 %3 <%4>
" ) + .arg( start ) + .arg( end ) + .arg( name ) + .arg( email ); + } +}; + +static constexpr const Maintainer maintainers[] = { + { 2014, 2017, "Teo Mrnjavac", "teo@kde.org" }, + { 2017, 2022, "Adriaan de Groot", "groot@kde.org" }, +}; + +static QString +aboutMaintainers() +{ + return std::accumulate( std::cbegin( maintainers ), + std::cend( maintainers ), + QString(), + []( QString& s, const Maintainer& m ) + { + s += m.text(); + return s; + } ); +} + +static QString +substituteVersions( const QString& s ) +{ + return s.arg( CALAMARES_APPLICATION_NAME ).arg( CALAMARES_VERSION ); +} + +const QString +Calamares::aboutString() +{ + return substituteVersions( QCoreApplication::translate( "AboutData", s_header ) ) + aboutMaintainers() + + QCoreApplication::translate( "AboutData", s_footer ); +} + +const QString +Calamares::aboutStringUntranslated() +{ + return substituteVersions( QString( s_header ) ) + aboutMaintainers() + QString( s_footer ); +} diff --git a/src/libcalamares/CalamaresAbout.h b/src/libcalamares/CalamaresAbout.h new file mode 100644 index 000000000..88d499953 --- /dev/null +++ b/src/libcalamares/CalamaresAbout.h @@ -0,0 +1,31 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2022 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_CALAMARESABOUT_H +#define CALAMARES_CALAMARESABOUT_H + +#include "DllMacro.h" + +#include + +namespace Calamares +{ +/** @brief Returns an about string for the application + * + * The about string includes a header-statement, a list of maintainer + * addresses, and a thank-you to Blue Systems. There is on %-substitution + * left, where you can fill in the name of the product (e.g. to say + * "Calamares for Netrunner" or ".. for Manjaro"). + */ +DLLEXPORT const QString aboutStringUntranslated(); +/// @brief As above, but translated in the current Calamares language +DLLEXPORT const QString aboutString(); +} // namespace Calamares + +#endif diff --git a/src/modules/fstab/fstab.conf b/src/modules/fstab/fstab.conf index 1e87acddc..be43db99a 100644 --- a/src/modules/fstab/fstab.conf +++ b/src/modules/fstab/fstab.conf @@ -61,3 +61,26 @@ ssdExtraMountOptions: crypttabOptions: luks # For Debian and Debian-based distributions, change the above line to: # crypttabOptions: luks,keyscript=/bin/cat + +# Options for handling /tmp in /etc/fstab +# Currently default (required) and ssd are supported +# The corresponding string can contain the following variables: +# tmpfs: true or tmpfs: false to either mount /tmp as tmpfs or not +# options: "" +# +# Example: +#tmpOptions: +# default: +# tmpfs: false +# options: "" +# ssd: +# tmpfs: true +# options: "defaults,noatime,mode=1777" +# +tmpOptions: + default: + tmpfs: false + options: "" + ssd: + tmpfs: true + options: "defaults,noatime,mode=1777" diff --git a/src/modules/fstab/fstab.schema.yaml b/src/modules/fstab/fstab.schema.yaml index 087e82cac..f70d22c42 100644 --- a/src/modules/fstab/fstab.schema.yaml +++ b/src/modules/fstab/fstab.schema.yaml @@ -25,4 +25,22 @@ properties: btrfs_swap: { type: string } efiMountOptions: { type: string } crypttabOptions: { type: string } -required: [ mountOptions ] + tmpOptions: + type: object + additionalProperties: false + properties: + default: + type: object + additionalProperties: false + properties: + tmpfs: { type: bool } + options: { type: string } + ssd: + type: object + additionalProperties: false + properties: + tmpfs: { type: bool } + options: { type: string } +required: + - mountOptions + - tmpOptions: default diff --git a/src/modules/fstab/main.py b/src/modules/fstab/main.py index 95f10a643..fc0649e2a 100755 --- a/src/modules/fstab/main.py +++ b/src/modules/fstab/main.py @@ -14,7 +14,6 @@ import os import re -import subprocess import libcalamares @@ -106,14 +105,17 @@ class FstabGenerator(object): :param root_mount_point: :param mount_options: :param ssd_extra_mount_options: + :param crypttab_options: + :param tmp_options: """ def __init__(self, partitions, root_mount_point, mount_options, - ssd_extra_mount_options, crypttab_options): + ssd_extra_mount_options, crypttab_options, tmp_options): self.partitions = partitions self.root_mount_point = root_mount_point self.mount_options = mount_options self.ssd_extra_mount_options = ssd_extra_mount_options self.crypttab_options = crypttab_options + self.tmp_options = tmp_options self.ssd_disks = set() self.root_is_ssd = False @@ -162,8 +164,10 @@ class FstabGenerator(object): crypttab_options = self.crypttab_options # Set crypttab password for partition to none and remove crypttab options - # if crypto_keyfile.bin was not generated - if not os.path.exists(os.path.join(self.root_mount_point, "crypto_keyfile.bin")): + # if root partition was not encrypted + if any([p["mountPoint"] == "/" + and "luksMapperName" not in p + for p in self.partitions]): password = "none" crypttab_options = "" # on root partition when /boot is unencrypted @@ -173,7 +177,6 @@ class FstabGenerator(object): for p in self.partitions]): password = "none" crypttab_options = "" - return dict( name=mapper_name, @@ -213,21 +216,32 @@ class FstabGenerator(object): mount_entry["subvol"] = s["subvolume"] dct = self.generate_fstab_line_info(mount_entry) if dct: - self.print_fstab_line(dct, file=fstab_file) + self.print_fstab_line(dct, file=fstab_file) elif partition["fs"] != "zfs": # zfs partitions don't need an entry in fstab dct = self.generate_fstab_line_info(partition) if dct: self.print_fstab_line(dct, file=fstab_file) if self.root_is_ssd: - # Mount /tmp on a tmpfs - dct = dict(device="tmpfs", - mount_point="/tmp", - fs="tmpfs", - options="defaults,noatime,mode=1777", - check=0, - ) - self.print_fstab_line(dct, file=fstab_file) + # Old behavior was to mount /tmp as tmpfs + # New behavior is to use tmpOptions to decide + # if mounting /tmp as tmpfs and which options to use + ssd = self.tmp_options.get("ssd", {}) + if not ssd: + ssd = self.tmp_options.get("default", {}) + # Default to True to mimic old behavior + tmpfs = ssd.get("tmpfs", True) + + if tmpfs: + options = ssd.get("options", "defaults,noatime,mode=1777") + # Mount /tmp on a tmpfs + dct = dict(device="tmpfs", + mount_point="/tmp", + fs="tmpfs", + options=options, + check=0, + ) + self.print_fstab_line(dct, file=fstab_file) def generate_fstab_line_info(self, partition): """ @@ -342,10 +356,7 @@ def create_swapfile(root_mount_point, root_btrfs): swapfile_path = os.path.join(root_mount_point, "swap/swapfile") with open(swapfile_path, "wb") as f: pass - o = subprocess.check_output(["chattr", "+C", swapfile_path]) - libcalamares.utils.debug("swapfile attributes: {!s}".format(o)) - o = subprocess.check_output(["btrfs", "property", "set", swapfile_path, "compression", "none"]) - libcalamares.utils.debug("swapfile compression: {!s}".format(o)) + libcalamares.utils.host_env_process_output(["chattr", "+C", "+m", swapfile_path]) # No Copy-on-Write, no compression else: swapfile_path = os.path.join(root_mount_point, "swapfile") with open(swapfile_path, "wb") as f: @@ -363,8 +374,7 @@ def create_swapfile(root_mount_point, root_btrfs): libcalamares.job.setprogress(0.2 + 0.3 * ( total / desired_size ) ) total += chunk os.chmod(swapfile_path, 0o600) - o = subprocess.check_output(["mkswap", swapfile_path]) - libcalamares.utils.debug("swapfile mkswap: {!s}".format(o)) + libcalamares.utils.host_env_process_output(["mkswap", swapfile_path]) libcalamares.job.setprogress(0.5) @@ -410,6 +420,7 @@ def run(): mount_options = conf.get("mountOptions", {}) ssd_extra_mount_options = conf.get("ssdExtraMountOptions", {}) crypttab_options = conf.get("crypttabOptions", "luks") + tmp_options = conf.get("tmpOptions", {}) # We rely on mount_options having a default; if there wasn't one, # bail out with a meaningful error. @@ -422,7 +433,8 @@ def run(): root_mount_point, mount_options, ssd_extra_mount_options, - crypttab_options) + crypttab_options, + tmp_options) if swap_choice is not None: libcalamares.job.setprogress(0.2) diff --git a/src/modules/luksbootkeyfile/LuksBootKeyFileJob.cpp b/src/modules/luksbootkeyfile/LuksBootKeyFileJob.cpp index e8dfd4724..3dd7fe42a 100644 --- a/src/modules/luksbootkeyfile/LuksBootKeyFileJob.cpp +++ b/src/modules/luksbootkeyfile/LuksBootKeyFileJob.cpp @@ -17,6 +17,7 @@ #include "GlobalStorage.h" #include "JobQueue.h" +#include #include LuksBootKeyFileJob::LuksBootKeyFileJob( QObject* parent ) @@ -136,6 +137,28 @@ generateTargetKeyfile() static bool setupLuks( const LuksDevice& d ) { + // Sometimes this error is thrown: "All key slots full" + // luksAddKey will fail. So, remove the first slot to make room + auto luks_dump = CalamaresUtils::System::instance()->targetEnvCommand( + { "cryptsetup", "luksDump", d.device }, QString(), QString(), std::chrono::seconds( 5 ) ); + if ( luks_dump.getExitCode() == 0 ) + { + QRegularExpression re( QStringLiteral( R"(\d+:\s*enabled)" ), QRegularExpression::CaseInsensitiveOption ); + int count = luks_dump.getOutput().count(re); + cDebug() << "Luks Dump slot count: " << count; + if ( count >= 7 ) + { + auto r = CalamaresUtils::System::instance()->targetEnvCommand( + { "cryptsetup", "luksKillSlot", d.device, "1" }, QString(), d.passphrase, std::chrono::seconds( 60 ) ); + if ( r.getExitCode() != 0 ) + { + cWarning() << "Could not kill a slot to make room on" << d.device << ':' << r.getOutput() << "(exit code" + << r.getExitCode() << ')'; + return false; + } + } + } + // Adding the key can take some times, measured around 15 seconds with // a HDD (spinning rust) and a slow-ish computer. Give it a minute. auto r = CalamaresUtils::System::instance()->targetEnvCommand( diff --git a/src/modules/netinstall/NetInstallViewStep.cpp b/src/modules/netinstall/NetInstallViewStep.cpp index 99d51af9e..194b1fd2b 100644 --- a/src/modules/netinstall/NetInstallViewStep.cpp +++ b/src/modules/netinstall/NetInstallViewStep.cpp @@ -53,19 +53,19 @@ NetInstallViewStep::prettyName() const tr( "Browser software" ); tr( "Browser package" ); tr( "Web browser" ); - tr( "Kernel" ); - tr( "Services" ); - tr( "Login" ); - tr( "Desktop" ); + tr( "Kernel", "label for netinstall module, Linux kernel" ); + tr( "Services", "label for netinstall module, system services" ); + tr( "Login", "label for netinstall module, choose login manager" ); + tr( "Desktop", "label for netinstall module, choose desktop environment" ); tr( "Applications" ); - tr( "Communication" ); - tr( "Development" ); - tr( "Office" ); - tr( "Multimedia" ); - tr( "Internet" ); - tr( "Theming" ); - tr( "Gaming" ); - tr( "Utilities" ); + tr( "Communication", "label for netinstall module" ); + tr( "Development", "label for netinstall module" ); + tr( "Office", "label for netinstall module" ); + tr( "Multimedia", "label for netinstall module" ); + tr( "Internet", "label for netinstall module" ); + tr( "Theming", "label for netinstall module" ); + tr( "Gaming", "label for netinstall module" ); + tr( "Utilities", "label for netinstall module" ); #endif } diff --git a/src/modules/partition/core/KPMHelpers.cpp b/src/modules/partition/core/KPMHelpers.cpp index 92c9edb22..6cc188e67 100644 --- a/src/modules/partition/core/KPMHelpers.cpp +++ b/src/modules/partition/core/KPMHelpers.cpp @@ -34,11 +34,15 @@ Partition* findPartitionByMountPoint( const QList< Device* >& devices, const QString& mountPoint ) { for ( auto device : devices ) + { for ( auto it = PartitionIterator::begin( device ); it != PartitionIterator::end( device ); ++it ) + { if ( PartitionInfo::mountPoint( *it ) == mountPoint ) { return *it; } + } + } return nullptr; } @@ -167,42 +171,35 @@ testPassphrase( FS::luks* fs, const QString& deviceNode, const QString& passphra } #endif -// Adapted from luks cryptOpen which always opens a dialog to ask for a passphrase -int -updateLuksDevice( Partition* partition, const QString& passphrase ) +// Adapted from src/fs/luks.cpp cryptOpen which always opens a dialog to ask for a passphrase +SavePassphraseValue +savePassphrase( Partition* partition, const QString& passphrase ) { - const QString deviceNode = partition->partitionPath(); - - cDebug() << "Update Luks device: " << deviceNode; if ( passphrase.isEmpty() ) { - cWarning() << Logger::SubEntry << "#1: Passphrase is empty"; - return 1; + return SavePassphraseValue::EmptyPassphrase; } if ( partition->fileSystem().type() != FileSystem::Luks ) { - cWarning() << Logger::SubEntry << "#2: Not a luks encrypted device"; - return 2; + return SavePassphraseValue::NotLuksPartition; } - // Cast partition fs to luks fs FS::luks* luksFs = dynamic_cast< FS::luks* >( &partition->fileSystem() ); + const QString deviceNode = partition->partitionPath(); // Test the given passphrase if ( !testPassphrase( luksFs, deviceNode, passphrase ) ) { - cWarning() << Logger::SubEntry << "#3: Passphrase incorrect"; - return 3; + return SavePassphraseValue::IncorrectPassphrase; } if ( luksFs->isCryptOpen() ) { if ( !luksFs->mapperName().isEmpty() ) { - cWarning() << Logger::SubEntry << "#4: Device already decrypted"; - return 4; + return SavePassphraseValue::NoError; } else { @@ -213,34 +210,28 @@ updateLuksDevice( Partition* partition, const QString& passphrase ) ExternalCommand openCmd( QStringLiteral( "cryptsetup" ), { QStringLiteral( "open" ), deviceNode, luksFs->suggestedMapperName( deviceNode ) } ); - if ( !( openCmd.write( passphrase.toLocal8Bit() + '\n' ) && openCmd.start( -1 ) && openCmd.exitCode() == 0 ) ) { cWarning() << Logger::SubEntry << openCmd.exitCode() << ": cryptsetup command failed"; - return openCmd.exitCode(); + return SavePassphraseValue::CryptsetupError; } // Save the existing passphrase luksFs->setPassphrase( passphrase ); - luksFs->scan( deviceNode ); - if ( luksFs->mapperName().isEmpty() ) { - cWarning() << Logger::SubEntry << "#5: No mapper node found"; - return 5; + return SavePassphraseValue::NoMapperNode; } luksFs->loadInnerFileSystem( luksFs->mapperName() ); luksFs->setCryptOpen( luksFs->innerFS() != nullptr ); - if ( !luksFs->isCryptOpen() ) { - cWarning() << Logger::SubEntry << "#6: Device could not be decrypted"; - return 6; + return SavePassphraseValue::DeviceNotDecrypted; } - return 0; + return SavePassphraseValue::NoError; } Calamares::JobResult diff --git a/src/modules/partition/core/KPMHelpers.h b/src/modules/partition/core/KPMHelpers.h index 261bd7b39..e059c934a 100644 --- a/src/modules/partition/core/KPMHelpers.h +++ b/src/modules/partition/core/KPMHelpers.h @@ -43,6 +43,25 @@ class PartitionRole; namespace KPMHelpers { +/** @brief Return (errors) for savePassphrase() + * + * There's a handful of things that can go wrong when + * saving a passphrase for a given partition; this + * expresses clearly which ones are wrong. + * + * @c NoError is "Ok" when saving the passphrase succeeds. + */ +enum class SavePassphraseValue +{ + NoError, + EmptyPassphrase, + NotLuksPartition, + IncorrectPassphrase, + CryptsetupError, + NoMapperNode, + DeviceNotDecrypted +}; + /** * Iterates on all devices and return the first partition which is associated * with mountPoint. This uses PartitionInfo::mountPoint(), not Partition::mountPoint() @@ -74,7 +93,15 @@ Partition* createNewEncryptedPartition( PartitionNode* parent, Partition* clonePartition( Device* device, Partition* partition ); -int updateLuksDevice( Partition* partition, const QString& passphrase ); +/** @brief Save an existing passphrase for a previously encrypted partition. + * + * Tries to apply the passphrase to the partition; this checks if the + * @p partition is one that can have a passphrase applied, and + * runs `cryptsetup` to check that the passphrase actually works + * for the partition. Returns `NoError` on success, or an explanatory + * other value if it fails. + */ +SavePassphraseValue savePassphrase( Partition* partition, const QString& passphrase ); /** @brief Return a result for an @p operation * diff --git a/src/modules/partition/gui/EditExistingPartitionDialog.cpp b/src/modules/partition/gui/EditExistingPartitionDialog.cpp index eeb548ba1..0ed18a96e 100644 --- a/src/modules/partition/gui/EditExistingPartitionDialog.cpp +++ b/src/modules/partition/gui/EditExistingPartitionDialog.cpp @@ -254,8 +254,7 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core ) const QString passphrase = m_ui->encryptWidget->passphrase(); if ( !passphrase.isEmpty() ) { - int retCode = KPMHelpers::updateLuksDevice( m_partition, passphrase ); - if ( retCode != 0 ) + if ( KPMHelpers::savePassphrase( m_partition, passphrase ) != KPMHelpers::SavePassphraseValue::NoError ) { QString message = tr( "Passphrase for existing partition" ); QString description = tr( "Partition %1 could not be decrypted " diff --git a/src/modules/welcome/Config.cpp b/src/modules/welcome/Config.cpp index 0baadd82f..e5b2da211 100644 --- a/src/modules/welcome/Config.cpp +++ b/src/modules/welcome/Config.cpp @@ -10,6 +10,7 @@ #include "Config.h" #include "Branding.h" +#include "CalamaresAbout.h" #include "GlobalStorage.h" #include "JobQueue.h" #include "Settings.h" @@ -247,6 +248,13 @@ Config::setSupportUrl( const QString& url ) emit supportUrlChanged(); } +QString +Config::aboutMessage() const +{ + return Calamares::aboutString(); +} + + QString Config::genericWelcomeMessage() const { diff --git a/src/modules/welcome/Config.h b/src/modules/welcome/Config.h index c01bdf7e3..49d9a1b11 100644 --- a/src/modules/welcome/Config.h +++ b/src/modules/welcome/Config.h @@ -52,6 +52,7 @@ class Config : public QObject Q_PROPERTY( QString countryCode MEMBER m_countryCode NOTIFY countryCodeChanged FINAL ) Q_PROPERTY( int localeIndex READ localeIndex WRITE setLocaleIndex NOTIFY localeIndexChanged ) + Q_PROPERTY( QString aboutMessage READ aboutMessage CONSTANT FINAL ) Q_PROPERTY( QString genericWelcomeMessage MEMBER m_genericWelcomeMessage NOTIFY genericWelcomeMessageChanged FINAL ) Q_PROPERTY( QString warningMessage READ warningMessage NOTIFY warningMessageChanged FINAL ) @@ -89,6 +90,7 @@ public: QString donateUrl() const { return m_donateUrl; } void setDonateUrl( const QString& url ); + QString aboutMessage() const; QString genericWelcomeMessage() const; QString warningMessage() const; diff --git a/src/modules/welcome/WelcomePage.cpp b/src/modules/welcome/WelcomePage.cpp index dcfdea79e..5b26434a1 100644 --- a/src/modules/welcome/WelcomePage.cpp +++ b/src/modules/welcome/WelcomePage.cpp @@ -15,6 +15,7 @@ #include "ui_WelcomePage.h" #include "Branding.h" +#include "CalamaresAbout.h" #include "CalamaresVersion.h" #include "Config.h" #include "Settings.h" @@ -208,20 +209,7 @@ WelcomePage::setLanguageIcon( QPixmap i ) void WelcomePage::retranslate() { - QString message; - - if ( Calamares::Settings::instance()->isSetupMode() ) - { - message = Calamares::Branding::instance()->welcomeStyleCalamares() - ? tr( "

Welcome to the Calamares setup program for %1.

" ) - : tr( "

Welcome to %1 setup.

" ); - } - else - { - message = Calamares::Branding::instance()->welcomeStyleCalamares() - ? tr( "

Welcome to the Calamares installer for %1.

" ) - : tr( "

Welcome to the %1 installer.

" ); - } + const QString message = m_conf->genericWelcomeMessage(); ui->mainText->setText( message.arg( Calamares::Branding::instance()->versionedName() ) ); ui->retranslateUi( this ); @@ -235,21 +223,7 @@ WelcomePage::showAboutBox() = Calamares::Settings::instance()->isSetupMode() ? tr( "About %1 setup" ) : tr( "About %1 installer" ); QMessageBox mb( QMessageBox::Information, title.arg( CALAMARES_APPLICATION_NAME ), - tr( "

%1


" - "%2
" - "for %3


" - "Copyright 2014-2017 Teo Mrnjavac <teo@kde.org>
" - "Copyright 2017-2020 Adriaan de Groot <groot@kde.org>
" - "Thanks to the Calamares team " - "and the Calamares " - "translators team.

" - "Calamares " - "development is sponsored by
" - "Blue Systems - " - "Liberating Software." ) - .arg( CALAMARES_APPLICATION_NAME ) - .arg( CALAMARES_VERSION ) - .arg( Calamares::Branding::instance()->versionedName() ), + m_conf->aboutMessage().arg( Calamares::Branding::instance()->versionedName() ), QMessageBox::Ok, this ); Calamares::fixButtonLabels( &mb ); diff --git a/src/modules/welcomeq/about.qml b/src/modules/welcomeq/about.qml index 21050c4ea..bb7b06056 100644 --- a/src/modules/welcomeq/about.qml +++ b/src/modules/welcomeq/about.qml @@ -18,9 +18,6 @@ Item { height: parent.height focus: true - property var appName: "Calamares" - property var appVersion: "3.2.24" - Rectangle { id: textArea x: 28 @@ -44,21 +41,7 @@ Item { width: 400 height: 250 anchors.centerIn: parent - text: qsTr("

%1


- %2
- for %3


- Copyright 2014-2017 Teo Mrnjavac <teo@kde.org>
- Copyright 2017-2020 Adriaan de Groot <groot@kde.org>
- Thanks to the Calamares team - and the Calamares - translators team.

- Calamares - development is sponsored by
- Blue Systems - - Liberating Software." ) - .arg(appName) - .arg(appVersion) - .arg(Branding.string(Branding.VersionedName)) + text: config.aboutMessage.arg(Branding.string(Branding.VersionedName)) onLinkActivated: Qt.openUrlExternally(link)