38 Commits

Author SHA1 Message Date
Adriaan de Groot
51e89e58dd CMake: bump version and requirements
Builds cleanly with Calamares 3.3.1
2024-01-12 23:15:55 +01:00
Adriaan de Groot
d8d5a1fb26 CMake: find Calamares first, then other bits 2024-01-09 23:10:38 +01:00
Adriaan de Groot
85f05e8121 Merge pull request #31 from Undef-a/wip/undef/fix-ftbfs
Fix failure to configure/build with newer CMake and Calamares 3.3.0
2024-01-09 23:06:45 +01:00
undef
c28c9b8114 Port modules to Calamares 3.3.0
Calamares 3.3.0 changed the import name and namespace of
CalamaresUtilsSystem and CalamaresUtilsGui.
2023-12-17 03:40:44 +00:00
undef
935f21ace5 CMakeLists: Copy KF CoreAddons and FeatureSummary imports from Calamares
Without these the package no-longer compiles with recent versions of
cmake.
2023-12-17 03:40:40 +00:00
demmm
6bd75570de Merge pull request #30 from Undef-a/wip/undef/librem5-screen
mobile: Ensure welcome screen fits on Librem5
2023-09-29 14:19:05 +02:00
undef
acdcdea668 mobile: Ensure welcome screen fits on Librem5
Now that the sizing has changed this line overhangs the screen.
2023-09-29 11:36:34 +00:00
Adriaan de Groot
edb405a4d1 Merge pull request #29 from Undef-a/wip/undef/fix-sizing-with-3.3.x
mobile: Adjust all sizes for Calamares 3.3.x
2023-09-28 23:03:26 +02:00
undef
3ad9c221d4 mobile: Adjust all sizes for Calamares 3.3.x
Element sizing in Calamares 3.3.x seems to have dramatically changed,
with for example a button that was previously fine at width 500 now
being huge to the point of causing the installer to overhang the screen.

This reduces the size of most elements such that they fit on a mobile
screen again.

Fixes: https://github.com/calamares/calamares/issues/2192
2023-09-28 08:56:44 +00:00
Adriaan de Groot
dc3550eccd CMake: post-release housekeeping
- Update CMake requirements to shut KF5 up
- Require the next Calamares main branch
- Bump to 1.4.0 version
2023-08-28 23:52:02 +02:00
Adriaan de Groot
fac16ac6a7 CMake: be more chatty about Calamares version used 2023-08-28 21:04:36 +02:00
Adriaan de Groot
a442ed8c12 CI: remove Linuxisms from release script 2023-08-28 20:34:32 +02:00
Adriaan de Groot
e08d687061 CI: update signing key 2023-08-28 00:33:12 +02:00
Adriaan de Groot
15f6369537 Changes: pre-release housekeeping 2023-08-28 00:31:50 +02:00
Adriaan de Groot
f01b4ed2f8 docs: drop mention of IRC, prefer Matrix for communication 2023-08-27 22:47:08 +02:00
Adriaan de Groot
ea762b0945 Merge pull request #26 from nmschulte/nms/fix-username-prompt
add usermod command about default username
2023-08-06 15:24:10 +02:00
Nathan Schulte
cae8acb4a4 custom username also changes the fullname/comment 2023-08-03 14:52:59 -05:00
Nathan Schulte
ae204d0108 change username before password 2023-07-19 20:49:48 -05:00
Nathan Schulte
a2fae600c4 add usermod command about default username
cmdUsermod supports changing the default username

in case any extra initial state needs updated, cmdUsermod can be amended to run additional commands
2023-07-11 01:29:32 -05:00
Adriaan de Groot
349acad491 Merge pull request #23 from Undef-a/calamares
mobile: Reflow confirm page to fit smaller screens
2023-07-01 22:58:31 +02:00
Adriaan de Groot
9b4c4876bc Merge pull request #24 from ollieparanoid/6-char-pass
mobile: change min pass length to 6 digits
2023-07-01 22:58:18 +02:00
Oliver Smith
cc02baed9a mobile: change min pass length to 6 digits
Require at least 6 characters instead of 8 and mention that digits can
also be used. Most users set something like a 6 digit number on their
lockscreen, it is even what we have by default in the postmarketOS
images when you don't use the installer. So requiring 8 in the installer
does not make sense.

Fixes: https://gitlab.com/postmarketOS/postmarketos-ondev/-/issues/62
2023-06-05 17:49:42 +02:00
undef
dfab089938 mobile: Reflow confirm page to fit smaller screens
When testing on the Librem5 this page hangs slightly off the screen,
causing the "Install" button to be only just selectable on the bottom
edge.

This slightly reflows the page such that on these smaller screens the
button is still in a selectable location.
2023-05-20 09:48:22 +00:00
Adriaan de Groot
2dd9a7ba8a Merge pull request #22 from nmschulte/nms/username-prompt-origin
[mobile] add username prompt, reservedUsernames config, minor cleanup
2023-02-24 22:34:35 +01:00
Nathan Schulte
82f31f3cd9 use reservedUsernames config 2023-02-20 01:24:40 -06:00
Nathan Schulte
c5b01a574d add reservedUsernames config 2023-02-20 01:24:40 -06:00
Nathan Schulte
bc4789d57f add username input field to user_pass module 2023-02-20 01:24:40 -06:00
Nathan Schulte
0962b98494 add username validation routine to mobile module 2022-09-22 00:55:24 -05:00
Nathan Schulte
108476c025 fix typo and cleanup code style 2022-09-22 00:55:24 -05:00
Adriaan de Groot
04a1bc9e2c Merge pull request #21 from demmm/calamares
[KaOS Branding] add navigation & sidebar examples
2022-08-07 14:19:14 +02:00
demmm
bff5e485f4 [KaOS Branding] add navigation & sidebar examples
both set to QML
navigation set vertical, progress set horizontal
include some updates
2022-08-04 18:55:01 +02:00
Adriaan de Groot
3c838436c2 [mobile] Increase timeout for PartitionJob: 10 min 2022-07-16 08:01:53 +02:00
Adriaan de Groot
644c9cf4f3 [unpackfsc] Improve tar support
Enable support for tar option in unpackfsc module for all compression algorithms
2022-07-16 08:00:33 +02:00
Oliver Smith
dcaa378ddd [mobile] Increase timeout for PartitionJob: 10 min
Increase the PartitionJob's timeout from 2 min to 10 min, as there was
an report of hitting the timeout with the PinePhone Pro's 128 GiB eMMC.

Related: https://gitlab.com/postmarketOS/pmaports/-/merge_requests/3280#note_1021536268
2022-07-11 08:25:55 +02:00
sravanpannala
14fd23dcef remove z option from tar so that it works with all 2022-07-06 17:56:28 -04:00
Adriaan de Groot
f4bc7052e0 [unpackfsc] Add (untested) tarball support 2022-05-17 12:30:28 +02:00
Adriaan de Groot
4f0f48d99d [unpackfsc] Unsquash is implemented 2022-05-17 12:21:52 +02:00
Adriaan de Groot
1344880f2e [unpackfsc] Add fsarchiver "fs" mode
- Add "restfs" suppotr for fsarchiver
- Apply coding style from Calamares
2022-05-17 12:12:30 +02:00
45 changed files with 1038 additions and 247 deletions

18
CHANGES
View File

@@ -6,6 +6,24 @@ This is the changelog for Calamares-Extensions. For each release, the major
changes and contributors are listed. Note that Calamares-Extensions does not changes and contributors are listed. Note that Calamares-Extensions does not
have a historical changelog -- this log starts with version 1.0.0. have a historical changelog -- this log starts with version 1.0.0.
# 1.3.2 (2023-08-28)
We skipped a couple of releases in the release-notes, then tagged
1.3.1 without a version bump or release-notes. So 1.3.2 brings us
back to "regular releases".
This release contains contributions from (alphabetically by first name):
- Anke Boersma
- Nathan Schulte
- Oliver Smith
- stravanpannala
- undef
Changes and new modules in this release:
- *mobile* Has new configuration options. (Thanks Nathan, Oliver)
- *unpackfsc* Uses a more portable invocation of tar. (Thanks sravanpannala)
# 1.2.1 (2021-11-16) # 1.2.1 (2021-11-16)
The 1.2.0 release had no release-notes for that version, and failed to The 1.2.0 release had no release-notes for that version, and failed to

View File

@@ -32,15 +32,18 @@
# In this repository, there is just one "group" to which USE_* applies: # In this repository, there is just one "group" to which USE_* applies:
# USE_os : operating-system-specific modules. # USE_os : operating-system-specific modules.
# #
# There is a knob WITH_QT6 which can be used to build against Qt6 rather
# than Qt5. This must match what Calamares itself is built with.
#
### NOTES ### NOTES
# #
# Call this CMake file in script mode, e.g. `cmake -P CMakeLists.txt` # Call this CMake file in script mode, e.g. `cmake -P CMakeLists.txt`
# to print out version information. Use `cmake -DVERSION_STYLE=short` # to print out version information. Use `cmake -DVERSION_STYLE=short`
# to get just the short versioning. # to get just the short versioning.
# #
cmake_minimum_required(VERSION 3.3 FATAL_ERROR) cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set( CALAMARES_EXTENSIONS_VERSION 1.2.1 ) set( CALAMARES_EXTENSIONS_VERSION 3.3.1 )
include( ${CMAKE_CURRENT_LIST_DIR}/CMakeModules/ExtendedVersion.cmake ) include( ${CMAKE_CURRENT_LIST_DIR}/CMakeModules/ExtendedVersion.cmake )
if ( CMAKE_SCRIPT_MODE_FILE ) if ( CMAKE_SCRIPT_MODE_FILE )
@@ -60,12 +63,42 @@ set( CMAKE_CXX_STANDARD_REQUIRED ON )
# consumers by loading the developer's config from a build # consumers by loading the developer's config from a build
# directory (which doesn't have the rest of the config # directory (which doesn't have the rest of the config
# installed inside it). # installed inside it).
set( CALAMARES_VERSION_REQUIRED 3.2.46 ) set( CALAMARES_VERSION_REQUIRED 3.3.1 )
find_package(Calamares ${CALAMARES_VERSION_REQUIRED} NO_CMAKE_PACKAGE_REGISTRY) find_package(Calamares ${CALAMARES_VERSION_REQUIRED} NO_CMAKE_PACKAGE_REGISTRY)
if (NOT TARGET Calamares::calamares OR NOT TARGET Calamares::calamaresui) if (NOT TARGET Calamares::calamares OR NOT TARGET Calamares::calamaresui)
find_package(Calamares ${CALAMARES_VERSION_REQUIRED} REQUIRED) find_package(Calamares ${CALAMARES_VERSION_REQUIRED} REQUIRED)
endif() endif()
message(STATUS "Found Calamares version ${Calamares_VERSION}")
message(STATUS " libraries ${Calamares_LIB_DIRS}")
message(STATUS "")
### EXTRACTING DEPENDENCIES AND CONFIGURATION FROM CALAMARES
#
#
if(WITH_QT6)
set(kfname "KF6")
set(KF_VERSION 5.240) # KDE Neon weirdness
else()
message(STATUS "Building Calamares with Qt5")
set(kfname "KF5")
set(KF_VERSION 5.78)
# API that was deprecated before Qt 5.15 causes a compile error
add_compile_definitions(QT_DISABLE_DEPRECATED_BEFORE=0x050f00)
endif()
include( FeatureSummary )
find_package(${kfname}CoreAddons ${KF_VERSION} QUIET)
set_package_properties(
${kfname}CoreAddons
PROPERTIES
TYPE REQUIRED
DESCRIPTION "KDE Framework CoreAddons"
URL "https://api.kde.org/frameworks/"
PURPOSE "Essential Framework for AboutData and Macros"
)
### CMAKE SETUP ### CMAKE SETUP
# #
# Enable IN_LIST # Enable IN_LIST
@@ -119,6 +152,8 @@ calamares_add_module_subdirectory( modules/refind LIST_SKIPPED_MODULES )
calamares_add_module_subdirectory( modules/slowpython LIST_SKIPPED_MODULES ) # Python job calamares_add_module_subdirectory( modules/slowpython LIST_SKIPPED_MODULES ) # Python job
calamares_add_module_subdirectory( modules/unpackfsc LIST_SKIPPED_MODULES ) calamares_add_module_subdirectory( modules/unpackfsc LIST_SKIPPED_MODULES )
message(STATUS "Calamares extensions ${CALAMARES_EXTENSIONS_VERSION} for Calamares version ${Calamares_VERSION}")
# If modules cannot be built, they usually call a macro # If modules cannot be built, they usually call a macro
# which builds a list of explanations; show that list. # which builds a list of explanations; show that list.
calamares_explain_skipped_modules( ${LIST_SKIPPED_MODULES} ) calamares_explain_skipped_modules( ${LIST_SKIPPED_MODULES} )

View File

@@ -29,15 +29,12 @@ rules of decent behavior in both communities are pretty much the same).
GitHub Issues are **one** place for discussing Calamares and its extensions if there are concrete GitHub Issues are **one** place for discussing Calamares and its extensions if there are concrete
problems or a new feature to discuss. problems or a new feature to discuss.
Issues are not a help channel.
Visit Matrix for help with configuration or compilation.
Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/) Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/)
room, `#calamares:kde.org`. The conversation is bridged with IRC room, `#calamares:kde.org`. Responsiveness is best during the day
on [Libera.Chat](https://libera.chat/). in Europe, but feel free to idle.
Responsiveness is best during the day
in Europe, but feel free to idle. If you use IRC, **DO NOT** ask-and-leave. Keep
that chat window open because it can easily take a few hours for
someone to notice a message.
Matrix is persistent, and we'll see your message eventually. Matrix is persistent, and we'll see your message eventually.
* [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org) * [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org)
* [![Chat on IRC](https://img.shields.io/badge/IRC-Libera.Chat%20%23calamares-green)](https://kiwiirc.com/client/irc.libera.chat/#calamares)

View File

@@ -40,7 +40,10 @@ and documentation for the framework that Calamares ships with.
(probably moreso than the default slideshow). (probably moreso than the default slideshow).
- [`kaos_branding/`](branding/kaos_branding/branding.desc) - [`kaos_branding/`](branding/kaos_branding/branding.desc)
is a copy of the KaOS branding component, which is a copy of the KaOS branding component, which
has translations and a bunch of fancy graphics. has translations and a bunch of fancy graphics for the
slideshow. Plus it includes examples of using different
QML options for a vertical navigation bar and horizontal
sidebar.
- [`samegame/` ](branding/default/branding.desc) - [`samegame/` ](branding/default/branding.desc)
is a copy of the Qt Company "Same Game" QML demo. It is a copy of the Qt Company "Same Game" QML demo. It
shows that **any** QML can be used for branding purposes. shows that **any** QML can be used for branding purposes.
@@ -217,15 +220,12 @@ The API is loosely documented in the
GitHub Issues are **one** place for discussing Calamares (and Calamares Extensions) GitHub Issues are **one** place for discussing Calamares (and Calamares Extensions)
if there are concrete if there are concrete
problems or a new feature to discuss. problems or a new feature to discuss.
Issues are not a help channel.
Visit Matrix for help with configuration or compilation.
Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/) Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/)
room, `#calamares:kde.org`. The conversation is bridged with IRC room, `#calamares:kde.org`. Responsiveness is best during the day
on [Libera.Chat](https://libera.chat/). in Europe, but feel free to idle.
Responsiveness is best during the day
in Europe, but feel free to idle. If you use IRC, **DO NOT** ask-and-leave. Keep
that chat window open because it can easily take a few hours for
someone to notice a message.
Matrix is persistent, and we'll see your message eventually. Matrix is persistent, and we'll see your message eventually.
* [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org) * [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org)
* [![Chat on IRC](https://img.shields.io/badge/IRC-Libera.Chat%20%23calamares-green)](https://kiwiirc.com/client/irc.libera.chat/#calamares)

View File

@@ -0,0 +1,101 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2020 2022 Anke Boersma <demm@kaosx.us>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
import io.calamares.core 1.0
import io.calamares.ui 1.0
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.3
ApplicationWindow {
id: about
visible: true
width: 760
height: 400
title: qsTr("About Calamares")
property var appName: "Calamares"
property var appVersion: "3.3 RC"
Rectangle {
id: textArea
anchors.fill: parent
color: "#f2f2f2"
Column {
id: column
anchors.centerIn: parent
Rectangle {
width: 560
height: 250
radius: 10
border.width: 0
Text {
width: 400
height: 250
anchors.centerIn: parent
text: qsTr("<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-2022 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/kaos/kaos/\">KaOS
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." )
.arg(appName)
.arg(appVersion)
.arg(Branding.string(Branding.VersionedName))
onLinkActivated: Qt.openUrlExternally(link)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
font.pointSize: 10
anchors.verticalCenterOffset: 10
anchors.horizontalCenterOffset: 40
wrapMode: Text.WordWrap
}
Image {
id: image
x: 8
y: 12
height: 100
fillMode: Image.PreserveAspectFit
source: "squid.png"
}
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
icon.name: "window-close"
text: qsTr("Close")
hoverEnabled: true
onClicked: about.close();
}
}
}

View File

@@ -1,22 +1,32 @@
--- ---
componentName: kaos componentName: kaos
welcomeStyleCalamares: false
# Should the welcome image (productWelcome, below) be scaled # Should the welcome image (productWelcome, below) be scaled
# up beyond its natural size? # up beyond its natural size?
welcomeExpandingLogo: true welcomeExpandingLogo: true
windowExpanding: normal
windowSize: 920px,630px
windowPlacement: center
sidebar: qml,bottom
navigation: qml,right
strings: strings:
productName: KaOS productName: KaOS
shortProductName: KaOS shortProductName: KaOS
version: 2018.03 version: 2022.08
shortVersion: KaOS shortVersion: KaOS
versionedName: KaOS 2018.03 versionedName: KaOS 2022.08
shortVersionedName: KaOS 2018.03 shortVersionedName: KaOS 2018.03
bootloaderEntryName: KaOS bootloaderEntryName: KaOS
productUrl: https://kaosx.us/ productUrl: https://kaosx.us/
supportUrl: https://kaosx.us/docs/ supportUrl: https://kaosx.us/docs/
knownIssuesUrl: https://kaosx.us/pages/download/#known-issues knownIssuesUrl: https://kaosx.us/pages/download/#known-issues
releaseNotesUrl: https://kaosx.us/pages/release_notes releaseNotesUrl: https://kaosx.us/pages/release_notes
donateUrl: https://kaosx.us/about/donors
images: images:
productLogo: "kaos.png" productLogo: "kaos.png"
@@ -27,6 +37,7 @@ slideshow: "show.qml"
slideshowAPI: 1 slideshowAPI: 1
style: style:
sidebarBackground: "#bdc3c7" SidebarBackground: "#bdc3c7"
sidebarText: "#1F1F1F" SidebarText: "#1F1F1F"
sidebarTextSelect: "#3498DB" SidebarTextCurrent: "#3498DB"
SidebarBackgroundCurrent: "#eff0f1"

View File

@@ -0,0 +1,224 @@
/* Sample of QML navigation.
SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
SPDX-FileCopyrightText: 2021 - 2022 Anke Boersma <demm@kaosx.us>
SPDX-License-Identifier: GPL-3.0-or-later
This navigation panel is for a "vertical" layout, with
mouse areas for next and previous and it includes the logo
plus About & Debug buttons.
*/
import io.calamares.ui 1.0
import io.calamares.core 1.0
import QtQuick 2.3
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.3
Rectangle {
id: navigationBar;
color: Branding.styleString( Branding.SidebarBackground );
height: parent.height;
width:64;
ColumnLayout {
id: buttonBar
anchors.fill: parent;
spacing: 1
Image {
Layout.topMargin: 1;
Layout.bottomMargin:parent.height / 7;
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
id: logo;
width: 62;
height: width; // square
source: "file:/" + Branding.imagePath(Branding.ProductLogo);
sourceSize.width: width;
sourceSize.height: height;
}
Rectangle {
id: backArea
Layout.fillWidth: true;
Layout.preferredHeight: parent.height / 7;
color: mouseBack.containsMouse ? "#e6e9ea" : "#d9dcde";
enabled: ViewManager.backEnabled;
visible: ViewManager.backAndNextVisible;
MouseArea {
id: mouseBack
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Back")
color: Branding.styleString( !backArea.enabled ? Branding.SidebarBackground : (mouseBack.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText ));
font.pointSize : 8
}
Image {
source: "pan-start-symbolic.svg"
anchors.centerIn: parent
anchors.verticalCenterOffset : 18
fillMode: Image.PreserveAspectFit
height: 32
opacity: backArea.enabled ? 1 : 0.2
}
onClicked: { ViewManager.back(); }
}
}
Rectangle {
id: nextArea
Layout.preferredHeight: parent.height / 7;
Layout.fillWidth: true
color: mouseNext.containsMouse ? "#f4f5f6" : "#e6e9ea";
enabled: ViewManager.nextEnabled;
visible: ViewManager.backAndNextVisible;
MouseArea {
id: mouseNext
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Next")
color: Branding.styleString( !nextArea.enabled ? Branding.SidebarBackground : (mouseNext.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText ));
font.pointSize : 8
}
Image {
source: "pan-end-symbolic.svg"
anchors.centerIn: parent
anchors.verticalCenterOffset : 18
fillMode: Image.PreserveAspectFit
height: 32
opacity: nextArea.enabled ? 1 : 0.2
}
onClicked: { ViewManager.next(); }
}
}
Rectangle {
id: cancelArea
height: parent.height / 7;
Layout.fillWidth: true
color: mouseCancel.containsMouse ? "#e6e9ea" : "#d9dcde";
/*
* The ViewManager has settings -- user-controlled via the
* branding component, and party based on program state --
* whether the quit button should be enabled and visible.
*
* QML navigation *should* follow this pattern, but can also
* add other qualifications. For instance, you may have a
* "finished" module that handles quit in its own way, and
* want to hide the quit button then. The ViewManager has a
* current step and a total count, so compare them:
*
* visible: ViewManager.quitVisible && ( ViewManager.currentStepIndex < ViewManager.rowCount()-1);
*/
enabled: ViewManager.quitEnabled;
visible: ViewManager.quitVisible && ( ViewManager.currentStepIndex < ViewManager.rowCount()-1);
ToolTip {
width: 59
visible: mouseCancel.containsMouse
timeout: 5000
delay: 1000
text: ViewManager.quitTooltip;
}
MouseArea {
id: mouseCancel
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Cancel")
color: Branding.styleString( !cancelArea.enabled ? Branding.SidebarBackground : (mouseCancel.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText ));
font.pointSize : 8
}
Image {
source: "draw-rectangle.svg"
anchors.centerIn: parent
anchors.verticalCenterOffset : 18
fillMode: Image.PreserveAspectFit
height: 9
opacity: cancelArea.enabled ? 1 : 0.2
}
onClicked: { ViewManager.quit(); }
}
}
Item {
Layout.fillHeight: true;
}
Rectangle {
id: debugArea
Layout.fillWidth: true;
height: 35
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
color: Branding.styleString( mouseAreaDebug.containsMouse ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground);
visible: debug.enabled
MouseArea {
id: mouseAreaDebug
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Debug")
color: Branding.styleString( mouseAreaDebug.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarBackground );
font.pointSize : 8
}
onClicked: debug.toggle()
}
}
Rectangle {
id: aboutArea
Layout.fillWidth: true;
height: 35
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
color: Branding.styleString( mouseAreaAbout.containsMouse ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground);
visible: true
MouseArea {
id: mouseAreaAbout
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("About")
ToolTip {
visible: mouseAreaAbout.containsMouse
delay: 1000
text: qsTr("Info about Calamares")
}
color: Branding.styleString( mouseAreaAbout.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarBackgroundCurrent );
font.pointSize : 8
}
property variant window;
onClicked: {
var component = Qt.createComponent("about.qml");
window = component.createObject();
window.show();
}
}
}
}
}

View File

@@ -0,0 +1,72 @@
/* Sample of QML progress tree.
SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
SPDX-FileCopyrightText: 2021 - 2022 Anke Boersma <demm@kaosx.us>
SPDX-License-Identifier: GPL-3.0-or-later
The progress tree (actually a list) is "horizontal" in this example,
with the steps going to the right.
*/
import io.calamares.ui 1.0
import io.calamares.core 1.0
import QtQuick 2.3
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.15
Rectangle {
id: sideBar;
color: Branding.styleString( Branding.SidebarBackground );
height: 48;
width: parent.width
RowLayout {
anchors.fill: parent;
spacing: 2;
Item {
Layout.fillHeight: true;
}
Repeater {
model: ViewManager
Rectangle {
Layout.leftMargin: 0;
Layout.fillWidth: true;
Layout.alignment: Qt.AlignTop;
height: 42;
radius: 0;
color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground );
Text {
anchors.verticalCenter: parent.verticalCenter;
anchors.horizontalCenter: parent.horizontalCenter
x: parent.x + 12;
color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarTextCurrent : Branding.SidebarText );
text: display;
font.pointSize : index == ViewManager.currentStepIndex ? 10 : 9
}
Rectangle {
height: 2
width: 800
anchors.bottom: parent.bottom;
border.color: Branding.styleString(ViewManager.currentStepIndex === index ? Branding.SidebarTextCurrent : (ViewManager.currentStepIndex >= index ? Branding.SidebarTextCurrent : Branding.SidebarBackgroundCurrent))
border.width: 3
Image {
source: "pan-up-symbolic.svg"
id: image
anchors.verticalCenter: parent.verticalCenter;
anchors.verticalCenterOffset : -3
x: parent.x + 35;
fillMode: Image.PreserveAspectFit
height: 32
visible: index == ViewManager.currentStepIndex ? true : false
}
}
}
}
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"><path d="m2 1034.36v16h16v-16z" fill="#566060" transform="translate(1-1031.36)"/></svg>

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

@@ -0,0 +1,15 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height="16" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.1" width="16" xmlns="http://www.w3.org/2000/svg" enable-background="new">
<metadata id="metadata90"/>
<defs id="defs7386">
<linearGradient id="linearGradient5606" osb:paint="solid">
<stop id="stop5608"/>
</linearGradient>
<filter inkscape:collect="always" color-interpolation-filters="sRGB" id="filter7554">
<feBlend inkscape:collect="always" id="feBlend7556" in2="BackgroundImage" mode="darken"/>
</filter>
</defs>
<g inkscape:groupmode="layer" id="layer12" inkscape:label="actions" transform="translate(-445.0002,-129)">
<path inkscape:connector-curvature="0" d="m 451.0002,142 5,-5 -5,-5 z" id="path6412" sodipodi:nodetypes="cccc" fill="#555555"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 896 B

View File

@@ -0,0 +1,15 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height="16" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.1" width="16" xmlns="http://www.w3.org/2000/svg" enable-background="new">
<metadata id="metadata90"/>
<defs id="defs7386">
<linearGradient id="linearGradient5606" osb:paint="solid">
<stop id="stop5608"/>
</linearGradient>
<filter inkscape:collect="always" color-interpolation-filters="sRGB" id="filter7554">
<feBlend inkscape:collect="always" id="feBlend7556" in2="BackgroundImage" mode="darken"/>
</filter>
</defs>
<g inkscape:groupmode="layer" id="layer12" inkscape:label="actions" transform="translate(-425.0002,-129)">
<path inkscape:connector-curvature="0" d="m 435.0002,142 -5,-5 5,-5 z" id="path6400-8" sodipodi:nodetypes="cccc" fill="#555555"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 898 B

View File

@@ -0,0 +1,15 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height="16" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.1" width="16" xmlns="http://www.w3.org/2000/svg" enable-background="new">
<metadata id="metadata90"/>
<defs id="defs7386">
<linearGradient id="linearGradient5606" osb:paint="solid">
<stop id="stop5608"/>
</linearGradient>
<filter inkscape:collect="always" color-interpolation-filters="sRGB" id="filter7554">
<feBlend inkscape:collect="always" id="feBlend7556" in2="BackgroundImage" mode="darken"/>
</filter>
</defs>
<g inkscape:groupmode="layer" id="layer12" inkscape:label="actions" transform="translate(-465.0002,-129.00001)">
<path inkscape:connector-curvature="0" d="m 478.0002,139 -5,-5 -5,5 z" id="path6418" sodipodi:nodetypes="cccc" fill="#3498DB"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 902 B

View File

@@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2015, Teo Mrnjavac <teo@kde.org> * Copyright 2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2015-2018, Anke Boersma <demm@kaosx.us>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -24,13 +25,12 @@ Presentation
id: presentation id: presentation
Timer { Timer {
id: advanceTimer
interval: 5000 interval: 5000
running: false running: false
repeat: true repeat: true
onTriggered: presentation.goToNextSlide() onTriggered: presentation.goToNextSlide()
} }
Slide { Slide {
anchors.fill: parent anchors.fill: parent
@@ -38,7 +38,7 @@ Presentation
id: background id: background
source: "1.svg" source: "1.svg"
anchors.fill: parent anchors.fill: parent
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
anchors.verticalCenterOffset: 0 anchors.verticalCenterOffset: 0
@@ -96,15 +96,15 @@ Presentation
anchors.horizontalCenterOffset: -100 anchors.horizontalCenterOffset: -100
font.pixelSize: parent.width *.015 font.pixelSize: parent.width *.015
color: 'white' color: 'white'
text: qsTr("The default Office Suite is Calligra.<br/>"+ text: qsTr("The default Office Suite is LibreOffice.<br/>"+
"LibreOffice is available in the repositories. <br/>") "Calligra is available in the repositories. <br/>")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: 450 width: 450
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
} }
} }
} }
Slide { Slide {
anchors.fill: parent anchors.fill: parent
@@ -120,7 +120,7 @@ Presentation
font.pixelSize: parent.width *.015 font.pixelSize: parent.width *.015
color: 'white' color: 'white'
text: qsTr("Qt/KDE specific internet applications include the <br/>"+ text: qsTr("Qt/KDE specific internet applications include the <br/>"+
"Qupzilla web-browser and kde-telepathy for <br/>"+ "Falkon web-browser and kde-telepathy for <br/>"+
"chat and Instant Messaging. <br/>") "chat and Instant Messaging. <br/>")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: 450 width: 450
@@ -128,7 +128,7 @@ Presentation
} }
} }
} }
Slide { Slide {
anchors.fill: parent anchors.fill: parent
@@ -150,7 +150,7 @@ Presentation
} }
} }
} }
Slide { Slide {
anchors.fill: parent anchors.fill: parent
@@ -172,6 +172,4 @@ Presentation
} }
} }
} }
Component.onCompleted: advanceTimer.running = true
} }

View File

@@ -10,7 +10,7 @@
# NOTE: this is largely a copy of the release script for Calamares, # NOTE: this is largely a copy of the release script for Calamares,
# with not-applicable parts (such as translation-freeze) either # with not-applicable parts (such as translation-freeze) either
# commented-out, or skipped with if(false). # commented-out, or skipped with if(false).
# NOTE: this script contains Linuxisms (in particular, expects GNU mktemp(1)) # NOTE: this script may contain Linuxisms
# #
# This attempts to perform the different steps of the RELEASE.md # This attempts to perform the different steps of the RELEASE.md
# document automatically. It's not tested on other machines or # document automatically. It's not tested on other machines or
@@ -93,7 +93,7 @@ fi
### Setup ### Setup
# #
# #
BUILDDIR=$(mktemp -d --suffix=-build --tmpdir=.) BUILDDIR=$(mktemp -d -p . -t build.XXXXX)
### Build with default compiler ### Build with default compiler
# #
@@ -148,7 +148,7 @@ test -n "$V" || { echo "Could not obtain version in $BUILDDIR ." ; exit 1 ; }
# This is the signing key ID associated with the GitHub account adriaandegroot, # This is the signing key ID associated with the GitHub account adriaandegroot,
# which is used to create all "verified" tags in the Calamares repo. # which is used to create all "verified" tags in the Calamares repo.
# #
KEY_ID="CFDDC96F12B1915C" KEY_ID="328D742D8807A435"
git tag -u "$KEY_ID" -m "Release v$V" "v$V" || { echo "Could not sign tag v$V." ; exit 1 ; } git tag -u "$KEY_ID" -m "Release v$V" "v$V" || { echo "Could not sign tag v$V." ; exit 1 ; }
### Create the tarball ### Create the tarball
@@ -167,7 +167,7 @@ gpg -s -u $KEY_ID --detach --armor $TAR_FILE # Sign the tarball
# #
# #
D=$(date +%Y%m%d-%H%M%S) D=$(date +%Y%m%d-%H%M%S)
TMPDIR=$(mktemp -d --suffix="-calamares-$D") TMPDIR=$(mktemp -d -p . -t calamares.XXXXX)
test -d "$TMPDIR" || { echo "Could not create tarball-build directory." ; exit 1 ; } test -d "$TMPDIR" || { echo "Could not create tarball-build directory." ; exit 1 ; }
tar xzf "$TAR_FILE" -C "$TMPDIR" || { echo "Could not unpack tarball." ; exit 1 ; } tar xzf "$TAR_FILE" -C "$TMPDIR" || { echo "Could not unpack tarball." ; exit 1 ; }
test -d "$TMPDIR/$TAR_V" || { echo "Tarball did not contain source directory." ; exit 1 ; } test -d "$TMPDIR/$TAR_V" || { echo "Tarball did not contain source directory." ; exit 1 ; }

View File

@@ -18,7 +18,7 @@ Config::Config( QObject* parent )
void void
Config::setConfigurationMap( const QVariantMap& cfgMap ) Config::setConfigurationMap( const QVariantMap& cfgMap )
{ {
using namespace CalamaresUtils; using namespace Calamares;
if ( getBool( cfgMap, "bogus", false ) ) if ( getBool( cfgMap, "bogus", false ) )
{ {
@@ -30,6 +30,13 @@ Config::setConfigurationMap( const QVariantMap& cfgMap )
m_device = getString( cfgMap, "device", "(unknown)" ); m_device = getString( cfgMap, "device", "(unknown)" );
m_userInterface = getString( cfgMap, "userInterface", "(unknown)" ); m_userInterface = getString( cfgMap, "userInterface", "(unknown)" );
m_version = getString( cfgMap, "version", "(unknown)" ); m_version = getString( cfgMap, "version", "(unknown)" );
m_reservedUsernames = getStringList( cfgMap, "reservedUsernames", QStringList { "adm", "at ", "bin", "colord",
"cron", "cyrus", "daemon", "ftp", "games", "geoclue", "guest", "halt", "lightdm", "lp", "mail", "man",
"messagebus", "news", "nobody", "ntp", "operator", "polkitd", "postmaster", "pulse", "root", "shutdown",
"smmsp", "squid", "sshd", "sync", "uucp", "vpopmail", "xfs" } );
// ensure m_cmdUsermod matches m_username
m_username = getString( cfgMap, "username", "user" ); m_username = getString( cfgMap, "username", "user" );
m_userPasswordNumeric = getBool( cfgMap, "userPasswordNumeric", true ); m_userPasswordNumeric = getBool( cfgMap, "userPasswordNumeric", true );
@@ -54,6 +61,8 @@ Config::setConfigurationMap( const QVariantMap& cfgMap )
m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" ); m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" );
m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" ); m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" );
m_cmdUsermod = getString( cfgMap, "cmdUsermod", "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user");
m_cmdSshdEnable = getString( cfgMap, "cmdSshdEnable", "systemctl enable sshd.service" ); m_cmdSshdEnable = getString( cfgMap, "cmdSshdEnable", "systemctl enable sshd.service" );
m_cmdSshdDisable = getString( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" ); m_cmdSshdDisable = getString( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" );
m_cmdSshdUseradd = getString( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" ); m_cmdSshdUseradd = getString( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" );
@@ -68,6 +77,7 @@ Config::createJobs()
/* Put users job in queue (should run after unpackfs) */ /* Put users job in queue (should run after unpackfs) */
Calamares::Job* j = new UsersJob( m_featureSshd, Calamares::Job* j = new UsersJob( m_featureSshd,
m_cmdPasswd, m_cmdPasswd,
m_cmdUsermod,
cmdSshd, cmdSshd,
m_cmdSshdUseradd, m_cmdSshdUseradd,
m_isSshEnabled, m_isSshEnabled,
@@ -132,6 +142,13 @@ Config::runPartitionJobThenLeave( bool b )
} }
} }
void
Config::setUsername( const QString& username )
{
m_username = username;
emit usernameChanged( m_username );
}
void void
Config::setUserPassword( const QString& userPassword ) Config::setUserPassword( const QString& userPassword )
{ {

View File

@@ -20,8 +20,11 @@ class Config : public QObject
Q_PROPERTY( QString userInterface READ userInterface CONSTANT FINAL ) Q_PROPERTY( QString userInterface READ userInterface CONSTANT FINAL )
Q_PROPERTY( QString version READ version CONSTANT FINAL ) Q_PROPERTY( QString version READ version CONSTANT FINAL )
/* reserved usernames (user_pass, ssh_credentials )*/
Q_PROPERTY( QStringList reservedUsernames READ reservedUsernames CONSTANT FINAL )
/* default user */ /* default user */
Q_PROPERTY( QString username READ username CONSTANT FINAL ) Q_PROPERTY( QString username READ username WRITE setUsername NOTIFY usernameChanged )
Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged ) Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged )
Q_PROPERTY( bool userPasswordNumeric READ userPasswordNumeric CONSTANT FINAL ) Q_PROPERTY( bool userPasswordNumeric READ userPasswordNumeric CONSTANT FINAL )
@@ -72,13 +75,17 @@ public:
QString userInterface() const { return m_userInterface; } QString userInterface() const { return m_userInterface; }
QString version() const { return m_version; } QString version() const { return m_version; }
/* default user */ /* reserved usernames (user_pass, ssh_credentials) */
QStringList reservedUsernames() const { return m_reservedUsernames; };
/* user */
QString username() const { return m_username; } QString username() const { return m_username; }
QString userPassword() const { return m_userPassword; } QString userPassword() const { return m_userPassword; }
void setUsername( const QString& username );
void setUserPassword( const QString& userPassword ); void setUserPassword( const QString& userPassword );
bool userPasswordNumeric() const { return m_userPasswordNumeric; } bool userPasswordNumeric() const { return m_userPasswordNumeric; }
/* ssh server + credetials */ /* ssh server + credentials */
bool featureSshd() { return m_featureSshd; } bool featureSshd() { return m_featureSshd; }
QString sshdUsername() const { return m_sshdUsername; } QString sshdUsername() const { return m_sshdUsername; }
QString sshdPassword() const { return m_sshdPassword; } QString sshdPassword() const { return m_sshdPassword; }
@@ -120,6 +127,7 @@ public:
/* users job */ /* users job */
QString cmdPasswd() const { return m_cmdPasswd; } QString cmdPasswd() const { return m_cmdPasswd; }
QString cmdUsermod() const { return m_cmdUsermod; }
QString cmdSshdEnable() const { return m_cmdSshdEnable; } QString cmdSshdEnable() const { return m_cmdSshdEnable; }
QString cmdSshdDisable() const { return m_cmdSshdDisable; } QString cmdSshdDisable() const { return m_cmdSshdDisable; }
QString cmdSshdUseradd() const { return m_cmdSshdUseradd; } QString cmdSshdUseradd() const { return m_cmdSshdUseradd; }
@@ -135,6 +143,9 @@ private:
QString m_userInterface; QString m_userInterface;
QString m_version; QString m_version;
/* reserved usernames (user_pass, ssh_credentials) */
QStringList m_reservedUsernames;
/* default user */ /* default user */
QString m_username; QString m_username;
QString m_userPassword; QString m_userPassword;
@@ -172,6 +183,7 @@ private:
/* users job */ /* users job */
QString m_cmdPasswd; QString m_cmdPasswd;
QString m_cmdUsermod;
QString m_cmdSshdEnable; QString m_cmdSshdEnable;
QString m_cmdSshdDisable; QString m_cmdSshdDisable;
QString m_cmdSshdUseradd; QString m_cmdSshdUseradd;
@@ -181,6 +193,7 @@ signals:
/* default user */ /* default user */
void userPasswordChanged( QString userPassword ); void userPasswordChanged( QString userPassword );
void usernameChanged( QString username );
/* ssh server + credentials */ /* ssh server + credentials */
void sshdUsernameChanged( QString sshdUsername ); void sshdUsernameChanged( QString sshdUsername );

View File

@@ -5,7 +5,7 @@
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "Settings.h" #include "Settings.h"
#include "utils/CalamaresUtilsSystem.h" #include "utils/System.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include <QDir> #include <QDir>
@@ -75,7 +75,7 @@ Calamares::JobResult
PartitionJob::exec() PartitionJob::exec()
{ {
using namespace Calamares; using namespace Calamares;
using namespace CalamaresUtils; using namespace Calamares;
using namespace std; using namespace std;
const QString pathMount = "/mnt/install"; const QString pathMount = "/mnt/install";
@@ -118,7 +118,7 @@ PartitionJob::exec()
const QString pathRoot = "/"; const QString pathRoot = "/";
ProcessResult res ProcessResult res
= System::runCommand( System::RunLocation::RunInHost, args, pathRoot, stdInput, chrono::seconds( 120 ) ); = System::runCommand( System::RunLocation::RunInHost, args, pathRoot, stdInput, chrono::seconds( 600 ) );
if ( res.getExitCode() ) if ( res.getExitCode() )
{ {
return JobResult::error( "Command failed:<br><br>" return JobResult::error( "Command failed:<br><br>"

View File

@@ -5,7 +5,7 @@
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "Settings.h" #include "Settings.h"
#include "utils/CalamaresUtilsSystem.h" #include "utils/System.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include <QDir> #include <QDir>
@@ -14,6 +14,7 @@
UsersJob::UsersJob( bool featureSshd, UsersJob::UsersJob( bool featureSshd,
const QString& cmdPasswd, const QString& cmdPasswd,
const QString& cmdUsermod,
const QString& cmdSshd, const QString& cmdSshd,
const QString& cmdSshdUseradd, const QString& cmdSshdUseradd,
bool isSshEnabled, bool isSshEnabled,
@@ -24,6 +25,7 @@ UsersJob::UsersJob( bool featureSshd,
: Calamares::Job() : Calamares::Job()
, m_featureSshd( featureSshd ) , m_featureSshd( featureSshd )
, m_cmdPasswd( cmdPasswd ) , m_cmdPasswd( cmdPasswd )
, m_cmdUsermod( cmdUsermod )
, m_cmdSshd( cmdSshd ) , m_cmdSshd( cmdSshd )
, m_cmdSshdUseradd( cmdSshdUseradd ) , m_cmdSshdUseradd( cmdSshdUseradd )
, m_isSshEnabled( isSshEnabled ) , m_isSshEnabled( isSshEnabled )
@@ -45,13 +47,15 @@ Calamares::JobResult
UsersJob::exec() UsersJob::exec()
{ {
using namespace Calamares; using namespace Calamares;
using namespace CalamaresUtils; using namespace Calamares;
using namespace std; using namespace std;
QList< QPair< QStringList, QString > > commands = { QList< QPair< QStringList, QString > > commands = {
{ { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" }, { { "sh", "-c", m_cmdUsermod }, m_username + "\n" }
}; };
commands.append( { { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" } );
if ( m_featureSshd ) if ( m_featureSshd )
{ {
commands.append( { { "sh", "-c", m_cmdSshd }, QString() } ); commands.append( { { "sh", "-c", m_cmdSshd }, QString() } );

View File

@@ -10,6 +10,7 @@ class UsersJob : public Calamares::Job
public: public:
UsersJob( bool featureSshd, UsersJob( bool featureSshd,
const QString& cmdPasswd, const QString& cmdPasswd,
const QString& cmdUsermod,
const QString& cmdSshd, const QString& cmdSshd,
const QString& cmdSshdUseradd, const QString& cmdSshdUseradd,
bool isSshEnabled, bool isSshEnabled,
@@ -26,6 +27,7 @@ public:
private: private:
bool m_featureSshd; bool m_featureSshd;
QString m_cmdPasswd; QString m_cmdPasswd;
QString m_cmdUsermod;
QString m_cmdSshd; QString m_cmdSshd;
QString m_cmdSshdUseradd; QString m_cmdSshdUseradd;
bool m_isSshEnabled; bool m_isSshEnabled;

View File

@@ -22,7 +22,7 @@ Item {
id: mainText id: mainText
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 10
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: "To protect your data in case your device gets stolen," + text: "To protect your data in case your device gets stolen," +
@@ -33,15 +33,15 @@ Item {
" boot your device or access any data on it. Make sure that" + " boot your device or access any data on it. Make sure that" +
" you don't lose this password!" " you don't lose this password!"
width: 500 width: 200
} }
Button { Button {
id: firstButton id: firstButton
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom anchors.top: mainText.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("Enable") text: qsTr("Enable")
onClicked: { onClicked: {
@@ -53,8 +53,8 @@ Item {
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom anchors.top: firstButton.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("Disable") text: qsTr("Disable")
onClicked: { onClicked: {

View File

@@ -36,7 +36,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 50
width: 500 width: 200
} }
TextField { TextField {
@@ -49,8 +49,8 @@ Item {
text: config.fdePassword text: config.fdePassword
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 10
width: 500 width: 200
} }
Text { Text {
@@ -59,15 +59,15 @@ Item {
visible: false visible: false
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 10
width: 500 width: 200
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: errorText.bottom anchors.top: errorText.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("Continue") text: qsTr("Continue")
onClicked: { onClicked: {

View File

@@ -22,21 +22,21 @@ Item {
id: mainText id: mainText
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 10
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: "Select the filesystem for root partition. If unsure, leave the default." text: "Select the filesystem for root partition. If unsure, leave the default."
width: 500 width: 200
} }
ComboBox { ComboBox {
id: fsTypeCB id: fsTypeCB
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom anchors.top: mainText.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 150
height: 60 height: 30
editable: false editable: false
model: config.fsList model: config.fsList
/* Save the current state on selection so it is there when the back button is pressed */ /* Save the current state on selection so it is there when the back button is pressed */
@@ -47,8 +47,8 @@ Item {
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: fsTypeCB.bottom anchors.top: fsTypeCB.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("Continue") text: qsTr("Continue")
onClicked: { onClicked: {

View File

@@ -22,13 +22,13 @@ Item {
id: mainText id: mainText
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 10
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: (function() { text: (function() {
var ret = "Once you hit 'install', the installation will begin." + var ret = "Once you hit 'install', the installation will begin." +
" It will typically take a few minutes. Do not power off the" + " It will typically take a few minutes. Do not power off the" +
" device until it is done.<br><br>"; " device until it is done.<br>";
if (config.installFromExternalToInternal) { if (config.installFromExternalToInternal) {
ret += "<b>After the installation, your device will shutdown" + ret += "<b>After the installation, your device will shutdown" +
@@ -44,15 +44,15 @@ Item {
return ret; return ret;
}()) }())
width: 500 width: 200
} }
Button { Button {
id: firstButton id: firstButton
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom anchors.top: mainText.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("Install") text: qsTr("Install")
onClicked: navFinish() onClicked: navFinish()

View File

@@ -22,7 +22,7 @@ Item {
id: mainText id: mainText
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 10
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: "The installation was started from an external storage medium." + text: "The installation was started from an external storage medium." +
@@ -32,15 +32,15 @@ Item {
"<br>" + "<br>" +
"Where would you like to install " + config.osName + "?" "Where would you like to install " + config.osName + "?"
width: 500 width: 200
} }
Button { Button {
id: firstButton id: firstButton
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom anchors.top: mainText.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("Internal (eMMC)") text: qsTr("Internal (eMMC)")
onClicked: { onClicked: {
@@ -52,8 +52,8 @@ Item {
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom anchors.top: firstButton.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("External (SD card)") text: qsTr("External (SD card)")
onClicked: { onClicked: {

View File

@@ -22,21 +22,21 @@ Item {
id: mainText id: mainText
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 10
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: "Are you sure that you want to overwrite the internal storage?" + text: "Are you sure that you want to overwrite the internal storage?" +
"<br><br>" + "<br><br>" +
"<b>All existing data on the device will be lost!</b>" "<b>All existing data on the device will be lost!</b>"
width: 500 width: 200
} }
Button { Button {
id: firstButton id: firstButton
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom anchors.top: mainText.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("Yes") text: qsTr("Yes")
onClicked: { onClicked: {
@@ -47,8 +47,8 @@ Item {
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom anchors.top: firstButton.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("No") text: qsTr("No")
onClicked: { onClicked: {

View File

@@ -26,8 +26,45 @@ bogus: true
# version: "(unknown)" # version: "(unknown)"
## Default username (for which the password will be set) ## Default username (for which the password will be set)
## Ensure also cmdUsermod command matches the default user, so it can be changed if desired.
# username: "user" # username: "user"
## reserved usernames (for user_pass username prompt and ssh_credentials)
# reservedUsernames:
# - adm
# - at
# - bin
# - colord
# - cron
# - cyrus
# - daemon
# - ftp
# - games
# - geoclue
# - guest
# - halt
# - lightdm
# - lp
# - mail
# - man
# - messagebus
# - news
# - nobody
# - ntp
# - operator
# - polkitd
# - postmaster
# - pulse
# - root
# - shutdown
# - smmsp
# - squid
# - sshd
# - sync
# - uucp
# - vpopmail
# - xfs
####### #######
### Target device information ### Target device information
####### #######
@@ -118,6 +155,10 @@ bogus: true
### Commands running in the target OS (chroot) ### Commands running in the target OS (chroot)
####### #######
## Change the username for the default user
## Stdin: username with \n
# cmdUsermod: "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user"
## Set the password for default user and sshd user ## Set the password for default user and sshd user
## Arguments: <username> ## Arguments: <username>
## Stdin: password twice, each time with \n ## Stdin: password twice, each time with \n

View File

@@ -77,7 +77,7 @@ Page
Rectangle { Rectangle {
id: mobileNavigation id: mobileNavigation
width: parent.width width: parent.width
height: 60 height: 30
color: "#e6e4e1" color: "#e6e4e1"
Layout.fillWidth: true Layout.fillWidth: true
@@ -98,8 +98,8 @@ Page
text: "<" text: "<"
background: Rectangle { background: Rectangle {
implicitWidth: 32 implicitWidth: 10
implicitHeight: 30 implicitHeight: 7
border.color: "#c1bab5" border.color: "#c1bab5"
border.width: 1 border.width: 1
radius: 4 radius: 4
@@ -109,7 +109,7 @@ Page
onClicked: navBack() onClicked: navBack()
} }
Rectangle { Rectangle {
implicitHeight: 30 implicitHeight: 10
Layout.fillWidth: true Layout.fillWidth: true
color: "#e6e4e1" color: "#e6e4e1"
@@ -253,7 +253,7 @@ Page
return true; return true;
} }
/* Input validation: user-screens (user_pass, ssh_credentials) */ /* Input validation: user-screens (fde_pass, user_pass, ssh_credentials) */
function validatePin(userPin, userPinRepeat, errorText) { function validatePin(userPin, userPinRepeat, errorText) {
var pin = userPin.text; var pin = userPin.text;
var repeat = userPinRepeat.text; var repeat = userPinRepeat.text;
@@ -278,47 +278,12 @@ Page
return validationFailureClear(errorText); return validationFailureClear(errorText);
} }
function validateSshdUsername(username, errorText) { function validateUsername(username, errorText, extraReservedUsernames = []) {
var name = username.text; var name = username.text;
var reserved = [ /* FIXME: make configurable */ var reserved = config.reservedUsernames.concat(extraReservedUsernames);
config.username,
"adm",
"at ",
"bin",
"colord",
"cron",
"cyrus",
"daemon",
"ftp",
"games",
"geoclue",
"guest",
"halt",
"lightdm",
"lp",
"mail",
"man",
"messagebus",
"news",
"nobody",
"ntp",
"operator",
"polkitd",
"postmaster",
"pulse",
"root",
"shutdown",
"smmsp",
"squid",
"sshd",
"sync",
"uucp",
"vpopmail",
"xfs",
]
/* Validate characters */ /* Validate characters */
for (var i=0; i<name.length; i++) { for (var i = 0; i < name.length; i++) {
if (i) { if (i) {
if (!name[i].match(/^[a-z0-9_-]$/)) if (!name[i].match(/^[a-z0-9_-]$/))
return validationFailure(errorText, return validationFailure(errorText,
@@ -335,16 +300,20 @@ Page
} }
/* Validate against reserved usernames */ /* Validate against reserved usernames */
for (var i=0;i<reserved.length;i++) { for (var i = 0; i < reserved.length; i++) {
if (name == reserved[i]) if (name == reserved[i])
return validationFailure(errorText, "Username '" + return validationFailure(errorText, "Username '" +
reserved[i] + reserved[i] +
"' is reserved.") "' is reserved.");
} }
/* Passed */ /* Passed */
return validationFailureClear(errorText); return validationFailureClear(errorText);
} }
function validateSshdUsername(username, errorText) {
return validateUsername(username, errorText, [config.username]);
}
function validateSshdPassword(password, passwordRepeat, errorText) { function validateSshdPassword(password, passwordRepeat, errorText) {
var pass = password.text; var pass = password.text;
var repeat = passwordRepeat.text; var repeat = passwordRepeat.text;
@@ -352,10 +321,10 @@ Page
if (pass == "") if (pass == "")
return validationFailure(errorText); return validationFailure(errorText);
if (pass.length < 8) if (pass.length < 6)
return validationFailure(errorText, return validationFailure(errorText,
"Too short: needs at least 8" + "Too short: needs at least 6" +
" characters."); " digits/characters.");
if (repeat == "") if (repeat == "")
return validationFailure(errorText); return validationFailure(errorText);
@@ -365,8 +334,6 @@ Page
return validationFailureClear(errorText); return validationFailureClear(errorText);
} }
/* Input validation: fde_pass */
function check_chars(input) { function check_chars(input) {
for (var i = 0; i < input.length; i++) { for (var i = 0; i < input.length; i++) {
if (allowed_chars.indexOf(input[i]) == -1) if (allowed_chars.indexOf(input[i]) == -1)
@@ -407,10 +374,10 @@ Page
"\n" + "\n" +
allowed_chars_multiline()); allowed_chars_multiline());
if (pass.length < 8) if (pass.length < 6)
return validationFailure(errorText, return validationFailure(errorText,
"Too short: needs at least 8" + "Too short: needs at least 6" +
" characters."); " digits/characters.");
if (repeat == "") if (repeat == "")
return validationFailure(errorText); return validationFailure(errorText);

View File

@@ -7,7 +7,7 @@
<file>install_target.qml</file> <!-- install from external to internal? --> <file>install_target.qml</file> <!-- install from external to internal? -->
<file>install_target_confirm.qml</file> <!-- overwrite internal storage? --> <file>install_target_confirm.qml</file> <!-- overwrite internal storage? -->
<file>user_pass.qml</file> <!-- default user: password --> <file>user_pass.qml</file> <!-- default user: username, password -->
<file>ssh_confirm.qml</file> <!-- sshd: enable or not? --> <file>ssh_confirm.qml</file> <!-- sshd: enable or not? -->
<file>ssh_credentials.qml</file> <!-- sshd user: username, password --> <file>ssh_credentials.qml</file> <!-- sshd user: username, password -->
<file>fs_selection.qml</file> <!-- filesystem selection --> <file>fs_selection.qml</file> <!-- filesystem selection -->

View File

@@ -36,7 +36,7 @@ Item {
"More information:<br>" + "More information:<br>" +
"https://postmarketos.org/ssh" "https://postmarketos.org/ssh"
width: 500 width: 200
} }
Button { Button {
@@ -44,7 +44,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom anchors.top: mainText.bottom
anchors.topMargin: 40 anchors.topMargin: 40
width: 500 width: 200
text: qsTr("Enable") text: qsTr("Enable")
onClicked: { onClicked: {
@@ -57,7 +57,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom anchors.top: firstButton.bottom
anchors.topMargin: 40 anchors.topMargin: 40
width: 500 width: 200
text: qsTr("Disable") text: qsTr("Disable")
onClicked: { onClicked: {

View File

@@ -34,7 +34,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 50
width: 500 width: 200
} }
Text { Text {
@@ -45,7 +45,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 50
width: 500 width: 200
} }
TextField { TextField {
@@ -59,7 +59,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 50
width: 500 width: 200
} }
TextField { TextField {
@@ -73,7 +73,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 50
width: 500 width: 200
} }
Text { Text {
@@ -84,13 +84,13 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 50
width: 500 width: 200
} }
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: errorTextPassword.bottom anchors.top: errorTextPassword.bottom
anchors.topMargin: 40 anchors.topMargin: 40
width: 500 width: 200
text: qsTr("Continue") text: qsTr("Continue")
onClicked: { onClicked: {

View File

@@ -12,16 +12,18 @@ import QtQuick.Window 2.3
import QtQuick.VirtualKeyboard 2.1 import QtQuick.VirtualKeyboard 2.1
Item { Item {
property var placeholder: (config.userPasswordNumeric property var passPlaceholder: (config.userPasswordNumeric
? "PIN" ? "PIN"
: "Password") : "Password")
property var hints: (config.userPasswordNumeric property var hints: (config.userPasswordNumeric
? Qt.ImhDigitsOnly ? Qt.ImhDigitsOnly
: Qt.ImhPreferLowercase) : Qt.ImhPreferLowercase)
property var validateFunc: (config.userPasswordNumeric property var validatePassFunc: (config.userPasswordNumeric
? validatePin ? validatePin
: validatePassword); : validatePassword);
property var validateNameFunc: validateUsername;
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
@@ -30,10 +32,36 @@ Item {
height: parent.height height: parent.height
Text { Text {
id: description id: usernameDescription
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 10
wrapMode: Text.WordWrap
text: (function() {
return "Set the username of your user. The default" +
" username is \"" + config.username + "\".";
}())
width: 200
}
TextField {
id: username
anchors.top: usernameDescription.bottom
placeholderText: qsTr("Username")
onTextChanged: validateNameFunc(username, errorText)
text: config.username
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 10
width: 200
}
Text {
id: userPassDescription
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: username.bottom
anchors.topMargin: 10
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: (function() { text: (function() {
@@ -48,15 +76,15 @@ Item {
} }
}()) }())
width: 500 width: 200
} }
TextField { TextField {
id: userPass id: userPass
anchors.top: description.bottom anchors.top: userPassDescription.bottom
placeholderText: qsTr(placeholder) placeholderText: qsTr(passPlaceholder)
echoMode: TextInput.Password echoMode: TextInput.Password
onTextChanged: validateFunc(userPass, userPassRepeat, errorText) onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText)
text: config.userPassword text: config.userPassword
/* Let the virtual keyboard change to digits only */ /* Let the virtual keyboard change to digits only */
@@ -68,22 +96,22 @@ Item {
} }
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 10
width: 500 width: 200
} }
TextField { TextField {
id: userPassRepeat id: userPassRepeat
anchors.top: userPass.bottom anchors.top: userPass.bottom
placeholderText: qsTr(placeholder + " (repeat)") placeholderText: qsTr(passPlaceholder + " (repeat)")
inputMethodHints: hints inputMethodHints: hints
echoMode: TextInput.Password echoMode: TextInput.Password
onTextChanged: validateFunc(userPass, userPassRepeat, errorText) onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText)
text: config.userPassword text: config.userPassword
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 10
width: 500 width: 200
} }
Text { Text {
@@ -93,20 +121,21 @@ Item {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50 anchors.topMargin: 10
width: 500 width: 200
} }
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: errorText.bottom anchors.top: errorText.bottom
anchors.topMargin: 40 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("Continue") text: qsTr("Continue")
onClicked: { onClicked: {
if (validateFunc(userPass, userPassRepeat, errorText)) { if (validatePassFunc(userPass, userPassRepeat, errorText) && validateNameFunc(username, errorText)) {
config.userPassword = userPass.text; config.userPassword = userPass.text;
config.username = username.text;
navNext(); navNext();
} }
} }

View File

@@ -26,8 +26,8 @@ Page
id: logo id: logo
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 50 anchors.topMargin: 10
height: 250 height: 50
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: "file:///usr/share/calamares/branding/default-mobile/logo.png" source: "file:///usr/share/calamares/branding/default-mobile/logo.png"
} }
@@ -35,11 +35,11 @@ Page
id: waitText id: waitText
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: logo.bottom anchors.top: logo.bottom
anchors.topMargin: 150 anchors.topMargin: 50
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: "Formatting and mounting target partition. This may" + text: "Formatting and mounting target partition. This may" +
" take up to two minutes, please be patient." " take up to ten minutes, please be patient."
width: 500 width: 200
} }
} }
} }

View File

@@ -28,8 +28,8 @@ Page
id: logo id: logo
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 50 anchors.topMargin: 10
height: 250 height: 50
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: "file:///usr/share/calamares/branding/default-mobile/logo.png" source: "file:///usr/share/calamares/branding/default-mobile/logo.png"
} }
@@ -37,7 +37,7 @@ Page
id: mainText id: mainText
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: logo.bottom anchors.top: logo.bottom
anchors.topMargin: 50 anchors.topMargin: 10
horizontalAlignment: Text.AlignRight horizontalAlignment: Text.AlignRight
text: "You are about to install<br>" + text: "You are about to install<br>" +
"<b>" + config.osName + "<b>" + config.osName +
@@ -46,16 +46,16 @@ Page
"<b>" + config.userInterface + "</b><br>" + "<b>" + config.userInterface + "</b><br>" +
"architecture " + "architecture " +
"<b>" + config.arch + "</b><br>" + "<b>" + config.arch + "</b><br>" +
"on your " + "on your <br>" +
"<b>" + config.device + "</b><br>" "<b>" + config.device + "</b><br>"
width: 500 width: 200
} }
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom anchors.top: mainText.bottom
anchors.topMargin: 50 anchors.topMargin: 10
width: 500 width: 200
text: qsTr("Continue") text: qsTr("Continue")
onClicked: navNext() onClicked: navNext()

View File

@@ -9,6 +9,7 @@ calamares_add_plugin( unpackfsc
# The workers for differently-packed filesystems # The workers for differently-packed filesystems
Runners.cpp Runners.cpp
FSArchiverRunner.cpp FSArchiverRunner.cpp
TarballRunner.cpp
UnsquashRunner.cpp UnsquashRunner.cpp
SHARED_LIB SHARED_LIB
) )

View File

@@ -15,46 +15,11 @@
#include <QProcess> #include <QProcess>
static constexpr const int chunk_size = 137; static constexpr const int chunk_size = 137;
static const QString&
Calamares::JobResult toolName()
FSArchiverRunner::run()
{ {
const QString toolName = QStringLiteral( "fsarchiver" ); static const QString name = QStringLiteral( "fsarchiver" );
return name;
if ( !checkSourceExists() )
{
return Calamares::JobResult::internalError(
tr( "Invalid fsarchiver configuration" ),
tr( "The source archive <i>%1</i> does not exist." ).arg( m_source ),
Calamares::JobResult::InvalidConfiguration );
}
QString fsarchiverExecutable;
if ( !checkToolExists( toolName, fsarchiverExecutable ) )
{
return Calamares::JobResult::internalError(
tr( "Missing tools" ),
tr( "The <i>%1</i> tool is not installed on the system." ).arg( toolName ),
Calamares::JobResult::MissingRequirements );
}
const QString destinationPath = CalamaresUtils::System::instance()->targetPath( m_destination );
if ( destinationPath.isEmpty() )
{
return Calamares::JobResult::internalError(
tr( "Invalid fsarchiver configuration" ),
tr( "No destination could be found for <i>%1</i>." ).arg( m_destination ),
Calamares::JobResult::InvalidConfiguration );
}
Calamares::Utils::Runner r( { fsarchiverExecutable,
QStringLiteral( "-v" ),
QStringLiteral( "restdir" ),
m_source,
destinationPath } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
connect( &r, &decltype( r )::output, this, &FSArchiverRunner::fsarchiverProgress );
return r.run().explainProcess( toolName, std::chrono::seconds( 0 ) );
} }
void void
@@ -64,11 +29,89 @@ FSArchiverRunner::fsarchiverProgress( QString line )
// Typical line of output is this: // Typical line of output is this:
// -[00][ 99%][REGFILEM] /boot/thing // -[00][ 99%][REGFILEM] /boot/thing
// 5 9 ^21 // 5 9 ^21
if (m_since >= chunk_size && line.length() > 21 && line[5] == '[' && line[9] == '%') if ( m_since >= chunk_size && line.length() > 21 && line[ 5 ] == '[' && line[ 9 ] == '%' )
{ {
m_since = 0; m_since = 0;
double p = double(line.mid(6,3).toInt()) / 100.0; double p = double( line.mid( 6, 3 ).toInt() ) / 100.0;
const QString filename = line.mid(22); const QString filename = line.mid( 22 );
Q_EMIT progress(p, filename); Q_EMIT progress( p, filename );
} }
} }
Calamares::JobResult
FSArchiverRunner::checkPrerequisites( QString& fsarchiverExecutable ) const
{
if ( !checkToolExists( toolName(), fsarchiverExecutable ) )
{
return Calamares::JobResult::internalError(
tr( "Missing tools" ),
tr( "The <i>%1</i> tool is not installed on the system." ).arg( toolName() ),
Calamares::JobResult::MissingRequirements );
}
if ( !checkSourceExists() )
{
return Calamares::JobResult::internalError(
tr( "Invalid fsarchiver configuration" ),
tr( "The source archive <i>%1</i> does not exist." ).arg( m_source ),
Calamares::JobResult::InvalidConfiguration );
}
return Calamares::JobResult::ok();
}
Calamares::JobResult
FSArchiverRunner::checkDestination( QString& destinationPath ) const
{
destinationPath = Calamares::System::instance()->targetPath( m_destination );
if ( destinationPath.isEmpty() )
{
return Calamares::JobResult::internalError(
tr( "Invalid fsarchiver configuration" ),
tr( "No destination could be found for <i>%1</i>." ).arg( m_destination ),
Calamares::JobResult::InvalidConfiguration );
}
return Calamares::JobResult::ok();
}
Calamares::JobResult
FSArchiverDirRunner::run()
{
QString fsarchiverExecutable;
if ( auto res = checkPrerequisites( fsarchiverExecutable ); !res )
{
return res;
}
QString destinationPath;
if ( auto res = checkDestination( destinationPath ); !res )
{
return res;
}
Calamares::Utils::Runner r(
{ fsarchiverExecutable, QStringLiteral( "-v" ), QStringLiteral( "restdir" ), m_source, destinationPath } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
connect( &r, &decltype( r )::output, this, &FSArchiverDirRunner::fsarchiverProgress );
return r.run().explainProcess( toolName(), std::chrono::seconds( 0 ) );
}
Calamares::JobResult
FSArchiverFSRunner::run()
{
QString fsarchiverExecutable;
if ( auto res = checkPrerequisites( fsarchiverExecutable ); !res )
{
return res;
}
QString destinationPath;
if ( auto res = checkDestination( destinationPath ); !res )
{
return res;
}
Calamares::Utils::Runner r(
{ fsarchiverExecutable, QStringLiteral( "-v" ), QStringLiteral( "restfs" ), m_source, destinationPath } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
connect( &r, &decltype( r )::output, this, &FSArchiverFSRunner::fsarchiverProgress );
return r.run().explainProcess( toolName(), std::chrono::seconds( 0 ) );
}

View File

@@ -12,19 +12,48 @@
#include "Runners.h" #include "Runners.h"
/** @brief Base class for runners of FSArchiver
*
*/
class FSArchiverRunner : public Runner class FSArchiverRunner : public Runner
{ {
Q_OBJECT Q_OBJECT
public: public:
using Runner::Runner; using Runner::Runner;
Calamares::JobResult run() override;
protected Q_SLOTS: protected Q_SLOTS:
void fsarchiverProgress( QString line ); void fsarchiverProgress( QString line );
private: protected:
/** @brief Checks prerequisites, sets full path of fsarchiver in @p executable
*/
Calamares::JobResult checkPrerequisites( QString& executable ) const;
Calamares::JobResult checkDestination( QString& destinationPath ) const;
int m_since = 0; int m_since = 0;
}; };
/** @brief Running FSArchiver in **dir** mode
*
*/
class FSArchiverDirRunner : public FSArchiverRunner
{
public:
using FSArchiverRunner::FSArchiverRunner;
Calamares::JobResult run() override;
};
/** @brief Running FSArchiver in **dir** mode
*
*/
class FSArchiverFSRunner : public FSArchiverRunner
{
public:
using FSArchiverRunner::FSArchiverRunner;
Calamares::JobResult run() override;
};
#endif #endif

View File

@@ -9,7 +9,7 @@
#include "Runners.h" #include "Runners.h"
#include <utils/CalamaresUtilsSystem.h> #include <utils/System.h>
#include <utils/Logger.h> #include <utils/Logger.h>
#include <QFileInfo> #include <QFileInfo>
@@ -21,7 +21,7 @@ Runner::Runner( const QString& source, const QString& destination )
{ {
} }
Runner::~Runner() {} Runner::~Runner() { }
bool bool
Runner::checkSourceExists() const Runner::checkSourceExists() const

View File

@@ -0,0 +1,86 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "TarballRunner.h"
#include <utils/Logger.h>
#include <utils/Runner.h>
#include <utils/String.h>
#include <QString>
static constexpr const int chunk_size = 107;
Calamares::JobResult
TarballRunner::run()
{
if ( !checkSourceExists() )
{
return Calamares::JobResult::internalError(
tr( "Invalid tarball configuration" ),
tr( "The source archive <i>%1</i> does not exist." ).arg( m_source ),
Calamares::JobResult::InvalidConfiguration );
}
const QString toolName = QStringLiteral( "tar" );
QString tarExecutable;
if ( !checkToolExists( toolName, tarExecutable ) )
{
return Calamares::JobResult::internalError(
tr( "Missing tools" ),
tr( "The <i>%1</i> tool is not installed on the system." ).arg( toolName ),
Calamares::JobResult::MissingRequirements );
}
const QString destinationPath = Calamares::System::instance()->targetPath( m_destination );
if ( destinationPath.isEmpty() )
{
return Calamares::JobResult::internalError(
tr( "Invalid tarball configuration" ),
tr( "No destination could be found for <i>%1</i>." ).arg( m_destination ),
Calamares::JobResult::InvalidConfiguration );
}
// Get the stats (number of inodes) from the FS
{
m_total = 0;
Calamares::Utils::Runner r( { tarExecutable, QStringLiteral( "-tf" ), m_source } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
QObject::connect( &r, &decltype( r )::output, [ & ]( QString line ) { m_total++; } );
/* ignored */ r.run();
}
if ( m_total <= 0 )
{
cWarning() << "No stats could be obtained from" << tarExecutable << "-tf" << m_source;
}
// Now do the actual unpack
{
m_processed = 0;
m_since = 0;
Calamares::Utils::Runner r(
{ tarExecutable, QStringLiteral( "-xpvf" ), m_source, QStringLiteral( "-C" ), destinationPath } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
connect( &r, &decltype( r )::output, this, &TarballRunner::tarballProgress );
return r.run().explainProcess( toolName, std::chrono::seconds( 0 ) );
}
}
void
TarballRunner::tarballProgress( QString line )
{
m_processed++;
m_since++;
if ( m_since > chunk_size )
{
m_since = 0;
double p = m_total > 0 ? ( double( m_processed ) / double( m_total ) ) : 0.5;
Q_EMIT progress( p, tr( "Tarball extract file %1" ).arg( line ) );
}
}

View File

@@ -0,0 +1,35 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef UNPACKFSC_TARBALLRUNNER_H
#define UNPACKFSC_TARBALLRUNNER_H
#include "Runners.h"
/** @brief Use (GNU) tar for extracting a filesystem
*
*/
class TarballRunner : public Runner
{
public:
using Runner::Runner;
Calamares::JobResult run() override;
protected Q_SLOTS:
void tarballProgress( QString line );
private:
// Progress reporting
int m_total = 0;
int m_processed = 0;
int m_since = 0;
};
#endif

View File

@@ -10,6 +10,7 @@
#include "UnpackFSCJob.h" #include "UnpackFSCJob.h"
#include "FSArchiverRunner.h" #include "FSArchiverRunner.h"
#include "TarballRunner.h"
#include "UnsquashRunner.h" #include "UnsquashRunner.h"
#include <utils/Logger.h> #include <utils/Logger.h>
@@ -31,10 +32,14 @@ typeNames()
{ "fsarchive", T::FSArchive }, { "fsarchive", T::FSArchive },
{ "fsa", T::FSArchive }, { "fsa", T::FSArchive },
{ "fsa-dir", T::FSArchive }, { "fsa-dir", T::FSArchive },
// TODO: support fsa-block, savefs/restfs format { "fsa-block", T::FSArchiveFS },
{ "fsa-fs", T::FSArchiveFS },
{ "squashfs", T::Squashfs }, { "squashfs", T::Squashfs },
{ "squash", T::Squashfs }, { "squash", T::Squashfs },
{ "unsquash", T::Squashfs }, { "unsquash", T::Squashfs },
{ "tar", T::Tarball },
{ "tarball", T::Tarball },
{ "tgz", T::Tarball },
}; };
// clang-format on // clang-format on
return names; return names;
@@ -45,7 +50,7 @@ UnpackFSCJob::UnpackFSCJob( QObject* parent )
{ {
} }
UnpackFSCJob::~UnpackFSCJob() {} UnpackFSCJob::~UnpackFSCJob() { }
QString QString
UnpackFSCJob::prettyName() const UnpackFSCJob::prettyName() const
@@ -66,29 +71,38 @@ UnpackFSCJob::exec()
switch ( m_type ) switch ( m_type )
{ {
case Type::FSArchive: case Type::FSArchive:
r = std::make_unique< FSArchiverRunner >( m_source, m_destination ); r = std::make_unique< FSArchiverDirRunner >( m_source, m_destination );
break;
case Type::FSArchiveFS:
r = std::make_unique< FSArchiverFSRunner >( m_source, m_destination );
break; break;
case Type::Squashfs: case Type::Squashfs:
r = std::make_unique< UnsquashRunner >( m_source, m_destination ); r = std::make_unique< UnsquashRunner >( m_source, m_destination );
break; break;
case Type::Tarball:
r = std::make_unique< TarballRunner >( m_source, m_destination );
break;
case Type::None: case Type::None:
default: default:
cDebug() << "Nothing to do."; cDebug() << "Nothing to do.";
return Calamares::JobResult::ok(); return Calamares::JobResult::ok();
} }
connect( r.get(), &Runner::progress, [=]( qreal percent, const QString& message ) { connect( r.get(),
m_progressMessage = message; &Runner::progress,
Q_EMIT progress( percent ); [ = ]( qreal percent, const QString& message )
} ); {
m_progressMessage = message;
Q_EMIT progress( percent );
} );
return r->run(); return r->run();
} }
void void
UnpackFSCJob::setConfigurationMap( const QVariantMap& map ) UnpackFSCJob::setConfigurationMap( const QVariantMap& map )
{ {
QString source = CalamaresUtils::getString( map, "source" ); QString source = Calamares::getString( map, "source" );
QString sourceTypeName = CalamaresUtils::getString( map, "sourcefs" ); QString sourceTypeName = Calamares::getString( map, "sourcefs" );
if ( source.isEmpty() || sourceTypeName.isEmpty() ) if ( source.isEmpty() || sourceTypeName.isEmpty() )
{ {
cWarning() << "Skipping item with bad source data:" << map; cWarning() << "Skipping item with bad source data:" << map;
@@ -101,7 +115,7 @@ UnpackFSCJob::setConfigurationMap( const QVariantMap& map )
cWarning() << "Skipping item with source type None"; cWarning() << "Skipping item with source type None";
return; return;
} }
QString destination = CalamaresUtils::getString( map, "destination" ); QString destination = Calamares::getString( map, "destination" );
if ( destination.isEmpty() ) if ( destination.isEmpty() )
{ {
cWarning() << "Skipping item with empty destination"; cWarning() << "Skipping item with empty destination";

View File

@@ -23,7 +23,9 @@ public:
{ {
None, /// << Invalid None, /// << Invalid
FSArchive, FSArchive,
FSArchiveFS,
Squashfs, Squashfs,
Tarball,
}; };
explicit UnpackFSCJob( QObject* parent = nullptr ); explicit UnpackFSCJob( QObject* parent = nullptr );

View File

@@ -38,7 +38,7 @@ UnsquashRunner::run()
Calamares::JobResult::MissingRequirements ); Calamares::JobResult::MissingRequirements );
} }
const QString destinationPath = CalamaresUtils::System::instance()->targetPath( m_destination ); const QString destinationPath = Calamares::System::instance()->targetPath( m_destination );
if ( destinationPath.isEmpty() ) if ( destinationPath.isEmpty() )
{ {
return Calamares::JobResult::internalError( return Calamares::JobResult::internalError(
@@ -52,12 +52,15 @@ UnsquashRunner::run()
m_inodes = -1; m_inodes = -1;
Calamares::Utils::Runner r( { unsquashExecutable, QStringLiteral( "-s" ), m_source } ); Calamares::Utils::Runner r( { unsquashExecutable, QStringLiteral( "-s" ), m_source } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing(); r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
QObject::connect( &r, &decltype( r )::output, [&]( QString line ) { QObject::connect( &r,
if ( line.startsWith( "Number of inodes " ) ) &decltype( r )::output,
{ [ & ]( QString line )
m_inodes = line.split( ' ', SplitSkipEmptyParts ).last().toInt(); {
} if ( line.startsWith( "Number of inodes " ) )
} ); {
m_inodes = line.split( ' ', SplitSkipEmptyParts ).last().toInt();
}
} );
/* ignored */ r.run(); /* ignored */ r.run();
} }
if ( m_inodes <= 0 ) if ( m_inodes <= 0 )

View File

@@ -14,7 +14,6 @@
/** @brief Use Unsquash for extracting a filesystem /** @brief Use Unsquash for extracting a filesystem
* *
* NOTE: not implemented
*/ */
class UnsquashRunner : public Runner class UnsquashRunner : public Runner
{ {

View File

@@ -21,15 +21,19 @@
# - *sourcefs* the type of the source files; valid entries are # - *sourcefs* the type of the source files; valid entries are
# - `none` (this entry is ignored; kind of useless) # - `none` (this entry is ignored; kind of useless)
# - `fsarchiver` # - `fsarchiver`
# Aliases of this are `fsarchive`, `fsa` and `fsa-dir`. # Aliases of this are `fsarchive`, `fsa` and `fsa-dir`. Uses
# fsarchiver in "restdir" mode.
# - `fsarchiver-block`
# Aliases of this are `fsa-block` and `fsa-fs`. Uses fsarchiver
# in "restfs" mode.
# - `squashfs` # - `squashfs`
# Aliases of this are `squash` and `unsquash`. # Aliases of this are `squash` and `unsquash`.
# - `tar`
# - *destination* path relative to rootMountPoint (so in the target # - *destination* path relative to rootMountPoint (so in the target
# system) where this filesystem is unpacked. It may be an # system) where this filesystem is unpacked. It may be an
# empty string, which effectively is / (the root) of the target # empty string, which effectively is / (the root) of the target
# system. # system.
# #
# TODO: add `fsa-block` and support for *savefs/restfs* mode.
source: /data/rootfs.fsa source: /data/rootfs.fsa
sourcefs: fsarchiver sourcefs: fsarchiver
destination: "/" destination: "/"