diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index c647936d7..e8d2821ae 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -1,17 +1,26 @@ name: issues -on: issues +on: + issues: + types: [opened, reopened, closed] jobs: irc: runs-on: ubuntu-latest steps: - - - name: notify + - name: "notify: new" uses: rectalogic/notify-irc@v1 + if: github.event.issue.state == 'open' with: server: chat.freenode.net + nickname: cala-issues channel: "#calamares" - nickname: gh-issues - message: | - ${{ github.actor }} issue ${{ github.event.issue.title }} + message: "OPENED ${{ github.event.issue.html_url }} by ${{ github.actor }} ${{ github.event.issue.title }}" + - name: "notify: closed" + uses: rectalogic/notify-irc@v1 + if: github.event.issue.state != 'open' + with: + server: chat.freenode.net + nickname: cala-issues + channel: "#calamares" + message: "CLOSED ${{ github.event.issue.html_url }} by ${{ github.actor }} ${{ github.event.issue.title }}" diff --git a/.github/workflows/nightly-debian.yml b/.github/workflows/nightly-debian.yml new file mode 100644 index 000000000..3fd558e21 --- /dev/null +++ b/.github/workflows/nightly-debian.yml @@ -0,0 +1,100 @@ +name: nightly-debian-10 + +on: + schedule: + - cron: "12 23 * * *" + workflow_dispatch: + +env: + BUILDDIR: /build + SRCDIR: ${{ github.workspace }} + CMAKE_ARGS: | + -DWEBVIEW_FORCE_WEBKIT=1 + -DKDE_INSTALL_USE_QT_SYS_PATHS=ON + -DWITH_PYTHONQT=OFF" + -DCMAKE_BUILD_TYPE=Debug + +jobs: + build: + runs-on: ubuntu-latest + container: + image: docker://debian:10 + options: --tmpfs /build:rw --user 0:0 + steps: + - name: "prepare env" + run: | + apt-get update + apt-get -y install git-core + apt-get -y install \ + build-essential \ + cmake \ + extra-cmake-modules \ + gettext \ + kio-dev \ + libatasmart-dev \ + libboost-python-dev \ + libkf5config-dev \ + libkf5coreaddons-dev \ + libkf5i18n-dev \ + libkf5iconthemes-dev \ + libkf5parts-dev \ + libkf5service-dev \ + libkf5solid-dev \ + libparted-dev \ + libpolkit-qt5-1-dev \ + libqt5svg5-dev \ + libqt5webkit5-dev \ + libyaml-cpp-dev \ + os-prober \ + pkg-config \ + python3-dev \ + qtbase5-dev \ + qtdeclarative5-dev \ + qttools5-dev \ + qttools5-dev-tools + # Same name as on KDE neon, different version + apt-get -y install libkpmcore-dev + # Additional dependencies + apt-get -y install \ + libappstreamqt-dev \ + libicu-dev \ + libkf5crash-dev \ + libkf5package-dev \ + libkf5plasma-dev \ + libpwquality-dev \ + libqt5webenginewidgets5 \ + qtwebengine5-dev + - name: "prepare source" + uses: actions/checkout@v2 + - name: "prepare build" + id: pre_build + run: | + test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; } + mkdir -p $BUILDDIR + test -f $SRCDIR/CMakeLists.txt || { echo "! Missing $SRCDIR/CMakeLists.txt" ; exit 1 ; } + echo "::set-output name=message::"`git log -1 --abbrev-commit --pretty=oneline --no-decorate ${{ github.event.head_commit.id }}` + - name: "Calamares: cmake" + working-directory: ${{ env.BUILDDIR }} + run: cmake $CMAKE_ARGS $SRCDIR + - name: "Calamares: make" + working-directory: ${{ env.BUILDDIR }} + run: make -j2 VERBOSE=1 + - name: "Calamares: install" + working-directory: ${{ env.BUILDDIR }} + run: make install VERBOSE=1 + - name: "notify: ok" + uses: rectalogic/notify-irc@v1 + if: ${{ success() && github.repository == 'calamares/calamares' }} + with: + server: chat.freenode.net + nickname: cala-ci + channel: "#calamares" + message: "OK ${{ github.workflow }} in ${{ github.repository }} ${{ steps.pre_build.outputs.message }}" + - name: "notify: fail" + uses: rectalogic/notify-irc@v1 + if: ${{ failure() && github.repository == 'calamares/calamares' }} + with: + server: chat.freenode.net + nickname: cala-ci + channel: "#calamares" + message: "FAIL ${{ github.workflow }} in ${{ github.repository }} ${{ steps.pre_build.outputs.message }}" diff --git a/.github/workflows/nightly-neon.yml b/.github/workflows/nightly-neon.yml new file mode 100644 index 000000000..92f71c10b --- /dev/null +++ b/.github/workflows/nightly-neon.yml @@ -0,0 +1,99 @@ +name: nightly-neon + +on: + schedule: + - cron: "52 23 * * *" + workflow_dispatch: + +env: + BUILDDIR: /build + SRCDIR: ${{ github.workspace }} + CMAKE_ARGS: | + -DWEBVIEW_FORCE_WEBKIT=1 + -DKDE_INSTALL_USE_QT_SYS_PATHS=ON + -DWITH_PYTHONQT=OFF" + -DCMAKE_BUILD_TYPE=Debug + +jobs: + build: + runs-on: ubuntu-latest + container: + image: docker://kdeneon/plasma:user + options: --tmpfs /build:rw --user 0:0 + steps: + - name: "prepare env" + run: | + sudo apt-get update + sudo apt-get -y install git-core + sudo apt-get -y install \ + build-essential \ + cmake \ + extra-cmake-modules \ + gettext \ + kio-dev \ + libatasmart-dev \ + libboost-python-dev \ + libkf5config-dev \ + libkf5coreaddons-dev \ + libkf5i18n-dev \ + libkf5iconthemes-dev \ + libkf5parts-dev \ + libkf5service-dev \ + libkf5solid-dev \ + libkpmcore-dev \ + libparted-dev \ + libpolkit-qt5-1-dev \ + libqt5svg5-dev \ + libqt5webkit5-dev \ + libyaml-cpp-dev \ + os-prober \ + pkg-config \ + python3-dev \ + qtbase5-dev \ + qtdeclarative5-dev \ + qttools5-dev \ + qttools5-dev-tools + - name: "prepare source" + uses: actions/checkout@v2 + - name: "prepare build" + id: pre_build + run: | + test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; } + mkdir -p $BUILDDIR + test -f $SRCDIR/CMakeLists.txt || { echo "! Missing $SRCDIR/CMakeLists.txt" ; exit 1 ; } + echo "::set-output name=message::"`git log -1 --abbrev-commit --pretty=oneline --no-decorate ${{ github.event.head_commit.id }}` + - name: "Calamares: cmake" + working-directory: ${{ env.BUILDDIR }} + run: cmake $CMAKE_ARGS $SRCDIR + - name: "Calamares: make" + working-directory: ${{ env.BUILDDIR }} + run: make -j2 VERBOSE=1 + - name: "Calamares: install" + working-directory: ${{ env.BUILDDIR }} + run: make install VERBOSE=1 DESTDIR=${{ env.BUILDDIR }}/stage + - name: "Calamares: archive" + working-directory: ${{ env.BUILDDIR }} + run: tar czf calamares.tar.gz stage + - name: "upload" + uses: actions/upload-artifact@v2 + with: + name: calamares-tarball + path: ${{ env.BUILDDIR }}/calamares.tar.gz + if-no-files-found: error + retention-days: 3 + - name: "notify: ok" + uses: rectalogic/notify-irc@v1 + if: ${{ success() && github.repository == 'calamares/calamares' }} + with: + server: chat.freenode.net + nickname: cala-ci + channel: "#calamares" + message: "OK ${{ github.workflow }} in ${{ github.repository }} ${{ steps.pre_build.outputs.message }}" + - name: "notify: fail" + uses: rectalogic/notify-irc@v1 + if: ${{ failure() && github.repository == 'calamares/calamares' }} + with: + server: chat.freenode.net + nickname: cala-ci + channel: "#calamares" + message: "FAIL ${{ github.workflow }} in ${{ github.repository }} ${{ steps.pre_build.outputs.message }}" diff --git a/.github/workflows/nightly-opensuse.yml b/.github/workflows/nightly-opensuse.yml new file mode 100644 index 000000000..c3762fb99 --- /dev/null +++ b/.github/workflows/nightly-opensuse.yml @@ -0,0 +1,98 @@ +name: nightly-opensuse + +on: + schedule: + - cron: "32 23 * * *" + workflow_dispatch: + +env: + BUILDDIR: /build + SRCDIR: ${{ github.workspace }} + CMAKE_ARGS: | + -DWEBVIEW_FORCE_WEBKIT=1 + -DKDE_INSTALL_USE_QT_SYS_PATHS=ON + -DWITH_PYTHONQT=OFF" + -DCMAKE_BUILD_TYPE=Debug + +jobs: + build: + runs-on: ubuntu-latest + container: + image: docker://opensuse/leap + options: --tmpfs /build:rw --user 0:0 + steps: + - name: "prepare env" + run: | + zypper --non-interactive up + zypper --non-interactive in git-core + # From deploycala.py + zypper --non-interactive in \ + "autoconf" \ + "automake" \ + "bison" \ + "flex" \ + "git" \ + "libtool" \ + "m4" \ + "make" \ + "cmake" \ + "extra-cmake-modules" \ + "gcc-c++" \ + "libqt5-qtbase-devel" \ + "libqt5-linguist-devel" \ + "libqt5-qtsvg-devel" \ + "libqt5-qtdeclarative-devel" \ + "libqt5-qtwebengine-devel" \ + "yaml-cpp-devel" \ + "libpolkit-qt5-1-devel" \ + "kservice-devel" \ + "kpackage-devel" \ + "kparts-devel" \ + "kcrash-devel" \ + "kpmcore-devel" \ + "plasma5-workspace-devel" \ + "plasma-framework-devel" \ + "libpwquality-devel" \ + "parted-devel" \ + "python3-devel" \ + "boost-devel" \ + "libboost_python-py3-*-devel" + # Additional dependencies + zypper --non-interactive in \ + libicu-devel \ + libAppStreamQt-devel \ + libatasmart-devel + - name: "prepare source" + uses: actions/checkout@v2 + - name: "prepare build" + id: pre_build + run: | + test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; } + mkdir -p $BUILDDIR + test -f $SRCDIR/CMakeLists.txt || { echo "! Missing $SRCDIR/CMakeLists.txt" ; exit 1 ; } + echo "::set-output name=message::"`git log -1 --abbrev-commit --pretty=oneline --no-decorate ${{ github.event.head_commit.id }}` + - name: "Calamares: cmake" + working-directory: ${{ env.BUILDDIR }} + run: cmake $CMAKE_ARGS $SRCDIR + - name: "Calamares: make" + working-directory: ${{ env.BUILDDIR }} + run: make -j2 VERBOSE=1 + - name: "Calamares: install" + working-directory: ${{ env.BUILDDIR }} + run: make install VERBOSE=1 + - name: "notify: ok" + uses: rectalogic/notify-irc@v1 + if: ${{ success() && github.repository == 'calamares/calamares' }} + with: + server: chat.freenode.net + nickname: cala-ci + channel: "#calamares" + message: "OK ${{ github.workflow }} in ${{ github.repository }} ${{ steps.pre_build.outputs.message }}" + - name: "notify: fail" + uses: rectalogic/notify-irc@v1 + if: ${{ failure() && github.repository == 'calamares/calamares' }} + with: + server: chat.freenode.net + nickname: cala-ci + channel: "#calamares" + message: "FAIL ${{ github.workflow }} in ${{ github.repository }} ${{ steps.pre_build.outputs.message }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/push.yml similarity index 63% rename from .github/workflows/ci.yml rename to .github/workflows/push.yml index c2699507d..0889ea278 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/push.yml @@ -1,4 +1,4 @@ -name: ci +name: ci-push on: push: @@ -27,17 +27,10 @@ jobs: image: docker://kdeneon/plasma:user options: --tmpfs /build:rw --user 0:0 steps: - - - name: prepare env + - name: "prepare env" run: | sudo apt-get update sudo apt-get -y install git-core - - - name: checkout - uses: actions/checkout@v2 - - - name: install dependencies - run: | sudo apt-get -y install \ build-essential \ cmake \ @@ -66,31 +59,37 @@ jobs: qtdeclarative5-dev \ qttools5-dev \ qttools5-dev-tools - - - name: prepare build + - name: "prepare source" + uses: actions/checkout@v2 + - name: "prepare build" + id: pre_build run: | test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; } mkdir -p $BUILDDIR test -f $SRCDIR/CMakeLists.txt || { echo "! Missing $SRCDIR/CMakeLists.txt" ; exit 1 ; } - - - name: cmake + echo "::set-output name=message::"`git log -1 --abbrev-commit --pretty=oneline --no-decorate ${{ github.event.head_commit.id }}` + - name: "Calamares: cmake" working-directory: ${{ env.BUILDDIR }} run: cmake $CMAKE_ARGS $SRCDIR - - - name: make + - name: "Calamares: make" working-directory: ${{ env.BUILDDIR }} run: make -j2 VERBOSE=1 - - - name: install + - name: "Calamares: install" working-directory: ${{ env.BUILDDIR }} - run: | - make install VERBOSE=1 - - - name: notify + run: make install VERBOSE=1 + - name: "notify: ok" uses: rectalogic/notify-irc@v1 + if: ${{ success() && github.repository == 'calamares/calamares' }} with: server: chat.freenode.net + nickname: cala-ci channel: "#calamares" - nickname: gh-notify - message: | - ${{ github.actor }} pushed ${{ github.event.ref }} CI ${{ steps.install.conclusion }} JOB ${{ github.job }} RUN ${{ github.run_id }} + message: "OK ${{ github.workflow }} in ${{ github.repository }} ${{ github.actor }} on ${{ github.event.ref }}\n.. ${{ steps.pre_build.outputs.message }}" + - name: "notify: fail" + uses: rectalogic/notify-irc@v1 + if: ${{ failure() && github.repository == 'calamares/calamares' }} + with: + server: chat.freenode.net + nickname: cala-ci + channel: "#calamares" + message: "FAIL ${{ github.workflow }} in ${{ github.repository }} ${{ github.actor }} on ${{ github.event.ref }}\n.. ${{ steps.pre_build.outputs.message }}\n.. DIFF ${{ github.event.compare }}" diff --git a/AUTHORS b/AUTHORS index 912d48da7..1c4d7aa24 100644 --- a/AUTHORS +++ b/AUTHORS @@ -38,6 +38,7 @@ and moral support from (alphabetically by first name or nickname): - Kevin Kofler - Kyle Robertze - Lisa Vitolo + - Neal Gompa - n3rdopolis - Philip Müller - Ramon Buldó diff --git a/CHANGES b/CHANGES index d112470b4..e6e8de223 100644 --- a/CHANGES +++ b/CHANGES @@ -7,17 +7,71 @@ 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.36 (unreleased) # +# 3.2.38 (unreleased) # + +This release contains contributions from (alphabetically by first name): + - No external contributors yet + +## Core ## + - No core changes yet + +## Modules ## + - No module changes yet + + +# 3.2.37 (2021-02-23) # + +This release contains contributions from (alphabetically by first name): + - benne-dee + +## Core ## + - Calamares has a table of 'best guess' languages for each country + and when GeoIP is enabled, it will automatically select that + country's language as default -- the user can of course pick + a different one. The 'best guess' is based on Unicode / ISO + data, which is sometimes dubious. Based on some personal notes, + the 'best guess' language for Belarus has been changed to Russian. + - Calamares has a table of 'best guess' keyboard mappings, + allowing native language input. However, usernames and + passwords should be in US-ASCII (this is a limitation of + the login system -- **some** parts of the system will support + non-ASCII input, but it's better safe than sorry). + Add Greek to the list of languages that needs US-ASCII + in addition to native input. + - The CI infrastructure now builds Calamares and Calamares-extensions + on a nightly basis. + +## Modules ## + - The *netinstall* module has a YAML schema, allowing packagers + to validate and verify their netinstall configurations before + shipping an ISO (or writing bug reports). Thanks benne-dee. + - The *finished* module has been heavily refactored, opening + the way to a QML-based version of the same module. This is + also preparatory work for allowing packagers (e.g. PostmarketOS) + to customize the messages on the finished page. + + +# 3.2.36 (2021-02-03) # This release contains contributions from (alphabetically by first name): - Anubhav Choudhary + - benne-dee - Gaël PORTAY + - Jonas Strassel + - Kevin Kofler + - Matti Hyttinen + - Neal Gompa ## Core ## - It is now possible to hide the *next* and *back* buttons during - the "exec" phase of installation. THanks Anubhav. + the "exec" phase of installation. Thanks Anubhav. + - The Calamares CI has migrated to GitHub actions. Thanks Jonas. ## Modules ## + - *bootloader* now uses the current file names for the UEFI Secure Boot + shim instead of obsolete ones. + - The *mount* module creates swap in its own subvolume, if btrfs is used. + Thanks Matti. - *partition* includes more information about what it will do, including GPT partition types (in human-readable format, if possible). Thanks Gaël. - Some edge-cases with overlay filesystems have been resolved in the diff --git a/CMakeLists.txt b/CMakeLists.txt index 976823805..758ae3332 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ # TODO:3.3: Require CMake 3.12 cmake_minimum_required( VERSION 3.3 FATAL_ERROR ) project( CALAMARES - VERSION 3.2.36 + VERSION 3.2.38 LANGUAGES C CXX ) @@ -194,7 +194,7 @@ include( CMakeColors ) set( CMAKE_CXX_STANDARD 17 ) set( CMAKE_CXX_STANDARD_REQUIRED ON ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror=return-type" ) -set( CMAKE_CXX_FLAGS_DEBUG "-g ${CMAKE_CXX_FLAGS_DEBUG}" ) +set( CMAKE_CXX_FLAGS_DEBUG "-Og -g ${CMAKE_CXX_FLAGS_DEBUG}" ) set( CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG" ) set( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" ) set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g" ) @@ -202,7 +202,7 @@ set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g" ) set( CMAKE_C_STANDARD 99 ) set( CMAKE_C_STANDARD_REQUIRED ON ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall" ) -set( CMAKE_C_FLAGS_DEBUG "-g" ) +set( CMAKE_C_FLAGS_DEBUG "-Og -g" ) set( CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG" ) set( CMAKE_C_FLAGS_RELEASE "-O4 -DNDEBUG" ) set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g" ) @@ -230,6 +230,10 @@ if( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) string( APPEND CMAKE_CXX_FLAGS " ${CLANG_WARNINGS}" ) endforeach() + # The dwarf-debugging flags are slightly different, too + string( APPEND CMAKE_CXX_FLAGS_DEBUG " -gdwarf" ) + string( APPEND CMAKE_C_FLAGS_DEBUG " -gdwarf" ) + # Third-party code where we don't care so much about compiler warnings # (because it's uncomfortable to patch) get different flags; use # mark_thirdparty_code( [...] ) @@ -686,3 +690,16 @@ feature_summary( DESCRIPTION "The following REQUIRED packages were not found:" QUIET_ON_EMPTY ) + +### PACKAGING +# +# Note: most distro's will do distro-specific packaging rather than +# using CPack, and this duplicates information in the AppStream, too. +# TODO:3.3 With newer CMake, move HOMEPAGE_URL to the project()call +set(CPACK_PACKAGE_VENDOR calamares) +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A Linux system installer") +set(CPACK_PACKAGE_DESCRIPTION "Calamares is a Linux system installer, intended for Linux distributions to use on their ISOs and other bootable media to install the distribution to the end-user's computer. Calamares can also be used as an OEM configuration tool. It is modular, extensible and highly-configurable for Linux distributions from all five major Linux families.") +set(CPACK_PACKAGE_HOMEPAGE_URL "https://calamares.io") +set(CPACK_PACKAGE_ICON "data/images/squid.png") + +include(CPack) diff --git a/ci/RELEASE.sh b/ci/RELEASE.sh index 6f1b198c9..706d4c2ea 100755 --- a/ci/RELEASE.sh +++ b/ci/RELEASE.sh @@ -7,6 +7,8 @@ # # Release script for Calamares # +# NOTE: this script contains Linuxisms (in particular, expects GNU mktemp(1)) +# # This attempts to perform the different steps of the RELEASE.md # document automatically. It's not tested on other machines or # setups other than [ade]'s development VM. diff --git a/lang/calamares_he.ts b/lang/calamares_he.ts index 7e8ae9cdb..57d654e05 100644 --- a/lang/calamares_he.ts +++ b/lang/calamares_he.ts @@ -145,7 +145,7 @@ Done - בוצע + סיום @@ -495,7 +495,7 @@ The installer will quit and all changes will be lost. %1 Installer - תכנית התקנת %1 + אשף התקנת %1 @@ -539,7 +539,7 @@ The installer will quit and all changes will be lost. Reuse %1 as home partition for %2. - להשתמש ב־%1 כמחיצת הבית (home) עבור %2. + שימוש ב־%1 כמחיצת הבית (home) עבור %2. @@ -2504,7 +2504,7 @@ The installer will quit and all changes will be lost. Free Space - זכרון פנוי + שטח פנוי @@ -2931,7 +2931,7 @@ Output: Unpartitioned space or unknown partition table - הזכרון לא מחולק למחיצות או שטבלת המחיצות אינה מוכרת + השטח לא מחולק למחיצות או שטבלת המחיצות אינה מוכרת @@ -2991,7 +2991,7 @@ Output: %1 cannot be installed on empty space. Please select an existing partition. - לא ניתן להתקין את %1 על זכרון ריק. אנא בחר מחיצה קיימת. + לא ניתן להתקין את %1 על שטח ריק. נא לבחור מחיצה קיימת. @@ -3341,7 +3341,7 @@ Output: Flag new partition as <strong>%1</strong>. - סמן מחיצה חדשה כ <strong>%1</strong>. + סימון המחיצה החדשה בתור <strong>%1</strong>. @@ -4065,7 +4065,7 @@ Output: Your Full Name - שם המלא + שמך המלא diff --git a/lang/calamares_id.ts b/lang/calamares_id.ts index 33966e696..b790ef61b 100644 --- a/lang/calamares_id.ts +++ b/lang/calamares_id.ts @@ -11,12 +11,12 @@ This system was started with an <strong>EFI</strong> boot environment.<br><br>To configure startup from an EFI environment, this installer must deploy a boot loader application, like <strong>GRUB</strong> or <strong>systemd-boot</strong> on an <strong>EFI System Partition</strong>. This is automatic, unless you choose manual partitioning, in which case you must choose it or create it on your own. - Sistem ini telah dimulai dengan lingkungan boot <strong>EFI</strong>.<br><br>Untuk mengkonfigurasi startup dari lingkungan EFI, installer ini seharusnya memaparkan sebuah aplikasi boot loader, seperti <strong>GRUB</strong> atau <strong>systemd-boot</strong> pada sebuah <strong>EFI System Partition</strong>. Ini adalah otomatis, kecuali kalau kamu memilih pemartisian manual, dalam beberapa kasus kamu harus memilihnya atau menciptakannya pada milikmu. + Sistem ini telah dimulai dengan lingkungan boot <strong>EFI</strong>.<br><br>Untuk mengkonfigurasi startup dari lingkungan EFI, installer ini seharusnya memaparkan sebuah aplikasi boot loader, seperti <strong>GRUB</strong> atau <strong>systemd-boot</strong> pada sebuah <strong>EFI System Partition</strong>. Ini adalah otomatis, kecuali kalau kamu memilih pemartisian manual, dalam kasus ini kamu harus memilihnya atau menciptakannya sendiri. This system was started with a <strong>BIOS</strong> boot environment.<br><br>To configure startup from a BIOS environment, this installer must install a boot loader, like <strong>GRUB</strong>, either at the beginning of a partition or on the <strong>Master Boot Record</strong> near the beginning of the partition table (preferred). This is automatic, unless you choose manual partitioning, in which case you must set it up on your own. - Sistem ini dimulai dengan sebuah lingkungan boot <strong>BIOS</strong>.<br><br>Untuk mengkonfigurasi startup dari sebuah lingkungan BIOS, installer ini seharusnya memasang sebuah boot loader, seperti <strong>GRUB</strong>, baik di awal partisi atau pada <strong>Master Boot Record</strong> di dekat awalan tabel partisi (yang disukai). Ini adalah otomatis, kecuali kalau kamu memilih pemartisian manual, dalam beberapa kasus kamu harus menyetelnya pada milikmu. + Sistem ini dimulai dengan sebuah lingkungan boot <strong>BIOS</strong>.<br><br>Untuk mengkonfigurasi startup dari sebuah lingkungan BIOS, installer ini harus memasang sebuah boot loader, seperti <strong>GRUB</strong>, baik di awal partisi atau pada <strong>Master Boot Record</strong> di dekat awalan tabel partisi (yang disukai). Ini adalah otomatis, kecuali kalau kamu memilih pemartisian manual, dalam beberapa kasus kamu harus menyetelnya sendiri. @@ -101,7 +101,7 @@ Reload Stylesheet - + Muat ulang Lembar gaya @@ -235,8 +235,8 @@ Waiting for %n module(s). - - + + Menunggu %n modul(). @@ -267,7 +267,7 @@ Would you like to paste the install log to the web? - Maukah anda untuk menempelkan log pemasangan ke situs? + Maukah anda untuk menempelkan log instalasi ke situs? @@ -324,7 +324,7 @@ Continue with installation? - + Lanjutkan instalasi? @@ -454,7 +454,8 @@ Instalasi akan ditutup dan semua perubahan akan hilang. Install log posted to: %1 - + Log instalasi terunggah ke: +%1 @@ -630,7 +631,7 @@ Instalasi akan ditutup dan semua perubahan akan hilang. No Swap - Tidak perlu SWAP + Tidak pakai SWAP @@ -640,7 +641,7 @@ Instalasi akan ditutup dan semua perubahan akan hilang. Swap (no Hibernate) - Swap (tidak hibernasi) + Swap (tanpa hibernasi) @@ -768,8 +769,7 @@ Instalasi akan ditutup dan semua perubahan akan hilang. This computer does not satisfy the minimum requirements for installing %1.<br/>Installation cannot continue. <a href="#details">Details...</a> - Komputer ini tidak memenuhi syarat minimum untuk memasang %1. -Installer tidak dapat dilanjutkan. <a href=" + Komputer ini tidak memenuhi syarat minimum untuk memasang %1.<br/>Instalasi tidak dapat dilanjutkan. <a href="#details">Lebih rinci...</a> @@ -790,7 +790,7 @@ Instalasi dapat dilanjutkan, namun beberapa fitur akan dinonfungsikan. <h1>Welcome to the Calamares setup program for %1</h1> - + <h1>Selamat datang ke program Calamares untuk %1</h1> @@ -815,12 +815,12 @@ Instalasi dapat dilanjutkan, namun beberapa fitur akan dinonfungsikan. '%1' is not allowed as username. - + '%1' tidak diperbolehkan sebagai nama pengguna. Your username must start with a lowercase letter or underscore. - + Nama penggunamu harus diawali dengan huruf kecil atau garis bawah. @@ -840,12 +840,12 @@ Instalasi dapat dilanjutkan, namun beberapa fitur akan dinonfungsikan. '%1' is not allowed as hostname. - + '%1' tidak diperbolehkan sebagai hostname. Only letters, numbers, underscore and hyphen are allowed. - + Hanya huruf, angka, garis bawah, dan tanda penghubung yang diperbolehkan. diff --git a/lang/calamares_ne_NP.ts b/lang/calamares_ne_NP.ts index 13990c086..6cd62c736 100644 --- a/lang/calamares_ne_NP.ts +++ b/lang/calamares_ne_NP.ts @@ -39,7 +39,7 @@ Do not install a boot loader - + बूट लोडर install नगर्ने @@ -52,7 +52,7 @@ Blank Page - + खाली पृष्ठ @@ -60,7 +60,7 @@ Form - + फारम @@ -80,7 +80,7 @@ Type: - + प्रकार @@ -96,7 +96,7 @@ Tools - + औजारहरु @@ -145,7 +145,7 @@ Done - + सकियो @@ -153,7 +153,7 @@ Example job (%1) - + उदाहरण कार्य (%1) @@ -212,7 +212,7 @@ Loading ... - + लोड हुँदैछ ... @@ -222,7 +222,7 @@ Loading failed. - + लोड भएन । @@ -366,7 +366,7 @@ Setup is complete. Close the setup program. - + सेटअप सकियो । सेटअप प्रोग्राम बन्द गर्नु होस  @@ -504,7 +504,7 @@ The installer will quit and all changes will be lost. Form - + फारम @@ -547,7 +547,7 @@ The installer will quit and all changes will be lost. Boot loader location: - + बूट लोडरको स्थान @@ -631,12 +631,12 @@ The installer will quit and all changes will be lost. No Swap - + swap छैन Reuse Swap - + swap पुनः प्रयोग गर्नुहोस @@ -789,22 +789,22 @@ The installer will quit and all changes will be lost. <h1>Welcome to the Calamares setup program for %1</h1> - + %1 को लागि Calamares Setup Programमा स्वागत छ । <h1>Welcome to %1 setup</h1> - + %1 को Setupमा स्वागत छ । <h1>Welcome to the Calamares installer for %1</h1> - + %1 को लागि Calamares Installerमा स्वागत छ । <h1>Welcome to the %1 installer</h1> - + %1 को Installerमा स्वागत छ । @@ -849,7 +849,7 @@ The installer will quit and all changes will be lost. Your passwords do not match! - + पासवर्डहरू मिलेन ।  @@ -1256,7 +1256,7 @@ The installer will quit and all changes will be lost. Form - + फारम @@ -1323,7 +1323,7 @@ The installer will quit and all changes will be lost. Form - + फारम @@ -1605,7 +1605,7 @@ The installer will quit and all changes will be lost. Form - + फारम @@ -2263,7 +2263,7 @@ The installer will quit and all changes will be lost. Form - + फारम @@ -2317,7 +2317,7 @@ The installer will quit and all changes will be lost. Form - + फारम @@ -2335,7 +2335,7 @@ The installer will quit and all changes will be lost. Form - + फारम @@ -2511,7 +2511,7 @@ The installer will quit and all changes will be lost. Form - + फारम @@ -2721,7 +2721,7 @@ The installer will quit and all changes will be lost. Form - + फारम @@ -2945,7 +2945,7 @@ Output: Form - + फारम @@ -3588,7 +3588,7 @@ Output: Form - + फारम @@ -3736,7 +3736,7 @@ Output: Form - + फारम diff --git a/lang/calamares_pt_BR.ts b/lang/calamares_pt_BR.ts index 563fa2982..9ce469a7d 100644 --- a/lang/calamares_pt_BR.ts +++ b/lang/calamares_pt_BR.ts @@ -291,7 +291,7 @@ &Close - Fe&char + &Fechar @@ -396,7 +396,7 @@ &Done - Concluí&do + &Concluído @@ -512,7 +512,7 @@ O instalador será fechado e todas as alterações serão perdidas. Select storage de&vice: - Selecione o dispositi&vo de armazenamento: + Selecione o dispositivo de armazenamento: @@ -898,7 +898,7 @@ O instalador será fechado e todas as alterações serão perdidas. Fi&le System: - Sistema de &Arquivos: + Sistema de Arquivos: @@ -908,7 +908,7 @@ O instalador será fechado e todas as alterações serão perdidas. &Mount Point: - Ponto de &Montagem: + Ponto de Montagem: diff --git a/lang/calamares_tr_TR.ts b/lang/calamares_tr_TR.ts index e69766fff..0df78b0c7 100644 --- a/lang/calamares_tr_TR.ts +++ b/lang/calamares_tr_TR.ts @@ -3845,7 +3845,7 @@ Kuruluma devam edebilirsiniz fakat bazı özellikler devre dışı kalabilir. <h1>%1</h1><br/><strong>%2<br/>for %3</strong><br/><br/>Copyright 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>Copyright 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/>Thanks to <a href="https://calamares.io/team/">the Calamares team</a> and the <a href="https://www.transifex.com/calamares/calamares/">Calamares translators team</a>.<br/><br/><a href="https://calamares.io/">Calamares</a> development is sponsored by <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Liberating Software. - <h1>%1</h1><br/><strong>%2<br/>for %3</strong><br/><br/>Telif Hakkı 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>Telif Hakkı 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/>Teşekkrürler <a href="https://calamares.io/team/">Calamares takımı</a> ve <a href="https://www.transifex.com/calamares/calamares/">Calamares çeviri ekibi</a>.<br/><br/><a href="https://calamares.io/">Calamares</a> gelişim sponsoru <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Özgür Yazılım + <h1>%1</h1><br/><strong>%2<br/>for %3</strong><br/><br/>Telif Hakkı 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>Telif Hakkı 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/>Teşekkürler <a href="https://calamares.io/team/">Calamares takımı</a> ve <a href="https://www.transifex.com/calamares/calamares/">Calamares çeviri ekibi</a>.<br/><br/><a href="https://calamares.io/">Calamares</a> gelişim sponsoru <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Özgür Yazılım diff --git a/lang/python/az/LC_MESSAGES/python.po b/lang/python/az/LC_MESSAGES/python.po index 5a03b748d..1b6dc2444 100644 --- a/lang/python/az/LC_MESSAGES/python.po +++ b/lang/python/az/LC_MESSAGES/python.po @@ -4,7 +4,7 @@ # FIRST AUTHOR , YEAR. # # Translators: -# xxmn77 , 2020 +# Xəyyam Qocayev , 2020 # #, fuzzy msgid "" @@ -13,7 +13,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-12-07 17:09+0100\n" "PO-Revision-Date: 2017-08-09 10:34+0000\n" -"Last-Translator: xxmn77 , 2020\n" +"Last-Translator: Xəyyam Qocayev , 2020\n" "Language-Team: Azerbaijani (https://www.transifex.com/calamares/teams/20061/az/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/lang/python/az_AZ/LC_MESSAGES/python.po b/lang/python/az_AZ/LC_MESSAGES/python.po index 027e819d5..546cd56c1 100644 --- a/lang/python/az_AZ/LC_MESSAGES/python.po +++ b/lang/python/az_AZ/LC_MESSAGES/python.po @@ -4,7 +4,7 @@ # FIRST AUTHOR , YEAR. # # Translators: -# xxmn77 , 2020 +# Xəyyam Qocayev , 2020 # #, fuzzy msgid "" @@ -13,7 +13,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-12-07 17:09+0100\n" "PO-Revision-Date: 2017-08-09 10:34+0000\n" -"Last-Translator: xxmn77 , 2020\n" +"Last-Translator: Xəyyam Qocayev , 2020\n" "Language-Team: Azerbaijani (Azerbaijan) (https://www.transifex.com/calamares/teams/20061/az_AZ/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/lang/python/he/LC_MESSAGES/python.po b/lang/python/he/LC_MESSAGES/python.po index 459e858b3..6f6200ac7 100644 --- a/lang/python/he/LC_MESSAGES/python.po +++ b/lang/python/he/LC_MESSAGES/python.po @@ -5,7 +5,7 @@ # # Translators: # Eli Shleifer , 2017 -# Omeritzics Games , 2020 +# Omer I.S. , 2020 # Yaron Shahrabani , 2020 # #, fuzzy diff --git a/src/branding/default/branding.desc b/src/branding/default/branding.desc index 5a8dd3f49..cd83b02f1 100644 --- a/src/branding/default/branding.desc +++ b/src/branding/default/branding.desc @@ -219,3 +219,16 @@ slideshow: "show.qml" slideshowAPI: 2 +# These options are to customize online uploading of logs to pastebins: +# - type : Defines the kind of pastebin service to be used.Currently +# it accepts two values: +# - none : disables the pastebin functionality +# - fiche : use fiche pastebin server +# - url : Defines the address of pastebin service to be used. +# Takes string as input +# - port : Defines the port number to be used to send logs. Takes +# integer as input +uploadServer : + type : "fiche" + url : "termbin.com" + port : 9999 diff --git a/src/calamares/DebugWindow.cpp b/src/calamares/DebugWindow.cpp index 4a68d4ae2..96f4fb335 100644 --- a/src/calamares/DebugWindow.cpp +++ b/src/calamares/DebugWindow.cpp @@ -59,7 +59,7 @@ dumpWidgetTree( QDebug& deb, const QWidget* widget, int depth ) { deb << ' '; } - deb << widget->objectName(); + deb << widget->metaObject()->className() << widget->objectName(); for ( const auto* w : widget->findChildren< QWidget* >( QString(), Qt::FindDirectChildrenOnly ) ) { diff --git a/src/libcalamares/JobQueue.cpp b/src/libcalamares/JobQueue.cpp index b66be7ebe..a975c2c91 100644 --- a/src/libcalamares/JobQueue.cpp +++ b/src/libcalamares/JobQueue.cpp @@ -19,6 +19,8 @@ #include #include +#include + namespace Calamares { diff --git a/src/libcalamares/locale/CountryData_p.cpp b/src/libcalamares/locale/CountryData_p.cpp index 932a1996d..f439de27c 100644 --- a/src/libcalamares/locale/CountryData_p.cpp +++ b/src/libcalamares/locale/CountryData_p.cpp @@ -29,7 +29,8 @@ /* MODIFICATIONS * * Edited anyway: - * 20191211 India changed to AnyLanguage, since Hindi doesn't make sense. #1284 + * 20191211 India (IN) changed to AnyLanguage, since Hindi doesn't make sense. #1284 + * 20210207 Belarus (BY) changed to Russian, as the more-common-language. #1634 * */ @@ -77,7 +78,7 @@ static const CountryData country_data_table[] = { { QLocale::Language::Portuguese, QLocale::Country::Brazil, 'B', 'R' }, { QLocale::Language::Dzongkha, QLocale::Country::Bhutan, 'B', 'T' }, { QLocale::Language::AnyLanguage, QLocale::Country::BouvetIsland, 'B', 'V' }, -{ QLocale::Language::Belarusian, QLocale::Country::Belarus, 'B', 'Y' }, +{ QLocale::Language::Russian, QLocale::Country::Belarus, 'B', 'Y' }, { QLocale::Language::Swahili, QLocale::Country::CongoKinshasa, 'C', 'D' }, { QLocale::Language::French, QLocale::Country::CentralAfricanRepublic, 'C', 'F' }, { QLocale::Language::French, QLocale::Country::CongoBrazzaville, 'C', 'G' }, diff --git a/src/libcalamares/partition/AutoMount.cpp b/src/libcalamares/partition/AutoMount.cpp index 0fc253067..adc844816 100644 --- a/src/libcalamares/partition/AutoMount.cpp +++ b/src/libcalamares/partition/AutoMount.cpp @@ -12,6 +12,8 @@ #include +#include + namespace CalamaresUtils { namespace Partition @@ -58,13 +60,21 @@ querySolidAutoMount( QDBusConnection& dbus, AutoMountInfo& info ) // Find previous setting; this **does** need to block auto msg = kdedCall( QStringLiteral( "isModuleAutoloaded" ) ); msg.setArguments( { moduleName } ); + std::optional< bool > result; QDBusMessage r = dbus.call( msg, QDBus::Block ); if ( r.type() == QDBusMessage::ReplyMessage ) { auto arg = r.arguments(); - cDebug() << arg; - info.wasSolidModuleAutoLoaded = false; + if ( arg.length() == 1 ) + { + auto v = arg.at( 0 ); + if ( v.isValid() && v.type() == QVariant::Bool ) + { + result = v.toBool(); + } + } } + info.wasSolidModuleAutoLoaded = result.has_value() ? result.value() : false; } std::shared_ptr< AutoMountInfo > @@ -79,7 +89,7 @@ automountDisable( bool disable ) void -automountRestore( std::shared_ptr< AutoMountInfo >&& t ) +automountRestore( const std::shared_ptr< AutoMountInfo >& t ) { QDBusConnection dbus = QDBusConnection::sessionBus(); enableSolidAutoMount( dbus, t->wasSolidModuleAutoLoaded ); diff --git a/src/libcalamares/partition/AutoMount.h b/src/libcalamares/partition/AutoMount.h index a9a88e320..c792eeb99 100644 --- a/src/libcalamares/partition/AutoMount.h +++ b/src/libcalamares/partition/AutoMount.h @@ -43,7 +43,7 @@ DLLEXPORT std::shared_ptr< AutoMountInfo > automountDisable( bool disable = true * Pass the value returned from automountDisable() to restore the * previous settings. */ -DLLEXPORT void automountRestore( std::shared_ptr< AutoMountInfo >&& t ); +DLLEXPORT void automountRestore( const std::shared_ptr< AutoMountInfo >& t ); } // namespace Partition } // namespace CalamaresUtils diff --git a/src/libcalamares/utils/Logger.h b/src/libcalamares/utils/Logger.h index a53ab7e19..f318c78a6 100644 --- a/src/libcalamares/utils/Logger.h +++ b/src/libcalamares/utils/Logger.h @@ -13,10 +13,13 @@ #ifndef UTILS_LOGGER_H #define UTILS_LOGGER_H -#include - #include "DllMacro.h" +#include +#include + +#include + namespace Logger { struct FuncSuppressor @@ -206,16 +209,34 @@ public: * Pointers are printed as void-pointer, so just an address (unlike, say, * QObject pointers which show an address and some metadata) preceded * by an '@'. This avoids C-style (void*) casts in the code. + * + * Shared pointers are indicated by 'S@' and unique pointers by 'U@'. */ struct Pointer { public: explicit Pointer( const void* p ) : ptr( p ) + , kind( 0 ) + { + } + + template < typename T > + explicit Pointer( const std::shared_ptr< T >& p ) + : ptr( p.get() ) + , kind( 'S' ) + { + } + + template < typename T > + explicit Pointer( const std::unique_ptr< T >& p ) + : ptr( p.get() ) + , kind( 'U' ) { } const void* const ptr; + const char kind; }; /** @brief output operator for DebugRow */ @@ -256,7 +277,12 @@ operator<<( QDebug& s, const DebugMap& t ) inline QDebug& operator<<( QDebug& s, const Pointer& p ) { - s << NoQuote << '@' << p.ptr << Quote; + s << NoQuote; + if ( p.kind ) + { + s << p.kind; + } + s << '@' << p.ptr << Quote; return s; } } // namespace Logger diff --git a/src/libcalamares/utils/NamedEnum.h b/src/libcalamares/utils/NamedEnum.h index cf56a26f2..1d839ddc4 100644 --- a/src/libcalamares/utils/NamedEnum.h +++ b/src/libcalamares/utils/NamedEnum.h @@ -11,11 +11,13 @@ /** @brief Support for "named" enumerations * - * For tables which map string names to enum values, provide a NamedEnumTable - * which hangs on to an initializer_list of pairs of names and values. - * This table can be used with find() to map names to values, or - * values to names. A convenience function smash() is provided to help - * in printing integer (underlying) values of an enum. + * When a string needs to be one specific string out of a set of + * alternatives -- one "name" from an enumerated set -- then it + * is useful to have an **enum type** for the enumeration so that + * C++ code can work with the (strong) type of the enum, while + * the string can be used for human-readable interaction. + * The `NamedEnumTable` template provides support for naming + * values of an enum. */ #ifndef UTILS_NAMEDENUM_H @@ -27,7 +29,100 @@ #include #include -/** @brief Type for collecting parts of a named enum. */ +/** @brief Type for collecting parts of a named enum. + * + * The `NamedEnumTable` template provides support for naming + * values of an enum. It supports mapping strings to enum values + * and mapping enum values to strings. + * + * ## Example + * + * Suppose we have code where there are three alternatives; it is + * useful to have a strong type to make the alternatives visible + * in that code, so the compiler can help check: + * + * ``` + * enum class MilkshakeSize { None, Small, Large }; + * ``` + * + * In a switch() statement, the compiler will check that all kinds + * of milkshakes are dealt with; we can pass a MilkshakeSize to + * a function and rest assured that nobody will call that function + * with a silly value, like `1`. + * + * There is no relation between the C++ identifiers used, and + * any I/O related to that enumeration. In other words, + * + * ``` + * std::cout << MilkshakeSize::Small; + * ``` + * + * Will **not** print out "Small", or "small", or 1. It won't even + * compile, because there is no mapping of the enum values to + * something that can be output. + * + * By making a `NamedEnumTable` we can define a mapping + * between strings (names) and enum values, so that we can easily + * output the human-readable name, and also take string input + * and convert it to an enum value. Suppose we have a function + * `milkshakeSizeNames()` that returns a reference to such a table, + * then we can use `find()` to map enums-to-names and names-to-enums. + * + * ``` + * const auto& names = milkshakeSizeNames(); + * MilkshakeSize sz{ MilkshakeSize::Large }; + * std::cout << names.find(sz); // Probably "large" + * + * bool ok; + * sz = names.find( "small", ok ); // Probably MilkshakeSize::Small + * ``` + * + * ## Usage + * + * It is recommended to use a static const declaration for the table; + * typical use will define a function that returns a reference to + * the table, for shared use. + * + * The constructor for a table takes an initializer_list; each element + * of the initializer_list is a **pair** consisting of a name and + * an associated enum value. The names should be QStrings. For each enum + * value that is listed, the canonical name should come **first** in the + * table, so that printing the enum values gives the canonical result. + * + * ``` + * static const NamedEnumTable& milkshakeSizeNames() + * { + * static NamedEnumTable n { // Initializer list for n + * { "large", MilkshakeSize::Large }, // One pair of name-and-value + * { "small", MilkshakeSize::Small }, + * { "big", MilkshakeSize::Large } + * }; + * return n; + * } + * ``` + * + * The function `eNames()`, above, returns a reference to a name table + * for the enum (presumably an enum class) `E`. It is possible to have + * more than one table for an enum, or to make the table locally, + * but **usually** you want one definitive table of names and values. + * The `eNames()` function gives you that definitive table. In Calamres + * code, such functions are usually named after the underlying enum. + * + * Using this particular table, looking up "large" will return `MilkshakeSize::Large`, + * looking up "big" will **also** return `MilkshakeSize::Large`, looking up "derp" + * will return `MilkshakeSize::Large` (because that is the first value in the table) + * but will set the boolean `ok` parameter to false. Conversely, looking + * up `MilkshakeSize::Large` will return "large" (never "big"). + * + * Note that this particular table does **not** name MilkshakeSize::None, + * so it is probably wrong: you can't get a string for that enum + * value, and no string will map to MilkshakeSize::None either. + * In general, tables should cover all of the enum values. + * + * Passing an empty initializer_list to the constructor is supported, + * but will cause UB if the table is ever used for looking up a string. + * + */ template < typename T > struct NamedEnumTable { @@ -43,7 +138,9 @@ struct NamedEnumTable * Use braced-initialisation for NamedEnum, and remember that the * elements of the list are **pairs**, e.g. * + * ``` * static const NamedEnumTable c{ {"red", Colors::Red } }; + * ``` */ NamedEnumTable( const std::initializer_list< pair_t >& v ) : table( v ) @@ -55,10 +152,12 @@ struct NamedEnumTable * * Searches case-insensitively. * - * If the name @p s is not found, @p ok is set to false and + * If the name @p s is not found, @p ok is set to @c false and * the first enum value in the table is returned. Otherwise, - * @p ok is set to true and the corresponding value is returned. - * + * @p ok is set to @c true and the corresponding value is returned. + * Use the output value of @p ok to determine if the lookup was + * successful: there is otherwise no sensible way to distinguish + * found-the-name-of-the-first-item from not-found. */ enum_t find( const string_t& s, bool& ok ) const { @@ -75,11 +174,17 @@ struct NamedEnumTable return table.begin()->second; } - /** @brief Find a value @p s in the table. + /** @brief Find a value @p s in the table and return its name. * - * If the value @p s is not found, @p ok is set to false and - * an empty string is returned. Otherwise, @p is set to true - * and the corresponding name is returned. + * If @p s is an enum value in the table, return the corresponding + * name (the first name with that value, if there are aliases) + * and set @p ok to @c true. + * + * If the value @p s is not found, @p ok is set to @c false and + * an empty string is returned. This indicates that the table does + * not cover all of the values * in `enum_t` (and @p s is one + * of them), **or** that the passed-in value of @p s is + * not a legal value, e.g. via a static_cast. */ string_t find( enum_t s, bool& ok ) const { @@ -98,7 +203,10 @@ struct NamedEnumTable /** @brief Find a value @p s in the table and return its name. * - * Returns emptry string if the value is not found. + * Returns an empty string if the value @p s is not found (this + * indicates that the table does not cover all of the values + * in `enum_t`, **or** that the passed-in value of @p s is + * not a legal value, e.g. via a static_cast). */ string_t find( enum_t s ) const { @@ -107,7 +215,24 @@ struct NamedEnumTable } }; -/** @brief Smashes an enum value to its underlying type. */ +/** @brief Smashes an enum value to its underlying type. + * + * While an enum **class** is not an integral type, and its values can't be + * printed or treated like an integer (like an old-style enum can), + * the underlying type **is** integral. This template function + * returns the value of an enum value, in its underlying type. + * This can be useful for debugging purposes, e.g. + * + * ``` + * MilkshakeSize sz{ MilkshakeSize::None }; + * std::cout << milkshakeSizeNames().find( sz ) << smash( sz ); + * ``` + * + * This will print both the name and the underlying integer for the + * value; assuming the table from the example is used, there is + * no name for MilkshakeSize::None, so it will print an empty string, + * followed by the integral representation -- probably a 0. + */ template < typename E > constexpr typename std::underlying_type< E >::type smash( const E e ) diff --git a/src/libcalamares/utils/String.cpp b/src/libcalamares/utils/String.cpp index 0c7bf8fb5..615d30309 100644 --- a/src/libcalamares/utils/String.cpp +++ b/src/libcalamares/utils/String.cpp @@ -135,14 +135,15 @@ truncateMultiLine( const QString& string, CalamaresUtils::LinesStartEnd lines, C return shorter; } - const int linesInString = string.count( NEWLINE ) + ( string.endsWith( NEWLINE ) ? 0 : 1 ); - if ( ( string.length() <= chars.total ) && ( linesInString <= maxLines ) ) + const int physicalLinesInString = string.count( NEWLINE ); + const int logicalLinesInString = physicalLinesInString + ( string.endsWith( NEWLINE ) ? 0 : 1 ); + if ( ( string.length() <= chars.total ) && ( logicalLinesInString <= maxLines ) ) { return string; } QString front, back; - if ( string.count( NEWLINE ) >= maxLines ) + if ( physicalLinesInString >= maxLines ) { int from = -1; for ( int i = 0; i < lines.atStart; ++i ) @@ -174,8 +175,53 @@ truncateMultiLine( const QString& string, CalamaresUtils::LinesStartEnd lines, C back = string.right( lastNewLine ); } } + else + { + // We have: <= maxLines and longer than chars.total, so: + // - carve out a chunk in the middle, based a little on + // how the balance of atStart and atEnd is + const int charsToChop = string.length() - chars.total; + if ( charsToChop < 1 ) + { + // That's strange, again + return string; + } + const int startPortion = charsToChop * lines.atStart / maxLines; + const int endPortion = charsToChop * lines.atEnd / maxLines; + front = string.left( string.length() / 2 - startPortion ); + back = string.right( string.length() / 2 - endPortion ); + } - return front + back; + if ( front.length() + back.length() <= chars.total ) + { + return front + back; + } + + // We need to cut off some bits, preserving whether there are + // newlines present at the end of the string. Go case-by-case: + if ( !front.isEmpty() && back.isEmpty() ) + { + // Truncate towards the front + bool needsNewline = front.endsWith( NEWLINE ); + front.truncate( chars.total ); + if ( !front.endsWith( NEWLINE ) && needsNewline ) + { + front.append( NEWLINE ); + } + return front; + } + if ( front.isEmpty() && !back.isEmpty() ) + { + // Truncate towards the tail + return back.right( chars.total ); + } + // Both are non-empty, so nibble away at both of them + front.truncate( chars.total / 2 ); + if ( !front.endsWith( NEWLINE ) && physicalLinesInString > 0 ) + { + front.append( NEWLINE ); + } + return front + back.right( chars.total / 2 ); } diff --git a/src/libcalamares/utils/String.h b/src/libcalamares/utils/String.h index 43e0474fa..e08255f86 100644 --- a/src/libcalamares/utils/String.h +++ b/src/libcalamares/utils/String.h @@ -89,6 +89,8 @@ struct CharCount * @p lines.atStart is zero) or end (if @p lines.atEnd is zero) or in the middle * (if both are nonzero). * + * Asking for 0 lines will make this behave like QString::truncate(). + * * @param string the input string. * @param lines number of lines to preserve. * @param chars maximum number of characters in the returned string. diff --git a/src/libcalamares/utils/Tests.cpp b/src/libcalamares/utils/Tests.cpp index 3992fe78a..cdb37f20d 100644 --- a/src/libcalamares/utils/Tests.cpp +++ b/src/libcalamares/utils/Tests.cpp @@ -67,6 +67,7 @@ private Q_SLOTS: /** @brief Test smart string truncation. */ void testStringTruncation(); void testStringTruncationShorter(); + void testStringTruncationDegenerate(); private: void recursiveCompareMap( const QVariantMap& a, const QVariantMap& b, int depth ); @@ -597,6 +598,8 @@ and the translations updated.)" ); QVERIFY( !longString.endsWith( NEWLINE ) ); QCOMPARE( longString.count( NEWLINE ), 2 ); QVERIFY( longString.length() > insufficientLength ); + // Even the first line must be more than the insufficientLength + QVERIFY( longString.indexOf( NEWLINE ) > insufficientLength ); // Grab first line, untruncated { @@ -626,11 +629,84 @@ and the translations updated.)" ); QVERIFY( longString.endsWith( s ) ); QVERIFY( !s.endsWith( NEWLINE ) ); QVERIFY( s.endsWith( "updated." ) ); - cDebug() << "Result-line" << Logger::Quote << s; QCOMPARE( s.count( NEWLINE ), 1 ); // Because last line doesn't end with a newline QVERIFY( s.startsWith( "displayed in " ) ); } + // First line, truncated + { + auto s = truncateMultiLine( longString, LinesStartEnd { 1, 0 }, CharCount { insufficientLength } ); + cDebug() << "Result-line" << Logger::Quote << s; + QVERIFY( s.length() > 1 ); + QVERIFY( s.endsWith( NEWLINE ) ); + QVERIFY( s.startsWith( "Some " ) ); + // Because the first line has a newline, the truncated version does too, + // but that makes it one longer than requested. + QCOMPARE( s.length(), insufficientLength + 1 ); + QVERIFY( longString.startsWith( s.left( insufficientLength ) ) ); + } + + // Last line, truncated; this line is quite short + { + const int quiteShort = 8; + QVERIFY( longString.lastIndexOf( NEWLINE ) < longString.length() - quiteShort ); + + auto s = truncateMultiLine( longString, LinesStartEnd { 0, 1 }, CharCount { quiteShort } ); + cDebug() << "Result-line" << Logger::Quote << s; + QVERIFY( s.length() > 1 ); + QVERIFY( !s.endsWith( NEWLINE ) ); // Because the original doesn't either + QVERIFY( s.startsWith( "upda" ) ); + QCOMPARE( s.length(), quiteShort ); // No extra newlines + QVERIFY( longString.endsWith( s ) ); + } + + // First and last, but both truncated + { + const int quiteShort = 16; + QVERIFY( longString.indexOf( NEWLINE ) > quiteShort ); + QVERIFY( longString.lastIndexOf( NEWLINE ) < longString.length() - quiteShort ); + + auto s = truncateMultiLine( longString, LinesStartEnd { 1, 1 }, CharCount { quiteShort } ); + cDebug() << "Result-line" << Logger::Quote << s; + QVERIFY( s.length() > 1 ); + QVERIFY( !s.endsWith( NEWLINE ) ); // Because the original doesn't either + QVERIFY( s.startsWith( "Some " ) ); + QVERIFY( s.endsWith( "updated." ) ); + QCOMPARE( s.length(), quiteShort + 1 ); // Newline between front and back part + } +} + +void +LibCalamaresTests::testStringTruncationDegenerate() +{ + Logger::setupLogLevel( Logger::LOGDEBUG ); + + using namespace CalamaresUtils; + + // This is quite long, 1 line only, with no newlines + const QString longString( "The portscout new distfile checker has detected that one or more of your " + "ports appears to be out of date. Please take the opportunity to check " + "each of the ports listed below, and if possible and appropriate, " + "submit/commit an update. If any ports have already been updated, you can " + "safely ignore the entry." ); + + const char NEWLINE = '\n'; + const int quiteShort = 16; + QVERIFY( longString.length() > quiteShort ); + QVERIFY( !longString.contains( NEWLINE ) ); + QVERIFY( longString.indexOf( NEWLINE ) < 0 ); + + { + auto s = truncateMultiLine( longString, LinesStartEnd { 1, 0 }, CharCount { quiteShort } ); + cDebug() << "Result-line" << Logger::Quote << s; + QVERIFY( s.length() > 1 ); + QCOMPARE( s.length(), quiteShort ); // No newline between front and back part + QVERIFY( s.startsWith( "The port" ) ); // 8, which is quiteShort / 2 + QVERIFY( s.endsWith( "e entry." ) ); // also 8 chars + + auto t = truncateMultiLine( longString, LinesStartEnd { 2, 2 }, CharCount { quiteShort } ); + QCOMPARE( s, t ); + } } diff --git a/src/libcalamaresui/Branding.cpp b/src/libcalamaresui/Branding.cpp index a5038d7ee..a71675e38 100644 --- a/src/libcalamaresui/Branding.cpp +++ b/src/libcalamaresui/Branding.cpp @@ -4,6 +4,7 @@ * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot * SPDX-FileCopyrightText: 2018 Raul Rodrigo Segura (raurodse) * SPDX-FileCopyrightText: 2019 Camilo Higuita + * SPDX-FileCopyrightText: 2021 Anubhav Choudhary * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -86,6 +87,13 @@ const QStringList Branding::s_styleEntryStrings = "sidebarTextSelect", "sidebarTextHighlight" }; + +const QStringList Branding::s_uploadServerStrings = +{ + "type", + "url", + "port" +}; // clang-format on // *INDENT-ON* @@ -218,6 +226,12 @@ Branding::Branding( const QString& brandingFilePath, QObject* parent ) return imageFi.absoluteFilePath(); } ); loadStrings( m_style, doc, "style", []( const QString& s ) -> QString { return s; } ); + + const QVariantMap temp = CalamaresUtils::yamlMapToVariant( doc[ "uploadServer" ] ); + for ( auto it = temp.constBegin(); it != temp.constEnd(); ++it ) + { + m_uploadServer.insert( it.key(), it.value().toString() ); + } } catch ( YAML::Exception& e ) { @@ -278,6 +292,11 @@ Branding::imagePath( Branding::ImageEntry imageEntry ) const return m_images.value( s_imageEntryStrings.value( imageEntry ) ); } +QString +Branding::uploadServer( Branding::UploadServerEntry uploadServerEntry ) const +{ + return m_uploadServer.value( s_uploadServerStrings.value( uploadServerEntry ) ); +} QPixmap Branding::image( Branding::ImageEntry imageEntry, const QSize& size ) const diff --git a/src/libcalamaresui/Branding.h b/src/libcalamaresui/Branding.h index b03df3acd..87f71e862 100644 --- a/src/libcalamaresui/Branding.h +++ b/src/libcalamaresui/Branding.h @@ -4,6 +4,7 @@ * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot * SPDX-FileCopyrightText: 2018 Raul Rodrigo Segura (raurodse) * SPDX-FileCopyrightText: 2019 Camilo Higuita + * SPDX-FileCopyrightText: 2021 Anubhav Choudhary * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -22,6 +23,7 @@ #include #include #include +#include namespace YAML { @@ -42,6 +44,7 @@ public: * e.g. *Branding::ProductName to get the string value for * the product name. */ + enum StringEntry { ProductName, @@ -80,6 +83,14 @@ public: }; Q_ENUM( StyleEntry ) + enum UploadServerEntry : short + { + Type, + URL, + Port + }; + Q_ENUM( UploadServerEntry ) + /** @brief Setting for how much the main window may expand. */ enum class WindowExpansion { @@ -223,6 +234,7 @@ public slots: QString styleString( StyleEntry styleEntry ) const; QString imagePath( ImageEntry imageEntry ) const; + QString uploadServer( UploadServerEntry uploadServerEntry ) const; PanelSide sidebarSide() const { return m_sidebarSide; } PanelSide navigationSide() const { return m_navigationSide; } @@ -233,12 +245,14 @@ private: static const QStringList s_stringEntryStrings; static const QStringList s_imageEntryStrings; static const QStringList s_styleEntryStrings; + static const QStringList s_uploadServerStrings; QString m_descriptorPath; // Path to descriptor (e.g. "/etc/calamares/default/branding.desc") QString m_componentName; // Matches last part of full path to containing directory QMap< QString, QString > m_strings; QMap< QString, QString > m_images; QMap< QString, QString > m_style; + QMap< QString, QString > m_uploadServer; /* The slideshow can be done in one of two ways: * - as a sequence of images diff --git a/src/libcalamaresui/ViewManager.cpp b/src/libcalamaresui/ViewManager.cpp index 39f0bb902..b229607ef 100644 --- a/src/libcalamaresui/ViewManager.cpp +++ b/src/libcalamaresui/ViewManager.cpp @@ -4,6 +4,7 @@ * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot * SPDX-FileCopyrightText: 2019 Dominic Hayes * SPDX-FileCopyrightText: 2019 Gabriel Craciunescu + * SPDX-FileCopyrightText: 2021 Anubhav Choudhary * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -141,7 +142,8 @@ ViewManager::insertViewStep( int before, ViewStep* step ) void ViewManager::onInstallationFailed( const QString& message, const QString& details ) { - bool shouldOfferWebPaste = false; // TODO: config var + QString serverType = Calamares::Branding::instance()->uploadServer( Calamares::Branding::Type ); + bool shouldOfferWebPaste = CalamaresUtils::UploadServersList.contains( serverType ); cError() << "Installation failed:" << message; cDebug() << Logger::SubEntry << "- message:" << message; @@ -153,7 +155,10 @@ ViewManager::onInstallationFailed( const QString& message, const QString& detail QString text = "

" + message + "

"; if ( !details.isEmpty() ) { - text += "

" + CalamaresUtils::truncateMultiLine( details, CalamaresUtils::LinesStartEnd { 8, 0 } ) + "

"; + text += "

" + + CalamaresUtils::truncateMultiLine( details, CalamaresUtils::LinesStartEnd { 6, 2 } ) + .replace( '\n', QStringLiteral( "
" ) ) + + "

"; } if ( shouldOfferWebPaste ) { @@ -184,8 +189,16 @@ ViewManager::onInstallationFailed( const QString& message, const QString& detail connect( msgBox, &QMessageBox::buttonClicked, [msgBox]( QAbstractButton* button ) { if ( msgBox->buttonRole( button ) == QMessageBox::ButtonRole::YesRole ) { - // TODO: host and port should be configurable - QString pasteUrlMsg = CalamaresUtils::sendLogToPastebin( msgBox, QStringLiteral( "termbin.com" ), 9999 ); + QString pasteUrlMsg; + QString serverType = Calamares::Branding::instance()->uploadServer( Calamares::Branding::Type ); + if ( serverType == "fiche" ) + { + pasteUrlMsg = CalamaresUtils::ficheLogUpload( msgBox ); + } + else + { + pasteUrlMsg = QString(); + } QString pasteUrlTitle = tr( "Install Log Paste URL" ); if ( pasteUrlMsg.isEmpty() ) diff --git a/src/libcalamaresui/utils/Paste.cpp b/src/libcalamaresui/utils/Paste.cpp index 8099d9024..27ae7f8f7 100644 --- a/src/libcalamaresui/utils/Paste.cpp +++ b/src/libcalamaresui/utils/Paste.cpp @@ -9,20 +9,35 @@ #include "Paste.h" +#include "Branding.h" #include "utils/Logger.h" #include #include #include #include +#include +#include +#include namespace CalamaresUtils { +QStringList UploadServersList = { + "fiche" + // In future more serverTypes can be added as Calamares support them + // "none" serverType is explicitly not mentioned here +}; + QString -sendLogToPastebin( QObject* parent, const QString& ficheHost, quint16 fichePort ) +ficheLogUpload( QObject* parent ) { - QString pasteUrlFmt = parent->tr( "Install log posted to:\n%1" ); + + const QString& ficheHost = Calamares::Branding::instance()->uploadServer( Calamares::Branding::URL ); + quint16 fichePort = Calamares::Branding::instance()->uploadServer( Calamares::Branding::Port ).toInt(); + + QString pasteUrlFmt = parent->tr( "Install log posted to\n\n%1\n\nLink copied to clipboard" ); + QFile pasteSourceFile( Logger::logFile() ); if ( !pasteSourceFile.open( QIODevice::ReadOnly | QIODevice::Text ) ) { @@ -78,7 +93,17 @@ sendLogToPastebin( QObject* parent, const QString& ficheHost, quint16 fichePort QRegularExpression pasteUrlRegex( "^http[s]?://" + ficheHost ); QString pasteUrlMsg = QString( pasteUrlFmt ).arg( pasteUrlStr ); - if ( nBytesRead < 8 || !pasteUrl.isValid() || !pasteUrlRegex.match( pasteUrlStr ).hasMatch() ) + if ( nBytesRead >= 8 && pasteUrl.isValid() && pasteUrlRegex.match( pasteUrlStr ).hasMatch() ) + { + QClipboard* clipboard = QApplication::clipboard(); + clipboard->setText(pasteUrlStr, QClipboard::Clipboard); + + if (clipboard->supportsSelection()) + { + clipboard->setText(pasteUrlStr, QClipboard::Selection); + } + } + else { cError() << "No data from paste server"; return QString(); diff --git a/src/libcalamaresui/utils/Paste.h b/src/libcalamaresui/utils/Paste.h index f802dfe2e..29e73fc1c 100644 --- a/src/libcalamaresui/utils/Paste.h +++ b/src/libcalamaresui/utils/Paste.h @@ -10,7 +10,7 @@ #ifndef UTILS_PASTE_H #define UTILS_PASTE_H -#include // for quint16 +#include class QObject; class QString; @@ -22,7 +22,9 @@ namespace CalamaresUtils * * Returns the (string) URL that the pastebin gives us. */ -QString sendLogToPastebin( QObject* parent, const QString& ficheHost, quint16 fichePort ); +QString ficheLogUpload( QObject* parent ); + +extern QStringList UploadServersList; } // namespace CalamaresUtils diff --git a/src/libcalamaresui/viewpages/ExecutionViewStep.cpp b/src/libcalamaresui/viewpages/ExecutionViewStep.cpp index bb629b217..cac9b28be 100644 --- a/src/libcalamaresui/viewpages/ExecutionViewStep.cpp +++ b/src/libcalamaresui/viewpages/ExecutionViewStep.cpp @@ -24,7 +24,6 @@ #include "utils/CalamaresUtilsGui.h" #include "utils/Dirs.h" #include "utils/Logger.h" -#include "utils/Qml.h" #include "utils/Retranslator.h" #include @@ -62,6 +61,10 @@ ExecutionViewStep::ExecutionViewStep( QObject* parent ) , m_label( new QLabel ) , m_slideshow( makeSlideshow( m_widget ) ) { + m_widget->setObjectName( "slideshow" ); + m_progressBar->setObjectName( "exec-progress" ); + m_label->setObjectName( "exec-message" ); + QVBoxLayout* layout = new QVBoxLayout( m_widget ); QVBoxLayout* innerLayout = new QVBoxLayout; diff --git a/src/libcalamaresui/viewpages/Slideshow.cpp b/src/libcalamaresui/viewpages/Slideshow.cpp index 268843421..f6df1cae4 100644 --- a/src/libcalamaresui/viewpages/Slideshow.cpp +++ b/src/libcalamaresui/viewpages/Slideshow.cpp @@ -43,6 +43,8 @@ SlideshowQML::SlideshowQML( QWidget* parent ) , m_qmlComponent( nullptr ) , m_qmlObject( nullptr ) { + m_qmlShow->setObjectName("qml"); + CalamaresUtils::registerQmlModels(); m_qmlShow->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); @@ -203,6 +205,8 @@ SlideshowPictures::SlideshowPictures( QWidget* parent ) , m_imageIndex( 0 ) , m_images( Branding::instance()->slideshowImages() ) { + m_label->setObjectName("image"); + m_label->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); m_label->setAlignment( Qt::AlignCenter ); m_timer->setInterval( std::chrono::milliseconds( 2000 ) ); diff --git a/src/modules/bootloader/main.py b/src/modules/bootloader/main.py index ec9a6f2e6..03fdb0c5b 100644 --- a/src/modules/bootloader/main.py +++ b/src/modules/bootloader/main.py @@ -14,6 +14,7 @@ # SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot # SPDX-FileCopyrightText: 2017 Gabriel Craciunescu # SPDX-FileCopyrightText: 2017 Ben Green +# SPDX-FileCopyrightText: 2021 Neal Gompa # SPDX-License-Identifier: GPL-3.0-or-later # # Calamares is Free Software: see the License-Identifier above. @@ -372,9 +373,9 @@ def install_secureboot(efi_directory): install_efi_directory = install_path + efi_directory if efi_word_size() == "64": - install_efi_bin = "shim64.efi" - else: - install_efi_bin = "shim.efi" + install_efi_bin = "shimx64.efi" + elif efi_word_size() == "32": + install_efi_bin = "shimia32.efi" # Copied, roughly, from openSUSE's install script, # and pythonified. *disk* is something like /dev/sda, diff --git a/src/modules/finished/CMakeLists.txt b/src/modules/finished/CMakeLists.txt index 21eb1ad18..b4d59db8f 100644 --- a/src/modules/finished/CMakeLists.txt +++ b/src/modules/finished/CMakeLists.txt @@ -11,6 +11,7 @@ calamares_add_plugin( finished TYPE viewmodule EXPORT_MACRO PLUGINDLLEXPORT_PRO SOURCES + Config.cpp FinishedViewStep.cpp FinishedPage.cpp UI diff --git a/src/modules/finished/Config.cpp b/src/modules/finished/Config.cpp new file mode 100644 index 000000000..5119da942 --- /dev/null +++ b/src/modules/finished/Config.cpp @@ -0,0 +1,201 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "Config.h" + +#include "Branding.h" +#include "Settings.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include +#include +#include +#include + +const NamedEnumTable< Config::RestartMode >& +restartModes() +{ + using M = Config::RestartMode; + static const NamedEnumTable< M > table { { "never", M::Never }, + { "user-unchecked", M::UserDefaultUnchecked }, + { "unchecked", M::UserDefaultUnchecked }, + { "user-checked", M::UserDefaultChecked }, + { "checked", M::UserDefaultChecked }, + { "always", M::Always } + + }; + return table; +} + + +Config::Config( QObject* parent ) + : QObject( parent ) +{ +} + +void +Config::setRestartNowMode( Config::RestartMode m ) +{ + // Can only go "down" in state (Always > UserDefaultChecked > .. > Never) + if ( m > m_restartNowMode ) + { + return; + } + + // If changing to an unconditional mode, also set other flag + if ( m == RestartMode::Always || m == RestartMode::Never ) + { + setRestartNowWanted( m == RestartMode::Always ); + } + + if ( m != m_restartNowMode ) + { + m_restartNowMode = m; + emit restartModeChanged( m ); + } +} + +void +Config::setRestartNowWanted( bool w ) +{ + // Follow the mode which may affect @p w + if ( m_restartNowMode == RestartMode::Always ) + { + w = true; + } + if ( m_restartNowMode == RestartMode::Never ) + { + w = false; + } + + if ( w != m_userWantsRestart ) + { + m_userWantsRestart = w; + emit restartNowWantedChanged( w ); + } +} + +void +Config::doRestart() +{ + if ( restartNowMode() != RestartMode::Never && restartNowWanted() ) + { + cDebug() << "Running restart command" << m_restartNowCommand; + QProcess::execute( "/bin/sh", { "-c", m_restartNowCommand } ); + } +} + + +void +Config::doNotify( bool hasFailed ) +{ + if ( !notifyOnFinished() ) + { + return; + } + + QDBusInterface notify( + "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications" ); + if ( notify.isValid() ) + { + cDebug() << "Sending notification of completion. Failed?" << hasFailed; + + QString title; + QString message; + if ( hasFailed ) + { + title = Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Failed" ) : tr( "Installation Failed" ); + message = Calamares::Settings::instance()->isSetupMode() + ? tr( "The setup of %1 did not complete successfully." ) + : tr( "The installation of %1 did not complete successfully." ); + } + else + { + title = Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Complete" ) + : tr( "Installation Complete" ); + message = Calamares::Settings::instance()->isSetupMode() ? tr( "The setup of %1 is complete." ) + : tr( "The installation of %1 is complete." ); + } + + const auto* branding = Calamares::Branding::instance(); + QDBusReply< uint > r = notify.call( "Notify", + QString( "Calamares" ), + QVariant( 0U ), + QString( "calamares" ), + title, + message.arg( branding->versionedName() ), + QStringList(), + QVariantMap(), + QVariant( 0 ) ); + if ( !r.isValid() ) + { + cWarning() << "Could not call org.freedesktop.Notifications.Notify at end of installation." << r.error(); + } + } + else + { + cWarning() << "Could not get dbus interface for notifications at end of installation." << notify.lastError(); + } +} + + +void +Config::setConfigurationMap( const QVariantMap& configurationMap ) +{ + RestartMode mode = RestartMode::Never; + + //TODO:3.3 remove deprecated restart settings + QString restartMode = CalamaresUtils::getString( configurationMap, "restartNowMode" ); + if ( restartMode.isEmpty() ) + { + if ( configurationMap.contains( "restartNowEnabled" ) ) + { + cWarning() << "Configuring the finished module with deprecated restartNowEnabled settings"; + } + + bool restartNowEnabled = CalamaresUtils::getBool( configurationMap, "restartNowEnabled", false ); + bool restartNowChecked = CalamaresUtils::getBool( configurationMap, "restartNowChecked", false ); + + if ( !restartNowEnabled ) + { + mode = RestartMode::Never; + } + else + { + mode = restartNowChecked ? RestartMode::UserDefaultChecked : RestartMode::UserDefaultUnchecked; + } + } + else + { + bool ok = false; + mode = restartModes().find( restartMode, ok ); + if ( !ok ) + { + cWarning() << "Configuring the finished module with bad restartNowMode" << restartMode; + } + } + + m_restartNowMode = mode; + m_userWantsRestart = ( mode == RestartMode::Always || mode == RestartMode::UserDefaultChecked ); + emit restartModeChanged( m_restartNowMode ); + emit restartNowWantedChanged( m_userWantsRestart ); + + if ( mode != RestartMode::Never ) + { + QString restartNowCommand = CalamaresUtils::getString( configurationMap, "restartNowCommand" ); + if ( restartNowCommand.isEmpty() ) + { + restartNowCommand = QStringLiteral( "shutdown -r now" ); + } + m_restartNowCommand = restartNowCommand; + } + + m_notifyOnFinished = CalamaresUtils::getBool( configurationMap, "notifyOnFinished", false ); +} diff --git a/src/modules/finished/Config.h b/src/modules/finished/Config.h new file mode 100644 index 000000000..78078b99b --- /dev/null +++ b/src/modules/finished/Config.h @@ -0,0 +1,81 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef FINISHED_CONFIG_H +#define FINISHED_CONFIG_H + +#include "utils/NamedEnum.h" + +#include + +class Config : public QObject +{ + Q_OBJECT + + Q_PROPERTY( RestartMode restartNowMode READ restartNowMode WRITE setRestartNowMode NOTIFY restartModeChanged ) + Q_PROPERTY( bool restartNowWanted READ restartNowWanted WRITE setRestartNowWanted NOTIFY restartNowWantedChanged ) + + Q_PROPERTY( QString restartNowCommand READ restartNowCommand CONSTANT FINAL ) + Q_PROPERTY( bool notifyOnFinished READ notifyOnFinished CONSTANT FINAL ) + +public: + Config( QObject* parent = nullptr ); + + enum class RestartMode + { + Never, + UserDefaultUnchecked, + UserDefaultChecked, + Always + }; + Q_ENUM( RestartMode ) + + RestartMode restartNowMode() const { return m_restartNowMode; } + bool restartNowWanted() const { return m_userWantsRestart; } + + QString restartNowCommand() const { return m_restartNowCommand; } + bool notifyOnFinished() const { return m_notifyOnFinished; } + + void setConfigurationMap( const QVariantMap& configurationMap ); + +public slots: + void setRestartNowMode( RestartMode m ); + void setRestartNowWanted( bool w ); + + /** @brief Run the restart command, if desired. + * + * This should generally not be called somewhere during the + * application's execution, but only in response to QApplication::quit() + * or something like that when the user expects the system to restart. + */ + void doRestart(); + + /** @brief Send DBus notification, if desired. + * + * This takes notifyOnFinished() into account. + * + * At the end of installation (when the FinishedViewStep is activated), + * send a desktop notification via DBus that the install is done. + */ + void doNotify( bool hasFailed = false ); + +signals: + void restartModeChanged( RestartMode m ); + void restartNowWantedChanged( bool w ); + +private: + QString m_restartNowCommand; + RestartMode m_restartNowMode = RestartMode::Never; + bool m_userWantsRestart = false; + bool m_notifyOnFinished = false; +}; + +const NamedEnumTable< Config::RestartMode >& restartModes(); + +#endif diff --git a/src/modules/finished/FinishedPage.cpp b/src/modules/finished/FinishedPage.cpp index 23f09df99..6c5f9ad16 100644 --- a/src/modules/finished/FinishedPage.cpp +++ b/src/modules/finished/FinishedPage.cpp @@ -11,26 +11,19 @@ #include "FinishedPage.h" -#include "CalamaresVersion.h" -#include "ViewManager.h" +#include "Config.h" #include "ui_FinishedPage.h" -#include "utils/CalamaresUtilsGui.h" -#include "utils/Logger.h" -#include "utils/Retranslator.h" - -#include -#include -#include -#include -#include #include "Branding.h" #include "Settings.h" +#include "utils/Retranslator.h" -FinishedPage::FinishedPage( QWidget* parent ) +#include + + +FinishedPage::FinishedPage( Config* config, QWidget* parent ) : QWidget( parent ) , ui( new Ui::FinishedPage ) - , m_mode( FinishedViewStep::RestartMode::UserUnchecked ) { ui->setupUi( this ); @@ -38,68 +31,19 @@ FinishedPage::FinishedPage( QWidget* parent ) ui->mainText->setWordWrap( true ); ui->mainText->setOpenExternalLinks( true ); - CALAMARES_RETRANSLATE( - const auto* branding = Calamares::Branding::instance(); ui->retranslateUi( this ); - if ( Calamares::Settings::instance()->isSetupMode() ) { - ui->mainText->setText( tr( "

All done.


" - "%1 has been set up on your computer.
" - "You may now start using your new system." ) - .arg( branding->versionedName() ) ); - ui->restartCheckBox->setToolTip( tr( "" - "

When this box is checked, your system will " - "restart immediately when you click on " - "Done " - "or close the setup program.

" ) ); - } else { - ui->mainText->setText( tr( "

All done.


" - "%1 has been installed on your computer.
" - "You may now restart into your new system, or continue " - "using the %2 Live environment." ) - .arg( branding->versionedName(), branding->productName() ) ); - ui->restartCheckBox->setToolTip( tr( "" - "

When this box is checked, your system will " - "restart immediately when you click on " - "Done " - "or close the installer.

" ) ); - } ) -} + connect( config, &Config::restartModeChanged, [this]( Config::RestartMode mode ) { + using Mode = Config::RestartMode; - -void -FinishedPage::setRestart( FinishedViewStep::RestartMode mode ) -{ - using Mode = FinishedViewStep::RestartMode; - - m_mode = mode; - - ui->restartCheckBox->setVisible( mode != Mode::Never ); - ui->restartCheckBox->setEnabled( mode != Mode::Always ); - ui->restartCheckBox->setChecked( ( mode == Mode::Always ) || ( mode == Mode::UserChecked ) ); -} - - -void -FinishedPage::setRestartNowCommand( const QString& command ) -{ - m_restartNowCommand = command; -} - - -void -FinishedPage::setUpRestart() -{ - cDebug() << "FinishedPage::setUpRestart(), Quit button" - << "setup=" << FinishedViewStep::modeName( m_mode ) << "command=" << m_restartNowCommand; - - connect( qApp, &QApplication::aboutToQuit, [this]() { - if ( ui->restartCheckBox->isVisible() && ui->restartCheckBox->isChecked() ) - { - cDebug() << "Running restart command" << m_restartNowCommand; - QProcess::execute( "/bin/sh", { "-c", m_restartNowCommand } ); - } + ui->restartCheckBox->setVisible( mode != Mode::Never ); + ui->restartCheckBox->setEnabled( mode != Mode::Always ); + } ); + connect( config, &Config::restartNowWantedChanged, ui->restartCheckBox, &QCheckBox::setChecked ); + connect( ui->restartCheckBox, &QCheckBox::stateChanged, [config]( int state ) { + config->setRestartNowWanted( state != 0 ); } ); -} + CALAMARES_RETRANSLATE_SLOT( &FinishedPage::retranslate ); +} void FinishedPage::focusInEvent( QFocusEvent* e ) @@ -110,19 +54,64 @@ FinishedPage::focusInEvent( QFocusEvent* e ) void FinishedPage::onInstallationFailed( const QString& message, const QString& details ) { - const auto* branding = Calamares::Branding::instance(); - Q_UNUSED( details ) - if ( Calamares::Settings::instance()->isSetupMode() ) - ui->mainText->setText( tr( "

Setup Failed


" - "%1 has not been set up on your computer.
" - "The error message was: %2." ) - .arg( branding->versionedName() ) - .arg( message ) ); - else - ui->mainText->setText( tr( "

Installation Failed


" - "%1 has not been installed on your computer.
" - "The error message was: %2." ) - .arg( branding->versionedName() ) - .arg( message ) ); - setRestart( FinishedViewStep::RestartMode::Never ); + m_failure = !message.isEmpty() ? message : details; + retranslate(); +} + +void +FinishedPage::retranslate() +{ + + const auto* branding = Calamares::Branding::instance(); + + ui->retranslateUi( this ); + if ( !m_failure.has_value() ) + { + if ( Calamares::Settings::instance()->isSetupMode() ) + { + ui->mainText->setText( tr( "

All done.


" + "%1 has been set up on your computer.
" + "You may now start using your new system." ) + .arg( branding->versionedName() ) ); + ui->restartCheckBox->setToolTip( tr( "" + "

When this box is checked, your system will " + "restart immediately when you click on " + "Done " + "or close the setup program.

" ) ); + } + else + { + ui->mainText->setText( tr( "

All done.


" + "%1 has been installed on your computer.
" + "You may now restart into your new system, or continue " + "using the %2 Live environment." ) + .arg( branding->versionedName(), branding->productName() ) ); + ui->restartCheckBox->setToolTip( tr( "" + "

When this box is checked, your system will " + "restart immediately when you click on " + "Done " + "or close the installer.

" ) ); + } + } + else + { + const QString message = m_failure.value(); + + if ( Calamares::Settings::instance()->isSetupMode() ) + { + ui->mainText->setText( tr( "

Setup Failed


" + "%1 has not been set up on your computer.
" + "The error message was: %2." ) + .arg( branding->versionedName() ) + .arg( message ) ); + } + else + { + ui->mainText->setText( tr( "

Installation Failed


" + "%1 has not been installed on your computer.
" + "The error message was: %2." ) + .arg( branding->versionedName() ) + .arg( message ) ); + } + } } diff --git a/src/modules/finished/FinishedPage.h b/src/modules/finished/FinishedPage.h index 1df022f58..068efbdb5 100644 --- a/src/modules/finished/FinishedPage.h +++ b/src/modules/finished/FinishedPage.h @@ -11,10 +11,12 @@ #ifndef FINISHEDPAGE_H #define FINISHEDPAGE_H + #include -#include "FinishedViewStep.h" +#include +class Config; namespace Ui { class FinishedPage; @@ -24,24 +26,19 @@ class FinishedPage : public QWidget { Q_OBJECT public: - explicit FinishedPage( QWidget* parent = nullptr ); + explicit FinishedPage( Config* config, QWidget* parent = nullptr ); - void setRestart( FinishedViewStep::RestartMode mode ); - void setRestartNowCommand( const QString& command ); - - void setUpRestart(); public slots: void onInstallationFailed( const QString& message, const QString& details ); + void retranslate(); protected: void focusInEvent( QFocusEvent* e ) override; //choose the child widget to focus private: Ui::FinishedPage* ui; - - FinishedViewStep::RestartMode m_mode; - QString m_restartNowCommand; + std::optional< QString > m_failure; }; #endif // FINISHEDPAGE_H diff --git a/src/modules/finished/FinishedViewStep.cpp b/src/modules/finished/FinishedViewStep.cpp index 525108dc7..3f9fd3aab 100644 --- a/src/modules/finished/FinishedViewStep.cpp +++ b/src/modules/finished/FinishedViewStep.cpp @@ -10,42 +10,21 @@ */ #include "FinishedViewStep.h" + +#include "Config.h" #include "FinishedPage.h" -#include "Branding.h" #include "JobQueue.h" -#include "Settings.h" -#include "utils/Logger.h" -#include "utils/NamedEnum.h" -#include "utils/Variant.h" - -#include -#include -#include -#include - -static const NamedEnumTable< FinishedViewStep::RestartMode >& -modeNames() -{ - using Mode = FinishedViewStep::RestartMode; - - static const NamedEnumTable< Mode > names { { QStringLiteral( "never" ), Mode::Never }, - { QStringLiteral( "user-unchecked" ), Mode::UserUnchecked }, - { QStringLiteral( "user-checked" ), Mode::UserChecked }, - { QStringLiteral( "always" ), Mode::Always } }; - - return names; -} +#include FinishedViewStep::FinishedViewStep( QObject* parent ) : Calamares::ViewStep( parent ) - , m_widget( new FinishedPage() ) - , installFailed( false ) - , m_notifyOnFinished( false ) + , m_config( new Config( this ) ) + , m_widget( new FinishedPage( m_config ) ) + , m_installFailed( false ) { auto jq = Calamares::JobQueue::instance(); - connect( jq, &Calamares::JobQueue::failed, m_widget, &FinishedPage::onInstallationFailed ); connect( jq, &Calamares::JobQueue::failed, this, &FinishedViewStep::onInstallationFailed ); emit nextStatusChanged( true ); @@ -102,54 +81,12 @@ FinishedViewStep::isAtEnd() const return true; } -void -FinishedViewStep::sendNotification() -{ - // If the installation failed, don't send notification popup; - // there's a (modal) dialog popped up with the failure notice. - if ( installFailed ) - { - return; - } - - QDBusInterface notify( - "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications" ); - if ( notify.isValid() ) - { - const auto* branding = Calamares::Branding::instance(); - QDBusReply< uint > r = notify.call( - "Notify", - QString( "Calamares" ), - QVariant( 0U ), - QString( "calamares" ), - Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Complete" ) : tr( "Installation Complete" ), - Calamares::Settings::instance()->isSetupMode() - ? tr( "The setup of %1 is complete." ).arg( branding->versionedName() ) - : tr( "The installation of %1 is complete." ).arg( branding->versionedName() ), - QStringList(), - QVariantMap(), - QVariant( 0 ) ); - if ( !r.isValid() ) - { - cWarning() << "Could not call org.freedesktop.Notifications.Notify at end of installation." << r.error(); - } - } - else - { - cWarning() << "Could not get dbus interface for notifications at end of installation." << notify.lastError(); - } -} - void FinishedViewStep::onActivate() { - m_widget->setUpRestart(); - - if ( m_notifyOnFinished ) - { - sendNotification(); - } + m_config->doNotify( m_installFailed ); + connect( qApp, &QApplication::aboutToQuit, m_config, &Config::doRestart ); } @@ -162,67 +99,15 @@ FinishedViewStep::jobs() const void FinishedViewStep::onInstallationFailed( const QString& message, const QString& details ) { - Q_UNUSED( message ) - Q_UNUSED( details ) - installFailed = true; + m_installFailed = true; + m_config->setRestartNowMode( Config::RestartMode::Never ); + m_widget->onInstallationFailed( message, details ); } void FinishedViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { - RestartMode mode = RestartMode::Never; - - QString restartMode = CalamaresUtils::getString( configurationMap, "restartNowMode" ); - if ( restartMode.isEmpty() ) - { - if ( configurationMap.contains( "restartNowEnabled" ) ) - { - cWarning() << "Configuring the finished module with deprecated restartNowEnabled settings"; - } - - bool restartNowEnabled = CalamaresUtils::getBool( configurationMap, "restartNowEnabled", false ); - bool restartNowChecked = CalamaresUtils::getBool( configurationMap, "restartNowChecked", false ); - - if ( !restartNowEnabled ) - { - mode = RestartMode::Never; - } - else - { - mode = restartNowChecked ? RestartMode::UserChecked : RestartMode::UserUnchecked; - } - } - else - { - bool ok = false; - mode = modeNames().find( restartMode, ok ); - if ( !ok ) - { - cWarning() << "Configuring the finished module with bad restartNowMode" << restartMode; - } - } - - m_widget->setRestart( mode ); - - if ( mode != RestartMode::Never ) - { - QString restartNowCommand = CalamaresUtils::getString( configurationMap, "restartNowCommand" ); - if ( restartNowCommand.isEmpty() ) - { - restartNowCommand = QStringLiteral( "shutdown -r now" ); - } - m_widget->setRestartNowCommand( restartNowCommand ); - } - - m_notifyOnFinished = CalamaresUtils::getBool( configurationMap, "notifyOnFinished", false ); + m_config->setConfigurationMap( configurationMap ); } -QString -FinishedViewStep::modeName( FinishedViewStep::RestartMode m ) -{ - bool ok = false; - return modeNames().find( m, ok ); // May be QString() -} - - CALAMARES_PLUGIN_FACTORY_DEFINITION( FinishedViewStepFactory, registerPlugin< FinishedViewStep >(); ) diff --git a/src/modules/finished/FinishedViewStep.h b/src/modules/finished/FinishedViewStep.h index 729f9126d..a35d7fac8 100644 --- a/src/modules/finished/FinishedViewStep.h +++ b/src/modules/finished/FinishedViewStep.h @@ -11,13 +11,11 @@ #ifndef FINISHEDVIEWSTEP_H #define FINISHEDVIEWSTEP_H -#include - +#include "DllMacro.h" #include "utils/PluginFactory.h" #include "viewpages/ViewStep.h" -#include "DllMacro.h" - +class Config; class FinishedPage; class PLUGINDLLEXPORT FinishedViewStep : public Calamares::ViewStep @@ -25,16 +23,6 @@ class PLUGINDLLEXPORT FinishedViewStep : public Calamares::ViewStep Q_OBJECT public: - enum class RestartMode - { - Never = 0, ///< @brief Don't show button, just exit - UserUnchecked, ///< @brief Show button, starts unchecked - UserChecked, ///< @brief Show button, starts checked - Always ///< @brief Show button, can't change, checked - }; - /// @brief Returns the config-name of the given restart-mode @p m - static QString modeName( RestartMode m ); - explicit FinishedViewStep( QObject* parent = nullptr ); ~FinishedViewStep() override; @@ -58,16 +46,9 @@ public slots: void onInstallationFailed( const QString& message, const QString& details ); private: + Config* m_config; FinishedPage* m_widget; - - /** - * @brief At the end of installation (when this step is activated), - * send a desktop notification via DBus that the install is done. - */ - void sendNotification(); - - bool installFailed; - bool m_notifyOnFinished; + bool m_installFailed; // Track if onInstallationFailed() was called }; CALAMARES_PLUGIN_FACTORY_DECLARATION( FinishedViewStepFactory ) diff --git a/src/modules/keyboard/non-ascii-layouts b/src/modules/keyboard/non-ascii-layouts index 454278a3e..fe3f285a2 100644 --- a/src/modules/keyboard/non-ascii-layouts +++ b/src/modules/keyboard/non-ascii-layouts @@ -6,3 +6,4 @@ #layout additional-layout additional-variant vconsole-keymap ru us - ruwin_alt_sh-UTF-8 ua us - ua-utf +gr us - gr diff --git a/src/modules/netinstall/netinstall.schema.yaml b/src/modules/netinstall/netinstall.schema.yaml index a66c877d1..05ab4cd08 100644 --- a/src/modules/netinstall/netinstall.schema.yaml +++ b/src/modules/netinstall/netinstall.schema.yaml @@ -1,18 +1,89 @@ # SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-FileContributor: benne-dee ( worked on groups schema ) # SPDX-License-Identifier: GPL-3.0-or-later --- -$schema: https://json-schema.org/schema# +$schema: https://json-schema.org/draft-07/schema# $id: https://calamares.io/schemas/netinstall -additionalProperties: false -type: object -properties: - groupsUrl: { type: string } - required: { type: boolean, default: false } - label: # Translatable labels +definitions: + package: + $id: '#definitions/package' + oneOf: + - + type: string + description: bare package - actual package name as passed to the package manager + (e.g. `qt5-creator-dev`). + - + type: object + description: rich package - one with a package-name (for the package-manager) and + a description (for the human). + properties: + name: { type: string } + description: { type: string } + group: + $id: '#definitions/group' type: object - additionalProperties: true + description: Longer discussion in `netinstall.conf` file under 'Groups Format' properties: - sidebar: { type: string } - title: { type: string } - groups: { type: array } # TODO: the schema for the whole groups file -required: [ groupsUrl ] + name: { type: string } + description: { type: string } + packages: + type: array + items: { $ref: '#definitions/package' } + hidden: { type: boolean, default: false } + selected: { type: boolean } + critical: { type: boolean, default: false } + immutable: { type: boolean } + expanded: { type: boolean } + subgroups: + type: array + items: { $ref: '#definitions/group' } + pre-install: + type: string + description: an optional command to run within the new system before the group's + packages are installed. It will run before **each** package in the group + is installed. + post-install: + type: string + description: an optional command to run within the new system after the group's + packages are installed. It will run after **each** package in the group + is installed. + required: [name, description] # Always required, at any level in the subgroups hirearchy + if: + properties: + subgroups: + maxItems: 0 + then: + required: [name, description, packages] # bottom-most (sub)group requires some package (otherwise, why bother?) + # This should validate `netinstall.yaml` also. + groups: + $id: '#definitions/groups' + type: array + items: { $ref: '#definitions/group' } + +oneOf: +- # netinstall.conf + type: object + description: netinstall.conf schema + additionalProperties: false + properties: + groupsUrl: { type: string } + required: { type: boolean, default: false } + label: # Translatable labels + type: object + additionalProperties: true + properties: + sidebar: { type: string } + title: { type: string } + groups: { $ref: '#definitions/groups' } + required: [ groupsUrl ] + +- # Groups file with top level *groups* key + type: object + description: Groups file with top level *groups* key + additionalProperties: false + properties: + groups: { $ref: '#definitions/groups' } + required: [ groups ] + +- # Groups file bare + { $ref: '#definitions/groups' } diff --git a/src/modules/partition/CMakeLists.txt b/src/modules/partition/CMakeLists.txt index b623309c5..ac47714ce 100644 --- a/src/modules/partition/CMakeLists.txt +++ b/src/modules/partition/CMakeLists.txt @@ -79,6 +79,7 @@ if ( KPMcore_FOUND AND Qt5DBus_FOUND AND KF5CoreAddons_FOUND AND KF5Config_FOUND gui/ScanningDialog.cpp gui/ReplaceWidget.cpp gui/VolumeGroupBaseDialog.cpp + jobs/AutoMountManagementJob.cpp jobs/ClearMountsJob.cpp jobs/ClearTempMountsJob.cpp jobs/CreatePartitionJob.cpp diff --git a/src/modules/partition/core/PartitionCoreModule.cpp b/src/modules/partition/core/PartitionCoreModule.cpp index 254d007c1..f99c78745 100644 --- a/src/modules/partition/core/PartitionCoreModule.cpp +++ b/src/modules/partition/core/PartitionCoreModule.cpp @@ -21,6 +21,7 @@ #include "core/PartUtils.h" #include "core/PartitionInfo.h" #include "core/PartitionModel.h" +#include "jobs/AutoMountManagementJob.h" #include "jobs/ClearMountsJob.h" #include "jobs/ClearTempMountsJob.h" #include "jobs/CreatePartitionJob.h" @@ -576,6 +577,11 @@ PartitionCoreModule::jobs( const Config* config ) const #endif #endif + // The automountControl job goes in the list twice: the first + // time it runs, it disables automount and remembers the old setting + // for automount; the second time it restores that old setting. + Calamares::job_ptr automountControl( new AutoMountManagementJob( true /* disable automount */ ) ); + lst << automountControl; lst << Calamares::job_ptr( new ClearTempMountsJob() ); for ( auto info : m_deviceInfos ) @@ -592,6 +598,7 @@ PartitionCoreModule::jobs( const Config* config ) const devices << info->device.data(); } lst << Calamares::job_ptr( new FillGlobalStorageJob( config, devices, m_bootLoaderInstallPath ) ); + lst << automountControl; return lst; } diff --git a/src/modules/partition/jobs/AutoMountManagementJob.cpp b/src/modules/partition/jobs/AutoMountManagementJob.cpp new file mode 100644 index 000000000..9472eae9c --- /dev/null +++ b/src/modules/partition/jobs/AutoMountManagementJob.cpp @@ -0,0 +1,40 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "AutoMountManagementJob.h" + +#include "utils/Logger.h" + +AutoMountManagementJob::AutoMountManagementJob( bool disable ) + : m_disable( disable ) +{ +} + +QString +AutoMountManagementJob::prettyName() const +{ + return tr( "Manage auto-mount settings" ); +} + +Calamares::JobResult +AutoMountManagementJob::exec() +{ + Logger::CDebug( Logger::LOGVERBOSE ) << "this" << Logger::Pointer( this ) << "value" << Logger::Pointer( m_stored ) + << ( m_stored ? "restore" : m_disable ? "disable" : "enable" ); + if ( m_stored ) + { + CalamaresUtils::Partition::automountRestore( m_stored ); + m_stored.reset(); + } + else + { + m_stored = CalamaresUtils::Partition::automountDisable( m_disable ); + } + return Calamares::JobResult::ok(); +} diff --git a/src/modules/partition/jobs/AutoMountManagementJob.h b/src/modules/partition/jobs/AutoMountManagementJob.h new file mode 100644 index 000000000..e1dcf16dc --- /dev/null +++ b/src/modules/partition/jobs/AutoMountManagementJob.h @@ -0,0 +1,42 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef PARTITION_AUTOMOUNTMANAGEMENTJOB_H +#define PARTITION_AUTOMOUNTMANAGEMENTJOB_H + +#include "Job.h" + +#include "partition/AutoMount.h" + +/** + * This job sets automounting to a specific value, and when run a + * second time, **re**sets to the original value. See the documentation + * for CalamaresUtils::Partition::automountDisable() for details. + * Use @c true to **disable** automounting. + * + * Effectively: queue the **same** job twice; the first time it runs + * it will set the automount behavior, and the second time it + * restores the original. + * + */ +class AutoMountManagementJob : public Calamares::Job +{ + Q_OBJECT +public: + AutoMountManagementJob( bool disable = true ); + + QString prettyName() const override; + Calamares::JobResult exec() override; + +private: + bool m_disable; + decltype( CalamaresUtils::Partition::automountDisable( true ) ) m_stored; +}; + +#endif /* PARTITION_AUTOMOUNTMANAGEMENTJOB_H */ diff --git a/src/modules/partition/tests/AutoMountTests.cpp b/src/modules/partition/tests/AutoMountTests.cpp new file mode 100644 index 000000000..fcebe02bd --- /dev/null +++ b/src/modules/partition/tests/AutoMountTests.cpp @@ -0,0 +1,87 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "jobs/AutoMountManagementJob.h" + +#include "utils/Logger.h" +#include "JobQueue.h" + +#include +#include + +class AutoMountJobTests : public QObject +{ + Q_OBJECT +public: + AutoMountJobTests(); + +private Q_SLOTS: + void testRunThrice(); + void testRunQueue(); +}; + +AutoMountJobTests::AutoMountJobTests() {} + +/* This doesn't really test anything, since automount management + * is supposed to be opaque: the job always returns true. What + * is interesting is the debug output, where the job informs + * about the pointer it holds. + * + * That should output 0, then non-zero, then 0 again. + * + */ +void +AutoMountJobTests::testRunThrice() +{ + Logger::setupLogLevel( Logger::LOGVERBOSE ); + + auto original = CalamaresUtils::Partition::automountDisable( true ); + cDebug() << "Got automount info" << Logger::Pointer( original ); + + AutoMountManagementJob j( false ); + QVERIFY( j.exec() ); + QVERIFY( j.exec() ); + QVERIFY( j.exec() ); + + CalamaresUtils::Partition::automountRestore( original ); +} + +void AutoMountJobTests::testRunQueue() +{ + Calamares::JobQueue q; + Calamares::job_ptr jp( new AutoMountManagementJob( false ) ); + QSignalSpy progress( &q, &Calamares::JobQueue::progress ); + QSignalSpy finish( &q, &Calamares::JobQueue::finished ); + QSignalSpy fail( &q, &Calamares::JobQueue::failed ); + + Logger::setupLogLevel( Logger::LOGVERBOSE ); + cDebug() << "Got automount job" << jp; + + QVERIFY( !q.isRunning() ); + q.enqueue( 2, { jp, jp } ); + QVERIFY( !q.isRunning() ); + + QEventLoop loop; + QTimer::singleShot( std::chrono::milliseconds( 100 ), [&q](){ q.start(); } ); + QTimer::singleShot( std::chrono::milliseconds( 5000 ), [&loop](){ loop.quit(); } ); + connect( &q, &Calamares::JobQueue::finished, &loop, &QEventLoop::quit ); + loop.exec(); + + QCOMPARE( fail.count(), 0 ); + QCOMPARE( finish.count(), 1 ); + // 5 progress: 0% and 100% for each *job* and then 100% overall + QCOMPARE( progress.count(), 5 ); +} + + +QTEST_GUILESS_MAIN( AutoMountJobTests ) + +#include "utils/moc-warnings.h" + +#include "AutoMountTests.moc" diff --git a/src/modules/partition/tests/CMakeLists.txt b/src/modules/partition/tests/CMakeLists.txt index f16435230..8edef484c 100644 --- a/src/modules/partition/tests/CMakeLists.txt +++ b/src/modules/partition/tests/CMakeLists.txt @@ -58,3 +58,11 @@ calamares_add_test( DEFINITIONS ${_partition_defs} ) +calamares_add_test( + automounttests + SOURCES + ${PartitionModule_SOURCE_DIR}/jobs/AutoMountManagementJob.cpp + AutoMountTests.cpp + LIBRARIES + calamares +) diff --git a/src/modules/users/Config.cpp b/src/modules/users/Config.cpp index ba7341a9c..165e21b9c 100644 --- a/src/modules/users/Config.cpp +++ b/src/modules/users/Config.cpp @@ -36,6 +36,8 @@ static const char TRANSLITERATOR_ID[] = "Russian-Latin/BGN;" "Latin-ASCII"; #endif +#include + static const QRegExp USERNAME_RX( "^[a-z_][a-z0-9_-]*[$]?$" ); static constexpr const int USERNAME_MAX_LENGTH = 31; diff --git a/src/modules/users/TestGroupInformation.cpp b/src/modules/users/TestGroupInformation.cpp index 21b8d47ce..31ca032c7 100644 --- a/src/modules/users/TestGroupInformation.cpp +++ b/src/modules/users/TestGroupInformation.cpp @@ -64,10 +64,9 @@ GroupTests::testReadGroup() #else QVERIFY( groups.contains( QStringLiteral( "root" ) ) ); #endif - // openSUSE doesn't have "sys" - // QVERIFY( groups.contains( QStringLiteral( "sys" ) ) ); - QVERIFY( groups.contains( QStringLiteral( "nogroup" ) ) ); QVERIFY( groups.contains( QStringLiteral( "tty" ) ) ); + // openSUSE doesn't have "sys", KaOS doesn't have "nogroup" + QVERIFY( groups.contains( QStringLiteral( "sys" ) ) || groups.contains( QStringLiteral( "nogroup" ) ) ); for ( const QString& s : groups ) { diff --git a/src/modules/welcome/welcome.conf b/src/modules/welcome/welcome.conf index bd15a6f2d..b3da8d366 100644 --- a/src/modules/welcome/welcome.conf +++ b/src/modules/welcome/welcome.conf @@ -10,14 +10,19 @@ # can check requirements for installation. --- # Display settings for various buttons on the welcome page. -# The URLs themselves come from branding.desc is the setting -# here is "true". If the setting is false, the button is hidden. +# The URLs themselves come from `branding.desc`. Each button +# is show if the corresponding *show* setting +# here is "true". If the setting is "false", the button is hidden. +# Empty or not-set is interpreted as "false". +# +# TODO:3.3 Remove the URL fallback here; URLs only in `branding.desc` +# # The setting can also be a full URL which will then be used -# instead of the one from the branding file, or empty or not-set -# which will hide the button. +# instead of the one from the branding file. showSupportUrl: true showKnownIssuesUrl: true showReleaseNotesUrl: false +# TODO:3.3 Move to branding, keep only a bool here showDonateUrl: https://kde.org/community/donations/ # Requirements checking. These are general, generic, things @@ -26,7 +31,7 @@ showDonateUrl: https://kde.org/community/donations/ requirements: # Amount of available disk, in GiB. Floating-point is allowed here. # Note that this does not account for *usable* disk, so it is possible - # to pass this requirement, yet have no space to install to. + # to satisfy this requirement, yet have no space to install to. requiredStorage: 5.5 # Amount of available RAM, in GiB. Floating-point is allowed here. @@ -34,7 +39,10 @@ requirements: # To check for internet connectivity, Calamares does a HTTP GET # on this URL; on success (e.g. HTTP code 200) internet is OK. - internetCheckUrl: http://google.com + # Use a privacy-respecting URL here, preferably in your distro's domain. + # + # The URL is only used if "internet" is in the *check* list below. + internetCheckUrl: http://example.com # List conditions to check. Each listed condition will be # probed in some way, and yields true or false according to