Compare commits
23 Commits
packagecho
...
old-module
| Author | SHA1 | Date | |
|---|---|---|---|
| 379f38bfe9 | |||
| 25acdeceba | |||
| 5462411d8a | |||
| b7f606cba1 | |||
| d51ba1e8df | |||
| 10dc285ed2 | |||
| 916f1c2fae | |||
| 0d9923ed0b | |||
| 8947b568a7 | |||
| 3dc5e111bb | |||
| c6e2cacd51 | |||
| 3d3e73f015 | |||
| fe012233e3 | |||
| e55d467f12 | |||
| c99c53714f | |||
| 9e10e21381 | |||
| c6884e1044 | |||
| fc2b4c54a5 | |||
| c04875fb6a | |||
| bac0c002d8 | |||
| 999e45c64a | |||
| a1012b9a82 | |||
| 39427ffc4c |
@@ -56,6 +56,29 @@ project(calamares-extensions
|
||||
set( CMAKE_CXX_STANDARD 17 )
|
||||
set( CMAKE_CXX_STANDARD_REQUIRED ON )
|
||||
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
# On developer's machine, the user package registry breaks
|
||||
# consumers by loading the developer's config from a build
|
||||
# directory (which doesn't have the rest of the config
|
||||
@@ -96,18 +119,19 @@ include( CTest )
|
||||
# Typically you would use only one branding, since that's
|
||||
# the (single) branding for your distro.
|
||||
#
|
||||
calamares_add_branding_subdirectory( branding/default NAME default )
|
||||
calamares_add_branding_subdirectory( branding/default-mobile NAME default-mobile )
|
||||
calamares_add_branding_subdirectory( branding/fancy NAME fancy )
|
||||
# calamares_add_branding_subdirectory( branding/default NAME default )
|
||||
# calamares_add_branding_subdirectory( branding/default-mobile NAME default-mobile )
|
||||
# calamares_add_branding_subdirectory( branding/fancy NAME fancy )
|
||||
|
||||
# This one has files in subdirectories
|
||||
calamares_add_branding_subdirectory( branding/samegame NAME samegame SUBDIRECTORIES img )
|
||||
# calamares_add_branding_subdirectory( branding/samegame NAME samegame SUBDIRECTORIES img )
|
||||
|
||||
# KaOS branding, with translations, note we can *NAME* something
|
||||
# different from the source directory it lives in; this will be installed
|
||||
# to a directory called *NAME* though -- and the `branding.desc` must
|
||||
# have a *componentName* that matches this *NAME*.
|
||||
calamares_add_branding_subdirectory( branding/kaos_branding NAME kaos )
|
||||
# calamares_add_branding_subdirectory( branding/kaos_branding NAME kaos )
|
||||
calamares_add_branding_subdirectory( branding/artix NAME artix )
|
||||
|
||||
### MODULES
|
||||
#
|
||||
@@ -115,13 +139,22 @@ calamares_add_branding_subdirectory( branding/kaos_branding NAME kaos )
|
||||
#
|
||||
set(LIST_SKIPPED_MODULES "")
|
||||
|
||||
calamares_add_module_subdirectory( modules/freebsddisk LIST_SKIPPED_MODULES ) # C++ viewmodule
|
||||
calamares_add_module_subdirectory( modules/mobile LIST_SKIPPED_MODULES )
|
||||
calamares_add_module_subdirectory( modules/os-freebsd LIST_SKIPPED_MODULES )
|
||||
calamares_add_module_subdirectory( modules/os-nixos LIST_SKIPPED_MODULES )
|
||||
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/unpackfsc LIST_SKIPPED_MODULES )
|
||||
# calamares_add_module_subdirectory( modules/freebsddisk LIST_SKIPPED_MODULES ) # C++ viewmodule
|
||||
# calamares_add_module_subdirectory( modules/mobile LIST_SKIPPED_MODULES )
|
||||
# calamares_add_module_subdirectory( modules/os-freebsd LIST_SKIPPED_MODULES )
|
||||
# calamares_add_module_subdirectory( modules/os-nixos LIST_SKIPPED_MODULES )
|
||||
# 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/unpackfsc LIST_SKIPPED_MODULES )
|
||||
|
||||
calamares_add_module_subdirectory( modules/basestrap LIST_SKIPPED_MODULES )
|
||||
calamares_add_module_subdirectory( modules/services-artix LIST_SKIPPED_MODULES )
|
||||
# calamares_add_module_subdirectory( modules/services-runit LIST_SKIPPED_MODULES )
|
||||
# calamares_add_module_subdirectory( modules/services-dinit LIST_SKIPPED_MODULES )
|
||||
# calamares_add_module_subdirectory( modules/services-s6 LIST_SKIPPED_MODULES )
|
||||
calamares_add_module_subdirectory( modules/postcfg LIST_SKIPPED_MODULES )
|
||||
calamares_add_module_subdirectory( modules/packagechooser LIST_SKIPPED_MODULES )
|
||||
calamares_add_module_subdirectory( modules/packagechooserq LIST_SKIPPED_MODULES )
|
||||
|
||||
message(STATUS "Calamares extensions ${CALAMARES_EXTENSIONS_VERSION} for Calamares version ${Calamares_VERSION}")
|
||||
|
||||
|
||||
BIN
branding/artix/artix.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
branding/artix/banner.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
239
branding/artix/branding.desc
Normal file
@@ -0,0 +1,239 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Product branding information. This influences some global
|
||||
# user-visible aspects of Calamares, such as the product
|
||||
# name, window behavior, and the slideshow during installation.
|
||||
#
|
||||
# Additional styling can be done using the stylesheet.qss
|
||||
# file, also in the branding directory.
|
||||
---
|
||||
componentName: artix
|
||||
|
||||
|
||||
### WELCOME / OVERALL WORDING
|
||||
#
|
||||
# These settings affect some overall phrasing and looks,
|
||||
# which are most visible in the welcome page.
|
||||
|
||||
# This selects between different welcome texts. When false, uses
|
||||
# the traditional "Welcome to the %1 installer.", and when true,
|
||||
# uses "Welcome to the Calamares installer for %1." This allows
|
||||
# to distinguish this installer from other installers for the
|
||||
# same distribution.
|
||||
welcomeStyleCalamares: false
|
||||
|
||||
# Should the welcome image (productWelcome, below) be scaled
|
||||
# up beyond its natural size? If false, the image does not grow
|
||||
# with the window but remains the same size throughout (this
|
||||
# may have surprising effects on HiDPI monitors).
|
||||
welcomeExpandingLogo: true
|
||||
|
||||
### WINDOW CONFIGURATION
|
||||
#
|
||||
# The settings here affect the placement of the Calamares
|
||||
# window through hints to the window manager and initial
|
||||
# sizing of the Calamares window.
|
||||
|
||||
# Size and expansion policy for Calamares.
|
||||
# - "normal" or unset, expand as needed, use *windowSize*
|
||||
# - "fullscreen", start as large as possible, ignore *windowSize*
|
||||
# - "noexpand", don't expand automatically, use *windowSize*
|
||||
windowExpanding: normal
|
||||
|
||||
# Size of Calamares window, expressed as w,h. Both w and h
|
||||
# may be either pixels (suffix px) or font-units (suffix em).
|
||||
# e.g. "800px,600px"
|
||||
# "60em,480px"
|
||||
# This setting is ignored if "fullscreen" is selected for
|
||||
# *windowExpanding*, above. If not set, use constants defined
|
||||
# in CalamaresUtilsGui, 800x520.
|
||||
windowSize: 800px,520px
|
||||
|
||||
# Placement of Calamares window. Either "center" or "free".
|
||||
# Whether "center" actually works does depend on the window
|
||||
# manager in use (and only makes sense if you're not using
|
||||
# *windowExpanding* set to "fullscreen").
|
||||
windowPlacement: center
|
||||
|
||||
### PANELS CONFIGURATION
|
||||
#
|
||||
# Calamares has a main content area, and two panels (navigation
|
||||
# and progress / sidebar). The panels can be controlled individually,
|
||||
# or switched off. If both panels are switched off, the layout of
|
||||
# the main content area loses its margins, on the assumption that
|
||||
# you're doing something special.
|
||||
|
||||
# Kind of sidebar (panel on the left, showing progress).
|
||||
# - "widget" or unset, use traditional sidebar (logo, items)
|
||||
# - "none", hide it entirely
|
||||
# - "qml", use calamares-sidebar.qml from branding folder
|
||||
# In addition, you **may** specify a side, separated by a comma,
|
||||
# from the kind. Valid sides are:
|
||||
# - "left" (if not specified, uses this)
|
||||
# - "right"
|
||||
# - "top"
|
||||
# - "bottom"
|
||||
# For instance, "widget,right" is valid; so is "qml", which defaults
|
||||
# to putting the sidebar on the left. Also valid is "qml,top".
|
||||
# While "widget,top" is valid, the widgets code is **not** flexible
|
||||
# and results will be terrible.
|
||||
sidebar: widget
|
||||
|
||||
# Kind of navigation (button panel on the bottom).
|
||||
# - "widget" or unset, use traditional navigation
|
||||
# - "none", hide it entirely
|
||||
# - "qml", use calamares-navigation.qml from branding folder
|
||||
# In addition, you **may** specify a side, separated by a comma,
|
||||
# from the kind. The same sides are valid as for *sidebar*,
|
||||
# except the default is *bottom*.
|
||||
navigation: widget
|
||||
|
||||
|
||||
### STRINGS, IMAGES AND COLORS
|
||||
#
|
||||
# This section contains the "branding proper" of names
|
||||
# and images, rather than global-look settings.
|
||||
|
||||
# These are strings shown to the user in the user interface.
|
||||
# There is no provision for translating them -- since they
|
||||
# are names, the string is included as-is.
|
||||
#
|
||||
# The four Url strings are the Urls used by the buttons in
|
||||
# the welcome screen, and are not shown to the user. Clicking
|
||||
# on the "Support" button, for instance, opens the link supportUrl.
|
||||
# If a Url is empty, the corresponding button is not shown.
|
||||
#
|
||||
# bootloaderEntryName is how this installation / distro is named
|
||||
# in the boot loader (e.g. in the GRUB menu).
|
||||
#
|
||||
# These strings support substitution from /etc/os-release
|
||||
# if KDE Frameworks 5.58 are available at build-time. When
|
||||
# enabled, ${varname} is replaced by the equivalent value
|
||||
# from os-release. All the supported var-names are in all-caps,
|
||||
# and are listed on the FreeDesktop.org site,
|
||||
# https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
# Note that ANSI_COLOR and CPE_NAME don't make sense here, and
|
||||
# are not supported (the rest are). Remember to quote the string
|
||||
# if it contains substitutions, or you'll get YAML exceptions.
|
||||
#
|
||||
# The *Url* entries are used on the welcome page, and they
|
||||
# are visible as buttons there if the corresponding *show* keys
|
||||
# are set to "true" (they can also be overridden).
|
||||
strings:
|
||||
productName: Artix Linux
|
||||
shortProductName: Artix
|
||||
version: rolling
|
||||
shortVersion: rolling
|
||||
versionedName: Artix Linux "rolling"
|
||||
shortVersionedName: Artix rolling
|
||||
bootloaderEntryName: Artix
|
||||
productUrl: https://www.artixlinux.org/
|
||||
supportUrl: https://github.com/calamares/calamares/issues
|
||||
knownIssuesUrl: https://calamares.io/about/
|
||||
releaseNotesUrl: https://calamares.io/about/
|
||||
# donateUrl: https://kde.org/community/donations/index.php
|
||||
|
||||
# These images are loaded from the branding module directory.
|
||||
#
|
||||
# productBanner is an optional image, which if present, will be shown
|
||||
# on the welcome page of the application, above the welcome text.
|
||||
# It is intended to have a width much greater than height.
|
||||
# It is displayed at 64px height (also on HiDPI).
|
||||
# Recommended size is 64px tall, and up to 460px wide.
|
||||
# productIcon is used as the window icon, and will (usually) be used
|
||||
# by the window manager to represent the application. This image
|
||||
# should be square, and may be displayed by the window manager
|
||||
# as small as 16x16 (but possibly larger).
|
||||
# productLogo is used as the logo at the top of the left-hand column
|
||||
# which shows the steps to be taken. The image should be square,
|
||||
# and is displayed at 80x80 pixels (also on HiDPI).
|
||||
# productWallpaper is an optional image, which if present, will replace
|
||||
# the normal solid background on every page of the application.
|
||||
# It can be any size and proportion,
|
||||
# and will be tiled to fit the entire window.
|
||||
# For a non-tiled wallpaper, the size should be the same as
|
||||
# the overall window, see *windowSize* above (800x520).
|
||||
# productWelcome is shown on the welcome page of the application in
|
||||
# the middle of the window, below the welcome text. It can be
|
||||
# any size and proportion, and will be scaled to fit inside
|
||||
# the window. Use `welcomeExpandingLogo` to make it non-scaled.
|
||||
# Recommended size is 320x150.
|
||||
#
|
||||
# These filenames can also use substitutions from os-release (see above).
|
||||
images:
|
||||
productBanner: "banner.png"
|
||||
productIcon: "logo.png"
|
||||
productLogo: "logo.png"
|
||||
# productWallpaper: "wallpaper.png"
|
||||
productWelcome: "languages.png"
|
||||
|
||||
# Colors for text and background components.
|
||||
#
|
||||
# - SidebarBackground is the background of the sidebar
|
||||
# - SidebarText is the (foreground) text color
|
||||
# - SidebarBackgroundCurrent sets the background of the current step.
|
||||
# Optional, and defaults to the application palette.
|
||||
# - SidebarTextCurrent is the text color of the current step.
|
||||
#
|
||||
# These colors can **also** be set through the stylesheet, if the
|
||||
# branding component also ships a stylesheet.qss. Then they are
|
||||
# the corresponding CSS attributes of #sidebarApp.
|
||||
style:
|
||||
SidebarBackground: "#292F34"
|
||||
SidebarText: "#FFFFFF"
|
||||
SidebarTextCurrent: "#292F34"
|
||||
SidebarBackgroundCurrent: "#16a3f5"
|
||||
|
||||
### SLIDESHOW
|
||||
#
|
||||
# The slideshow is displayed during execution steps (e.g. when the
|
||||
# installer is actually writing to disk and doing other slow things).
|
||||
|
||||
# The slideshow can be a QML file (recommended) which can display
|
||||
# arbitrary things -- text, images, animations, or even play a game --
|
||||
# during the execution step. The QML **is** abruptly stopped when the
|
||||
# execution step is done, though, so maybe a game isn't a great idea.
|
||||
#
|
||||
# The slideshow can also be a sequence of images (not recommended unless
|
||||
# you don't want QML at all in your Calamares). The images are displayed
|
||||
# at a rate of 1 every 2 seconds during the execution step.
|
||||
#
|
||||
# To configure a QML file, list a single filename:
|
||||
# slideshow: "show.qml"
|
||||
# To configure images, like the filenames (here, as an inline list):
|
||||
# slideshow: [ "/etc/calamares/slideshow/0.png", "/etc/logo.png" ]
|
||||
slideshow: "show.qml"
|
||||
|
||||
# There are two available APIs for a QML slideshow:
|
||||
# - 1 (the default) loads the entire slideshow when the installation-
|
||||
# slideshow page is shown and starts the QML then. The QML
|
||||
# is never stopped (after installation is done, times etc.
|
||||
# continue to fire).
|
||||
# - 2 loads the slideshow on startup and calls onActivate() and
|
||||
# onLeave() in the root object. After the installation is done,
|
||||
# the show is stopped (first by calling onLeave(), then destroying
|
||||
# the QML components).
|
||||
#
|
||||
# An image slideshow does not need to have the API defined.
|
||||
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. Important bits are the host and port,
|
||||
# the scheme is not used.
|
||||
# - sizeLimit : Defines maximum size limit (in KiB) of log file to be pasted.
|
||||
# The option must be set, to have the log option work.
|
||||
# Takes integer as input. If < 0, no limit will be forced,
|
||||
# else only last (approximately) 'n' KiB of log file will be pasted.
|
||||
# Please note that upload size may be slightly over the limit (due
|
||||
# to last minute logging), so provide a suitable value.
|
||||
uploadServer :
|
||||
type : "fiche"
|
||||
url : "http://termbin.com:9999"
|
||||
sizeLimit : -1
|
||||
BIN
branding/artix/browsers.png
Normal file
|
After Width: | Height: | Size: 399 KiB |
BIN
branding/artix/customise.png
Normal file
|
After Width: | Height: | Size: 818 KiB |
BIN
branding/artix/desktops.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
branding/artix/languages.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
2
branding/artix/languages.png.license
Normal file
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
BIN
branding/artix/logo.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
branding/artix/multimedia.png
Normal file
|
After Width: | Height: | Size: 363 KiB |
BIN
branding/artix/office.png
Normal file
|
After Width: | Height: | Size: 359 KiB |
BIN
branding/artix/packages1.png
Normal file
|
After Width: | Height: | Size: 873 KiB |
BIN
branding/artix/packages2.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
branding/artix/productivity.png
Normal file
|
After Width: | Height: | Size: 502 KiB |
BIN
branding/artix/rolling.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
178
branding/artix/show.qml
Normal file
@@ -0,0 +1,178 @@
|
||||
/* === This file is part of Calamares - <http://github.com/calamares> ===
|
||||
*
|
||||
* Copyright 2015, Teo Mrnjavac <teo@kde.org>
|
||||
* Copyright 2016, Luca Giambonini <almack@chakralinux.org>
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Calamares is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0;
|
||||
import calamares.slideshow 1.0;
|
||||
|
||||
/* Tested with slide images of 1800x1200 and 1632x1248 pixels */
|
||||
|
||||
Presentation
|
||||
{
|
||||
id: presentation
|
||||
|
||||
Timer {
|
||||
interval: 10000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: presentation.goToNextSlide()
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic0
|
||||
source: "thanks.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic00
|
||||
source: "artix.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic02
|
||||
source: "tools.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic03
|
||||
source: "desktops.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic04
|
||||
source: "productivity.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic06
|
||||
source: "browsers.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic07
|
||||
source: "office.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic08
|
||||
source: "multimedia.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic09
|
||||
source: "web.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic11
|
||||
source: "packages1.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic13
|
||||
source: "packages2.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic15
|
||||
source: "customise.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
Image {
|
||||
id: pic16
|
||||
source: "rolling.png"
|
||||
width: parent.width * 1.12; height: parent.height * 1.5
|
||||
fillMode: Image.Stretch
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -20
|
||||
}
|
||||
}
|
||||
}
|
||||
96
branding/artix/stylesheet.qss
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: no
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
/*
|
||||
A branding component can ship a stylesheet (like this one)
|
||||
which is applied to parts of the Calamares user-interface.
|
||||
In principle, all parts can be styled through CSS.
|
||||
Missing parts should be filed as issues.
|
||||
|
||||
The IDs are based on the object names in the C++ code.
|
||||
You can use the Debug Dialog to find out object names:
|
||||
- Open the debug dialog
|
||||
- Choose tab *Tools*
|
||||
- Click *Widget Tree* button
|
||||
The list of object names is printed in the log.
|
||||
|
||||
Documentation for styling Qt Widgets through a stylesheet
|
||||
can be found at
|
||||
https://doc.qt.io/qt-5/stylesheet-examples.html
|
||||
https://doc.qt.io/qt-5/stylesheet-reference.html
|
||||
In Calamares, styling widget classes is supported (e.g.
|
||||
using `QComboBox` as a selector).
|
||||
|
||||
This example stylesheet has all the actual styling commented out.
|
||||
The examples are not exhaustive.
|
||||
|
||||
*/
|
||||
|
||||
/*** Generic Widgets.
|
||||
*
|
||||
* You can style **all** widgets of a given class by selecting
|
||||
* the class name. Some widgets have specialized sub-selectors.
|
||||
*/
|
||||
|
||||
/*
|
||||
QPushButton { background-color: green; }
|
||||
*/
|
||||
|
||||
/*** Main application window.
|
||||
*
|
||||
* The main application window has the sidebar, which in turn
|
||||
* contains a logo and a list of items -- note that the list
|
||||
* can **not** be styled, since it has its own custom C++
|
||||
* delegate code.
|
||||
*/
|
||||
|
||||
/*
|
||||
#mainApp { }
|
||||
#sidebarApp { }
|
||||
#logoApp { }
|
||||
*/
|
||||
|
||||
/*** Welcome module.
|
||||
*
|
||||
* There are plenty of parts, but the buttons are the most interesting
|
||||
* ones (donate, release notes, ...). The little icon image can be
|
||||
* styled through *qproperty-icon*, which is a little obscure.
|
||||
* URLs can reference the QRC paths of the Calamares application
|
||||
* or loaded via plugins or within the filesystem. There is no
|
||||
* comprehensive list of available icons, though.
|
||||
*/
|
||||
|
||||
/*
|
||||
QPushButton#aboutButton { qproperty-icon: url(:/data/images/release.svg); }
|
||||
#donateButton,
|
||||
#supportButton,
|
||||
#releaseNotesButton,
|
||||
#knownIssuesButton { qproperty-icon: url(:/data/images/help.svg); }
|
||||
*/
|
||||
|
||||
/*** Partitioning module.
|
||||
*
|
||||
* Many moving parts, which you will need to experiment with.
|
||||
*/
|
||||
|
||||
/*
|
||||
#bootInfoIcon { }
|
||||
#bootInfoLable { }
|
||||
#deviceInfoIcon { }
|
||||
#defineInfoLabel { }
|
||||
#scrollAreaWidgetContents { }
|
||||
#partitionBarView { }
|
||||
*/
|
||||
|
||||
/*** Licensing module.
|
||||
*
|
||||
* The licensing module paints individual widgets for each of
|
||||
* the licenses. The item can be collapsed or expanded.
|
||||
*/
|
||||
|
||||
/*
|
||||
#licenseItem { }
|
||||
#licenseItemFullText { }
|
||||
*/
|
||||
BIN
branding/artix/thanks.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
branding/artix/tools.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
branding/artix/web.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
75
modules/basestrap/basestrap.conf
Normal file
@@ -0,0 +1,75 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# The configuration for the package manager starts with the
|
||||
# *backend* key, which picks one of the backends to use.
|
||||
# In `main.py` there is a base class `PackageManager`.
|
||||
# Implementations must subclass that and set a (class-level)
|
||||
# property *backend* to the name of the backend (e.g. "dummy").
|
||||
# That property is used to match against the *backend* key here.
|
||||
#
|
||||
# You will have to add such a class for your package manager.
|
||||
# It is fairly simple Python code. The API is described in the
|
||||
# abstract methods in class `PackageManager`. Mostly, the only
|
||||
# trick is to figure out the correct commands to use, and in particular,
|
||||
# whether additional switches are required or not. Some package managers
|
||||
# have more installer-friendly defaults than others, e.g., DNF requires
|
||||
# passing --disablerepo=* -C to allow removing packages without Internet
|
||||
# connectivity, and it also returns an error exit code if the package did
|
||||
# not exist to begin with.
|
||||
---
|
||||
#
|
||||
# Which package manager to use, options are:
|
||||
# - pacman - Pacman
|
||||
#
|
||||
# Not actually a package manager, but suitable for testing:
|
||||
# - dummy - Dummy manager, only logs
|
||||
#
|
||||
backend: dummy
|
||||
|
||||
# pacman specific options
|
||||
#
|
||||
# *num_retries* should be a positive integer which specifies the
|
||||
# number of times the call to pacman will be retried in the event of a
|
||||
# failure. If it is missing, it will be set to 0.
|
||||
#
|
||||
# *disable_download_timeout* is a boolean that, when true, includes
|
||||
# the flag --disable-download-timeout on calls to pacman. When missing,
|
||||
# false is assumed.
|
||||
#
|
||||
# *needed_only* is a boolean that includes the pacman argument --needed
|
||||
# when set to true. If missing, false is assumed.
|
||||
# *handle_keyrings* is a boolean that includes initializing and populating keyrings
|
||||
# when set to true. If missing, false is assumed.
|
||||
|
||||
pacman:
|
||||
num_retries: 0
|
||||
disable_download_timeout: false
|
||||
needed_only: false
|
||||
handle_keyrings: false
|
||||
copy_pacconf: false
|
||||
requirements:
|
||||
- name: /etc
|
||||
mode: "0o755"
|
||||
- name: /var
|
||||
mode: "0o755"
|
||||
- name: /var/cache
|
||||
mode: "0o755"
|
||||
- name: /var/cache/pacman
|
||||
mode: "0o755"
|
||||
- name: /var/cache/pacman/pkg
|
||||
mode: "0o755"
|
||||
- name: /var/lib
|
||||
mode: "0o755"
|
||||
- name: /var/lib/pacman
|
||||
mode: "0o755"
|
||||
keyrings:
|
||||
- artix
|
||||
|
||||
# the artix base package allows selection of the init system tied to elogind
|
||||
# this option is artix specific
|
||||
# base_init: elogind
|
||||
|
||||
operations:
|
||||
- install:
|
||||
- base
|
||||
552
modules/basestrap/main.py
Normal file
@@ -0,0 +1,552 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# === This file is part of Calamares - <https://calamares.io> ===
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2014 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2015-2017 Teo Mrnjavac <teo@kde.org>
|
||||
# SPDX-FileCopyrightText: 2016-2017 Kyle Robbertze <kyle@aims.ac.za>
|
||||
# SPDX-FileCopyrightText: 2017 Alf Gaida <agaida@siduction.org>
|
||||
# SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
|
||||
# SPDX-FileCopyrightText: 2018 Philip Müller <philm@manjaro.org>
|
||||
# SPDX-FileCopyrightText: 2023 Artoo <artoo@artixlinux.org>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Calamares is Free Software: see the License-Identifier above.
|
||||
#
|
||||
|
||||
import abc
|
||||
from string import Template
|
||||
import os, shutil, subprocess, sys
|
||||
|
||||
import libcalamares
|
||||
from libcalamares.utils import host_env_process_output, target_env_process_output
|
||||
from libcalamares.utils import gettext_path, gettext_languages
|
||||
from os.path import join
|
||||
|
||||
import gettext
|
||||
_translation = gettext.translation("calamares-python",
|
||||
localedir=gettext_path(),
|
||||
languages=gettext_languages(),
|
||||
fallback=True)
|
||||
_ = _translation.gettext
|
||||
_n = _translation.ngettext
|
||||
|
||||
|
||||
total_packages = 0 # For the entire job
|
||||
completed_packages = 0 # Done so far for this job
|
||||
group_packages = 0 # One group of packages from an -install or -remove entry
|
||||
|
||||
# A PM object may set this to a string (take care of translations!)
|
||||
# to override the string produced by pretty_status_message()
|
||||
custom_status_message = None
|
||||
|
||||
INSTALL = object()
|
||||
REMOVE = object()
|
||||
mode_packages = None # Changes to INSTALL or REMOVE
|
||||
|
||||
|
||||
def _change_mode(mode):
|
||||
global mode_packages
|
||||
mode_packages = mode
|
||||
libcalamares.job.setprogress(completed_packages * 1.0 / total_packages)
|
||||
|
||||
|
||||
def pretty_name():
|
||||
return _("Install packages.")
|
||||
|
||||
|
||||
def pretty_status_message():
|
||||
if custom_status_message is not None:
|
||||
return custom_status_message
|
||||
if not group_packages:
|
||||
if (total_packages > 0):
|
||||
# Outside the context of an operation
|
||||
s = _("Processing packages (%(count)d / %(total)d)")
|
||||
else:
|
||||
s = _("Install packages.")
|
||||
|
||||
elif mode_packages is INSTALL:
|
||||
s = _n("Installing one package.",
|
||||
"Installing %(num)d packages.", group_packages)
|
||||
elif mode_packages is REMOVE:
|
||||
s = _n("Removing one package.",
|
||||
"Removing %(num)d packages.", group_packages)
|
||||
else:
|
||||
# No mode, generic description
|
||||
s = _("Install packages.")
|
||||
|
||||
return s % {"num": group_packages,
|
||||
"count": completed_packages,
|
||||
"total": total_packages}
|
||||
|
||||
|
||||
|
||||
class PackageManager(metaclass=abc.ABCMeta):
|
||||
"""
|
||||
Package manager base class. A subclass implements package management
|
||||
for a specific backend, and must have a class property `backend`
|
||||
with the string identifier for that backend.
|
||||
|
||||
Subclasses are collected below to populate the list of possible
|
||||
backends.
|
||||
"""
|
||||
backend = None
|
||||
|
||||
@abc.abstractmethod
|
||||
def install(self, pkgs, from_local=False):
|
||||
"""
|
||||
Install a list of packages (named) into the system.
|
||||
Although this handles lists, in practice it is called
|
||||
with one package at a time.
|
||||
|
||||
@param pkgs: list[str]
|
||||
list of package names
|
||||
@param from_local: bool
|
||||
if True, then these are local packages (on disk) and the
|
||||
pkgs names are paths.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def remove(self, pkgs):
|
||||
"""
|
||||
Removes packages.
|
||||
|
||||
@param pkgs: list[str]
|
||||
list of package names
|
||||
"""
|
||||
pass
|
||||
|
||||
def run(self, script):
|
||||
if script != "":
|
||||
host_env_process_output(script.split(" "))
|
||||
|
||||
def install_package(self, packagedata, from_local=False):
|
||||
"""
|
||||
Install a package from a single entry in the install list.
|
||||
This can be either a single package name, or an object
|
||||
with pre- and post-scripts. If @p packagedata is a dict,
|
||||
it is assumed to follow the documented structure.
|
||||
|
||||
@param packagedata: str|dict
|
||||
@param from_local: bool
|
||||
see install.from_local
|
||||
"""
|
||||
if isinstance(packagedata, str):
|
||||
self.install([packagedata], from_local=from_local)
|
||||
else:
|
||||
self.run(packagedata["pre-script"])
|
||||
self.install([packagedata["package"]], from_local=from_local)
|
||||
self.run(packagedata["post-script"])
|
||||
|
||||
def remove_package(self, packagedata):
|
||||
"""
|
||||
Remove a package from a single entry in the remove list.
|
||||
This can be either a single package name, or an object
|
||||
with pre- and post-scripts. If @p packagedata is a dict,
|
||||
it is assumed to follow the documented structure.
|
||||
|
||||
@param packagedata: str|dict
|
||||
"""
|
||||
if isinstance(packagedata, str):
|
||||
self.remove([packagedata])
|
||||
else:
|
||||
self.run(packagedata["pre-script"])
|
||||
self.remove([packagedata["package"]])
|
||||
self.run(packagedata["post-script"])
|
||||
|
||||
def operation_install(self, package_list, from_local=False):
|
||||
"""
|
||||
Installs the list of packages named in @p package_list .
|
||||
These can be strings -- plain package names -- or
|
||||
structures (with a pre- and post-install step).
|
||||
|
||||
This operation is called for "critical" packages,
|
||||
which are expected to succeed, or fail, all together.
|
||||
However, if there are packages with pre- or post-scripts,
|
||||
then packages are installed one-by-one instead.
|
||||
|
||||
NOTE: package managers may reimplement this method
|
||||
NOTE: exceptions are expected to leave this method, to indicate
|
||||
failure of the installation.
|
||||
"""
|
||||
if all([isinstance(x, str) for x in package_list]):
|
||||
self.install(package_list, from_local=from_local)
|
||||
else:
|
||||
for package in package_list:
|
||||
self.install_package(package, from_local=from_local)
|
||||
|
||||
def operation_try_install(self, package_list):
|
||||
"""
|
||||
Installs the list of packages named in @p package_list .
|
||||
These can be strings -- plain package names -- or
|
||||
structures (with a pre- and post-install step).
|
||||
|
||||
This operation is called for "non-critical" packages,
|
||||
which can succeed or fail without affecting the overall installation.
|
||||
Packages are installed one-by-one to support package managers
|
||||
that do not have a "install as much as you can" mode.
|
||||
|
||||
NOTE: package managers may reimplement this method
|
||||
NOTE: no package-installation exceptions should be raised
|
||||
"""
|
||||
# we make a separate package manager call for each package so a
|
||||
# single failing package won't stop all of them
|
||||
for package in package_list:
|
||||
try:
|
||||
self.install_package(package)
|
||||
except subprocess.CalledProcessError:
|
||||
libcalamares.utils.warning("Could not install package %s" % package)
|
||||
|
||||
def operation_remove(self, package_list):
|
||||
"""
|
||||
Removes the list of packages named in @p package_list .
|
||||
These can be strings -- plain package names -- or
|
||||
structures (with a pre- and post-install step).
|
||||
|
||||
This operation is called for "critical" packages, which are
|
||||
expected to succeed or fail all together.
|
||||
However, if there are packages with pre- or post-scripts,
|
||||
then packages are removed one-by-one instead.
|
||||
|
||||
NOTE: package managers may reimplement this method
|
||||
NOTE: exceptions should be raised to indicate failure
|
||||
"""
|
||||
if all([isinstance(x, str) for x in package_list]):
|
||||
self.remove(package_list)
|
||||
else:
|
||||
for package in package_list:
|
||||
self.remove_package(package)
|
||||
|
||||
def operation_try_remove(self, package_list):
|
||||
"""
|
||||
Same relation as try_install has to install, except it removes
|
||||
packages instead. Packages are removed one-by-one.
|
||||
|
||||
NOTE: package managers may reimplement this method
|
||||
NOTE: no package-installation exceptions should be raised
|
||||
"""
|
||||
for package in package_list:
|
||||
try:
|
||||
self.remove_package(package)
|
||||
except subprocess.CalledProcessError:
|
||||
libcalamares.utils.warning("Could not remove package %s" % package)
|
||||
|
||||
### PACKAGE MANAGER IMPLEMENTATIONS
|
||||
#
|
||||
# Keep these alphabetical (presumably both by class name and backend name),
|
||||
# even the Dummy implementation.
|
||||
#
|
||||
|
||||
class PMPacman(PackageManager):
|
||||
backend = "pacman"
|
||||
|
||||
def __init__(self):
|
||||
import re
|
||||
progress_match = re.compile("^\\((\\d+)/(\\d+)\\)")
|
||||
|
||||
def line_cb(line):
|
||||
if line.startswith(":: "):
|
||||
self.in_package_changes = "package" in line or "hooks" in line
|
||||
else:
|
||||
if self.in_package_changes and line.endswith("...\n"):
|
||||
# Update the message, untranslated; do not change the
|
||||
# progress percentage, since there may be more "installing..."
|
||||
# lines in the output for the group, than packages listed
|
||||
# explicitly. We don't know how to calculate proper progress.
|
||||
global custom_status_message
|
||||
custom_status_message = "pacman: " + line.strip()
|
||||
libcalamares.job.setprogress(self.progress_fraction)
|
||||
libcalamares.utils.debug(line)
|
||||
|
||||
self.in_package_changes = False
|
||||
self.line_cb = line_cb
|
||||
|
||||
pacman = libcalamares.job.configuration.get("pacman", None)
|
||||
if pacman is None:
|
||||
pacman = dict()
|
||||
if type(pacman) is not dict:
|
||||
libcalamares.utils.warning("Job configuration *pacman* will be ignored.")
|
||||
pacman = dict()
|
||||
self.pacman_num_retries = pacman.get("num_retries", 0)
|
||||
self.pacman_disable_timeout = pacman.get("disable_download_timeout", False)
|
||||
self.pacman_needed_only = pacman.get("needed_only", False)
|
||||
self.pacman_key = pacman.get("handle_keyrings", False)
|
||||
self.pacman_pacconf = pacman.get("copy_pacconf", False)
|
||||
self.pacman_requirements = pacman.get("requirements", [])
|
||||
self.pacman_keyrings = pacman.get("keyrings", [])
|
||||
|
||||
def reset_progress(self):
|
||||
self.in_package_changes = False
|
||||
# These are globals
|
||||
self.progress_fraction = (completed_packages * 1.0 / total_packages)
|
||||
|
||||
def run_pacman(self, command, callback=False):
|
||||
"""
|
||||
Call pacman in a loop until it is successful or the number of retries is exceeded
|
||||
:param command: The pacman command to run
|
||||
:param callback: An optional boolean that indicates if this pacman run should use the callback
|
||||
:return:
|
||||
"""
|
||||
|
||||
pacman_count = 0
|
||||
while pacman_count <= self.pacman_num_retries:
|
||||
pacman_count += 1
|
||||
try:
|
||||
if False: # callback:
|
||||
host_env_process_output(command, self.line_cb)
|
||||
else:
|
||||
host_env_process_output(command)
|
||||
|
||||
return
|
||||
except subprocess.CalledProcessError:
|
||||
if pacman_count <= self.pacman_num_retries:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
def install(self, pkgs, from_local=False):
|
||||
|
||||
install_root = libcalamares.globalstorage.value("rootMountPoint")
|
||||
|
||||
cal_umask = os.umask(0)
|
||||
for target in self.pacman_requirements:
|
||||
dest = install_root + target["name"]
|
||||
if not os.path.exists(dest):
|
||||
mod = int(target["mode"],8)
|
||||
os.mkdir(dest, mode=mod)
|
||||
libcalamares.utils.debug("Mode: {!s}".format(oct(mod)))
|
||||
libcalamares.utils.debug("Created: {!s}".format(dest))
|
||||
|
||||
path = join(install_root, "run")
|
||||
os.chmod(path, 0o755)
|
||||
os.umask(cal_umask)
|
||||
|
||||
f = "etc/resolv.conf"
|
||||
if os.path.exists(join("/",f)):
|
||||
shutil.copy2(join("/",f), join(install_root, f))
|
||||
|
||||
command = ["pacman"]
|
||||
|
||||
cachedir = join(install_root, "var/cache/pacman/pkg")
|
||||
dbdir = join(install_root, "var/lib/pacman")
|
||||
pacman_args = ["--root", install_root, "--dbpath", dbdir, "--cachedir", cachedir]
|
||||
command.extend(pacman_args)
|
||||
|
||||
# Don't ask for user intervention, take the default action
|
||||
command.append("--noconfirm")
|
||||
|
||||
# Don't report download progress for each file
|
||||
command.append("--noprogressbar")
|
||||
|
||||
if self.pacman_needed_only is True:
|
||||
command.append("--needed")
|
||||
|
||||
if self.pacman_disable_timeout is True:
|
||||
command.append("--disable-download-timeout")
|
||||
|
||||
if from_local:
|
||||
command.append("-U")
|
||||
else:
|
||||
command.append("-Sy")
|
||||
|
||||
command += pkgs
|
||||
|
||||
libcalamares.utils.debug("Command: {!s}".format(command))
|
||||
|
||||
self.reset_progress()
|
||||
self.run_pacman(command, True)
|
||||
|
||||
if self.pacman_key:
|
||||
self.init_keyring()
|
||||
self.populate_keyring()
|
||||
|
||||
if self.pacman_pacconf:
|
||||
f = "etc/pacman.conf"
|
||||
if os.path.exists(join("/",f)):
|
||||
shutil.copy2(join("/",f), join(install_root, f))
|
||||
|
||||
def remove(self, pkgs):
|
||||
self.reset_progress()
|
||||
self.run_pacman(["pacman", "-Rs", "--noconfirm"] + pkgs, True)
|
||||
|
||||
def init_keyring(self):
|
||||
target_env_process_output(["pacman-key", "--init"])
|
||||
|
||||
def populate_keyring(self):
|
||||
target_env_process_output(["pacman-key", "--populate"] + self.pacman_keyrings)
|
||||
|
||||
# Collect all the subclasses of PackageManager defined above,
|
||||
# and index them based on the backend property of each class.
|
||||
backend_managers = [
|
||||
(c.backend, c)
|
||||
for c in globals().values()
|
||||
if type(c) is abc.ABCMeta and issubclass(c, PackageManager) and c.backend]
|
||||
|
||||
|
||||
def subst_locale(plist):
|
||||
"""
|
||||
Returns a locale-aware list of packages, based on @p plist.
|
||||
Package names that contain LOCALE are localized with the
|
||||
BCP47 name of the chosen system locale; if the system
|
||||
locale is 'en' (e.g. English, US) then these localized
|
||||
packages are dropped from the list.
|
||||
|
||||
@param plist: list[str|dict]
|
||||
Candidate packages to install.
|
||||
@return: list[str|dict]
|
||||
"""
|
||||
locale = libcalamares.globalstorage.value("locale")
|
||||
if not locale:
|
||||
# It is possible to skip the locale-setting entirely.
|
||||
# Then pretend it is "en", so that {LOCALE}-decorated
|
||||
# package names are removed from the list.
|
||||
locale = "en"
|
||||
|
||||
ret = []
|
||||
for packagedata in plist:
|
||||
if isinstance(packagedata, str):
|
||||
packagename = packagedata
|
||||
else:
|
||||
packagename = packagedata["package"]
|
||||
|
||||
# Update packagename: substitute LOCALE, and drop packages
|
||||
# if locale is en and LOCALE is in the package name.
|
||||
if locale != "en":
|
||||
packagename = Template(packagename).safe_substitute(LOCALE=locale)
|
||||
elif 'LOCALE' in packagename:
|
||||
packagename = None
|
||||
|
||||
if packagename is not None:
|
||||
# Put it back in packagedata
|
||||
if isinstance(packagedata, str):
|
||||
packagedata = packagename
|
||||
else:
|
||||
packagedata["package"] = packagename
|
||||
|
||||
ret.append(packagedata)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def run_operations(pkgman, entry):
|
||||
"""
|
||||
Call package manager with suitable parameters for the given
|
||||
package actions.
|
||||
|
||||
:param pkgman: PackageManager
|
||||
This is the manager that does the actual work.
|
||||
:param entry: dict
|
||||
Keys are the actions -- e.g. "install" -- to take, and the values
|
||||
are the (list of) packages to apply the action to. The actions are
|
||||
not iterated in a specific order, so it is recommended to use only
|
||||
one action per dictionary. The list of packages may be package
|
||||
names (strings) or package information dictionaries with pre-
|
||||
and post-scripts.
|
||||
"""
|
||||
global group_packages, completed_packages, mode_packages
|
||||
|
||||
for key in entry.keys():
|
||||
package_list = subst_locale(entry[key])
|
||||
group_packages = len(package_list)
|
||||
if key == "install":
|
||||
_change_mode(INSTALL)
|
||||
pkgman.operation_install(package_list)
|
||||
elif key == "try_install":
|
||||
_change_mode(INSTALL)
|
||||
pkgman.operation_try_install(package_list)
|
||||
elif key == "remove":
|
||||
_change_mode(REMOVE)
|
||||
pkgman.operation_remove(package_list)
|
||||
elif key == "try_remove":
|
||||
_change_mode(REMOVE)
|
||||
pkgman.operation_try_remove(package_list)
|
||||
elif key == "localInstall":
|
||||
_change_mode(INSTALL)
|
||||
pkgman.operation_install(package_list, from_local=True)
|
||||
elif key == "source":
|
||||
libcalamares.utils.debug("Package-list from {!s}".format(entry[key]))
|
||||
else:
|
||||
libcalamares.utils.warning("Unknown package-operation key {!s}".format(key))
|
||||
completed_packages += len(package_list)
|
||||
libcalamares.job.setprogress(completed_packages * 1.0 / total_packages)
|
||||
libcalamares.utils.debug("Pretty name: {!s}, setting progress..".format(pretty_name()))
|
||||
|
||||
group_packages = 0
|
||||
_change_mode(None)
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Calls routine with detected package manager to install locale packages
|
||||
or remove drivers not needed on the installed system.
|
||||
|
||||
:return:
|
||||
"""
|
||||
global mode_packages, total_packages, completed_packages, group_packages
|
||||
|
||||
backend = libcalamares.job.configuration.get("backend")
|
||||
|
||||
for identifier, impl in backend_managers:
|
||||
if identifier == backend:
|
||||
pkgman = impl()
|
||||
break
|
||||
else:
|
||||
return "Bad backend", "backend=\"{}\"".format(backend)
|
||||
|
||||
if not libcalamares.globalstorage.value("hasInternet"):
|
||||
libcalamares.utils.warning( "Package installation has been skipped: no internet" )
|
||||
return None
|
||||
|
||||
operations = libcalamares.job.configuration.get("operations", [])
|
||||
# if libcalamares.globalstorage.contains("packageOperations"):
|
||||
# operations += libcalamares.globalstorage.value("packageOperations")
|
||||
|
||||
if libcalamares.job.configuration.get("base_init"):
|
||||
base_init = libcalamares.job.configuration.get("base_init", None)
|
||||
|
||||
if libcalamares.globalstorage.contains("netinstallAdd"):
|
||||
data = libcalamares.globalstorage.value("netinstallAdd")
|
||||
init_provider = data[0]["name"]
|
||||
libcalamares.utils.debug("Init provider: {!s}".format(init_provider))
|
||||
|
||||
if base_init is not None:
|
||||
init_pkg = "-".join([base_init, init_provider])
|
||||
libcalamares.utils.debug("Package added: {!s}".format(init_pkg))
|
||||
operations[0]["install"].append(init_pkg)
|
||||
|
||||
if init_provider is not None:
|
||||
libcalamares.globalstorage.insert("initProvider", init_provider)
|
||||
|
||||
libcalamares.globalstorage.insert("packageOperationsBasestrap", operations)
|
||||
|
||||
mode_packages = None
|
||||
total_packages = 0
|
||||
completed_packages = 0
|
||||
for op in operations:
|
||||
for packagelist in op.values():
|
||||
total_packages += len(subst_locale(packagelist))
|
||||
|
||||
if not total_packages:
|
||||
# Avoids potential divide-by-zero in progress reporting
|
||||
return None
|
||||
|
||||
for entry in operations:
|
||||
group_packages = 0
|
||||
libcalamares.utils.debug(pretty_name())
|
||||
try:
|
||||
run_operations(pkgman, entry)
|
||||
except subprocess.CalledProcessError as e:
|
||||
libcalamares.utils.warning(str(e))
|
||||
libcalamares.utils.debug("stdout:" + str(e.stdout))
|
||||
libcalamares.utils.debug("stderr:" + str(e.stderr))
|
||||
return (_("Package Manager error"),
|
||||
_("The package manager could not make changes to the installed system. The command <pre>{!s}</pre> returned error code {!s}.")
|
||||
.format(e.cmd, e.returncode))
|
||||
|
||||
mode_packages = None
|
||||
|
||||
libcalamares.job.setprogress(1.0)
|
||||
|
||||
return None
|
||||
7
modules/basestrap/module.desc
Normal file
@@ -0,0 +1,7 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
type: "job"
|
||||
name: "basestrap"
|
||||
interface: "python"
|
||||
script: "main.py"
|
||||
@@ -1,17 +0,0 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
if( NOT Calamares_WITH_QML )
|
||||
calamares_skip_module( "freebsddisk (QML is not supported in this build)" )
|
||||
return()
|
||||
endif()
|
||||
|
||||
calamares_add_plugin( freebsddisk
|
||||
TYPE viewmodule
|
||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||
SOURCES
|
||||
FreeBSDDiskViewStep.cpp
|
||||
RESOURCES
|
||||
freebsddisk.qrc
|
||||
SHARED_LIB
|
||||
)
|
||||
@@ -1,42 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Calamares is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSES/GPL-3.0
|
||||
*/
|
||||
|
||||
#include "FreeBSDDiskViewStep.h"
|
||||
|
||||
FreeBSDDiskViewStep::FreeBSDDiskViewStep( QObject* parent )
|
||||
: Calamares::QmlViewStep( parent )
|
||||
{
|
||||
}
|
||||
|
||||
FreeBSDDiskViewStep::~FreeBSDDiskViewStep() {}
|
||||
|
||||
QString
|
||||
FreeBSDDiskViewStep::prettyName() const
|
||||
{
|
||||
return tr( "Disk Setup" );
|
||||
}
|
||||
|
||||
void
|
||||
FreeBSDDiskViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last
|
||||
}
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( FreeBSDDiskViewStepFactory, registerPlugin< FreeBSDDiskViewStep >(); )
|
||||
@@ -1,44 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Calamares is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSES/GPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FREEBSDDISKVIEWSTEP_H
|
||||
#define FREEBSDDISKVIEWSTEP_H
|
||||
|
||||
#include "DllMacro.h"
|
||||
#include "utils/PluginFactory.h"
|
||||
#include "viewpages/QmlViewStep.h"
|
||||
|
||||
class PLUGINDLLEXPORT FreeBSDDiskViewStep : public Calamares::QmlViewStep
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FreeBSDDiskViewStep( QObject* parent = nullptr );
|
||||
virtual ~FreeBSDDiskViewStep() override;
|
||||
|
||||
QString prettyName() const override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( FreeBSDDiskViewStepFactory )
|
||||
|
||||
#endif
|
||||
@@ -1,10 +0,0 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# The *freebsddisk* module can be used to pick a disk
|
||||
# as an installer step. This module supports ZFSroot
|
||||
# on one whole disk, and UFSroot on one whole disk.
|
||||
#
|
||||
---
|
||||
qmlSearch: both
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Calamares is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSES/GPL-3.0
|
||||
*/
|
||||
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.1
|
||||
|
||||
Item {
|
||||
Text {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
text: "Select a disk on which to install FreeBSD."
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file alias="freebsddisk.qml">freebsddisk.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -1,15 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
calamares_add_plugin( mobile
|
||||
TYPE viewmodule
|
||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||
SOURCES
|
||||
Config.cpp
|
||||
MobileQmlViewStep.cpp
|
||||
PartitionJob.cpp
|
||||
UsersJob.cpp
|
||||
RESOURCES
|
||||
mobile.qrc
|
||||
SHARED_LIB
|
||||
)
|
||||
@@ -1,221 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#include "Config.h"
|
||||
#include "PartitionJob.h"
|
||||
#include "UsersJob.h"
|
||||
|
||||
#include "ViewManager.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
Config::Config( QObject* parent )
|
||||
: QObject( parent )
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Config::setConfigurationMap( const QVariantMap& cfgMap )
|
||||
{
|
||||
using namespace CalamaresUtils;
|
||||
|
||||
if ( getBool( cfgMap, "bogus", false ) )
|
||||
{
|
||||
cWarning() << "Configuration key \"bogus\" is still set for *mobile*";
|
||||
}
|
||||
|
||||
m_osName = getString( cfgMap, "osName", "(unknown)" );
|
||||
m_arch = getString( cfgMap, "arch", "(unknown)" );
|
||||
m_device = getString( cfgMap, "device", "(unknown)" );
|
||||
m_userInterface = getString( cfgMap, "userInterface", "(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_userPasswordNumeric = getBool( cfgMap, "userPasswordNumeric", true );
|
||||
|
||||
m_builtinVirtualKeyboard = getBool( cfgMap, "builtinVirtualKeyboard", true );
|
||||
|
||||
m_featureSshd = getBool( cfgMap, "featureSshd", true );
|
||||
m_featureFsType = getBool( cfgMap, "featureFsType", false );
|
||||
|
||||
m_cmdLuksFormat = getString( cfgMap, "cmdLuksFormat", "cryptsetup luksFormat --use-random" );
|
||||
m_cmdLuksOpen = getString( cfgMap, "cmdLuksOpen", "cryptsetup luksOpen" );
|
||||
m_cmdMount = getString( cfgMap, "cmdMount", "mount" );
|
||||
m_targetDeviceRoot = getString( cfgMap, "targetDeviceRoot", "/dev/unknown" );
|
||||
m_targetDeviceRootInternal = getString( cfgMap, "targetDeviceRootInternal", "" );
|
||||
|
||||
m_cmdMkfsRootBtrfs = getString( cfgMap, "cmdMkfsRootBtrfs", "mkfs.btrfs -L 'unknownOS_root'" );
|
||||
m_cmdMkfsRootExt4 = getString( cfgMap, "cmdMkfsRootExt4", "mkfs.ext4 -L 'unknownOS_root'" );
|
||||
m_cmdMkfsRootF2fs = getString( cfgMap, "cmdMkfsRootF2fs", "mkfs.f2fs -l 'unknownOS_root'" );
|
||||
m_fsList = getStringList( cfgMap, "fsModel", QStringList { "ext4", "f2fs", "btrfs" } );
|
||||
m_defaultFs = getString( cfgMap, "defaultFs", "ext4" );
|
||||
m_fsIndex = m_fsList.indexOf( m_defaultFs );
|
||||
m_fsType = m_defaultFs;
|
||||
|
||||
m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" );
|
||||
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_cmdSshdDisable = getString( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" );
|
||||
m_cmdSshdUseradd = getString( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" );
|
||||
}
|
||||
|
||||
Calamares::JobList
|
||||
Config::createJobs()
|
||||
{
|
||||
QList< Calamares::job_ptr > list;
|
||||
QString cmdSshd = m_isSshEnabled ? m_cmdSshdEnable : m_cmdSshdDisable;
|
||||
|
||||
/* Put users job in queue (should run after unpackfs) */
|
||||
Calamares::Job* j = new UsersJob( m_featureSshd,
|
||||
m_cmdPasswd,
|
||||
m_cmdUsermod,
|
||||
cmdSshd,
|
||||
m_cmdSshdUseradd,
|
||||
m_isSshEnabled,
|
||||
m_username,
|
||||
m_userPassword,
|
||||
m_sshdUsername,
|
||||
m_sshdPassword );
|
||||
list.append( Calamares::job_ptr( j ) );
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void
|
||||
Config::runPartitionJobThenLeave( bool b )
|
||||
{
|
||||
Calamares::ViewManager* v = Calamares::ViewManager::instance();
|
||||
QString cmdMkfsRoot;
|
||||
if ( m_fsType == QStringLiteral( "btrfs" ) )
|
||||
{
|
||||
cmdMkfsRoot = m_cmdMkfsRootBtrfs;
|
||||
}
|
||||
else if ( m_fsType == QStringLiteral( "f2fs" ) )
|
||||
{
|
||||
cmdMkfsRoot = m_cmdMkfsRootF2fs;
|
||||
}
|
||||
else if ( m_fsType == QStringLiteral( "ext4" ) )
|
||||
{
|
||||
cmdMkfsRoot = m_cmdMkfsRootExt4;
|
||||
}
|
||||
else
|
||||
{
|
||||
v->onInstallationFailed( "Unknown filesystem: '" + m_fsType + "'", "" );
|
||||
}
|
||||
/* HACK: run partition job
|
||||
* The "mobile" module has two jobs, the partition job and the users job.
|
||||
* If we added both of them in Config::createJobs(), Calamares would run
|
||||
* them right after each other. But we need the "unpackfs" module to run
|
||||
* inbetween, that's why as workaround, the partition job is started here.
|
||||
* To solve this properly, we would need to place the partition job in an
|
||||
* own module and pass everything via globalstorage. But then we might as
|
||||
* well refactor everything so we can unify the mobile's partition job with
|
||||
* the proper partition job from Calamares. */
|
||||
Calamares::Job* j = new PartitionJob( m_cmdInternalStoragePrepare,
|
||||
m_cmdLuksFormat,
|
||||
m_cmdLuksOpen,
|
||||
cmdMkfsRoot,
|
||||
m_cmdMount,
|
||||
m_targetDeviceRoot,
|
||||
m_targetDeviceRootInternal,
|
||||
m_installFromExternalToInternal,
|
||||
m_isFdeEnabled,
|
||||
m_fdePassword );
|
||||
Calamares::JobResult res = j->exec();
|
||||
|
||||
if ( res )
|
||||
{
|
||||
v->next();
|
||||
}
|
||||
else
|
||||
{
|
||||
v->onInstallationFailed( res.message(), res.details() );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::setUsername( const QString& username )
|
||||
{
|
||||
m_username = username;
|
||||
emit usernameChanged( m_username );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setUserPassword( const QString& userPassword )
|
||||
{
|
||||
m_userPassword = userPassword;
|
||||
emit userPasswordChanged( m_userPassword );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setSshdUsername( const QString& sshdUsername )
|
||||
{
|
||||
m_sshdUsername = sshdUsername;
|
||||
emit sshdUsernameChanged( m_sshdUsername );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setSshdPassword( const QString& sshdPassword )
|
||||
{
|
||||
m_sshdPassword = sshdPassword;
|
||||
emit sshdPasswordChanged( m_sshdPassword );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setIsSshEnabled( const bool isSshEnabled )
|
||||
{
|
||||
m_isSshEnabled = isSshEnabled;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setFdePassword( const QString& fdePassword )
|
||||
{
|
||||
m_fdePassword = fdePassword;
|
||||
emit fdePasswordChanged( m_fdePassword );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setIsFdeEnabled( const bool isFdeEnabled )
|
||||
{
|
||||
m_isFdeEnabled = isFdeEnabled;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setInstallFromExternalToInternal( const bool val )
|
||||
{
|
||||
m_installFromExternalToInternal = val;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setFsType( int idx )
|
||||
{
|
||||
if ( idx >= 0 && idx < m_fsList.length() )
|
||||
{
|
||||
setFsType( m_fsList[ idx ] );
|
||||
}
|
||||
}
|
||||
void
|
||||
Config::setFsType( const QString& fsType )
|
||||
{
|
||||
if ( fsType != m_fsType )
|
||||
{
|
||||
m_fsType = fsType;
|
||||
emit fsTypeChanged( m_fsType );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::setFsIndex( const int fsIndex )
|
||||
{
|
||||
m_fsIndex = fsIndex;
|
||||
emit fsIndexChanged( m_fsIndex );
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "Job.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
class Config : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
/* installer UI */
|
||||
Q_PROPERTY( bool builtinVirtualKeyboard READ builtinVirtualKeyboard CONSTANT FINAL )
|
||||
|
||||
/* welcome */
|
||||
Q_PROPERTY( QString osName READ osName CONSTANT FINAL )
|
||||
Q_PROPERTY( QString arch READ arch CONSTANT FINAL )
|
||||
Q_PROPERTY( QString device READ device CONSTANT FINAL )
|
||||
Q_PROPERTY( QString userInterface READ userInterface 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 */
|
||||
Q_PROPERTY( QString username READ username WRITE setUsername NOTIFY usernameChanged )
|
||||
Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged )
|
||||
Q_PROPERTY( bool userPasswordNumeric READ userPasswordNumeric CONSTANT FINAL )
|
||||
|
||||
/* ssh server + credentials */
|
||||
Q_PROPERTY( bool featureSshd READ featureSshd CONSTANT FINAL )
|
||||
Q_PROPERTY( QString sshdUsername READ sshdUsername WRITE setSshdUsername NOTIFY sshdUsernameChanged )
|
||||
Q_PROPERTY( QString sshdPassword READ sshdPassword WRITE setSshdPassword NOTIFY sshdPasswordChanged )
|
||||
Q_PROPERTY( bool isSshEnabled READ isSshEnabled WRITE setIsSshEnabled )
|
||||
|
||||
/* full disk encryption */
|
||||
Q_PROPERTY( QString fdePassword READ fdePassword WRITE setFdePassword NOTIFY fdePasswordChanged )
|
||||
Q_PROPERTY( bool isFdeEnabled READ isFdeEnabled WRITE setIsFdeEnabled )
|
||||
|
||||
/* filesystem selection */
|
||||
Q_PROPERTY( QString fsType READ fsType WRITE setFsType NOTIFY fsTypeChanged )
|
||||
Q_PROPERTY( bool featureFsType READ featureFsType CONSTANT FINAL )
|
||||
Q_PROPERTY( QStringList fsList READ fsList CONSTANT FINAL )
|
||||
Q_PROPERTY( QString defaultFs READ defaultFs CONSTANT FINAL )
|
||||
Q_PROPERTY( int fsIndex READ fsIndex WRITE setFsIndex NOTIFY fsIndexChanged )
|
||||
|
||||
/* partition job */
|
||||
Q_PROPERTY( bool runPartitionJobThenLeave READ runPartitionJobThenLeaveDummy WRITE runPartitionJobThenLeave )
|
||||
Q_PROPERTY( QString cmdInternalStoragePrepare READ cmdInternalStoragePrepare CONSTANT FINAL )
|
||||
Q_PROPERTY( QString cmdLuksFormat READ cmdLuksFormat CONSTANT FINAL )
|
||||
Q_PROPERTY( QString cmdLuksOpen READ cmdLuksOpen CONSTANT FINAL )
|
||||
Q_PROPERTY( QString cmdMount READ cmdMount CONSTANT FINAL )
|
||||
Q_PROPERTY( QString targetDeviceRoot READ targetDeviceRoot CONSTANT FINAL )
|
||||
Q_PROPERTY( QString targetDeviceRootInternal READ targetDeviceRootInternal CONSTANT FINAL )
|
||||
Q_PROPERTY(
|
||||
bool installFromExternalToInternal READ installFromExternalToInternal WRITE setInstallFromExternalToInternal )
|
||||
|
||||
/* users job */
|
||||
Q_PROPERTY( QString cmdSshdEnable READ cmdSshdEnable CONSTANT FINAL )
|
||||
Q_PROPERTY( QString cmdSshdDisable READ cmdSshdDisable CONSTANT FINAL )
|
||||
|
||||
public:
|
||||
Config( QObject* parent = nullptr );
|
||||
void setConfigurationMap( const QVariantMap& );
|
||||
Calamares::JobList createJobs();
|
||||
|
||||
/* installer UI */
|
||||
bool builtinVirtualKeyboard() { return m_builtinVirtualKeyboard; }
|
||||
|
||||
/* welcome */
|
||||
QString osName() const { return m_osName; }
|
||||
QString arch() const { return m_arch; }
|
||||
QString device() const { return m_device; }
|
||||
QString userInterface() const { return m_userInterface; }
|
||||
QString version() const { return m_version; }
|
||||
|
||||
/* reserved usernames (user_pass, ssh_credentials) */
|
||||
QStringList reservedUsernames() const { return m_reservedUsernames; };
|
||||
|
||||
/* user */
|
||||
QString username() const { return m_username; }
|
||||
QString userPassword() const { return m_userPassword; }
|
||||
void setUsername( const QString& username );
|
||||
void setUserPassword( const QString& userPassword );
|
||||
bool userPasswordNumeric() const { return m_userPasswordNumeric; }
|
||||
|
||||
/* ssh server + credentials */
|
||||
bool featureSshd() { return m_featureSshd; }
|
||||
QString sshdUsername() const { return m_sshdUsername; }
|
||||
QString sshdPassword() const { return m_sshdPassword; }
|
||||
bool isSshEnabled() { return m_isSshEnabled; }
|
||||
void setSshdUsername( const QString& sshdUsername );
|
||||
void setSshdPassword( const QString& sshdPassword );
|
||||
void setIsSshEnabled( bool isSshEnabled );
|
||||
|
||||
/* full disk encryption */
|
||||
QString fdePassword() const { return m_fdePassword; }
|
||||
bool isFdeEnabled() { return m_isFdeEnabled; }
|
||||
void setFdePassword( const QString& fdePassword );
|
||||
void setIsFdeEnabled( bool isFdeEnabled );
|
||||
|
||||
/* filesystem selection */
|
||||
bool featureFsType() { return m_featureFsType; };
|
||||
QString fsType() const { return m_fsType; };
|
||||
void setFsType( int idx );
|
||||
void setFsType( const QString& fsType );
|
||||
QStringList fsList() const { return m_fsList; };
|
||||
int fsIndex() const { return m_fsIndex; };
|
||||
void setFsIndex( const int fsIndex );
|
||||
QString defaultFs() const { return m_defaultFs; };
|
||||
|
||||
/* partition job */
|
||||
bool runPartitionJobThenLeaveDummy() { return 0; }
|
||||
void runPartitionJobThenLeave( bool b );
|
||||
QString cmdInternalStoragePrepare() const { return m_cmdInternalStoragePrepare; }
|
||||
QString cmdLuksFormat() const { return m_cmdLuksFormat; }
|
||||
QString cmdLuksOpen() const { return m_cmdLuksOpen; }
|
||||
QString cmdMkfsRootBtrfs() const { return m_cmdMkfsRootBtrfs; }
|
||||
QString cmdMkfsRootExt4() const { return m_cmdMkfsRootExt4; }
|
||||
QString cmdMkfsRootF2fs() const { return m_cmdMkfsRootF2fs; }
|
||||
QString cmdMount() const { return m_cmdMount; }
|
||||
QString targetDeviceRoot() const { return m_targetDeviceRoot; }
|
||||
QString targetDeviceRootInternal() const { return m_targetDeviceRootInternal; }
|
||||
bool installFromExternalToInternal() { return m_installFromExternalToInternal; }
|
||||
void setInstallFromExternalToInternal( const bool val );
|
||||
|
||||
/* users job */
|
||||
QString cmdPasswd() const { return m_cmdPasswd; }
|
||||
QString cmdUsermod() const { return m_cmdUsermod; }
|
||||
QString cmdSshdEnable() const { return m_cmdSshdEnable; }
|
||||
QString cmdSshdDisable() const { return m_cmdSshdDisable; }
|
||||
QString cmdSshdUseradd() const { return m_cmdSshdUseradd; }
|
||||
|
||||
private:
|
||||
/* installer UI */
|
||||
bool m_builtinVirtualKeyboard;
|
||||
|
||||
/* welcome */
|
||||
QString m_osName;
|
||||
QString m_arch;
|
||||
QString m_device;
|
||||
QString m_userInterface;
|
||||
QString m_version;
|
||||
|
||||
/* reserved usernames (user_pass, ssh_credentials) */
|
||||
QStringList m_reservedUsernames;
|
||||
|
||||
/* default user */
|
||||
QString m_username;
|
||||
QString m_userPassword;
|
||||
bool m_userPasswordNumeric;
|
||||
|
||||
/* ssh server + credentials */
|
||||
bool m_featureSshd = false;
|
||||
QString m_sshdUsername;
|
||||
QString m_sshdPassword;
|
||||
bool m_isSshEnabled = false;
|
||||
|
||||
/* full disk encryption */
|
||||
QString m_fdePassword;
|
||||
bool m_isFdeEnabled = false;
|
||||
|
||||
/* filesystem selection */
|
||||
bool m_featureFsType = false;
|
||||
QString m_defaultFs;
|
||||
QString m_fsType;
|
||||
// Index of the currently selected filesystem in UI.
|
||||
int m_fsIndex = -1;
|
||||
QStringList m_fsList;
|
||||
|
||||
/* partition job */
|
||||
QString m_cmdInternalStoragePrepare;
|
||||
QString m_cmdLuksFormat;
|
||||
QString m_cmdLuksOpen;
|
||||
QString m_cmdMkfsRootBtrfs;
|
||||
QString m_cmdMkfsRootExt4;
|
||||
QString m_cmdMkfsRootF2fs;
|
||||
QString m_cmdMount;
|
||||
QString m_targetDeviceRoot;
|
||||
QString m_targetDeviceRootInternal;
|
||||
bool m_installFromExternalToInternal = false;
|
||||
|
||||
/* users job */
|
||||
QString m_cmdPasswd;
|
||||
QString m_cmdUsermod;
|
||||
QString m_cmdSshdEnable;
|
||||
QString m_cmdSshdDisable;
|
||||
QString m_cmdSshdUseradd;
|
||||
|
||||
signals:
|
||||
/* booleans we don't read from QML (like isSshEnabled) don't need a signal */
|
||||
|
||||
/* default user */
|
||||
void userPasswordChanged( QString userPassword );
|
||||
void usernameChanged( QString username );
|
||||
|
||||
/* ssh server + credentials */
|
||||
void sshdUsernameChanged( QString sshdUsername );
|
||||
void sshdPasswordChanged( QString sshdPassword );
|
||||
|
||||
/* full disk encryption */
|
||||
void fdePasswordChanged( QString fdePassword );
|
||||
|
||||
void fsTypeChanged( QString fsType );
|
||||
void fsIndexChanged( int fsIndex );
|
||||
};
|
||||
@@ -1,73 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#include "MobileQmlViewStep.h"
|
||||
|
||||
#include "Branding.h"
|
||||
#include "GlobalStorage.h"
|
||||
#include "locale/TranslationsModel.h"
|
||||
#include "modulesystem/ModuleManager.h"
|
||||
#include "utils/Dirs.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( MobileQmlViewStepFactory, registerPlugin< MobileQmlViewStep >(); )
|
||||
|
||||
void
|
||||
MobileQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
m_config->setConfigurationMap( configurationMap );
|
||||
Calamares::QmlViewStep::setConfigurationMap( configurationMap );
|
||||
}
|
||||
|
||||
MobileQmlViewStep::MobileQmlViewStep( QObject* parent )
|
||||
: Calamares::QmlViewStep( parent )
|
||||
, m_config( new Config( this ) )
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MobileQmlViewStep::onLeave()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
MobileQmlViewStep::isNextEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
MobileQmlViewStep::isBackEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MobileQmlViewStep::isAtBeginning() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MobileQmlViewStep::isAtEnd() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Calamares::JobList
|
||||
MobileQmlViewStep::jobs() const
|
||||
{
|
||||
return m_config->createJobs();
|
||||
}
|
||||
|
||||
QObject*
|
||||
MobileQmlViewStep::getConfig()
|
||||
{
|
||||
return m_config;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#ifndef PARTITION_QMLVIEWSTEP_H
|
||||
#define PARTITION_QMLVIEWSTEP_H
|
||||
#include "Config.h"
|
||||
|
||||
#include "utils/PluginFactory.h"
|
||||
#include "viewpages/QmlViewStep.h"
|
||||
|
||||
#include <DllMacro.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
class PLUGINDLLEXPORT MobileQmlViewStep : public Calamares::QmlViewStep
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MobileQmlViewStep( QObject* parent = nullptr );
|
||||
|
||||
bool isNextEnabled() const override;
|
||||
bool isBackEnabled() const override;
|
||||
bool isAtBeginning() const override;
|
||||
bool isAtEnd() const override;
|
||||
|
||||
Calamares::JobList jobs() const override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
void onLeave() override;
|
||||
QObject* getConfig() override;
|
||||
|
||||
private:
|
||||
Config* m_config;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( MobileQmlViewStepFactory )
|
||||
|
||||
#endif // PARTITION_QMLVIEWSTEP_H
|
||||
@@ -1,136 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#include "PartitionJob.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
#include "utils/CalamaresUtilsSystem.h"
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
|
||||
PartitionJob::PartitionJob( const QString& cmdInternalStoragePrepare,
|
||||
const QString& cmdLuksFormat,
|
||||
const QString& cmdLuksOpen,
|
||||
const QString& cmdMkfsRoot,
|
||||
const QString& cmdMount,
|
||||
const QString& targetDeviceRoot,
|
||||
const QString& targetDeviceRootInternal,
|
||||
bool installFromExternalToInternal,
|
||||
bool isFdeEnabled,
|
||||
const QString& password )
|
||||
: Calamares::Job()
|
||||
, m_cmdInternalStoragePrepare( cmdInternalStoragePrepare )
|
||||
, m_cmdLuksFormat( cmdLuksFormat )
|
||||
, m_cmdLuksOpen( cmdLuksOpen )
|
||||
, m_cmdMkfsRoot( cmdMkfsRoot )
|
||||
, m_cmdMount( cmdMount )
|
||||
, m_targetDeviceRoot( targetDeviceRoot )
|
||||
, m_targetDeviceRootInternal( targetDeviceRootInternal )
|
||||
, m_installFromExternalToInternal( installFromExternalToInternal )
|
||||
, m_isFdeEnabled( isFdeEnabled )
|
||||
, m_password( password )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
PartitionJob::prettyName() const
|
||||
{
|
||||
return "Creating and formatting installation partition";
|
||||
}
|
||||
|
||||
/* Fill the "global storage", so the following jobs (like unsquashfs) work.
|
||||
The code is similar to modules/partition/jobs/FillGlobalStorageJob.cpp in
|
||||
Calamares. */
|
||||
void
|
||||
FillGlobalStorage( const QString device, const QString pathMount )
|
||||
{
|
||||
using namespace Calamares;
|
||||
|
||||
GlobalStorage* gs = JobQueue::instance()->globalStorage();
|
||||
QVariantList partitions;
|
||||
QVariantMap partition;
|
||||
|
||||
/* See mapForPartition() in FillGlobalStorageJob.cpp */
|
||||
partition[ "device" ] = device;
|
||||
partition[ "mountPoint" ] = "/";
|
||||
partition[ "claimed" ] = true;
|
||||
|
||||
/* Ignored by calamares modules used in combination with the "mobile"
|
||||
* module, so we can get away with leaving them empty for now. */
|
||||
partition[ "uuid" ] = "";
|
||||
partition[ "fsName" ] = "";
|
||||
partition[ "fs" ] = "";
|
||||
|
||||
partitions << partition;
|
||||
gs->insert( "partitions", partitions );
|
||||
gs->insert( "rootMountPoint", pathMount );
|
||||
}
|
||||
|
||||
Calamares::JobResult
|
||||
PartitionJob::exec()
|
||||
{
|
||||
using namespace Calamares;
|
||||
using namespace CalamaresUtils;
|
||||
using namespace std;
|
||||
|
||||
const QString pathMount = "/mnt/install";
|
||||
const QString cryptName = "calamares_crypt";
|
||||
QString cryptDev = "/dev/mapper/" + cryptName;
|
||||
QString passwordStdin = m_password + "\n";
|
||||
QString dev = m_targetDeviceRoot;
|
||||
QList< QPair< QStringList, QString > > commands = {};
|
||||
|
||||
if ( m_installFromExternalToInternal )
|
||||
{
|
||||
dev = m_targetDeviceRootInternal;
|
||||
|
||||
commands.append( {
|
||||
{ { "sh", "-c", m_cmdInternalStoragePrepare }, QString() },
|
||||
} );
|
||||
}
|
||||
|
||||
commands.append( { { { "mkdir", "-p", pathMount }, QString() } } );
|
||||
|
||||
if ( m_isFdeEnabled )
|
||||
{
|
||||
commands.append( {
|
||||
{ { "sh", "-c", m_cmdLuksFormat + " " + dev }, passwordStdin },
|
||||
{ { "sh", "-c", m_cmdLuksOpen + " " + dev + " " + cryptName }, passwordStdin },
|
||||
{ { "sh", "-c", m_cmdMkfsRoot + " " + cryptDev }, QString() },
|
||||
{ { "sh", "-c", m_cmdMount + " " + cryptDev + " " + pathMount }, QString() },
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
commands.append( { { { "sh", "-c", m_cmdMkfsRoot + " " + dev }, QString() },
|
||||
{ { "sh", "-c", m_cmdMount + " " + dev + " " + pathMount }, QString() } } );
|
||||
}
|
||||
|
||||
foreach ( auto command, commands )
|
||||
{
|
||||
const QStringList args = command.first;
|
||||
const QString stdInput = command.second;
|
||||
const QString pathRoot = "/";
|
||||
|
||||
ProcessResult res
|
||||
= System::runCommand( System::RunLocation::RunInHost, args, pathRoot, stdInput, chrono::seconds( 600 ) );
|
||||
if ( res.getExitCode() )
|
||||
{
|
||||
return JobResult::error( "Command failed:<br><br>"
|
||||
"'"
|
||||
+ args.join( " " )
|
||||
+ "'<br><br>"
|
||||
" with output:<br><br>"
|
||||
"'"
|
||||
+ res.getOutput() + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
FillGlobalStorage( m_isFdeEnabled ? cryptDev : dev, pathMount );
|
||||
return JobResult::ok();
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#pragma once
|
||||
#include "Job.h"
|
||||
|
||||
|
||||
class PartitionJob : public Calamares::Job
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PartitionJob( const QString& cmdInternalStoragePrepare,
|
||||
const QString& cmdLuksFormat,
|
||||
const QString& cmdLuksOpen,
|
||||
const QString& cmdMkfsRoot,
|
||||
const QString& cmdMount,
|
||||
const QString& targetDeviceRoot,
|
||||
const QString& targetDeviceRootInternal,
|
||||
bool installFromExternalToInternal,
|
||||
bool isFdeEnabled,
|
||||
const QString& password );
|
||||
|
||||
QString prettyName() const override;
|
||||
Calamares::JobResult exec() override;
|
||||
|
||||
Calamares::JobList createJobs();
|
||||
|
||||
private:
|
||||
QString m_cmdInternalStoragePrepare;
|
||||
QString m_cmdLuksFormat;
|
||||
QString m_cmdLuksOpen;
|
||||
QString m_cmdMkfsRoot;
|
||||
QString m_cmdMount;
|
||||
QString m_targetDeviceRoot;
|
||||
QString m_targetDeviceRootInternal;
|
||||
bool m_installFromExternalToInternal;
|
||||
bool m_isFdeEnabled;
|
||||
QString m_password;
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#include "UsersJob.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
#include "utils/CalamaresUtilsSystem.h"
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
|
||||
UsersJob::UsersJob( bool featureSshd,
|
||||
const QString& cmdPasswd,
|
||||
const QString& cmdUsermod,
|
||||
const QString& cmdSshd,
|
||||
const QString& cmdSshdUseradd,
|
||||
bool isSshEnabled,
|
||||
const QString& username,
|
||||
const QString& password,
|
||||
const QString& sshdUsername,
|
||||
const QString& sshdPassword )
|
||||
: Calamares::Job()
|
||||
, m_featureSshd( featureSshd )
|
||||
, m_cmdPasswd( cmdPasswd )
|
||||
, m_cmdUsermod( cmdUsermod )
|
||||
, m_cmdSshd( cmdSshd )
|
||||
, m_cmdSshdUseradd( cmdSshdUseradd )
|
||||
, m_isSshEnabled( isSshEnabled )
|
||||
, m_username( username )
|
||||
, m_password( password )
|
||||
, m_sshdUsername( sshdUsername )
|
||||
, m_sshdPassword( sshdPassword )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
UsersJob::prettyName() const
|
||||
{
|
||||
return "Configuring users";
|
||||
}
|
||||
|
||||
Calamares::JobResult
|
||||
UsersJob::exec()
|
||||
{
|
||||
using namespace Calamares;
|
||||
using namespace CalamaresUtils;
|
||||
using namespace std;
|
||||
|
||||
QList< QPair< QStringList, QString > > commands = {
|
||||
{ { "sh", "-c", m_cmdUsermod }, m_username + "\n" }
|
||||
};
|
||||
|
||||
commands.append( { { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" } );
|
||||
|
||||
if ( m_featureSshd )
|
||||
{
|
||||
commands.append( { { "sh", "-c", m_cmdSshd }, QString() } );
|
||||
|
||||
if ( m_isSshEnabled )
|
||||
{
|
||||
commands.append( { { "sh", "-c", m_cmdSshdUseradd + " " + m_sshdUsername }, QString() } );
|
||||
commands.append(
|
||||
{ { "sh", "-c", m_cmdPasswd + " " + m_sshdUsername }, m_sshdPassword + "\n" + m_sshdPassword + "\n" } );
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( auto command, commands )
|
||||
{
|
||||
auto location = System::RunLocation::RunInTarget;
|
||||
const QString pathRoot = "/";
|
||||
const QStringList args = command.first;
|
||||
const QString stdInput = command.second;
|
||||
|
||||
ProcessResult res = System::runCommand( location, args, pathRoot, stdInput, chrono::seconds( 30 ) );
|
||||
if ( res.getExitCode() )
|
||||
{
|
||||
return JobResult::error( "Command failed:<br><br>"
|
||||
"'"
|
||||
+ args.join( " " )
|
||||
+ "'<br><br>"
|
||||
" with output:<br><br>"
|
||||
"'"
|
||||
+ res.getOutput() + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
return JobResult::ok();
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#pragma once
|
||||
#include "Job.h"
|
||||
|
||||
|
||||
class UsersJob : public Calamares::Job
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
UsersJob( bool featureSshd,
|
||||
const QString& cmdPasswd,
|
||||
const QString& cmdUsermod,
|
||||
const QString& cmdSshd,
|
||||
const QString& cmdSshdUseradd,
|
||||
bool isSshEnabled,
|
||||
const QString& username,
|
||||
const QString& password,
|
||||
const QString& sshdUsername,
|
||||
const QString& sshdPassword );
|
||||
|
||||
QString prettyName() const override;
|
||||
Calamares::JobResult exec() override;
|
||||
|
||||
Calamares::JobList createJobs();
|
||||
|
||||
private:
|
||||
bool m_featureSshd;
|
||||
QString m_cmdPasswd;
|
||||
QString m_cmdUsermod;
|
||||
QString m_cmdSshd;
|
||||
QString m_cmdSshdUseradd;
|
||||
bool m_isSshEnabled;
|
||||
QString m_username;
|
||||
QString m_password;
|
||||
QString m_sshdUsername;
|
||||
QString m_sshdPassword;
|
||||
};
|
||||
@@ -1,65 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "To protect your data in case your device gets stolen," +
|
||||
" it is recommended to enable full disk encryption.<br>" +
|
||||
"<br>" +
|
||||
"If you enable full disk encryption, you will be asked for" +
|
||||
" a password. Without this password, it is not possible to" +
|
||||
" boot your device or access any data on it. Make sure that" +
|
||||
" you don't lose this password!"
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Enable")
|
||||
onClicked: {
|
||||
config.isFdeEnabled = true;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: firstButton.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Disable")
|
||||
onClicked: {
|
||||
config.isFdeEnabled = false;
|
||||
navNextFeature();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
TextField {
|
||||
id: password
|
||||
anchors.top: parent.top
|
||||
placeholderText: qsTr("Password")
|
||||
inputMethodHints: Qt.ImhPreferLowercase
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validatePassword(password, passwordRepeat,
|
||||
errorText)
|
||||
text: config.fdePassword
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
Qt.inputMethod.update(Qt.ImQueryInput);
|
||||
}
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: passwordRepeat
|
||||
anchors.top: password.bottom
|
||||
placeholderText: qsTr("Password (repeat)")
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validatePassword(password, passwordRepeat,
|
||||
errorText)
|
||||
text: config.fdePassword
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorText
|
||||
anchors.top: passwordRepeat.bottom
|
||||
visible: false
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: errorText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: {
|
||||
if (validatePassword(password, passwordRepeat, errorText)) {
|
||||
config.fdePassword = password.text;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Undef <calamares@undef.tools>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "Select the filesystem for root partition. If unsure, leave the default."
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: fsTypeCB
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 150
|
||||
height: 30
|
||||
editable: false
|
||||
model: config.fsList
|
||||
/* Save the current state on selection so it is there when the back button is pressed */
|
||||
onActivated: config.fsType = fsTypeCB.currentText;
|
||||
Component.onCompleted: fsTypeCB.currentIndex = find( config.fsType, Qt.MatchContains );
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: fsTypeCB.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: {
|
||||
config.fsType = fsTypeCB.currentText;
|
||||
navNextFeature();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: (function() {
|
||||
var ret = "Once you hit 'install', the installation will begin." +
|
||||
" It will typically take a few minutes. Do not power off the" +
|
||||
" device until it is done.<br>";
|
||||
|
||||
if (config.installFromExternalToInternal) {
|
||||
ret += "<b>After the installation, your device will shutdown" +
|
||||
" automatically. You must remove the external storage" +
|
||||
" (SD card) before booting again.</b>" +
|
||||
"<br><br>" +
|
||||
"Otherwise, your device will boot into the installer" +
|
||||
" again, and not into the installed system."
|
||||
} else {
|
||||
ret += "Afterwards, it will reboot into the installed system.";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}())
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Install")
|
||||
onClicked: navFinish()
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "The installation was started from an external storage medium." +
|
||||
"<br>" +
|
||||
"You can either install to the same medium and overwrite the" +
|
||||
" installer, or install to the internal storage.<br>" +
|
||||
"<br>" +
|
||||
"Where would you like to install " + config.osName + "?"
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Internal (eMMC)")
|
||||
onClicked: {
|
||||
config.installFromExternalToInternal = true;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: firstButton.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("External (SD card)")
|
||||
onClicked: {
|
||||
config.installFromExternalToInternal = false;
|
||||
navNextFeature();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "Are you sure that you want to overwrite the internal storage?" +
|
||||
"<br><br>" +
|
||||
"<b>All existing data on the device will be lost!</b>"
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Yes")
|
||||
onClicked: {
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: firstButton.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("No")
|
||||
onClicked: {
|
||||
navBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Commented out values are defaults.
|
||||
# All commands are running with 'sh -c'.
|
||||
---
|
||||
# This entry exists only to keep the tests happy, remove it in
|
||||
# any production configuration.
|
||||
bogus: true
|
||||
|
||||
#######
|
||||
### Target OS information
|
||||
#######
|
||||
|
||||
## Operating System Name
|
||||
# osName: "(unknown)"
|
||||
|
||||
## User Interface name (e.g. Plasma Mobile)
|
||||
# userInterface: "(unknown)"
|
||||
|
||||
## User Interface assumes that the password is numeric (as of writing, this is
|
||||
## the case with Plasma Mobile and Phosh)
|
||||
# userPasswordNumeric: true
|
||||
|
||||
## OS version
|
||||
# version: "(unknown)"
|
||||
|
||||
## 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"
|
||||
|
||||
## 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
|
||||
#######
|
||||
|
||||
## Architecture (e.g. aarch64)
|
||||
# arch: "(unknown)"
|
||||
|
||||
## Name of the device (e.g. PinePhone)
|
||||
# device: "(unknown)"
|
||||
|
||||
## Partition that will be formatted and mounted (optionally with FDE) for the
|
||||
## rootfs
|
||||
# targetDeviceRoot: "/dev/unknown"
|
||||
|
||||
## Partition that will be formatted and mounted (optionally with FDE) for the
|
||||
## rootfs, on internal storage. The installer OS must not set this, if it was
|
||||
## booted from the internal storage (this is not checked in the mobile
|
||||
## module!).
|
||||
## If this is set, the user gets asked whether they want to install on internal
|
||||
## or external storage. If the user chose internal storage,
|
||||
## cmdInternalStoragePrepare (see below) runs before this partition gets
|
||||
## formatted (see below). A note is displayed, that the device is powered off
|
||||
## after installation and that the user should remove the external storage
|
||||
## medium. So you need to adjust the installer OS to poweroff in that case, and
|
||||
## not reboot. See postmarketos-ondev.git for reference.
|
||||
# targetDeviceRootInternal: ""
|
||||
|
||||
######
|
||||
### Installer Features
|
||||
######
|
||||
|
||||
## Ask whether sshd should be enabled or not. If enabled, add a dedicated ssh
|
||||
## user with proper username and password and suggest to change to key-based
|
||||
## authentication after installation.
|
||||
# featureSshd: true
|
||||
|
||||
## Ask the user, which filesystem to use.
|
||||
# featureFsType: false
|
||||
## Filesystems that the user can choose from.
|
||||
#fsModel:
|
||||
# - ext4
|
||||
# - f2fs
|
||||
# - btrfs
|
||||
## Default filesystem to display in the dialog. If featureFsType is disabled,
|
||||
## this gets used without asking the user.
|
||||
# defaultFs: ext4
|
||||
|
||||
## Start Qt's virtual keyboard within the mobile module. Disable if you bring
|
||||
## your own virtual keyboard (e.g. svkbd).
|
||||
# builtinVirtualKeyboard: true
|
||||
|
||||
#######
|
||||
### Commands running in the installer OS
|
||||
#######
|
||||
|
||||
## Format the target partition with LUKS
|
||||
## Arguments: <device>
|
||||
## Stdin: password with \n
|
||||
# cmdLuksFormat: "cryptsetup luksFormat --use-random"
|
||||
|
||||
## Open the formatted partition
|
||||
## Arguments: <device> <mapping name>
|
||||
## Stdin: password with \n
|
||||
# cmdLuksOpen: "cryptsetup luksOpen"
|
||||
|
||||
## Format the rootfs with a file system
|
||||
## Arguments: <device>
|
||||
## Btrfs: to allow snapshots to work on the root subvolume, it is recommended that this
|
||||
## command be a script which will create a subvolume and make it default
|
||||
## An example can be found at:
|
||||
## https://gitlab.com/mobian1/calamares-settings-mobian/-/merge_requests/2/diffs#diff-content-dde34f5f1c89e3dea63608c553bbc452dedf428f
|
||||
# cmdMkfsRootBtrfs: "mkfs.btrfs -L 'unknownOS_root'"
|
||||
# cmdMkfsRootExt4: "mkfs.ext4 -L 'unknownOS_root'"
|
||||
# cmdMkfsRootF2fs: "mkfs.f2fs -l 'unknownOS_root'"
|
||||
|
||||
## Mount the partition after formatting with file system
|
||||
## Arguments: <device> <mountpoint>
|
||||
# cmdMount: "mount"
|
||||
|
||||
## When user selects installation from external storage to internal storage
|
||||
## (see targetDeviceRootInternal above), use this command to prepare the
|
||||
## internal storage medium. The command must create a partition table with
|
||||
## two partitions (boot, root) and fill the boot partition. See the
|
||||
## ondev-internal-storage-prepare.sh in postmarketos-ondev as example.
|
||||
# cmdInternalStoragePrepare: "ondev-internal-storage-prepare"
|
||||
|
||||
#######
|
||||
### 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
|
||||
## Arguments: <username>
|
||||
## Stdin: password twice, each time with \n
|
||||
# cmdPasswd: "passwd"
|
||||
|
||||
## Enable or disable sshd
|
||||
# cmdSshdEnable: "systemctl enable sshd.service"
|
||||
# cmdSshdDisable: "systemctl disable sshd.service"
|
||||
|
||||
## Create the user for sshd
|
||||
## Arguments: <username>
|
||||
# cmdSshdUseradd: "useradd -G wheel -m"
|
||||
@@ -1,390 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Page
|
||||
{
|
||||
property var screen: "welcome"
|
||||
property var screenPrevious: []
|
||||
property var titles: {
|
||||
"welcome": null, /* titlebar disabled */
|
||||
"install_target": "Installation target",
|
||||
"install_target_confirm": "Warning",
|
||||
"user_pass": "User password",
|
||||
"ssh_confirm": "SSH server",
|
||||
"ssh_credentials": "SSH credentials",
|
||||
"fs_selection": "Root filesystem",
|
||||
"fde_confirm": "Full disk encryption",
|
||||
"fde_pass": "Full disk encryption",
|
||||
"install_confirm": "Ready to install",
|
||||
"wait": null
|
||||
}
|
||||
property var features: [
|
||||
{"name": "welcome",
|
||||
"screens": ["welcome"]},
|
||||
{"name": "installTarget",
|
||||
"screens": ["install_target", "install_target_confirm"]},
|
||||
{"name": "userPassword",
|
||||
"screens": ["user_pass"]},
|
||||
{"name": "sshd",
|
||||
"screens": ["ssh_confirm", "ssh_credentials"]},
|
||||
{"name": "fsType",
|
||||
"screens": ["fs_selection"]},
|
||||
{"name": "fde",
|
||||
"screens": ["fde_confirm", "fde_pass"]},
|
||||
{"name": "installConfirm",
|
||||
"screens": ["install_confirm", "wait"]}
|
||||
]
|
||||
property var featureIdByScreen: (function() {
|
||||
/* Put "features" above into an index of screen name -> feature id:
|
||||
* featureIdByScreen = {"welcome": 0, "user_pass": 1, ...} */
|
||||
var ret = {};
|
||||
for (var i=0; i<features.length; i++) {
|
||||
for (var j=0; j<features[i]["screens"].length; j++) {
|
||||
ret[ features[i]["screens"][j] ] = i;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}())
|
||||
/* Only allow characters, that can be typed in with osk-sdl
|
||||
* (src/keyboard.cpp). Details in big comment in validatePassword(). */
|
||||
property var allowed_chars:
|
||||
/* layer 0 */ "abcdefghijklmnopqrstuvwxyz" +
|
||||
/* layer 1 */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
/* layer 2 */ "1234567890" + "@#$%&-_+()" + ",\"':;!?" +
|
||||
/* layer 3 */ "~`|·√πτ÷×¶" + "©®£€¥^°*{}" + "\\/<>=[]" +
|
||||
/* bottom row */ " ."
|
||||
|
||||
Item {
|
||||
id: appContainer
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: inputPanel.top
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Rectangle {
|
||||
id: mobileNavigation
|
||||
width: parent.width
|
||||
height: 30
|
||||
color: "#e6e4e1"
|
||||
Layout.fillWidth: true
|
||||
|
||||
border.width: 1
|
||||
border.color: "#a7a7a7"
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
spacing: 6
|
||||
|
||||
Button {
|
||||
Layout.leftMargin: 6
|
||||
id: mobileBack
|
||||
text: "<"
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 10
|
||||
implicitHeight: 7
|
||||
border.color: "#c1bab5"
|
||||
border.width: 1
|
||||
radius: 4
|
||||
color: mobileBack.down ? "#dbdbdb" : "#f2f2f2"
|
||||
}
|
||||
|
||||
onClicked: navBack()
|
||||
}
|
||||
Rectangle {
|
||||
implicitHeight: 10
|
||||
Layout.fillWidth: true
|
||||
color: "#e6e4e1"
|
||||
|
||||
Text {
|
||||
id: mobileTitle
|
||||
text: ""
|
||||
color: "#303638"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
color: "#e6e4e1"
|
||||
Layout.rightMargin: 6
|
||||
implicitWidth: 32
|
||||
implicitHeight: 30
|
||||
id: filler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: load
|
||||
anchors.left: parent.left
|
||||
anchors.top: mobileNavigation.bottom
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
InputPanel {
|
||||
id: inputPanel
|
||||
y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height
|
||||
visible: config.builtinVirtualKeyboard
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
}
|
||||
|
||||
function skipFeatureInstallTarget() {
|
||||
return config.targetDeviceRootInternal == "";
|
||||
}
|
||||
|
||||
/* Navigation related */
|
||||
function navTo(name, historyPush=true) {
|
||||
console.log("Navigating to screen: " + name);
|
||||
if (historyPush)
|
||||
screenPrevious.push(screen);
|
||||
screen = name;
|
||||
load.source = name + ".qml";
|
||||
mobileNavigation.visible = (titles[name] !== null);
|
||||
mobileTitle.text = "<b>" + titles[name] + "</b>";
|
||||
Qt.inputMethod.hide();
|
||||
}
|
||||
function navFinish() {
|
||||
/* Show a waiting screen and wait a second (so it can render). The big
|
||||
* comment in Config.cpp::runPartitionJobThenLeave() explains why this
|
||||
* is necessary. */
|
||||
navTo("wait");
|
||||
timer.interval = 1000;
|
||||
timer.repeat = false;
|
||||
timer.triggered.connect(function() {
|
||||
/* Trigger Config.cpp::runPartitionJobThenLeave(). (We could expose
|
||||
* the function directly with qmlRegisterSingletonType somehow, but
|
||||
* I haven't seen existing Calamares code do that with the Config
|
||||
* object, so just use the side effect of setting the variable, as
|
||||
* done in existing code of Calamares modules.) */
|
||||
config.runPartitionJobThenLeave = 1
|
||||
});
|
||||
timer.start();
|
||||
}
|
||||
function navNextFeature() {
|
||||
var id;
|
||||
|
||||
/* Skip disabled features */
|
||||
for (id = featureIdByScreen[screen] + 1; id < features.length; id++) {
|
||||
/* First letter uppercase */
|
||||
var name = features[id]["name"];
|
||||
var nameUp = name.charAt(0).toUpperCase() + name.slice(1);
|
||||
|
||||
/* Check config.Feature<Name> */
|
||||
var configOption = "feature" + nameUp;
|
||||
if (config[configOption] === false) {
|
||||
console.log("Skipping feature (disabled in config): " + name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check skipFeature<Name>() */
|
||||
var funcName = "skipFeature" + nameUp;
|
||||
if (eval("typeof " + funcName) === "function"
|
||||
&& eval(funcName + "()")) {
|
||||
console.log("Skipping feature (skip function): " + name);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
console.log("Navigating to feature: " + features[id]["name"]);
|
||||
return navTo(features[id]["screens"][0]);
|
||||
}
|
||||
function navNext() {
|
||||
var featureId = featureIdByScreen[screen];
|
||||
var featureScreens = features[featureId]["screens"];
|
||||
for (var i = 0; i<featureScreens.length; i++) {
|
||||
/* Seek ahead until i is current screen */
|
||||
if (featureScreens[i] != screen)
|
||||
continue;
|
||||
|
||||
/* Navigate to next screen in same feature */
|
||||
if (i + 1 < featureScreens.length) {
|
||||
var screenNext = featureScreens[i + 1];
|
||||
return navTo(screenNext);
|
||||
}
|
||||
|
||||
/* Screen is last in feature */
|
||||
return navNextFeature();
|
||||
}
|
||||
console.log("ERROR: navNext() failed for screen: " + screen);
|
||||
}
|
||||
function navBack() {
|
||||
if (screenPrevious.length)
|
||||
return navTo(screenPrevious.pop(), false);
|
||||
ViewManager.back();
|
||||
}
|
||||
function onActivate() {
|
||||
navTo(screen, false);
|
||||
}
|
||||
|
||||
/* Input validation: show/clear failures */
|
||||
function validationFailure(errorText, message="") {
|
||||
errorText.text = message;
|
||||
errorText.visible = true;
|
||||
return false;
|
||||
}
|
||||
function validationFailureClear(errorText) {
|
||||
errorText.text = "";
|
||||
errorText.visible = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Input validation: user-screens (fde_pass, user_pass, ssh_credentials) */
|
||||
function validatePin(userPin, userPinRepeat, errorText) {
|
||||
var pin = userPin.text;
|
||||
var repeat = userPinRepeat.text;
|
||||
|
||||
if (pin == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (!pin.match(/^[0-9]*$/))
|
||||
return validationFailure(errorText,
|
||||
"Only digits are allowed.");
|
||||
|
||||
if (pin.length < 5)
|
||||
return validationFailure(errorText,
|
||||
"Too short: needs at least 5 digits.");
|
||||
|
||||
if (repeat == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (repeat != pin)
|
||||
return validationFailure(errorText,
|
||||
"The PINs don't match.");
|
||||
|
||||
return validationFailureClear(errorText);
|
||||
}
|
||||
function validateUsername(username, errorText, extraReservedUsernames = []) {
|
||||
var name = username.text;
|
||||
var reserved = config.reservedUsernames.concat(extraReservedUsernames);
|
||||
|
||||
/* Validate characters */
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
if (i) {
|
||||
if (!name[i].match(/^[a-z0-9_-]$/))
|
||||
return validationFailure(errorText,
|
||||
"Characters must be lowercase" +
|
||||
" letters, numbers,<br>" +
|
||||
" underscores or minus signs.");
|
||||
} else {
|
||||
if (!name[i].match(/^[a-z_]$/))
|
||||
return validationFailure(errorText,
|
||||
"First character must be a" +
|
||||
" lowercase letter or an" +
|
||||
" underscore.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate against reserved usernames */
|
||||
for (var i = 0; i < reserved.length; i++) {
|
||||
if (name == reserved[i])
|
||||
return validationFailure(errorText, "Username '" +
|
||||
reserved[i] +
|
||||
"' is reserved.");
|
||||
}
|
||||
|
||||
/* Passed */
|
||||
return validationFailureClear(errorText);
|
||||
}
|
||||
|
||||
function validateSshdUsername(username, errorText) {
|
||||
return validateUsername(username, errorText, [config.username]);
|
||||
}
|
||||
function validateSshdPassword(password, passwordRepeat, errorText) {
|
||||
var pass = password.text;
|
||||
var repeat = passwordRepeat.text;
|
||||
|
||||
if (pass == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (pass.length < 6)
|
||||
return validationFailure(errorText,
|
||||
"Too short: needs at least 6" +
|
||||
" digits/characters.");
|
||||
|
||||
if (repeat == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (pass != repeat)
|
||||
return validationFailure(errorText, "Passwords don't match.");
|
||||
|
||||
return validationFailureClear(errorText);
|
||||
}
|
||||
function check_chars(input) {
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
if (allowed_chars.indexOf(input[i]) == -1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function allowed_chars_multiline() {
|
||||
/* return allowed_chars split across multiple lines */
|
||||
var step = 20;
|
||||
var ret = "";
|
||||
for (var i = 0; i < allowed_chars.length + step; i += step)
|
||||
ret += allowed_chars.slice(i, i + step) + "\n";
|
||||
return ret.trim();
|
||||
}
|
||||
function validatePassword(password, passwordRepeat, errorText) {
|
||||
var pass = password.text;
|
||||
var repeat = passwordRepeat.text;
|
||||
|
||||
if (pass == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
/* This function gets called for the FDE password and for the user
|
||||
* password. As of writing, all distributions shipping the mobile
|
||||
* module are using osk-sdl to type in the FDE password after the
|
||||
* installation, and another keyboard after booting up, to type in the
|
||||
* user password. The osk-sdl password has the same keys as
|
||||
* squeekboard's default layout, and other keyboards should be able to
|
||||
* type these characters in as well. For now, verify that the password
|
||||
* only contains characters that can be typed in by osk-sdl. If you
|
||||
* need this to be more sophisticated, feel free to submit patches to
|
||||
* make this more configurable. */
|
||||
if (!check_chars(pass))
|
||||
return validationFailure(errorText,
|
||||
"The password must only contain" +
|
||||
" these characters, others can possibly" +
|
||||
" not be typed in after installation:\n" +
|
||||
"\n" +
|
||||
allowed_chars_multiline());
|
||||
|
||||
if (pass.length < 6)
|
||||
return validationFailure(errorText,
|
||||
"Too short: needs at least 6" +
|
||||
" digits/characters.");
|
||||
|
||||
if (repeat == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (pass != repeat)
|
||||
return validationFailure(errorText, "Passwords don't match.");
|
||||
|
||||
return validationFailureClear(errorText);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>mobile.qml</file>
|
||||
|
||||
<file>welcome.qml</file>
|
||||
|
||||
<file>install_target.qml</file> <!-- install from external to internal? -->
|
||||
<file>install_target_confirm.qml</file> <!-- overwrite internal storage? -->
|
||||
|
||||
<file>user_pass.qml</file> <!-- default user: username, password -->
|
||||
<file>ssh_confirm.qml</file> <!-- sshd: enable or not? -->
|
||||
<file>ssh_credentials.qml</file> <!-- sshd user: username, password -->
|
||||
<file>fs_selection.qml</file> <!-- filesystem selection -->
|
||||
|
||||
<file>fde_confirm.qml</file> <!-- enable FDE or not? -->
|
||||
<file>fde_pass.qml</file> <!-- FDE password (optional) -->
|
||||
<file>install_confirm.qml</file> <!-- final confirmation before install -->
|
||||
|
||||
<file>wait.qml</file> <!-- please wait while partitioning -->
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -1,68 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 30
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "If you don't know what SSH is, choose 'disable'.<br>" +
|
||||
"<br>" +
|
||||
"With 'enable', you will be asked for a second username and" +
|
||||
" password. You will be able to login to the SSH server with" +
|
||||
" these credentials via USB (172.16.42.1), Wi-Fi and possibly" +
|
||||
" cellular network. It is recommended to replace the password" +
|
||||
" with an SSH key after the installation.<br>" +
|
||||
"<br>" +
|
||||
"More information:<br>" +
|
||||
"https://postmarketos.org/ssh"
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 40
|
||||
width: 200
|
||||
|
||||
text: qsTr("Enable")
|
||||
onClicked: {
|
||||
config.isSshEnabled = true;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: firstButton.bottom
|
||||
anchors.topMargin: 40
|
||||
width: 200
|
||||
|
||||
text: qsTr("Disable")
|
||||
onClicked: {
|
||||
config.isSshEnabled = false;
|
||||
navNextFeature();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
TextField {
|
||||
id: username
|
||||
anchors.top: parent.top
|
||||
placeholderText: qsTr("SSH username")
|
||||
inputMethodHints: Qt.ImhPreferLowercase
|
||||
onTextChanged: validateSshdUsername(username, errorTextUsername)
|
||||
text: config.sshdUsername
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
Qt.inputMethod.update(Qt.ImQueryInput);
|
||||
}
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorTextUsername
|
||||
anchors.top: username.bottom
|
||||
visible: false
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: password
|
||||
anchors.top: errorTextUsername.bottom
|
||||
placeholderText: qsTr("SSH password")
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validateSshdPassword(password, passwordRepeat,
|
||||
errorTextPassword)
|
||||
text: config.sshdPassword
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: passwordRepeat
|
||||
anchors.top: password.bottom
|
||||
placeholderText: qsTr("SSH password (repeat)")
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validateSshdPassword(password, passwordRepeat,
|
||||
errorTextPassword)
|
||||
text: config.sshdPassword
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorTextPassword
|
||||
anchors.top: passwordRepeat.bottom
|
||||
visible: false
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: errorTextPassword.bottom
|
||||
anchors.topMargin: 40
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: {
|
||||
if (validateSshdUsername(username, errorTextUsername) &&
|
||||
validateSshdPassword(password, passwordRepeat,
|
||||
errorTextPassword)) {
|
||||
config.sshdUsername = username.text;
|
||||
config.sshdPassword = password.text;
|
||||
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
property var passPlaceholder: (config.userPasswordNumeric
|
||||
? "PIN"
|
||||
: "Password")
|
||||
property var hints: (config.userPasswordNumeric
|
||||
? Qt.ImhDigitsOnly
|
||||
: Qt.ImhPreferLowercase)
|
||||
property var validatePassFunc: (config.userPasswordNumeric
|
||||
? validatePin
|
||||
: validatePassword);
|
||||
|
||||
property var validateNameFunc: validateUsername;
|
||||
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: usernameDescription
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
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
|
||||
|
||||
text: (function() {
|
||||
if (config.userPasswordNumeric) {
|
||||
return "Set the numeric password of your user. The" +
|
||||
" lockscreen will ask for this PIN. This is" +
|
||||
" <i>not</i> the PIN of your SIM card. Make sure to" +
|
||||
" remember it.";
|
||||
} else {
|
||||
return "Set the password of your user. The lockscreen will" +
|
||||
" ask for this password. Make sure to remember it.";
|
||||
}
|
||||
}())
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: userPass
|
||||
anchors.top: userPassDescription.bottom
|
||||
placeholderText: qsTr(passPlaceholder)
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText)
|
||||
text: config.userPassword
|
||||
|
||||
/* Let the virtual keyboard change to digits only */
|
||||
inputMethodHints: hints
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
Qt.inputMethod.update(Qt.ImQueryInput)
|
||||
}
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: userPassRepeat
|
||||
anchors.top: userPass.bottom
|
||||
placeholderText: qsTr(passPlaceholder + " (repeat)")
|
||||
inputMethodHints: hints
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText)
|
||||
text: config.userPassword
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.top: userPassRepeat.bottom
|
||||
id: errorText
|
||||
visible: false
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: errorText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: {
|
||||
if (validatePassFunc(userPass, userPassRepeat, errorText) && validateNameFunc(username, errorText)) {
|
||||
config.userPassword = userPass.text;
|
||||
config.username = username.text;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Page
|
||||
{
|
||||
id: fdeWait
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Image {
|
||||
id: logo
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
height: 50
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "file:///usr/share/calamares/branding/default-mobile/logo.png"
|
||||
}
|
||||
Text {
|
||||
id: waitText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: logo.bottom
|
||||
anchors.topMargin: 50
|
||||
wrapMode: Text.WordWrap
|
||||
text: "Formatting and mounting target partition. This may" +
|
||||
" take up to ten minutes, please be patient."
|
||||
width: 200
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Page
|
||||
{
|
||||
id: welcome
|
||||
|
||||
Item {
|
||||
id: appContainer
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Image {
|
||||
id: logo
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
height: 50
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "file:///usr/share/calamares/branding/default-mobile/logo.png"
|
||||
}
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: logo.bottom
|
||||
anchors.topMargin: 10
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: "You are about to install<br>" +
|
||||
"<b>" + config.osName +
|
||||
" " + config.version + "</b><br>" +
|
||||
"user interface " +
|
||||
"<b>" + config.userInterface + "</b><br>" +
|
||||
"architecture " +
|
||||
"<b>" + config.arch + "</b><br>" +
|
||||
"on your <br>" +
|
||||
"<b>" + config.device + "</b><br>"
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: navNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
# The OS-FreeBSD module does "all the things" in a FreeBSD installation.
|
||||
# Since the other modules -- users, fstab, grub, pretty much all of them
|
||||
# -- are Linux-specific, it doesn't make much sense to fork each of them
|
||||
# or provide alternatives, so instead we have one module that completes
|
||||
# a FreeBSD installation based on the GlobalStorage values set by
|
||||
# Calamares viewmodules.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# License-Filename: LICENSE
|
||||
#
|
||||
|
||||
calamares_add_plugin( os-freebsd
|
||||
TYPE job
|
||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||
SOURCES
|
||||
FreeBSDJob.cpp
|
||||
SHARED_LIB
|
||||
NO_CONFIG
|
||||
)
|
||||
@@ -1,58 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*/
|
||||
|
||||
#include "FreeBSDJob.h"
|
||||
|
||||
#include "CalamaresVersion.h"
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QProcess>
|
||||
#include <QThread>
|
||||
|
||||
|
||||
FreeBSDJob::FreeBSDJob( QObject* parent )
|
||||
: Calamares::CppJob( parent )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
FreeBSDJob::~FreeBSDJob() {}
|
||||
|
||||
|
||||
QString
|
||||
FreeBSDJob::prettyName() const
|
||||
{
|
||||
return tr( "FreeBSD Installation Job" );
|
||||
}
|
||||
|
||||
Calamares::JobResult
|
||||
FreeBSDJob::exec()
|
||||
{
|
||||
emit progress( 0.1 );
|
||||
cDebug() << "[FREEBSD]";
|
||||
|
||||
Calamares::JobQueue::instance()->globalStorage()->debugDump();
|
||||
emit progress( 0.5 );
|
||||
|
||||
QThread::sleep( 3 );
|
||||
emit progress( 1.0 );
|
||||
|
||||
return Calamares::JobResult::ok();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FreeBSDJob::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
// TODO: actually fetch something from that configuration
|
||||
m_configurationMap = configurationMap;
|
||||
}
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( FreeBSDJobFactory, registerPlugin< FreeBSDJob >(); )
|
||||
@@ -1,39 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*/
|
||||
|
||||
#ifndef FREEBSDJOB_H
|
||||
#define FREEBSDJOB_H
|
||||
|
||||
#include "CppJob.h"
|
||||
#include "DllMacro.h"
|
||||
#include "utils/PluginFactory.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
|
||||
class PLUGINDLLEXPORT FreeBSDJob : public Calamares::CppJob
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FreeBSDJob( QObject* parent = nullptr );
|
||||
virtual ~FreeBSDJob() override;
|
||||
|
||||
QString prettyName() const override;
|
||||
|
||||
Calamares::JobResult exec() override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
|
||||
private:
|
||||
QVariantMap m_configurationMap;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( FreeBSDJobFactory )
|
||||
|
||||
#endif // FREEBSDJOB_H
|
||||
@@ -1,276 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
#
|
||||
# Calamares is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Calamares is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
|
||||
"""
|
||||
=== NixOS Configuration
|
||||
|
||||
NixOS has its own "do all the things" configuration file which
|
||||
declaratively handles what things need to be done in the target
|
||||
system, and it has an existing tool to "execute" that declarative
|
||||
specification. This module takes configuration values set by
|
||||
Calamares viewmodules (e.g. the users module) and puts
|
||||
them into the configuration file in the target system,
|
||||
and then runs the necessary NixOS specific tools.
|
||||
"""
|
||||
|
||||
import libcalamares
|
||||
import os
|
||||
from time import gmtime, strftime, sleep
|
||||
|
||||
import gettext
|
||||
_ = gettext.translation("calamares-python",
|
||||
localedir=libcalamares.utils.gettext_path(),
|
||||
languages=libcalamares.utils.gettext_languages(),
|
||||
fallback=True).gettext
|
||||
|
||||
|
||||
# The following long **long** string is the entire text of
|
||||
# a nix-configuration file. It is cribbed from, and adapted from,
|
||||
# the sample file in https://github.com/itamar567/dotnix .
|
||||
#
|
||||
# We are going to substitute values into this text. However,
|
||||
# Python's .format() function wants parens { } around variable
|
||||
# names, and Nix's config file wants to use parens { } for block
|
||||
# structure. So we have a compromise format here:
|
||||
#
|
||||
# - Write the config file as you would normally,
|
||||
# - Write @@variable@@ instead of {variable}
|
||||
#
|
||||
# Some minor trickery later will massage this and substitute variables.
|
||||
#
|
||||
configuration_nix_sample = """# Nix configuration file
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
[ # Include the results of the hardware scan.
|
||||
./hardware-configuration.nix
|
||||
./command-not-found/command-not-found.nix
|
||||
];
|
||||
|
||||
# Use the systemd-boot EFI boot loader.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
# Use Zen Kernel
|
||||
boot.kernelPackages = pkgs.linuxPackages_zen;
|
||||
|
||||
networking.hostName = "@@hostname@@"; # Define your hostname.
|
||||
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
|
||||
|
||||
# Set your time zone.
|
||||
time.timeZone = "@@timezone@@";
|
||||
|
||||
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
|
||||
networking.useDHCP = false;
|
||||
networking.interfaces.enp42s0.useDHCP = true;
|
||||
|
||||
# Select internationalisation properties.
|
||||
i18n.defaultLocale = "en_US.UTF-8";
|
||||
|
||||
# Configure X11
|
||||
services.xserver = {
|
||||
enable = true;
|
||||
windowManager.i3 = {
|
||||
enable = true;
|
||||
package = pkgs.i3-gaps;
|
||||
};
|
||||
|
||||
# Set i3 to the default session in the display manager
|
||||
displayManager.defaultSession = "none+i3";
|
||||
};
|
||||
|
||||
# SSH fix
|
||||
programs.ssh.askPassword = pkgs.lib.mkForce "";
|
||||
|
||||
# Enable CUPS to print documents.
|
||||
services.printing.enable = true;
|
||||
|
||||
# Enable sound.
|
||||
sound.enable = true;
|
||||
hardware.pulseaudio.enable = true;
|
||||
|
||||
# Define a user account. Don't forget to set a password with ‘passwd’.
|
||||
users.users.username = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [ "wheel" "libvirtd" ];
|
||||
};
|
||||
|
||||
# Disable password for sudo
|
||||
security.sudo.extraRules= [{
|
||||
groups = [ "wheel" ];
|
||||
commands = [{
|
||||
command = "ALL" ;
|
||||
options= [ "NOPASSWD" ];
|
||||
}];
|
||||
}];
|
||||
|
||||
# Set ZSH as the default shell
|
||||
users.defaultUserShell = pkgs.zsh;
|
||||
|
||||
# clean /tmp on boot
|
||||
boot.cleanTmpDir=true;
|
||||
|
||||
# Config packages
|
||||
nixpkgs.config = {
|
||||
allowUnfree = true;
|
||||
|
||||
chromium = {
|
||||
enableWideVine = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Automatically upgrade the system
|
||||
# This service is a modified version of https://github.com/NixOS/nixpkgs/blob/nixos-21.05/nixos/modules/tasks/auto-upgrade.nix#L122
|
||||
systemd = {
|
||||
services.nixos-upgrade = {
|
||||
description = "NixOS Upgrade";
|
||||
|
||||
# We use --upgrade, so we need internet access
|
||||
wants = [ "network-online.target" ];
|
||||
|
||||
restartIfChanged = false;
|
||||
unitConfig.X-StopOnRemoval = false;
|
||||
|
||||
serviceConfig.Type = "oneshot";
|
||||
|
||||
environment = config.nix.envVars // {
|
||||
inherit (config.environment.sessionVariables) NIX_PATH;
|
||||
HOME = "/root";
|
||||
} // config.networking.proxy.envVars;
|
||||
|
||||
path = with pkgs; [
|
||||
coreutils
|
||||
gnutar
|
||||
xz.bin
|
||||
gzip
|
||||
gitMinimal
|
||||
config.nix.package.out
|
||||
];
|
||||
|
||||
script = let
|
||||
nixos-rebuild =
|
||||
"${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
|
||||
in ''
|
||||
${nixos-rebuild} boot --upgrade
|
||||
booted="$(readlink /run/booted-system/{initrd,kernel,kernel-modules})"
|
||||
built="$(readlink /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
|
||||
if [ "$booted" = "$built" ]; then
|
||||
${nixos-rebuild} switch
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
# To start the service at boot, we will use a systemd timer
|
||||
timers.nixos-upgrade = {
|
||||
wantedBy = [ "timers.target" ];
|
||||
partOf = [ "nixos-upgrade.service" ];
|
||||
timerConfig.OnBootSec = "5s";
|
||||
};
|
||||
};
|
||||
|
||||
# Some programs need SUID wrappers, can be configured further or are
|
||||
# started in user sessions.
|
||||
programs.mtr.enable = true;
|
||||
programs.gnupg.agent = {
|
||||
enable = true;
|
||||
enableSSHSupport = true;
|
||||
};
|
||||
|
||||
# Enable the OpenSSH daemon.
|
||||
services.openssh.enable = true;
|
||||
|
||||
# Open ports in the firewall.
|
||||
networking.firewall.enable = false;
|
||||
|
||||
# This value determines the NixOS release from which the default
|
||||
# settings for stateful data, like file locations and database versions
|
||||
# on your system were taken. It‘s perfectly fine and recommended to leave
|
||||
# this value at the release version of the first install of this system.
|
||||
# Before changing this value read the documentation for this option
|
||||
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
|
||||
system.stateVersion = "20.09"; # Did you read the comment?
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def pretty_name():
|
||||
return _("NixOS Configuration.")
|
||||
|
||||
|
||||
def catenate(d, key, *values):
|
||||
"""
|
||||
Sets @p d[key] to the string-concatenation of @p values
|
||||
if none of the values are None.
|
||||
|
||||
This can be used to set keys conditionally based on
|
||||
the values being found.
|
||||
"""
|
||||
if [v for v in values if v is None]:
|
||||
return
|
||||
|
||||
d[key] = "".join(values)
|
||||
|
||||
|
||||
def run():
|
||||
"""NixOS Configuration."""
|
||||
|
||||
gs = libcalamares.globalstorage
|
||||
text = configuration_nix_sample
|
||||
|
||||
# Collect variables to substitute into the main text
|
||||
variables = dict()
|
||||
catenate(variables, "hostname", gs.value("hostname"))
|
||||
catenate(variables, "timezone", gs.value("locationRegion"), "/", gs.value("locationZone"))
|
||||
|
||||
# Check that all variables are used
|
||||
for key in variables.keys():
|
||||
pattern = "@@{key}@@".format(key=key)
|
||||
if not pattern in text:
|
||||
libcalamares.utils.warning("Variable '{key}' is not used.".format(key=key))
|
||||
|
||||
# Check that all patterns exist
|
||||
import re
|
||||
variable_pattern = re.compile("@@\w+@@")
|
||||
for match in variable_pattern.finditer(text):
|
||||
variable_name = text[match.start()+2:match.end()-2]
|
||||
if not variable_name in variables:
|
||||
libcalamares.utils.warning("Variable '{key}' is used but not defined.".format(key=variable_name))
|
||||
|
||||
# Do the substitutions
|
||||
for key in variables.keys():
|
||||
pattern = "@@{key}@@".format(key=key)
|
||||
text = text.replace(pattern, str(variables[key]))
|
||||
|
||||
# Write the result to a temp-file, then run the main tool.
|
||||
# There is no progress reporting from the tool, so it's going
|
||||
# to seem like the module is hanging (see issue #1740).
|
||||
configuration_filename = "/tmp/configuration.nix"
|
||||
with open(configuration_filename, "w") as f:
|
||||
f.write(text)
|
||||
|
||||
libcalamares.job.setprogress(0.1)
|
||||
libcalamares.utils.check_target_env_call(["nix", configuration_filename])
|
||||
|
||||
# To indicate an error, return a tuple of:
|
||||
# (message, detailed-error-message)
|
||||
return None
|
||||
@@ -1,11 +0,0 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# The NixOS module writes a nix-configuration file and then calls
|
||||
# the Nix configuration tool to do the actual work of building
|
||||
# the target system.
|
||||
---
|
||||
type: "job"
|
||||
name: "os-nixos"
|
||||
interface: "python"
|
||||
script: "main.py"
|
||||
@@ -1,6 +0,0 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
hostname: my-nix-host
|
||||
locationRegion: Asia
|
||||
locationZone: Kolkata
|
||||
@@ -10,9 +10,9 @@ set(_extra_src "")
|
||||
### OPTIONAL AppData XML support in PackageModel
|
||||
#
|
||||
#
|
||||
option(BUILD_APPDATA "Support appdata: items in PackageChooser (requires QtXml)" OFF)
|
||||
option(BUILD_APPDATA "Support appdata: items in PackageChooser (requires QtXml)" ON)
|
||||
if(BUILD_APPDATA)
|
||||
find_package(${qtname} REQUIRED COMPONENTS Xml)
|
||||
find_package(${qtname} COMPONENTS Xml)
|
||||
if(TARGET ${qtname}::Xml)
|
||||
add_definitions(-DHAVE_APPDATA)
|
||||
list(APPEND _extra_libraries ${qtname}::Xml)
|
||||
@@ -23,8 +23,24 @@ endif()
|
||||
### OPTIONAL AppStream support in PackageModel
|
||||
#
|
||||
#
|
||||
# include(AppStreamHelper)
|
||||
#
|
||||
option(BUILD_APPSTREAM "Support appstream: items in PackageChooser (requires libappstream-qt)" ON)
|
||||
if(BUILD_APPSTREAM)
|
||||
find_package(AppStreamQt)
|
||||
set_package_properties(
|
||||
AppStreamQt
|
||||
PROPERTIES
|
||||
DESCRIPTION "Support for AppStream (cache) data"
|
||||
URL "https://github.com/ximion/appstream"
|
||||
PURPOSE "AppStream provides package data"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
if(AppStreamQt_FOUND)
|
||||
add_definitions(-DHAVE_APPSTREAM_VERSION=${AppStreamQt_VERSION_MAJOR})
|
||||
list(APPEND _extra_libraries AppStreamQt)
|
||||
list(APPEND _extra_src ItemAppStream.cpp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
calamares_add_plugin(packagechooser
|
||||
TYPE viewmodule
|
||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||
@@ -33,8 +49,6 @@ calamares_add_plugin(packagechooser
|
||||
PackageChooserPage.cpp
|
||||
PackageChooserViewStep.cpp
|
||||
PackageModel.cpp
|
||||
LoaderQueue.cpp
|
||||
PackageTreeItem.cpp
|
||||
${_extra_src}
|
||||
RESOURCES
|
||||
packagechooser.qrc
|
||||
@@ -45,11 +59,7 @@ calamares_add_plugin(packagechooser
|
||||
SHARED_LIB
|
||||
)
|
||||
|
||||
# if(AppStreamQt_FOUND)
|
||||
# target_link_libraries(calamares_viewmodule_packagechooser PRIVATE calamares::appstreamqt)
|
||||
# target_sources(calamares_viewmodule_packagechooser PRIVATE ItemAppStream.cpp)
|
||||
# endif()
|
||||
|
||||
# include(CalamaresAddTest)
|
||||
# calamares_add_test(
|
||||
# packagechoosertest
|
||||
# GUI
|
||||
|
||||
@@ -10,14 +10,13 @@
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "LoaderQueue.h"
|
||||
|
||||
#ifdef HAVE_APPDATA
|
||||
#include "ItemAppData.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_APPSTREAM_VERSION
|
||||
#include "ItemAppStream.h"
|
||||
#include <AppStreamQt/pool.h>
|
||||
#include <memory>
|
||||
#endif
|
||||
|
||||
@@ -27,9 +26,6 @@
|
||||
#include "packages/Globals.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
#include "network/Manager.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
/** @brief This removes any values from @p groups that match @p source
|
||||
*
|
||||
@@ -91,68 +87,12 @@ PackageChooserMethodNames()
|
||||
Config::Config( QObject* parent )
|
||||
: Calamares::ModuleSystem::Config( parent )
|
||||
, m_model( new PackageListModel( this ) )
|
||||
, m_netmodel( new PackageNetModel( this ) )
|
||||
, m_mode( PackageChooserMode::Required )
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
Config::~Config() {}
|
||||
|
||||
QString
|
||||
Config::status() const
|
||||
{
|
||||
switch ( m_status )
|
||||
{
|
||||
case Status::Ok:
|
||||
return QString();
|
||||
case Status::FailedBadConfiguration:
|
||||
return tr( "Network Installation. (Disabled: Incorrect configuration)" );
|
||||
case Status::FailedBadData:
|
||||
return tr( "Network Installation. (Disabled: Received invalid groups data)" );
|
||||
case Status::FailedInternalError:
|
||||
return tr( "Network Installation. (Disabled: Internal error)" );
|
||||
case Status::FailedNetworkError:
|
||||
return tr( "Network Installation. (Disabled: Unable to fetch package lists, check your network connection)" );
|
||||
case Status::FailedNoData:
|
||||
return tr( "Network Installation. (Disabled: No package list)" );
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void
|
||||
Config::setStatus( Status s )
|
||||
{
|
||||
m_status = s;
|
||||
emit statusChanged( status() );
|
||||
}
|
||||
|
||||
void
|
||||
Config::loadGroupList( const QVariantList& groupData )
|
||||
{
|
||||
m_netmodel->setupModelData( groupData );
|
||||
if ( m_netmodel->rowCount() < 1 )
|
||||
{
|
||||
cWarning() << "NetInstall groups data was empty.";
|
||||
setStatus( Status::FailedNoData );
|
||||
}
|
||||
else
|
||||
{
|
||||
setStatus( Status::Ok );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::loadingDone()
|
||||
{
|
||||
if ( m_queue )
|
||||
{
|
||||
m_queue->deleteLater();
|
||||
m_queue = nullptr;
|
||||
}
|
||||
emit statusReady();
|
||||
}
|
||||
|
||||
const PackageItem&
|
||||
Config::introductionPackage() const
|
||||
{
|
||||
@@ -384,29 +324,6 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
fillModel( m_model, configurationMap.value( "items" ).toList() );
|
||||
|
||||
// Lastly, load the groups data
|
||||
// const QString key = QStringLiteral( "groupsUrl" );
|
||||
//
|
||||
//
|
||||
// const auto& groupsUrlVariant = configurationMap.value( key );
|
||||
// m_queue = new LoaderQueue( this );
|
||||
// if ( Calamares::typeOf( groupsUrlVariant ) == Calamares::StringVariantType )
|
||||
// {
|
||||
// m_queue->append( SourceItem::makeSourceItem( groupsUrlVariant.toString(), configurationMap ) );
|
||||
// }
|
||||
// else if ( Calamares::typeOf( groupsUrlVariant ) == Calamares::ListVariantType )
|
||||
// {
|
||||
// for ( const auto& s : groupsUrlVariant.toStringList() )
|
||||
// {
|
||||
// m_queue->append( SourceItem::makeSourceItem( s, configurationMap ) );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// setStatus( required() ? Status::FailedNoData : Status::Ok );
|
||||
// cDebug() << "Loading netinstall from" << m_queue->count() << "alternate sources.";
|
||||
// connect( m_queue, &LoaderQueue::done, this, &Config::loadingDone );
|
||||
// m_queue->load();
|
||||
|
||||
QString default_item_id = Calamares::getString( configurationMap, "default" );
|
||||
if ( !default_item_id.isEmpty() )
|
||||
{
|
||||
|
||||
@@ -16,16 +16,9 @@
|
||||
#include "modulesystem/Config.h"
|
||||
#include "modulesystem/InstanceKey.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
|
||||
|
||||
class LoaderQueue;
|
||||
|
||||
enum class PackageChooserMode
|
||||
{
|
||||
Optional, // zero or one
|
||||
@@ -62,9 +55,6 @@ class Config : public Calamares::ModuleSystem::Config
|
||||
Q_PROPERTY( QString packageChoice READ packageChoice WRITE setPackageChoice NOTIFY packageChoiceChanged )
|
||||
Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL )
|
||||
|
||||
Q_PROPERTY( PackageNetModel* PackageNetModel MEMBER m_netmodel FINAL )
|
||||
Q_PROPERTY( QString status READ status NOTIFY statusChanged FINAL )
|
||||
|
||||
public:
|
||||
Config( QObject* parent = nullptr );
|
||||
~Config() override;
|
||||
@@ -83,10 +73,6 @@ public:
|
||||
PackageListModel* model() const { return m_model; }
|
||||
QModelIndex defaultSelectionIndex() const { return m_defaultModelIndex; }
|
||||
|
||||
PackageNetModel* netmodel() const { return m_netmodel; }
|
||||
void loadGroupList( const QVariantList& groupData );
|
||||
|
||||
|
||||
/** @brief Returns an "introductory package" which describes packagechooser
|
||||
*
|
||||
* If the model contains a "none" package, returns that one on
|
||||
@@ -115,43 +101,14 @@ public:
|
||||
QString prettyName() const;
|
||||
QString prettyStatus() const;
|
||||
|
||||
enum class Status
|
||||
{
|
||||
Ok,
|
||||
FailedBadConfiguration,
|
||||
FailedInternalError,
|
||||
FailedNetworkError,
|
||||
FailedBadData,
|
||||
FailedNoData
|
||||
};
|
||||
|
||||
/// Human-readable, translated representation of the status
|
||||
QString status() const;
|
||||
/// Internal code for the status
|
||||
Status statusCode() const { return m_status; }
|
||||
void setStatus( Status s );
|
||||
|
||||
signals:
|
||||
void packageChoiceChanged( QString packageChoice );
|
||||
void prettyStatusChanged();
|
||||
|
||||
Q_SIGNALS:
|
||||
void statusChanged( QString status ); ///< Something changed
|
||||
void sidebarLabelChanged( QString label );
|
||||
void titleLabelChanged( QString label );
|
||||
void statusReady(); ///< Loading groups is complete
|
||||
|
||||
private Q_SLOTS:
|
||||
void loadingDone();
|
||||
|
||||
private:
|
||||
PackageListModel* m_model = nullptr;
|
||||
QModelIndex m_defaultModelIndex;
|
||||
|
||||
PackageNetModel* m_netmodel = nullptr;
|
||||
LoaderQueue* m_queue = nullptr;
|
||||
Status m_status = Status::Ok;
|
||||
|
||||
/// Selection mode for this module
|
||||
PackageChooserMode m_mode = PackageChooserMode::Optional;
|
||||
/// How this module stores to GS
|
||||
|
||||
@@ -7,16 +7,20 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/** @brief Loading items from AppStream database.
|
||||
/** @brief Loading items from AppData XML files.
|
||||
*
|
||||
* Only used if AppStreamQt is found, implements PackageItem::fromAppStream().
|
||||
* Only used if QtXML is found, implements PackageItem::fromAppData().
|
||||
*/
|
||||
#include "ItemAppStream.h"
|
||||
#include "PackageModel.h"
|
||||
|
||||
#include "locale/TranslationsModel.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include <AppStreamQt/image.h>
|
||||
#include <AppStreamQt/pool.h>
|
||||
#include <AppStreamQt/screenshot.h>
|
||||
|
||||
/// @brief Return number of pixels in a size, for < ordering purposes
|
||||
static inline quint64
|
||||
sizeOrder( const QSize& size )
|
||||
|
||||
@@ -12,28 +12,10 @@
|
||||
|
||||
#include "PackageModel.h"
|
||||
|
||||
/*
|
||||
* This weird include mechanism is because an #include line is allowed
|
||||
* to consist of preprocessor-tokens, which are expanded, and then
|
||||
* the #include is *re*processed. But if it starts with < or ", then
|
||||
* preprocessor tokens are not expanded. So we build up a #include <>
|
||||
* style line with a suitable path -- if we are given a value for
|
||||
* HAVE_APPSTREAM_HEADERS, that is the directory that the AppStreamQt
|
||||
* headers live in.
|
||||
*/
|
||||
#define CALAMARES_LT <
|
||||
#define CALAMARES_GT >
|
||||
|
||||
#ifndef HAVE_APPSTREAM_HEADERS
|
||||
#define HAVE_APPSTREAM_HEADERS AppStreamQt
|
||||
#endif
|
||||
|
||||
#include CALAMARES_LT HAVE_APPSTREAM_HEADERS/pool.h CALAMARES_GT
|
||||
#include CALAMARES_LT HAVE_APPSTREAM_HEADERS/image.h CALAMARES_GT
|
||||
#include CALAMARES_LT HAVE_APPSTREAM_HEADERS/screenshot.h CALAMARES_GT
|
||||
|
||||
#undef CALAMARES_LT
|
||||
#undef CALAMARES_GT
|
||||
namespace AppStream
|
||||
{
|
||||
class Pool;
|
||||
} // namespace AppStream
|
||||
|
||||
/** @brief Loads an item from AppStream data.
|
||||
*
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2016 Luca Giambonini <almack@chakraos.org>
|
||||
* SPDX-FileCopyrightText: 2016 Lisa Vitolo <shainer@chakraos.org>
|
||||
* SPDX-FileCopyrightText: 2017 Kyle Robbertze <krobbertze@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2017-2018 2020, Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "LoaderQueue.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "network/Manager.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/RAII.h"
|
||||
#include "utils/Yaml.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QTimer>
|
||||
|
||||
/** @brief Call fetchNext() on the queue if it can
|
||||
*
|
||||
* On destruction, a new call to fetchNext() is queued, so that
|
||||
* the queue continues loading. Calling release() before the
|
||||
* destructor skips the fetchNext(), ending the queue-loading.
|
||||
*
|
||||
* Calling done(b) is a conditional release: if @p b is @c true,
|
||||
* queues a call to done() on the queue and releases it; otherwise,
|
||||
* does nothing.
|
||||
*/
|
||||
class FetchNextUnless
|
||||
{
|
||||
public:
|
||||
FetchNextUnless( LoaderQueue* q )
|
||||
: m_q( q )
|
||||
{
|
||||
}
|
||||
~FetchNextUnless()
|
||||
{
|
||||
if ( m_q )
|
||||
{
|
||||
QMetaObject::invokeMethod( m_q, "fetchNext", Qt::QueuedConnection );
|
||||
}
|
||||
}
|
||||
void release() { m_q = nullptr; }
|
||||
void done( bool b )
|
||||
{
|
||||
if ( b )
|
||||
{
|
||||
if ( m_q )
|
||||
{
|
||||
QMetaObject::invokeMethod( m_q, "done", Qt::QueuedConnection );
|
||||
}
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
LoaderQueue* m_q = nullptr;
|
||||
};
|
||||
|
||||
SourceItem
|
||||
SourceItem::makeSourceItem( const QString& groupsUrl, const QVariantMap& configurationMap )
|
||||
{
|
||||
if ( groupsUrl == QStringLiteral( "local" ) )
|
||||
{
|
||||
return SourceItem { QUrl(), configurationMap.value( "groups" ).toList() };
|
||||
}
|
||||
else
|
||||
{
|
||||
return SourceItem { QUrl { groupsUrl }, QVariantList() };
|
||||
}
|
||||
}
|
||||
|
||||
LoaderQueue::LoaderQueue( Config* parent )
|
||||
: QObject( parent )
|
||||
, m_config( parent )
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
LoaderQueue::append( SourceItem&& i )
|
||||
{
|
||||
m_queue.append( std::move( i ) );
|
||||
}
|
||||
|
||||
void
|
||||
LoaderQueue::load()
|
||||
{
|
||||
QMetaObject::invokeMethod( this, "fetchNext", Qt::QueuedConnection );
|
||||
}
|
||||
|
||||
void
|
||||
LoaderQueue::fetchNext()
|
||||
{
|
||||
if ( m_queue.isEmpty() )
|
||||
{
|
||||
emit done();
|
||||
return;
|
||||
}
|
||||
|
||||
auto source = m_queue.takeFirst();
|
||||
if ( source.isLocal() )
|
||||
{
|
||||
m_config->loadGroupList( source.data );
|
||||
emit done();
|
||||
}
|
||||
else
|
||||
{
|
||||
fetch( source.url );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LoaderQueue::fetch( const QUrl& url )
|
||||
{
|
||||
FetchNextUnless next( this );
|
||||
|
||||
if ( !url.isValid() )
|
||||
{
|
||||
m_config->setStatus( Config::Status::FailedBadConfiguration );
|
||||
cDebug() << "Invalid URL" << url;
|
||||
return;
|
||||
}
|
||||
|
||||
using namespace Calamares::Network;
|
||||
|
||||
cDebug() << "NetInstall loading groups from" << url;
|
||||
QNetworkReply* reply = Manager().asynchronousGet(
|
||||
url,
|
||||
RequestOptions( RequestOptions::FakeUserAgent | RequestOptions::FollowRedirect, std::chrono::seconds( 30 ) ) );
|
||||
|
||||
if ( !reply )
|
||||
{
|
||||
cDebug() << Logger::SubEntry << "Request failed immediately.";
|
||||
// If nobody sets a different status, this will remain
|
||||
m_config->setStatus( Config::Status::FailedBadConfiguration );
|
||||
}
|
||||
else
|
||||
{
|
||||
// When the network request is done, **then** we might
|
||||
// do the next item from the queue, so don't call fetchNext() now.
|
||||
next.release();
|
||||
m_reply = reply;
|
||||
connect( reply, &QNetworkReply::finished, this, &LoaderQueue::dataArrived );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LoaderQueue::dataArrived()
|
||||
{
|
||||
FetchNextUnless next( this );
|
||||
|
||||
if ( !m_reply || !m_reply->isFinished() )
|
||||
{
|
||||
cWarning() << "NetInstall data called too early.";
|
||||
m_config->setStatus( Config::Status::FailedInternalError );
|
||||
return;
|
||||
}
|
||||
|
||||
cDebug() << "NetInstall group data received" << m_reply->size() << "bytes from" << m_reply->url();
|
||||
|
||||
cqDeleter< QNetworkReply > d { m_reply };
|
||||
|
||||
// If m_required is *false* then we still say we're ready
|
||||
// even if the reply is corrupt or missing.
|
||||
if ( m_reply->error() != QNetworkReply::NoError )
|
||||
{
|
||||
cWarning() << "unable to fetch netinstall package lists.";
|
||||
cDebug() << Logger::SubEntry << "Netinstall reply error: " << m_reply->error();
|
||||
cDebug() << Logger::SubEntry << "Request for url: " << m_reply->url().toString()
|
||||
<< " failed with: " << m_reply->errorString();
|
||||
m_config->setStatus( Config::Status::FailedNetworkError );
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray yamlData = m_reply->readAll();
|
||||
try
|
||||
{
|
||||
auto groups = ::YAML::Load( yamlData.constData() );
|
||||
|
||||
if ( groups.IsSequence() )
|
||||
{
|
||||
m_config->loadGroupList( Calamares::YAML::sequenceToVariant( groups ) );
|
||||
next.done( m_config->statusCode() == Config::Status::Ok );
|
||||
}
|
||||
else if ( groups.IsMap() )
|
||||
{
|
||||
auto map = Calamares::YAML::mapToVariant( groups );
|
||||
m_config->loadGroupList( map.value( "groups" ).toList() );
|
||||
next.done( m_config->statusCode() == Config::Status::Ok );
|
||||
}
|
||||
else
|
||||
{
|
||||
cWarning() << "NetInstall groups data does not form a sequence.";
|
||||
}
|
||||
}
|
||||
catch ( ::YAML::Exception& e )
|
||||
{
|
||||
Calamares::YAML::explainException( e, yamlData, "netinstall groups data" );
|
||||
m_config->setStatus( Config::Status::FailedBadData );
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2016 Luca Giambonini <almack@chakraos.org>
|
||||
* SPDX-FileCopyrightText: 2016 Lisa Vitolo <shainer@chakraos.org>
|
||||
* SPDX-FileCopyrightText: 2017 Kyle Robbertze <krobbertze@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2017-2018 2020, Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NETINSTALL_LOADERQUEUE_H
|
||||
#define NETINSTALL_LOADERQUEUE_H
|
||||
|
||||
#include <QQueue>
|
||||
#include <QUrl>
|
||||
#include <QVariantList>
|
||||
|
||||
class Config;
|
||||
class QNetworkReply;
|
||||
|
||||
/** @brief Data about an entry in *groupsUrl*
|
||||
*
|
||||
* This can be a specific URL, or "local" which uses data stored
|
||||
* in the configuration file itself.
|
||||
*/
|
||||
struct SourceItem
|
||||
{
|
||||
QUrl url;
|
||||
QVariantList data;
|
||||
|
||||
bool isUrl() const { return url.isValid(); }
|
||||
bool isLocal() const { return !data.isEmpty(); }
|
||||
bool isValid() const { return isUrl() || isLocal(); }
|
||||
/** @brief Create a SourceItem
|
||||
*
|
||||
* If the @p groupsUrl is @c "local" then the *groups* key in
|
||||
* the @p configurationMap is used as the source; otherwise the
|
||||
* string is used as an actual URL.
|
||||
*/
|
||||
static SourceItem makeSourceItem( const QString& groupsUrl, const QVariantMap& configurationMap );
|
||||
};
|
||||
|
||||
/** @brief Queue of source items to load
|
||||
*
|
||||
* Queue things up by calling append() and then kick things off
|
||||
* by calling load(). This will try to load the items, in order;
|
||||
* the first one that succeeds will end the loading process.
|
||||
*
|
||||
* Signal done() is emitted when done (also when all of the items fail).
|
||||
*/
|
||||
class LoaderQueue : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LoaderQueue( Config* parent );
|
||||
|
||||
void append( SourceItem&& i );
|
||||
int count() const { return m_queue.count(); }
|
||||
|
||||
public Q_SLOTS:
|
||||
void load();
|
||||
|
||||
void fetchNext();
|
||||
void fetch( const QUrl& url );
|
||||
void dataArrived();
|
||||
|
||||
Q_SIGNALS:
|
||||
void done();
|
||||
|
||||
private:
|
||||
QQueue< SourceItem > m_queue;
|
||||
Config* m_config = nullptr;
|
||||
QNetworkReply* m_reply = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -13,9 +13,6 @@
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include "compat/Variant.h"
|
||||
#include "utils/Yaml.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
/** @brief A wrapper for Calamares::getSubMap that excludes the success param
|
||||
@@ -197,379 +194,3 @@ PackageListModel::data( const QModelIndex& index, int role ) const
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
/// Recursive helper for setSelections()
|
||||
static void
|
||||
setSelections( const QStringList& selectNames, PackageTreeItem* item )
|
||||
{
|
||||
for ( int i = 0; i < item->childCount(); i++ )
|
||||
{
|
||||
auto* child = item->child( i );
|
||||
setSelections( selectNames, child );
|
||||
}
|
||||
if ( item->isGroup() && selectNames.contains( item->name() ) )
|
||||
{
|
||||
item->setSelected( Qt::CheckState::Checked );
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Collects all the "source" values from @p groupList
|
||||
*
|
||||
* Iterates over @p groupList and returns all nonempty "source"
|
||||
* values from the maps.
|
||||
*
|
||||
*/
|
||||
static QStringList
|
||||
collectSources( const QVariantList& groupList )
|
||||
{
|
||||
QStringList sources;
|
||||
for ( const QVariant& group : groupList )
|
||||
{
|
||||
QVariantMap groupMap = group.toMap();
|
||||
if ( !groupMap[ "source" ].toString().isEmpty() )
|
||||
{
|
||||
sources.append( groupMap[ "source" ].toString() );
|
||||
}
|
||||
}
|
||||
|
||||
return sources;
|
||||
}
|
||||
|
||||
PackageNetModel::PackageNetModel( QObject* parent )
|
||||
: QAbstractItemModel( parent )
|
||||
{
|
||||
}
|
||||
|
||||
PackageNetModel::~PackageNetModel()
|
||||
{
|
||||
delete m_rootItem;
|
||||
}
|
||||
|
||||
QModelIndex
|
||||
PackageNetModel::index( int row, int column, const QModelIndex& parent ) const
|
||||
{
|
||||
if ( !m_rootItem || !hasIndex( row, column, parent ) )
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
PackageTreeItem* parentItem;
|
||||
|
||||
if ( !parent.isValid() )
|
||||
{
|
||||
parentItem = m_rootItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentItem = static_cast< PackageTreeItem* >( parent.internalPointer() );
|
||||
}
|
||||
|
||||
PackageTreeItem* childItem = parentItem->child( row );
|
||||
if ( childItem )
|
||||
{
|
||||
return createIndex( row, column, childItem );
|
||||
}
|
||||
else
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex
|
||||
PackageNetModel::parent( const QModelIndex& index ) const
|
||||
{
|
||||
if ( !m_rootItem || !index.isValid() )
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
PackageTreeItem* child = static_cast< PackageTreeItem* >( index.internalPointer() );
|
||||
PackageTreeItem* parent = child->parentItem();
|
||||
|
||||
if ( parent == m_rootItem )
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
return createIndex( parent->row(), 0, parent );
|
||||
}
|
||||
|
||||
int
|
||||
PackageNetModel::rowCount( const QModelIndex& parent ) const
|
||||
{
|
||||
if ( !m_rootItem || ( parent.column() > 0 ) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
PackageTreeItem* parentItem;
|
||||
if ( !parent.isValid() )
|
||||
{
|
||||
parentItem = m_rootItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentItem = static_cast< PackageTreeItem* >( parent.internalPointer() );
|
||||
}
|
||||
|
||||
return parentItem->childCount();
|
||||
}
|
||||
|
||||
int
|
||||
PackageNetModel::columnCount( const QModelIndex& ) const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
QVariant
|
||||
PackageNetModel::data( const QModelIndex& index, int role ) const
|
||||
{
|
||||
if ( !m_rootItem || !index.isValid() )
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() );
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::CheckStateRole:
|
||||
return index.column() == NameColumn ? ( item->isImmutable() ? QVariant() : item->isSelected() ) : QVariant();
|
||||
case Qt::DisplayRole:
|
||||
return item->isHidden() ? QVariant() : item->data( index.column() );
|
||||
case MetaExpandRole:
|
||||
return item->isHidden() ? false : item->expandOnStart();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PackageNetModel::setData( const QModelIndex& index, const QVariant& value, int role )
|
||||
{
|
||||
if ( !m_rootItem )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( role == Qt::CheckStateRole && index.isValid() )
|
||||
{
|
||||
PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() );
|
||||
item->setSelected( static_cast< Qt::CheckState >( value.toInt() ) );
|
||||
|
||||
emit dataChanged( this->index( 0, 0 ),
|
||||
index.sibling( index.column(), index.row() + 1 ),
|
||||
QVector< int >( Qt::CheckStateRole ) );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::ItemFlags
|
||||
PackageNetModel::flags( const QModelIndex& index ) const
|
||||
{
|
||||
if ( !m_rootItem || !index.isValid() )
|
||||
{
|
||||
return Qt::ItemFlags();
|
||||
}
|
||||
if ( index.column() == NameColumn )
|
||||
{
|
||||
PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() );
|
||||
if ( item->isImmutable() || item->isNoncheckable() )
|
||||
{
|
||||
return QAbstractItemModel::flags( index ); //Qt::NoItemFlags;
|
||||
}
|
||||
return Qt::ItemIsUserCheckable | QAbstractItemModel::flags( index );
|
||||
}
|
||||
return QAbstractItemModel::flags( index );
|
||||
}
|
||||
|
||||
QVariant
|
||||
PackageNetModel::headerData( int section, Qt::Orientation orientation, int role ) const
|
||||
{
|
||||
if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
|
||||
{
|
||||
return ( section == NameColumn ) ? tr( "Name" ) : tr( "Description" );
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void
|
||||
PackageNetModel::setSelections( const QStringList& selectNames )
|
||||
{
|
||||
if ( m_rootItem )
|
||||
{
|
||||
::setSelections( selectNames, m_rootItem );
|
||||
}
|
||||
}
|
||||
|
||||
PackageTreeItem::List
|
||||
PackageNetModel::getPackages() const
|
||||
{
|
||||
if ( !m_rootItem )
|
||||
{
|
||||
return PackageTreeItem::List();
|
||||
}
|
||||
|
||||
auto items = getItemPackages( m_rootItem );
|
||||
for ( auto package : m_hiddenItems )
|
||||
{
|
||||
if ( package->hiddenSelected() )
|
||||
{
|
||||
items.append( getItemPackages( package ) );
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
PackageTreeItem::List
|
||||
PackageNetModel::getItemPackages( PackageTreeItem* item ) const
|
||||
{
|
||||
PackageTreeItem::List selectedPackages;
|
||||
for ( int i = 0; i < item->childCount(); i++ )
|
||||
{
|
||||
auto* child = item->child( i );
|
||||
if ( child->isSelected() == Qt::Unchecked )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( child->isPackage() ) // package
|
||||
{
|
||||
selectedPackages.append( child );
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedPackages.append( getItemPackages( child ) );
|
||||
}
|
||||
}
|
||||
return selectedPackages;
|
||||
}
|
||||
|
||||
void
|
||||
PackageNetModel::setupModelData( const QVariantList& groupList, PackageTreeItem* parent )
|
||||
{
|
||||
for ( const auto& group : groupList )
|
||||
{
|
||||
QVariantMap groupMap = group.toMap();
|
||||
if ( groupMap.isEmpty() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
PackageTreeItem* item = new PackageTreeItem( groupMap, PackageTreeItem::GroupTag { parent } );
|
||||
if ( groupMap.contains( "selected" ) )
|
||||
{
|
||||
item->setSelected( Calamares::getBool( groupMap, "selected", false ) ? Qt::Checked : Qt::Unchecked );
|
||||
}
|
||||
if ( groupMap.contains( "packages" ) )
|
||||
{
|
||||
for ( const auto& packageName : groupMap.value( "packages" ).toList() )
|
||||
{
|
||||
if ( Calamares::typeOf( packageName ) == Calamares::StringVariantType )
|
||||
{
|
||||
item->appendChild( new PackageTreeItem( packageName.toString(), item ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
QVariantMap m = packageName.toMap();
|
||||
if ( !m.isEmpty() )
|
||||
{
|
||||
item->appendChild( new PackageTreeItem( m, PackageTreeItem::PackageTag { item } ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !item->childCount() )
|
||||
{
|
||||
cWarning() << "*packages* under" << item->name() << "is empty.";
|
||||
}
|
||||
}
|
||||
if ( groupMap.contains( "subgroups" ) )
|
||||
{
|
||||
bool haveWarned = false;
|
||||
const auto& subgroupValue = groupMap.value( "subgroups" );
|
||||
if ( !subgroupValue.canConvert< QVariantList >() )
|
||||
{
|
||||
cWarning() << "*subgroups* under" << item->name() << "is not a list.";
|
||||
haveWarned = true;
|
||||
}
|
||||
|
||||
QVariantList subgroups = groupMap.value( "subgroups" ).toList();
|
||||
if ( !subgroups.isEmpty() )
|
||||
{
|
||||
setupModelData( subgroups, item );
|
||||
// The children might be checked while the parent isn't (yet).
|
||||
// Children are added to their parent (below) without affecting
|
||||
// the checked-state -- do it manually. Items with subgroups
|
||||
// but no children have only hidden children -- those get
|
||||
// handled specially.
|
||||
if ( item->childCount() > 0 )
|
||||
{
|
||||
item->updateSelected();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !haveWarned )
|
||||
{
|
||||
cWarning() << "*subgroups* list under" << item->name() << "is empty.";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( item->isHidden() )
|
||||
{
|
||||
m_hiddenItems.append( item );
|
||||
if ( !item->isSelected() )
|
||||
{
|
||||
cWarning() << "Item" << ( item->parentItem() ? item->parentItem()->name() : QString() ) << '.'
|
||||
<< item->name() << "is hidden, but not selected.";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item->setCheckable( true );
|
||||
parent->appendChild( item );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PackageNetModel::setupModelData( const QVariantList& l )
|
||||
{
|
||||
beginResetModel();
|
||||
delete m_rootItem;
|
||||
m_rootItem = new PackageTreeItem();
|
||||
setupModelData( l, m_rootItem );
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void
|
||||
PackageNetModel::appendModelData( const QVariantList& groupList )
|
||||
{
|
||||
if ( m_rootItem )
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
const QStringList sources = collectSources( groupList );
|
||||
|
||||
if ( !sources.isEmpty() )
|
||||
{
|
||||
// Prune any existing data from the same source
|
||||
QList< int > removeList;
|
||||
for ( int i = 0; i < m_rootItem->childCount(); i++ )
|
||||
{
|
||||
PackageTreeItem* child = m_rootItem->child( i );
|
||||
if ( sources.contains( child->source() ) )
|
||||
{
|
||||
removeList.insert( 0, i );
|
||||
}
|
||||
}
|
||||
for ( const int& item : qAsConst( removeList ) )
|
||||
{
|
||||
m_rootItem->removeChild( item );
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new data to the model
|
||||
setupModelData( groupList, m_rootItem );
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
#include "locale/TranslatableConfiguration.h"
|
||||
#include "utils/NamedEnum.h"
|
||||
|
||||
#include "PackageTreeItem.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QString>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QPixmap>
|
||||
@@ -137,78 +132,4 @@ private:
|
||||
PackageList m_packages;
|
||||
};
|
||||
|
||||
|
||||
namespace YAML
|
||||
{
|
||||
class Node;
|
||||
} // namespace YAML
|
||||
|
||||
class PackageNetModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Names for columns (unused in the code)
|
||||
static constexpr const int NameColumn = 0;
|
||||
static constexpr const int DescriptionColumn = 1;
|
||||
|
||||
/* The only interesting roles are DisplayRole (with text depending
|
||||
* on the column, and MetaExpandRole which tells if an index
|
||||
* should be initially expanded.
|
||||
*/
|
||||
static constexpr const int MetaExpandRole = Qt::UserRole + 1;
|
||||
|
||||
explicit PackageNetModel( QObject* parent = nullptr );
|
||||
~PackageNetModel() override;
|
||||
|
||||
void setupModelData( const QVariantList& l );
|
||||
|
||||
QVariant data( const QModelIndex& index, int role ) const override;
|
||||
bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ) override;
|
||||
Qt::ItemFlags flags( const QModelIndex& index ) const override;
|
||||
|
||||
QModelIndex index( int row, int column, const QModelIndex& parent = QModelIndex() ) const override;
|
||||
QModelIndex parent( const QModelIndex& index ) const override;
|
||||
|
||||
QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override;
|
||||
int rowCount( const QModelIndex& parent = QModelIndex() ) const override;
|
||||
int columnCount( const QModelIndex& parent = QModelIndex() ) const override;
|
||||
|
||||
/** @brief Sets the checked flag on matching groups in the tree
|
||||
*
|
||||
* Recursively traverses the tree pointed to by m_rootItem and
|
||||
* checks if a group name matches any of the items in @p selectNames.
|
||||
* If a match is found, set check the box for that group and it's children.
|
||||
*
|
||||
* Individual packages will not be matched.
|
||||
*
|
||||
*/
|
||||
void setSelections( const QStringList& selectNames );
|
||||
|
||||
PackageTreeItem::List getPackages() const;
|
||||
PackageTreeItem::List getItemPackages( PackageTreeItem* item ) const;
|
||||
|
||||
/** @brief Appends groups to the tree
|
||||
*
|
||||
* Uses the data from @p groupList to add elements to the
|
||||
* existing tree that m_rootItem points to. If m_rootItem
|
||||
* is not valid, it does nothing
|
||||
*
|
||||
* Before adding anything to the model, it ensures that there
|
||||
* is no existing data from the same source. If there is, that
|
||||
* data is pruned first
|
||||
*
|
||||
*/
|
||||
void appendModelData( const QVariantList& groupList );
|
||||
|
||||
private:
|
||||
friend class ItemTests;
|
||||
|
||||
void setupModelData( const QVariantList& l, PackageTreeItem* parent );
|
||||
|
||||
PackageTreeItem* m_rootItem = nullptr;
|
||||
PackageTreeItem::List m_hiddenItems;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2017 Kyle Robbertze <kyle@aims.ac.za>
|
||||
* SPDX-FileCopyrightText: 2017 2020, Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "PackageTreeItem.h"
|
||||
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
/** @brief Should a package be selected, given its parent's state? */
|
||||
static Qt::CheckState
|
||||
parentCheckState( PackageTreeItem* parent )
|
||||
{
|
||||
if ( parent )
|
||||
{
|
||||
// Avoid partially-checked .. a package can't be partial
|
||||
return parent->isSelected() == Qt::Unchecked ? Qt::Unchecked : Qt::Checked;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Should a subgroup be marked critical?
|
||||
*
|
||||
* If set explicitly, then use that, otherwise use the parent's critical-ness.
|
||||
*/
|
||||
static bool
|
||||
parentCriticality( const QVariantMap& groupData, PackageTreeItem* parent )
|
||||
{
|
||||
if ( groupData.contains( "critical" ) )
|
||||
{
|
||||
return Calamares::getBool( groupData, "critical", false );
|
||||
}
|
||||
return parent ? parent->isCritical() : false;
|
||||
}
|
||||
|
||||
PackageTreeItem::PackageTreeItem( const QString& packageName, PackageTreeItem* parent )
|
||||
: m_parentItem( parent )
|
||||
, m_packageName( packageName )
|
||||
, m_selected( parentCheckState( parent ) )
|
||||
, m_isGroup( false )
|
||||
, m_isCritical( parent ? parent->isCritical() : false )
|
||||
, m_showReadOnly( parent ? parent->isImmutable() : false )
|
||||
, m_showNoncheckable( false )
|
||||
{
|
||||
}
|
||||
|
||||
PackageTreeItem::PackageTreeItem( const QVariantMap& groupData, PackageTag&& parent )
|
||||
: m_parentItem( parent.parent )
|
||||
, m_packageName( Calamares::getString( groupData, "name" ) )
|
||||
, m_selected( parentCheckState( parent.parent ) )
|
||||
, m_description( Calamares::getString( groupData, "description" ) )
|
||||
, m_isGroup( false )
|
||||
, m_isCritical( parent.parent ? parent.parent->isCritical() : false )
|
||||
, m_showReadOnly( parent.parent ? parent.parent->isImmutable() : false )
|
||||
, m_showNoncheckable( false )
|
||||
{
|
||||
}
|
||||
|
||||
PackageTreeItem::PackageTreeItem( const QVariantMap& groupData, GroupTag&& parent )
|
||||
: m_parentItem( parent.parent )
|
||||
, m_name( Calamares::getString( groupData, "name" ) )
|
||||
, m_selected( parentCheckState( parent.parent ) )
|
||||
, m_description( Calamares::getString( groupData, "description" ) )
|
||||
, m_preScript( Calamares::getString( groupData, "pre-install" ) )
|
||||
, m_postScript( Calamares::getString( groupData, "post-install" ) )
|
||||
, m_source( Calamares::getString( groupData, "source" ) )
|
||||
, m_isGroup( true )
|
||||
, m_isCritical( parentCriticality( groupData, parent.parent ) )
|
||||
, m_isHidden( Calamares::getBool( groupData, "hidden", false ) )
|
||||
, m_showReadOnly( Calamares::getBool( groupData, "immutable", false ) )
|
||||
, m_showNoncheckable( Calamares::getBool( groupData, "noncheckable", false ) )
|
||||
, m_startExpanded( Calamares::getBool( groupData, "expanded", false ) )
|
||||
{
|
||||
}
|
||||
|
||||
PackageTreeItem::PackageTreeItem::PackageTreeItem()
|
||||
: m_parentItem( nullptr )
|
||||
, m_name( QStringLiteral( "<root>" ) )
|
||||
, m_selected( Qt::Checked )
|
||||
, m_isGroup( true )
|
||||
{
|
||||
}
|
||||
|
||||
PackageTreeItem::~PackageTreeItem()
|
||||
{
|
||||
qDeleteAll( m_childItems );
|
||||
}
|
||||
|
||||
void
|
||||
PackageTreeItem::appendChild( PackageTreeItem* child )
|
||||
{
|
||||
m_childItems.append( child );
|
||||
}
|
||||
|
||||
PackageTreeItem*
|
||||
PackageTreeItem::child( int row )
|
||||
{
|
||||
return m_childItems.value( row );
|
||||
}
|
||||
|
||||
int
|
||||
PackageTreeItem::childCount() const
|
||||
{
|
||||
return m_childItems.count();
|
||||
}
|
||||
|
||||
int
|
||||
PackageTreeItem::row() const
|
||||
{
|
||||
if ( m_parentItem )
|
||||
{
|
||||
return m_parentItem->m_childItems.indexOf( const_cast< PackageTreeItem* >( this ) );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVariant
|
||||
PackageTreeItem::data( int column ) const
|
||||
{
|
||||
switch ( column )
|
||||
{
|
||||
case 0:
|
||||
// packages have a packagename, groups don't
|
||||
return QVariant( isPackage() ? packageName() : name() );
|
||||
case 1:
|
||||
// packages often have a blank description
|
||||
return QVariant( description() );
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
PackageTreeItem*
|
||||
PackageTreeItem::parentItem()
|
||||
{
|
||||
return m_parentItem;
|
||||
}
|
||||
|
||||
const PackageTreeItem*
|
||||
PackageTreeItem::parentItem() const
|
||||
{
|
||||
return m_parentItem;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageTreeItem::hiddenSelected() const
|
||||
{
|
||||
if ( !m_isHidden )
|
||||
{
|
||||
return m_selected != Qt::Unchecked;
|
||||
}
|
||||
|
||||
if ( m_selected == Qt::Unchecked )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const PackageTreeItem* currentItem = parentItem();
|
||||
while ( currentItem != nullptr )
|
||||
{
|
||||
if ( !currentItem->isHidden() )
|
||||
{
|
||||
return currentItem->isSelected() != Qt::Unchecked;
|
||||
}
|
||||
currentItem = currentItem->parentItem();
|
||||
}
|
||||
|
||||
/* Has no non-hidden parents */
|
||||
return m_selected != Qt::Unchecked;
|
||||
}
|
||||
|
||||
void
|
||||
PackageTreeItem::setSelected( Qt::CheckState isSelected )
|
||||
{
|
||||
if ( parentItem() == nullptr )
|
||||
{
|
||||
// This is the root, it is always checked so don't change state
|
||||
return;
|
||||
}
|
||||
|
||||
m_selected = isSelected;
|
||||
setChildrenSelected( isSelected );
|
||||
|
||||
// Look for suitable parent item which may change checked-state
|
||||
// when one of its children changes.
|
||||
PackageTreeItem* currentItem = parentItem();
|
||||
while ( ( currentItem != nullptr ) && ( currentItem->childCount() == 0 ) )
|
||||
{
|
||||
currentItem = currentItem->parentItem();
|
||||
}
|
||||
if ( currentItem == nullptr )
|
||||
{
|
||||
// Reached the root .. don't bother
|
||||
return;
|
||||
}
|
||||
|
||||
currentItem->updateSelected();
|
||||
}
|
||||
|
||||
void
|
||||
PackageTreeItem::updateSelected()
|
||||
{
|
||||
// Figure out checked-state based on the children
|
||||
int childrenSelected = 0;
|
||||
int childrenPartiallySelected = 0;
|
||||
for ( int i = 0; i < childCount(); i++ )
|
||||
{
|
||||
if ( child( i )->isSelected() == Qt::Checked )
|
||||
{
|
||||
childrenSelected++;
|
||||
}
|
||||
if ( child( i )->isSelected() == Qt::PartiallyChecked )
|
||||
{
|
||||
childrenPartiallySelected++;
|
||||
}
|
||||
}
|
||||
if ( !childrenSelected && !childrenPartiallySelected )
|
||||
{
|
||||
setSelected( Qt::Unchecked );
|
||||
}
|
||||
else if ( childrenSelected == childCount() )
|
||||
{
|
||||
setSelected( Qt::Checked );
|
||||
}
|
||||
else
|
||||
{
|
||||
setSelected( Qt::PartiallyChecked );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PackageTreeItem::setChildrenSelected( Qt::CheckState isSelected )
|
||||
{
|
||||
if ( isSelected != Qt::PartiallyChecked )
|
||||
{
|
||||
// Children are never root; don't need to use setSelected on them.
|
||||
for ( auto child : m_childItems )
|
||||
{
|
||||
child->m_selected = isSelected;
|
||||
child->setChildrenSelected( isSelected );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PackageTreeItem::removeChild( int row )
|
||||
{
|
||||
if ( 0 <= row && row < m_childItems.count() )
|
||||
{
|
||||
m_childItems.removeAt( row );
|
||||
}
|
||||
else
|
||||
{
|
||||
cWarning() << "Attempt to remove invalid child in removeChild() at row " << row;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
PackageTreeItem::type() const
|
||||
{
|
||||
return QStandardItem::UserType;
|
||||
}
|
||||
|
||||
QVariant
|
||||
PackageTreeItem::toOperation() const
|
||||
{
|
||||
// If it's a package with a pre- or post-script, replace
|
||||
// with the more complicated datastructure.
|
||||
if ( !m_preScript.isEmpty() || !m_postScript.isEmpty() )
|
||||
{
|
||||
QMap< QString, QVariant > sdetails;
|
||||
sdetails.insert( "pre-script", m_preScript );
|
||||
sdetails.insert( "package", m_packageName );
|
||||
sdetails.insert( "post-script", m_postScript );
|
||||
return sdetails;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_packageName;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PackageTreeItem::operator==( const PackageTreeItem& rhs ) const
|
||||
{
|
||||
if ( isGroup() != rhs.isGroup() )
|
||||
{
|
||||
// Different kinds
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( isGroup() )
|
||||
{
|
||||
return name() == rhs.name() && description() == rhs.description() && preScript() == rhs.preScript()
|
||||
&& postScript() == rhs.postScript() && isCritical() == rhs.isCritical() && isHidden() == rhs.isHidden()
|
||||
&& m_showReadOnly == rhs.m_showReadOnly && expandOnStart() == rhs.expandOnStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
return packageName() == rhs.packageName();
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2017 Kyle Robbertze <kyle@aims.ac.za>
|
||||
* SPDX-FileCopyrightText: 2017 2020, Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PACKAGETREEITEM_H
|
||||
#define PACKAGETREEITEM_H
|
||||
|
||||
#include <QList>
|
||||
#include <QStandardItem>
|
||||
#include <QVariant>
|
||||
|
||||
class PackageTreeItem : public QStandardItem
|
||||
{
|
||||
public:
|
||||
using List = QList< PackageTreeItem* >;
|
||||
|
||||
///@brief A tag class to distinguish package-from-map from group-from-map
|
||||
struct PackageTag
|
||||
{
|
||||
PackageTreeItem* parent;
|
||||
};
|
||||
///@brief A tag class to distinguish group-from-map from package-from-map
|
||||
struct GroupTag
|
||||
{
|
||||
PackageTreeItem* parent;
|
||||
};
|
||||
|
||||
///@brief A package (individual package)
|
||||
explicit PackageTreeItem( const QString& packageName, PackageTreeItem* parent = nullptr );
|
||||
///@brief A package (individual package with description)
|
||||
explicit PackageTreeItem( const QVariantMap& packageData, PackageTag&& parent );
|
||||
///@brief A group (sub-items and sub-groups are ignored)
|
||||
explicit PackageTreeItem( const QVariantMap& groupData, GroupTag&& parent );
|
||||
///@brief A root item, always selected, named "<root>"
|
||||
explicit PackageTreeItem();
|
||||
~PackageTreeItem() override;
|
||||
|
||||
void appendChild( PackageTreeItem* child );
|
||||
PackageTreeItem* child( int row );
|
||||
int childCount() const;
|
||||
QVariant data( int column ) const override;
|
||||
int row() const;
|
||||
|
||||
PackageTreeItem* parentItem();
|
||||
const PackageTreeItem* parentItem() const;
|
||||
|
||||
QString name() const { return m_name; }
|
||||
QString packageName() const { return m_packageName; }
|
||||
|
||||
QString description() const { return m_description; }
|
||||
QString preScript() const { return m_preScript; }
|
||||
QString postScript() const { return m_postScript; }
|
||||
QString source() const { return m_source; }
|
||||
|
||||
/** @brief Is this item a group-item?
|
||||
*
|
||||
* Groups have a (possibly empty) list of packages, and a
|
||||
* (possibly empty) list of sub-groups, and can be marked
|
||||
* critical, hidden, etc. Packages, on the other hand, only
|
||||
* have a meaningful packageName() and selection status.
|
||||
*
|
||||
* Root is a group.
|
||||
*/
|
||||
bool isGroup() const { return m_isGroup; }
|
||||
|
||||
/// @brief Is this item a single package?
|
||||
bool isPackage() const { return !isGroup(); }
|
||||
|
||||
/** @brief Is this item hidden?
|
||||
*
|
||||
* Hidden items (generally only groups) are maintained separately,
|
||||
* not shown to the user, but do enter into the package-installation process.
|
||||
*/
|
||||
bool isHidden() const { return m_isHidden; }
|
||||
|
||||
/** @brief Is this hidden item, considered "selected"?
|
||||
*
|
||||
* This asserts when called on a non-hidden item.
|
||||
* A hidden item has its own selected state, but really
|
||||
* falls under the selectedness of the parent item.
|
||||
*/
|
||||
bool hiddenSelected() const;
|
||||
|
||||
/** @brief Is this group critical?
|
||||
*
|
||||
* A critical group must be successfully installed, for the Calamares
|
||||
* installation to continue.
|
||||
*/
|
||||
bool isCritical() const { return m_isCritical; }
|
||||
|
||||
/** @brief Is this group expanded on start?
|
||||
*
|
||||
* This does not affect installation, only the UI. A group
|
||||
* that expands on start is shown expanded (not collapsed)
|
||||
* in the treeview when the page is loaded.
|
||||
*/
|
||||
bool expandOnStart() const { return m_startExpanded; }
|
||||
|
||||
/** @brief Is this an immutable item?
|
||||
*
|
||||
* Groups can be immutable: then you can't toggle the selected
|
||||
* state of any of its items.
|
||||
*/
|
||||
bool isImmutable() const { return m_showReadOnly; }
|
||||
|
||||
/** @brief Is this a non-checkable item?
|
||||
*
|
||||
* Groups can be non-checkable: then you can't toggle the selected
|
||||
* state of the group. This does not affect subgroups or packages.
|
||||
*/
|
||||
bool isNoncheckable() const { return m_showNoncheckable; }
|
||||
|
||||
/** @brief is this item selected?
|
||||
*
|
||||
* Groups may be partially selected; packages are only on or off.
|
||||
*/
|
||||
Qt::CheckState isSelected() const { return m_selected; }
|
||||
|
||||
/** @brief Turns this item into a variant for PackageOperations use
|
||||
*
|
||||
* For "plain" items, this is just the package name; items with
|
||||
* scripts return a map. See the package module for how it's interpreted.
|
||||
*/
|
||||
QVariant toOperation() const;
|
||||
|
||||
void setSelected( Qt::CheckState isSelected );
|
||||
void setChildrenSelected( Qt::CheckState isSelected );
|
||||
|
||||
void removeChild( int row );
|
||||
|
||||
/** @brief Update selectedness based on the children's states
|
||||
*
|
||||
* This only makes sense for groups, which might have packages
|
||||
* or subgroups; it checks only direct children.
|
||||
*/
|
||||
void updateSelected();
|
||||
|
||||
// QStandardItem methods
|
||||
int type() const override;
|
||||
|
||||
/** @brief Are two items equal
|
||||
*
|
||||
* This **disregards** parent-item and the child-items, and compares
|
||||
* only the fields for the items-proper (name, .. expanded). Note
|
||||
* also that *isSelected()* is a run-time state, and is **not**
|
||||
* compared either.
|
||||
*/
|
||||
bool operator==( const PackageTreeItem& rhs ) const;
|
||||
bool operator!=( const PackageTreeItem& rhs ) const { return !( *this == rhs ); }
|
||||
|
||||
private:
|
||||
PackageTreeItem* m_parentItem;
|
||||
List m_childItems;
|
||||
|
||||
// An entry can be a package, or a group.
|
||||
QString m_name;
|
||||
QString m_packageName;
|
||||
Qt::CheckState m_selected = Qt::Unchecked;
|
||||
|
||||
// These are only useful for groups
|
||||
QString m_description;
|
||||
QString m_preScript;
|
||||
QString m_postScript;
|
||||
QString m_source;
|
||||
bool m_isGroup = false;
|
||||
bool m_isCritical = false;
|
||||
bool m_isHidden = false;
|
||||
bool m_showReadOnly = false;
|
||||
bool m_showNoncheckable = false;
|
||||
bool m_startExpanded = false;
|
||||
};
|
||||
|
||||
#endif // PACKAGETREEITEM_H
|
||||
BIN
modules/packagechooser/images/calamares.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
2
modules/packagechooser/images/calamares.png.license
Normal file
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
BIN
modules/packagechooser/images/no-selection.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
@@ -1,5 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>images/no-selection.png</file>
|
||||
<file>images/calamares.png</file>
|
||||
<file>images/if.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
69
modules/packagechooserq/CMakeLists.txt
Normal file
@@ -0,0 +1,69 @@
|
||||
# === This file is part of Calamares - <https://calamares.io> ===
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
# SPDX-FileCopyrightText: 2021 Anke Boersma <demm@kaosx.us>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
if( NOT Calamares_WITH_QML )
|
||||
calamares_skip_module( "packagechooserq (QML is not supported in this build)" )
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(${qtname} ${QT_VERSION} CONFIG REQUIRED Core)
|
||||
|
||||
# Add optional libraries here
|
||||
set(USER_EXTRA_LIB)
|
||||
|
||||
# include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../packagechooser )
|
||||
set(_packagechooser ${CMAKE_CURRENT_SOURCE_DIR}/../packagechooser)
|
||||
include_directories(${_packagechooser})
|
||||
|
||||
### OPTIONAL AppData XML support in PackageModel
|
||||
#
|
||||
#
|
||||
option(BUILD_APPDATA "Support appdata: items in PackageChooser (requires QtXml)" ON)
|
||||
if(BUILD_APPDATA)
|
||||
find_package(${qtname} COMPONENTS Xml)
|
||||
if(TARGET ${qtname}::Xml)
|
||||
add_definitions(-DHAVE_APPDATA)
|
||||
list(APPEND _extra_libraries ${qtname}::Xml)
|
||||
list(APPEND _extra_src ${_packagechooser}/ItemAppData.cpp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
### OPTIONAL AppStream support in PackageModel
|
||||
#
|
||||
#
|
||||
option(BUILD_APPSTREAM "Support appstream: items in PackageChooser (requires libappstream-qt)" ON)
|
||||
if(BUILD_APPSTREAM)
|
||||
find_package(AppStreamQt)
|
||||
set_package_properties(
|
||||
AppStreamQt
|
||||
PROPERTIES
|
||||
DESCRIPTION "Support for AppStream (cache) data"
|
||||
URL "https://github.com/ximion/appstream"
|
||||
PURPOSE "AppStream provides package data"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
if(AppStreamQt_FOUND)
|
||||
add_definitions(-DHAVE_APPSTREAM_VERSION=${AppStreamQt_VERSION_MAJOR})
|
||||
list(APPEND _extra_libraries AppStreamQt)
|
||||
list(APPEND _extra_src ${_packagechooser}/ItemAppStream.cpp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
calamares_add_plugin(packagechooserq
|
||||
TYPE viewmodule
|
||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||
SOURCES
|
||||
PackageChooserQmlViewStep.cpp
|
||||
${_packagechooser}/Config.cpp
|
||||
${_packagechooser}/PackageModel.cpp
|
||||
${_extra_src}
|
||||
RESOURCES
|
||||
packagechooserq${QT_VERSION_SUFFIX}.qrc
|
||||
LINK_PRIVATE_LIBRARIES
|
||||
calamaresui
|
||||
${_extra_libraries}
|
||||
SHARED_LIB
|
||||
)
|
||||
86
modules/packagechooserq/PackageChooserQmlViewStep.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2021 Anke Boersma <demm@kaosx.us>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "PackageChooserQmlViewStep.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "locale/TranslatableConfiguration.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/System.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( PackageChooserQmlViewStepFactory, registerPlugin< PackageChooserQmlViewStep >(); )
|
||||
|
||||
PackageChooserQmlViewStep::PackageChooserQmlViewStep( QObject* parent )
|
||||
: Calamares::QmlViewStep( parent )
|
||||
, m_config( new Config( this ) )
|
||||
{
|
||||
emit nextStatusChanged( true );
|
||||
}
|
||||
|
||||
QString
|
||||
PackageChooserQmlViewStep::prettyName() const
|
||||
{
|
||||
return m_config->prettyName();
|
||||
}
|
||||
|
||||
QString
|
||||
PackageChooserQmlViewStep::prettyStatus() const
|
||||
{
|
||||
//QString option = m_pkgc;
|
||||
//return tr( "Install option: %1" ).arg( option );
|
||||
return m_config->prettyStatus();
|
||||
}
|
||||
|
||||
bool
|
||||
PackageChooserQmlViewStep::isNextEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageChooserQmlViewStep::isBackEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageChooserQmlViewStep::isAtBeginning() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageChooserQmlViewStep::isAtEnd() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Calamares::JobList
|
||||
PackageChooserQmlViewStep::jobs() const
|
||||
{
|
||||
Calamares::JobList l;
|
||||
return l;
|
||||
}
|
||||
|
||||
void
|
||||
PackageChooserQmlViewStep::onLeave()
|
||||
{
|
||||
m_config->updateGlobalStorage();
|
||||
}
|
||||
|
||||
void
|
||||
PackageChooserQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
m_config->setDefaultId( moduleInstanceKey() );
|
||||
m_config->setConfigurationMap( configurationMap );
|
||||
Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last
|
||||
}
|
||||
58
modules/packagechooserq/PackageChooserQmlViewStep.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2021 Anke Boersma <demm@kaosx.us>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PACKAGECHOOSERQMLVIEWSTEP_H
|
||||
#define PACKAGECHOOSERQMLVIEWSTEP_H
|
||||
|
||||
// Config from packagechooser module
|
||||
#include "Config.h"
|
||||
|
||||
#include "DllMacro.h"
|
||||
#include "locale/TranslatableConfiguration.h"
|
||||
#include "utils/PluginFactory.h"
|
||||
#include "viewpages/QmlViewStep.h"
|
||||
|
||||
#include <QVariantMap>
|
||||
|
||||
class Config;
|
||||
class PackageChooserPage;
|
||||
|
||||
class PLUGINDLLEXPORT PackageChooserQmlViewStep : public Calamares::QmlViewStep
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PackageChooserQmlViewStep( QObject* parent = nullptr );
|
||||
|
||||
QString prettyName() const override;
|
||||
QString prettyStatus() const override;
|
||||
|
||||
bool isNextEnabled() const override;
|
||||
bool isBackEnabled() const override;
|
||||
|
||||
bool isAtBeginning() const override;
|
||||
bool isAtEnd() const override;
|
||||
|
||||
//void onActivate() override;
|
||||
void onLeave() override;
|
||||
|
||||
Calamares::JobList jobs() const override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
|
||||
QObject* getConfig() override { return m_config; }
|
||||
|
||||
private:
|
||||
Config* m_config;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserQmlViewStepFactory )
|
||||
|
||||
#endif // PACKAGECHOOSERQMLVIEWSTEP_H
|
||||
BIN
modules/packagechooserq/images/if.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
modules/packagechooserq/images/libreoffice.jpg
Normal file
|
After Width: | Height: | Size: 47 KiB |
2
modules/packagechooserq/images/libreoffice.jpg.license
Normal file
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2020 demmm <anke62@gmail.com>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
BIN
modules/packagechooserq/images/no-selection.png
Normal file
|
After Width: | Height: | Size: 184 KiB |
2
modules/packagechooserq/images/no-selection.png.license
Normal file
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2020 demmm <anke62@gmail.com>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
BIN
modules/packagechooserq/images/plasma.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
2
modules/packagechooserq/images/plasma.png.license
Normal file
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2021 pngegg <https://www.pngegg.com/>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
304
modules/packagechooserq/packagechooserq-qt6.qml
Normal file
@@ -0,0 +1,304 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021 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
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#f2f2f2"
|
||||
|
||||
ButtonGroup {
|
||||
id: switchGroup
|
||||
}
|
||||
|
||||
Column {
|
||||
id: column
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
Rectangle {
|
||||
//id: rectangle
|
||||
width: 700
|
||||
height: 150
|
||||
color: "#ffffff"
|
||||
radius: 10
|
||||
border.width: 0
|
||||
Text {
|
||||
width: 450
|
||||
height: 104
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("OpenRC base system.<br/>
|
||||
Default option.")
|
||||
font.pointSize: 10
|
||||
anchors.verticalCenterOffset: -10
|
||||
anchors.horizontalCenterOffset: 100
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: element1
|
||||
x: 500
|
||||
y: 110
|
||||
width: 187
|
||||
height: 14
|
||||
text: qsTr("OpenRC")
|
||||
checked: true
|
||||
hoverEnabled: true
|
||||
ButtonGroup.group: switchGroup
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 40
|
||||
implicitHeight: 14
|
||||
radius: 10
|
||||
color: element1.checked ? "#3498db" : "#B9B9B9"
|
||||
border.color: element1.checked ? "#3498db" : "#cccccc"
|
||||
|
||||
Rectangle {
|
||||
x: element1.checked ? parent.width - width : 0
|
||||
y: (parent.height - height) / 2
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: element1.down ? "#cccccc" : "#ffffff"
|
||||
border.color: element1.checked ? (element1.down ? "#3498db" : "#3498db") : "#999999"
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if ( checked ) {
|
||||
config.packageChoice = "openrc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image1
|
||||
x: 8
|
||||
y: 25
|
||||
height: 100
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "images/if.png"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 700
|
||||
height: 150
|
||||
radius: 10
|
||||
border.width: 0
|
||||
Text {
|
||||
width: 450
|
||||
height: 104
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Dinit base system.")
|
||||
font.pointSize: 10
|
||||
anchors.verticalCenterOffset: -10
|
||||
anchors.horizontalCenterOffset: 100
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: element2
|
||||
x: 500
|
||||
y: 110
|
||||
width: 187
|
||||
height: 14
|
||||
text: qsTr("Dinit")
|
||||
checked: false
|
||||
hoverEnabled: true
|
||||
ButtonGroup.group: switchGroup
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 40
|
||||
implicitHeight: 14
|
||||
radius: 10
|
||||
color: element2.checked ? "#3498db" : "#B9B9B9"
|
||||
border.color: element2.checked ? "#3498db" : "#cccccc"
|
||||
|
||||
Rectangle {
|
||||
x: element2.checked ? parent.width - width : 0
|
||||
y: (parent.height - height) / 2
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: element2.down ? "#cccccc" : "#ffffff"
|
||||
border.color: element2.checked ? (element2.down ? "#3498db" : "#3498db") : "#999999"
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if ( checked ) {
|
||||
config.packageChoice = "dinit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image2
|
||||
x: 8
|
||||
y: 25
|
||||
height: 100
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "images/if.png"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 700
|
||||
height: 150
|
||||
color: "#ffffff"
|
||||
radius: 10
|
||||
border.width: 0
|
||||
Text {
|
||||
width: 450
|
||||
height: 104
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Runit base system.")
|
||||
font.pointSize: 10
|
||||
anchors.verticalCenterOffset: -10
|
||||
anchors.horizontalCenterOffset: 100
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: element3
|
||||
x: 500
|
||||
y: 110
|
||||
width: 187
|
||||
height: 14
|
||||
text: qsTr("Runit")
|
||||
checked: false
|
||||
hoverEnabled: true
|
||||
ButtonGroup.group: switchGroup
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 40
|
||||
implicitHeight: 14
|
||||
radius: 10
|
||||
color: element3.checked ? "#3498db" : "#B9B9B9"
|
||||
border.color: element3.checked ? "#3498db" : "#cccccc"
|
||||
|
||||
Rectangle {
|
||||
x: element3.checked ? parent.width - width : 0
|
||||
y: (parent.height - height) / 2
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: element3.down ? "#cccccc" : "#ffffff"
|
||||
border.color: element3.checked ? (element3.down ? "#3498db" : "#3498db") : "#999999"
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if ( checked ) {
|
||||
config.packageChoice = "runit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image3
|
||||
x: 8
|
||||
y: 25
|
||||
height: 100
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "images/if.png"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 700
|
||||
height: 150
|
||||
color: "#ffffff"
|
||||
radius: 10
|
||||
border.width: 0
|
||||
Text {
|
||||
width: 450
|
||||
height: 104
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("S6 base system.")
|
||||
font.pointSize: 10
|
||||
anchors.verticalCenterOffset: -10
|
||||
anchors.horizontalCenterOffset: 100
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: element4
|
||||
x: 500
|
||||
y: 110
|
||||
width: 187
|
||||
height: 14
|
||||
text: qsTr("S6")
|
||||
checked: false
|
||||
hoverEnabled: true
|
||||
ButtonGroup.group: switchGroup
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 40
|
||||
implicitHeight: 14
|
||||
radius: 10
|
||||
color: element4.checked ? "#3498db" : "#B9B9B9"
|
||||
border.color: element4.checked ? "#3498db" : "#cccccc"
|
||||
|
||||
Rectangle {
|
||||
x: element4.checked ? parent.width - width : 0
|
||||
y: (parent.height - height) / 2
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: element4.down ? "#cccccc" : "#ffffff"
|
||||
border.color: element4.checked ? (element4.down ? "#3498db" : "#3498db") : "#999999"
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if ( checked ) {
|
||||
config.packageChoice = "s6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image4
|
||||
x: 8
|
||||
y: 25
|
||||
height: 100
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "images/if.png"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 700
|
||||
height: 25
|
||||
color: "#f2f2f2"
|
||||
border.width: 0
|
||||
Text {
|
||||
height: 25
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Please select an option for your install, or use the default: OpenRC.")
|
||||
font.pointSize: 10
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
9
modules/packagechooserq/packagechooserq-qt6.qrc
Normal file
@@ -0,0 +1,9 @@
|
||||
<RCC>
|
||||
<qresource>
|
||||
<file alias="packagechooserq.qml">packagechooserq-qt6.qml</file>
|
||||
<file>images/libreoffice.jpg</file>
|
||||
<file>images/no-selection.png</file>
|
||||
<file>images/plasma.png</file>
|
||||
<file>images/if.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
66
modules/packagechooserq/packagechooserq.conf
Normal file
@@ -0,0 +1,66 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Configuration for the low-density software chooser, QML implementation
|
||||
#
|
||||
# The example QML implementation uses single-selection, rather than
|
||||
# a model for the available packages. That makes it simpler: the
|
||||
# QML itself codes the available options, descriptions and images
|
||||
# -- after all, this is **low density** selection, so a custom UI
|
||||
# can make sense for the few choices that need to be made.
|
||||
#
|
||||
#
|
||||
|
||||
---
|
||||
# Software installation method:
|
||||
#
|
||||
# - "legacy" or "custom" or "contextualprocess"
|
||||
# When set to "legacy", writes a GlobalStorage value for the choice that
|
||||
# has been made. The key is *packagechooser_<id>*. The module's
|
||||
# instance name is used; see the *instances* section of `settings.conf`.
|
||||
# If there is just one packagechooserq module, and no special instance is set,
|
||||
# resulting GS key is probably *packagechooser_packagechooserq*.
|
||||
# (Do note that the prefix of the GS key remains "packagechooser_")
|
||||
#
|
||||
# The GS value is a comma-separated list of the IDs of the selected
|
||||
# packages, or an empty string if none is selected.
|
||||
#
|
||||
# With "legacy" installation, you should have a contextualprocess or similar
|
||||
# module somewhere in the `exec` phase to process the GlobalStorage key
|
||||
# and actually **do** something for the packages.
|
||||
#
|
||||
# - "packages"
|
||||
# When set to "packages", writes GlobalStorage values suitable for
|
||||
# consumption by the *packages* module (which should appear later
|
||||
# in the `exec` section. These package settings will then be handed
|
||||
# off to whatever package manager is configured there.
|
||||
#
|
||||
# There is no need to put this module in the `exec` section. There
|
||||
# are no jobs that this module provides. You should put **other**
|
||||
# modules, either *contextualprocess* or *packages* or some custom
|
||||
# module, in the `exec` section to do the actual work.
|
||||
#
|
||||
method: legacy
|
||||
|
||||
# Human-visible strings in this module. These are all optional.
|
||||
# The following translated keys are used:
|
||||
# - *step*, used in the overall progress view (left-hand pane)
|
||||
#
|
||||
# Each key can have a [locale] added to it, which is used as
|
||||
# the translated string for that locale. For the strings
|
||||
# associated with the "no-selection" item, see *items*, below
|
||||
# with the explicit item-*id* "".
|
||||
#
|
||||
labels:
|
||||
step: "Packages"
|
||||
step[nl]: "Pakketten"
|
||||
|
||||
# The *packageChoice* value is used for setting the default selection
|
||||
# in the QML view; this should match one of the keys used in the QML
|
||||
# module for package names.
|
||||
#
|
||||
# (e.g. the sample QML uses "no_office_suite", "minimal_install" and
|
||||
# "libreoffice" as possible choices).
|
||||
#
|
||||
packageChoice: libreoffice
|
||||
|
||||
304
modules/packagechooserq/packagechooserq.qml
Normal file
@@ -0,0 +1,304 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021 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.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#f2f2f2"
|
||||
|
||||
ButtonGroup {
|
||||
id: switchGroup
|
||||
}
|
||||
|
||||
Column {
|
||||
id: column
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
Rectangle {
|
||||
//id: rectangle
|
||||
width: 700
|
||||
height: 150
|
||||
color: "#ffffff"
|
||||
radius: 10
|
||||
border.width: 0
|
||||
Text {
|
||||
width: 450
|
||||
height: 104
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("OpenRC base system.<br/>
|
||||
Default option.")
|
||||
font.pointSize: 10
|
||||
anchors.verticalCenterOffset: -10
|
||||
anchors.horizontalCenterOffset: 100
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: element1
|
||||
x: 500
|
||||
y: 110
|
||||
width: 187
|
||||
height: 14
|
||||
text: qsTr("OpenRC")
|
||||
checked: true
|
||||
hoverEnabled: true
|
||||
ButtonGroup.group: switchGroup
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 40
|
||||
implicitHeight: 14
|
||||
radius: 10
|
||||
color: element1.checked ? "#3498db" : "#B9B9B9"
|
||||
border.color: element1.checked ? "#3498db" : "#cccccc"
|
||||
|
||||
Rectangle {
|
||||
x: element1.checked ? parent.width - width : 0
|
||||
y: (parent.height - height) / 2
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: element1.down ? "#cccccc" : "#ffffff"
|
||||
border.color: element1.checked ? (element1.down ? "#3498db" : "#3498db") : "#999999"
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if ( checked ) {
|
||||
config.packageChoice = "openrc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image1
|
||||
x: 8
|
||||
y: 25
|
||||
height: 100
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "images/if.png"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 700
|
||||
height: 150
|
||||
radius: 10
|
||||
border.width: 0
|
||||
Text {
|
||||
width: 450
|
||||
height: 104
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Dinit base system.")
|
||||
font.pointSize: 10
|
||||
anchors.verticalCenterOffset: -10
|
||||
anchors.horizontalCenterOffset: 100
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: element2
|
||||
x: 500
|
||||
y: 110
|
||||
width: 187
|
||||
height: 14
|
||||
text: qsTr("Dinit")
|
||||
checked: false
|
||||
hoverEnabled: true
|
||||
ButtonGroup.group: switchGroup
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 40
|
||||
implicitHeight: 14
|
||||
radius: 10
|
||||
color: element2.checked ? "#3498db" : "#B9B9B9"
|
||||
border.color: element2.checked ? "#3498db" : "#cccccc"
|
||||
|
||||
Rectangle {
|
||||
x: element2.checked ? parent.width - width : 0
|
||||
y: (parent.height - height) / 2
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: element2.down ? "#cccccc" : "#ffffff"
|
||||
border.color: element2.checked ? (element2.down ? "#3498db" : "#3498db") : "#999999"
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if ( checked ) {
|
||||
config.packageChoice = "dinit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image2
|
||||
x: 8
|
||||
y: 25
|
||||
height: 100
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "images/if.png"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 700
|
||||
height: 150
|
||||
color: "#ffffff"
|
||||
radius: 10
|
||||
border.width: 0
|
||||
Text {
|
||||
width: 450
|
||||
height: 104
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Runit base system.")
|
||||
font.pointSize: 10
|
||||
anchors.verticalCenterOffset: -10
|
||||
anchors.horizontalCenterOffset: 100
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: element3
|
||||
x: 500
|
||||
y: 110
|
||||
width: 187
|
||||
height: 14
|
||||
text: qsTr("Runit")
|
||||
checked: false
|
||||
hoverEnabled: true
|
||||
ButtonGroup.group: switchGroup
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 40
|
||||
implicitHeight: 14
|
||||
radius: 10
|
||||
color: element3.checked ? "#3498db" : "#B9B9B9"
|
||||
border.color: element3.checked ? "#3498db" : "#cccccc"
|
||||
|
||||
Rectangle {
|
||||
x: element3.checked ? parent.width - width : 0
|
||||
y: (parent.height - height) / 2
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: element3.down ? "#cccccc" : "#ffffff"
|
||||
border.color: element3.checked ? (element3.down ? "#3498db" : "#3498db") : "#999999"
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if ( checked ) {
|
||||
config.packageChoice = "runit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image3
|
||||
x: 8
|
||||
y: 25
|
||||
height: 100
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "images/if.png"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 700
|
||||
height: 150
|
||||
color: "#ffffff"
|
||||
radius: 10
|
||||
border.width: 0
|
||||
Text {
|
||||
width: 450
|
||||
height: 104
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("S6 base system.")
|
||||
font.pointSize: 10
|
||||
anchors.verticalCenterOffset: -10
|
||||
anchors.horizontalCenterOffset: 100
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: element4
|
||||
x: 500
|
||||
y: 110
|
||||
width: 187
|
||||
height: 14
|
||||
text: qsTr("S6")
|
||||
checked: false
|
||||
hoverEnabled: true
|
||||
ButtonGroup.group: switchGroup
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 40
|
||||
implicitHeight: 14
|
||||
radius: 10
|
||||
color: element4.checked ? "#3498db" : "#B9B9B9"
|
||||
border.color: element4.checked ? "#3498db" : "#cccccc"
|
||||
|
||||
Rectangle {
|
||||
x: element4.checked ? parent.width - width : 0
|
||||
y: (parent.height - height) / 2
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: element4.down ? "#cccccc" : "#ffffff"
|
||||
border.color: element4.checked ? (element4.down ? "#3498db" : "#3498db") : "#999999"
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if ( checked ) {
|
||||
config.packageChoice = "s6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image4
|
||||
x: 8
|
||||
y: 25
|
||||
height: 100
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "images/if.png"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 700
|
||||
height: 25
|
||||
color: "#f2f2f2"
|
||||
border.width: 0
|
||||
Text {
|
||||
height: 25
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Please select an option for your install, or use the default: OpenRC.")
|
||||
font.pointSize: 10
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
9
modules/packagechooserq/packagechooserq.qrc
Normal file
@@ -0,0 +1,9 @@
|
||||
<RCC>
|
||||
<qresource>
|
||||
<file>packagechooserq.qml</file>
|
||||
<file>images/libreoffice.jpg</file>
|
||||
<file>images/no-selection.png</file>
|
||||
<file>images/plasma.png</file>
|
||||
<file>images/if.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
80
modules/postcfg/main.py
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# === This file is part of Calamares - <http://github.com/calamares> ===
|
||||
#
|
||||
# Copyright 2014 - 2016, Philip Müller <philm@manjaro.org>
|
||||
# Copyright 2016, Artoo <artoo@manjaro.org>
|
||||
# Copyright 2018, Artoo <artoo@artixlinux.org>
|
||||
#
|
||||
# Calamares is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Calamares is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from os.path import join, exists
|
||||
|
||||
import libcalamares
|
||||
from libcalamares.utils import target_env_call
|
||||
|
||||
|
||||
class ConfigController:
|
||||
"""Configuration controller
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.root = libcalamares.globalstorage.value("rootMountPoint")
|
||||
|
||||
def terminate(self, proc):
|
||||
"""Send SIGKILL to the given proccess
|
||||
"""
|
||||
target_env_call(['killall', '-9', proc])
|
||||
|
||||
def sedFile(self, pattern, file):
|
||||
"""Sed the given file with the given pattern
|
||||
"""
|
||||
target_env_call(["sed", "-e", pattern, "-i", file])
|
||||
|
||||
def configure(self):
|
||||
"""Configure the services
|
||||
"""
|
||||
if exists(join(self.root, "/etc/conf.d/keymaps")):
|
||||
exp = 's|^.*keymap=.*|keymap="{}"|'.format(
|
||||
libcalamares.globalstorage.value("keyboardLayout")
|
||||
)
|
||||
self.sedFile(exp, "/etc/conf.d/keymaps")
|
||||
|
||||
if exists(join(self.root, "/etc/conf.d/xdm")):
|
||||
for dm in libcalamares.globalstorage.value("displayManagers"):
|
||||
exp = 's|^.*DISPLAYMANAGER=.*|DISPLAYMANAGER="{}"|'.format(dm)
|
||||
self.sedFile(exp, "/etc/conf.d/xdm")
|
||||
|
||||
|
||||
def run(self):
|
||||
"""Run the controller
|
||||
|
||||
Workaround for pacman-key bug
|
||||
FS#45351 https://bugs.archlinux.org/task/45351
|
||||
We have to kill gpg-agent because if it stays
|
||||
around we can't reliably unmount
|
||||
the target partition.
|
||||
"""
|
||||
self.configure()
|
||||
self.terminate('gpg-agent')
|
||||
|
||||
|
||||
def run():
|
||||
""" Misc postinstall configurations """
|
||||
|
||||
config = ConfigController()
|
||||
|
||||
return config.run()
|
||||
7
modules/postcfg/module.desc
Normal file
@@ -0,0 +1,7 @@
|
||||
# Syntax is YAML 1.2
|
||||
---
|
||||
type: "job"
|
||||
name: "postcfg"
|
||||
interface: "python"
|
||||
script: "main.py" #assumed relative to the current directory
|
||||
noconfig: true
|
||||
104
modules/services-artix/main.py
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# === This file is part of Calamares - <https://calamares.io> ===
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2016 Artoo <artoo@manjaro.org>
|
||||
# SPDX-FileCopyrightText: 2017 Philip Müller <philm@manjaro.org>
|
||||
# SPDX-FileCopyrightText: 2018 Artoo <artoo@artixlinux.org>
|
||||
# SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org>
|
||||
# SPDX-FileCopyrightText: 2023-2024 Artoo <artoo@artixlinux.org>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Calamares is Free Software: see the License-Identifier above.
|
||||
#
|
||||
|
||||
import libcalamares
|
||||
|
||||
import gettext
|
||||
_ = gettext.translation("calamares-python",
|
||||
localedir=libcalamares.utils.gettext_path(),
|
||||
languages=libcalamares.utils.gettext_languages(),
|
||||
fallback=True).gettext
|
||||
|
||||
|
||||
def pretty_name():
|
||||
return _("Configure services")
|
||||
|
||||
|
||||
def manage_services(services, manager, cmd):
|
||||
"""
|
||||
For each entry in @p services, run "<command> <action> <name>",
|
||||
where each service is a mapping of service name, action, and a flag.
|
||||
|
||||
Returns a failure message, or None if this was successful.
|
||||
Services that are not mandatory have their failures suppressed
|
||||
silently.
|
||||
"""
|
||||
for sv in services:
|
||||
if isinstance(sv, str):
|
||||
name = sv
|
||||
action = "enable"
|
||||
mandatory = False
|
||||
alias = "add"
|
||||
target = "default"
|
||||
else:
|
||||
if "name" not in sv:
|
||||
libcalamares.utils.error("The key 'name' is missing from the mapping {_sv!s}. Continuing to the next service.".format(_sv=str(sv)))
|
||||
continue
|
||||
name = sv["name"]
|
||||
action = sv.get("action", "enable")
|
||||
mandatory = sv.get("mandatory", False)
|
||||
alias = sv.get("alias", "add")
|
||||
target = sv.get("target", "default")
|
||||
|
||||
command = [ cmd ]
|
||||
|
||||
match manager:
|
||||
case "openrc":
|
||||
args = [alias, name, target]
|
||||
case "s6":
|
||||
args = [alias, target, name]
|
||||
case _:
|
||||
args = [action, name]
|
||||
|
||||
command.extend(args)
|
||||
|
||||
libcalamares.utils.debug("Manager command: {!s}".format(command))
|
||||
|
||||
ec = libcalamares.utils.target_env_call(command)
|
||||
|
||||
if ec != 0:
|
||||
libcalamares.utils.warning(
|
||||
"Cannot {} service {}".format(action, name)
|
||||
)
|
||||
libcalamares.utils.warning(
|
||||
"service {} call in chroot returned error code {}".format(action, ec)
|
||||
)
|
||||
if mandatory:
|
||||
title = _("Cannot modify service")
|
||||
diagnostic = _("<code>service {_action!s}</code> call in chroot returned error code {_ec!s}.").format(_action=action, _ec=ec)
|
||||
description = _("Cannot {_action!s} service <code>{_name!s}</code>.").format(_action=action, _name=name)
|
||||
return (
|
||||
title,
|
||||
description + " " + diagnostic
|
||||
)
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Setup services
|
||||
"""
|
||||
manager = libcalamares.job.configuration.get("manager")
|
||||
|
||||
if libcalamares.globalstorage.contains("initProvider"):
|
||||
manager = libcalamares.globalstorage.value("initProvider")
|
||||
|
||||
commands = libcalamares.job.configuration.get("commands")
|
||||
cmd = commands.get(manager)
|
||||
services = libcalamares.job.configuration.get("services", [])
|
||||
|
||||
r = manage_services(services, manager, cmd)
|
||||
if r is not None:
|
||||
return r
|
||||
|
||||
7
modules/services-artix/module.desc
Normal file
@@ -0,0 +1,7 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
type: "job"
|
||||
name: "services-artix"
|
||||
interface: "python"
|
||||
script: "main.py"
|
||||
74
modules/services-artix/services-artix.conf
Normal file
@@ -0,0 +1,74 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# openrc services module to modify service runlevels via rc-update in the chroot
|
||||
#
|
||||
# Services can be added (to any runlevel, or multiple runlevels) or deleted.
|
||||
# Handle del with care and only use it if absolutely necessary.
|
||||
#
|
||||
# if a service is listed in the conf but is not present/detected on the target system,
|
||||
# or a runlevel does not exist, it will be ignored and skipped; a warning is logged.
|
||||
#
|
||||
---
|
||||
#
|
||||
# Which service manager to use, options are:
|
||||
# - openrc
|
||||
# - dinit
|
||||
# - s6
|
||||
# - runit
|
||||
#
|
||||
|
||||
manager: openrc
|
||||
|
||||
commands:
|
||||
openrc: rc-update
|
||||
dinit: artix-svc
|
||||
s6: s6-service
|
||||
runit: rsm
|
||||
|
||||
# There is one key for this module: *services*. Its value is a list of entries.
|
||||
# Each entry has three keys:
|
||||
# - *name* is the (string) name of the manager service that is being changed.
|
||||
# Use quotes. You can use any valid initProvider service here (for example,
|
||||
# "NetworkManager", "cupsd", "lightdm", "gdm", etc.)
|
||||
# - *action* is the (string) action that you want to perform over the service
|
||||
# (for example, "enable", "disable"). Please
|
||||
# ensure that the action can actually run under chroot (otherwise it is
|
||||
# pointless)
|
||||
# - *mandatory* is a boolean option, which states whether the change
|
||||
# must be done successfully. If manager reports an error while changing
|
||||
# a mandatory entry, the installation will fail. When mandatory is false,
|
||||
# errors for that manager service are ignored. If mandatory
|
||||
# is not specified, the default is false.
|
||||
#
|
||||
# The order of operations is the same as the order in which entries
|
||||
# appear in the list
|
||||
|
||||
# # This example enables NetworkManager.service (and fails if it can't),
|
||||
# # disables cups.socket (and ignores failure). Then it enables the
|
||||
# # graphical target (e.g. so that SDDM runs for login), and
|
||||
# # finally masks pacman-init (an ArchLinux-only service).
|
||||
# #
|
||||
# services:
|
||||
# - name: "NetworkManager"
|
||||
# action: "enable"
|
||||
# alias: "add"
|
||||
# target: "default"
|
||||
# mandatory: true
|
||||
#
|
||||
# - name: "cupsd"
|
||||
# action: "disable"
|
||||
# alias: "del"
|
||||
# target: "default"
|
||||
# # The property "mandatory" is taken to be false by default here
|
||||
# # because it is not specified
|
||||
#
|
||||
# - name: "sddm"
|
||||
# action: "enable"
|
||||
# alias: "add"
|
||||
# target: "default"
|
||||
# # The property "mandatory" is taken to be false by default here
|
||||
# # because it is not specified
|
||||
|
||||
services: []
|
||||
|
||||
133
modules/services-dinit/main.py
Normal file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
#
|
||||
# Copyright 2018-2019, Adriaan de Groot <groot@kde.org>
|
||||
# Copyright 2021, Artoo <artoo@artixlinux.org>
|
||||
#
|
||||
# Calamares is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Calamares is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import libcalamares
|
||||
|
||||
from libcalamares.utils import target_env_call, warning
|
||||
from os.path import exists, join
|
||||
|
||||
|
||||
import gettext
|
||||
_ = gettext.translation("calamares-python",
|
||||
localedir=libcalamares.utils.gettext_path(),
|
||||
languages=libcalamares.utils.gettext_languages(),
|
||||
fallback=True).gettext
|
||||
|
||||
|
||||
def pretty_name():
|
||||
return _("Configure Dinit services")
|
||||
|
||||
|
||||
class DinitController:
|
||||
"""
|
||||
This is the dinit service controller.
|
||||
All of its state comes from global storage and the job
|
||||
configuration at initialization time.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.root = libcalamares.globalstorage.value('rootMountPoint')
|
||||
|
||||
# Translate the entries in the config to the actions passed to sv-helper
|
||||
self.services = dict()
|
||||
self.services["enable"] = libcalamares.job.configuration.get('services', [])
|
||||
self.services["disable"] = libcalamares.job.configuration.get('disable', [])
|
||||
|
||||
self.initdDir = libcalamares.job.configuration['initdDir']
|
||||
self.runsvDir = libcalamares.job.configuration['runsvDir']
|
||||
|
||||
|
||||
def make_failure_description(self, state, name):
|
||||
"""
|
||||
Returns a generic "could not <foo>" failure message, specialized
|
||||
for the action @p state and the specific service @p name.
|
||||
"""
|
||||
if state == "enable":
|
||||
description = _("Cannot enable service {name!s}.")
|
||||
elif state == "disable":
|
||||
description = _("Cannot disable service {name!s}.")
|
||||
else:
|
||||
description = _("Unknown service-action <code>{arg!s}</code> for service {name!s}.")
|
||||
|
||||
return description.format(arg=state, name=name)
|
||||
|
||||
|
||||
def update(self, state):
|
||||
"""
|
||||
Process each service listed
|
||||
in services for the given @p state.
|
||||
"""
|
||||
|
||||
for svc in self.services.get(state, []):
|
||||
if isinstance(svc, str):
|
||||
name = svc
|
||||
mandatory = False
|
||||
else:
|
||||
name = svc["name"]
|
||||
mandatory = svc.get("mandatory", False)
|
||||
|
||||
service_path = self.root + self.initdDir + "/" + name
|
||||
runlevel_path = self.root + self.runsvDir
|
||||
src = self.initdDir + "/" + name
|
||||
dest = self.runsvDir + "/"
|
||||
|
||||
if state == 'enable':
|
||||
cmd = ["ln", "-sv", src, dest]
|
||||
elif state == 'disable':
|
||||
cmd = ["rm", "-rv", dest]
|
||||
|
||||
if exists(service_path):
|
||||
if exists(runlevel_path):
|
||||
ec = target_env_call(cmd)
|
||||
if ec != 0:
|
||||
warning("Cannot {} service {}".format(state, name))
|
||||
warning("{} returned error code {!s}".format(cmd, ec))
|
||||
if mandatory:
|
||||
title = _("Cannot modify service")
|
||||
diagnostic = _("<code>cmd {arg!s}</code> call in chroot returned error code {num!s}.").format(arg=state, num=ec)
|
||||
return (title,
|
||||
self.make_failure_description(state, name) + " " + diagnostic
|
||||
)
|
||||
else:
|
||||
warning("Target service {} does not exist in {}.".format(name, self.initdDir))
|
||||
if mandatory:
|
||||
title = _("Target service does not exist")
|
||||
diagnostic = _("The path for service {name!s} is <code>{path!s}</code>, which does not exist.").format(name=name, path=service_path)
|
||||
return (title,
|
||||
self.make_failure_description(state, name) + " " + diagnostic
|
||||
)
|
||||
|
||||
|
||||
def run(self):
|
||||
"""Run the controller
|
||||
"""
|
||||
|
||||
for state in ("enable", "disable"):
|
||||
r = self.update(state)
|
||||
if r is not None:
|
||||
return r
|
||||
|
||||
def run():
|
||||
"""
|
||||
Setup services
|
||||
"""
|
||||
|
||||
return DinitController().run()
|
||||
5
modules/services-dinit/module.desc
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: "job"
|
||||
name: "services-dinit"
|
||||
interface: "python"
|
||||
script: "main.py"
|
||||
41
modules/services-dinit/services-dinit.conf
Normal file
@@ -0,0 +1,41 @@
|
||||
# runit services module to modify service runlevels via symlinks in the chroot
|
||||
#
|
||||
# Services can be added (to any runlevel, or multiple runlevels) or deleted.
|
||||
# Handle disable with care and only use it if absolutely necessary.
|
||||
#
|
||||
# if a service is listed in the conf but is not present/detected on the target system,
|
||||
# it will be ignored and skipped; a warning is logged.
|
||||
#
|
||||
---
|
||||
# initdDir: holds the runit service directory location
|
||||
initdDir: /etc/dinit.d
|
||||
|
||||
# runsvDir: holds the runlevels directory location
|
||||
runsvDir: /etc/dinit.d/boot.d
|
||||
|
||||
# services: a list of entries to **enable**
|
||||
# disable: a list of entries to **disable**
|
||||
#
|
||||
# Each entry has three fields:
|
||||
# - name: the service name
|
||||
# - (optional) mandatory: if set to true, a failure to modify
|
||||
# the service will result in installation failure, rather than just
|
||||
# a warning. The default is false.
|
||||
#
|
||||
# an entry may also be a single string, which is interpreted
|
||||
# as the name field.
|
||||
#
|
||||
# # Example services and disable settings:
|
||||
# # - add foo1, but it must succeed
|
||||
# # - add foo2
|
||||
# # - remove foo3
|
||||
# # - remove foo4
|
||||
# services:
|
||||
# - name: foo1
|
||||
# mandatory: true
|
||||
# - name: foo2
|
||||
# disable:
|
||||
# - name: foo3
|
||||
# - foo4
|
||||
services: []
|
||||
disable: []
|
||||
144
modules/services-runit/main.py
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
#
|
||||
# Copyright 2018-2019, Adriaan de Groot <groot@kde.org>
|
||||
# Copyright 2019, Artoo <artoo@artixlinux.org>
|
||||
#
|
||||
# Calamares is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Calamares is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import libcalamares
|
||||
|
||||
from libcalamares.utils import target_env_call, warning
|
||||
from os.path import exists, join
|
||||
|
||||
|
||||
import gettext
|
||||
_ = gettext.translation("calamares-python",
|
||||
localedir=libcalamares.utils.gettext_path(),
|
||||
languages=libcalamares.utils.gettext_languages(),
|
||||
fallback=True).gettext
|
||||
|
||||
|
||||
def pretty_name():
|
||||
return _("Configure Runit services")
|
||||
|
||||
|
||||
class RunitController:
|
||||
"""
|
||||
This is the runit service controller.
|
||||
All of its state comes from global storage and the job
|
||||
configuration at initialization time.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.root = libcalamares.globalstorage.value('rootMountPoint')
|
||||
|
||||
# Translate the entries in the config to the actions passed to sv-helper
|
||||
self.services = dict()
|
||||
self.services["enable"] = libcalamares.job.configuration.get('services', [])
|
||||
self.services["disable"] = libcalamares.job.configuration.get('disable', [])
|
||||
|
||||
self.svDir = libcalamares.job.configuration['svDir']
|
||||
self.runsvDir = libcalamares.job.configuration['runsvDir']
|
||||
|
||||
|
||||
def make_failure_description(self, state, name, runlevel):
|
||||
"""
|
||||
Returns a generic "could not <foo>" failure message, specialized
|
||||
for the action @p state and the specific service @p name in @p runlevel.
|
||||
"""
|
||||
if state == "enable":
|
||||
description = _("Cannot enable service {name!s} to run-level {level!s}.")
|
||||
elif state == "disable":
|
||||
description = _("Cannot disable service {name!s} from run-level {level!s}.")
|
||||
else:
|
||||
description = _("Unknown service-action <code>{arg!s}</code> for service {name!s} in run-level {level!s}.")
|
||||
|
||||
return description.format(arg=state, name=name, level=runlevel)
|
||||
|
||||
|
||||
def update(self, state):
|
||||
"""
|
||||
Call sv-helper for each service listed
|
||||
in services for the given @p state.
|
||||
"""
|
||||
|
||||
for svc in self.services.get(state, []):
|
||||
if isinstance(svc, str):
|
||||
name = svc
|
||||
runlevel = "default"
|
||||
mandatory = False
|
||||
else:
|
||||
name = svc["name"]
|
||||
runlevel = svc.get("runlevel", "default")
|
||||
mandatory = svc.get("mandatory", False)
|
||||
|
||||
service_path = self.root + self.svDir + "/" + name
|
||||
runlevel_path = self.root + self.runsvDir + "/" + runlevel
|
||||
src = self.svDir + "/" + name
|
||||
dest = self.runsvDir + "/" + runlevel + "/"
|
||||
|
||||
if state == 'enable':
|
||||
cmd = ["ln", "-sv", src, dest]
|
||||
elif state == 'disable':
|
||||
cmd = ["rm", "-rv", dest]
|
||||
|
||||
if exists(service_path):
|
||||
if exists(runlevel_path):
|
||||
ec = target_env_call(cmd)
|
||||
if ec != 0:
|
||||
warning("Cannot {} service {} to {}".format(state, name, runlevel))
|
||||
warning("{} returned error code {!s}".format(cmd, ec))
|
||||
if mandatory:
|
||||
title = _("Cannot modify service")
|
||||
diagnostic = _("<code>cmd {arg!s}</code> call in chroot returned error code {num!s}.").format(arg=state, num=ec)
|
||||
return (title,
|
||||
self.make_failure_description(state, name, runlevel) + " " + diagnostic
|
||||
)
|
||||
else:
|
||||
warning("Target runlevel {} does not exist for {}.".format(runlevel, name))
|
||||
if mandatory:
|
||||
title = _("Target runlevel does not exist")
|
||||
diagnostic = _("The path for runlevel {level!s} is <code>{path!s}</code>, which does not exist.").format(level=runlevel, path=runlevel_path)
|
||||
|
||||
return (title,
|
||||
self.make_failure_description(state, name, runlevel) + " " + diagnostic
|
||||
)
|
||||
else:
|
||||
warning("Target service {} does not exist in {}.".format(name, self.svDir))
|
||||
if mandatory:
|
||||
title = _("Target service does not exist")
|
||||
diagnostic = _("The path for service {name!s} is <code>{path!s}</code>, which does not exist.").format(name=name, path=service_path)
|
||||
return (title,
|
||||
self.make_failure_description(state, name, runlevel) + " " + diagnostic
|
||||
)
|
||||
|
||||
|
||||
def run(self):
|
||||
"""Run the controller
|
||||
"""
|
||||
|
||||
for state in ("enable", "disable"):
|
||||
r = self.update(state)
|
||||
if r is not None:
|
||||
return r
|
||||
|
||||
def run():
|
||||
"""
|
||||
Setup services
|
||||
"""
|
||||
|
||||
return RunitController().run()
|
||||
5
modules/services-runit/module.desc
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: "job"
|
||||
name: "services-runit"
|
||||
interface: "python"
|
||||
script: "main.py"
|
||||