174 Commits

Author SHA1 Message Date
379f38bfe9 basestrap: add copy_pacconf property 2024-01-02 18:47:43 +01:00
25acdeceba remove old service modules from build 2024-01-02 16:51:02 +01:00
5462411d8a basestrap: create dirs sequentially 2024-01-02 13:38:36 +01:00
b7f606cba1 services-artix: add debug info 2024-01-01 22:49:09 +01:00
d51ba1e8df services-artix: fix alias 2024-01-01 21:43:16 +01:00
10dc285ed2 services-artixc: add alias and target service properties 2024-01-01 21:40:03 +01:00
916f1c2fae services-artix: small refactor 2024-01-01 21:18:53 +01:00
0d9923ed0b services-artix: simplify greatly 2024-01-01 20:55:00 +01:00
8947b568a7 rm 2024-01-01 03:07:22 +01:00
3dc5e111bb add packagechooserxq; experimental module since packagechooserq somehow doesn't work in non legacy mode and no GS written 2024-01-01 02:30:46 +01:00
c6e2cacd51 packagechooserq: fix qt5 qml 2024-01-01 01:37:19 +01:00
3d3e73f015 cleaning 2023-12-31 21:27:43 +01:00
fe012233e3 fix it 2023-12-31 18:29:27 +01:00
e55d467f12 cmake: include packagechooserq 2023-12-31 18:28:15 +01:00
c99c53714f update packagechooserq 2023-12-31 18:23:06 +01:00
9e10e21381 no test 2023-12-31 17:06:02 +01:00
c6884e1044 remove packagechooser tests for now 2023-12-31 17:03:03 +01:00
fc2b4c54a5 pkgch: reenable test 2023-12-31 03:04:41 +01:00
c04875fb6a try fix build 2023-12-31 02:42:08 +01:00
bac0c002d8 packagechooser: take out test 2023-12-31 00:59:48 +01:00
999e45c64a add packagechooser 2023-12-31 00:41:15 +01:00
a1012b9a82 fix 2023-12-30 23:33:07 +01:00
39427ffc4c add artix modules 2023-12-30 16:21:50 +01:00
demmm
6bd75570de Merge pull request #30 from Undef-a/wip/undef/librem5-screen
mobile: Ensure welcome screen fits on Librem5
2023-09-29 14:19:05 +02:00
undef
acdcdea668 mobile: Ensure welcome screen fits on Librem5
Now that the sizing has changed this line overhangs the screen.
2023-09-29 11:36:34 +00:00
Adriaan de Groot
edb405a4d1 Merge pull request #29 from Undef-a/wip/undef/fix-sizing-with-3.3.x
mobile: Adjust all sizes for Calamares 3.3.x
2023-09-28 23:03:26 +02:00
undef
3ad9c221d4 mobile: Adjust all sizes for Calamares 3.3.x
Element sizing in Calamares 3.3.x seems to have dramatically changed,
with for example a button that was previously fine at width 500 now
being huge to the point of causing the installer to overhang the screen.

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

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

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

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

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

Related: https://gitlab.com/postmarketOS/pmaports/-/merge_requests/3280#note_1021536268
2022-07-11 08:25:55 +02:00
sravanpannala
14fd23dcef remove z option from tar so that it works with all 2022-07-06 17:56:28 -04:00
Adriaan de Groot
f4bc7052e0 [unpackfsc] Add (untested) tarball support 2022-05-17 12:30:28 +02:00
Adriaan de Groot
4f0f48d99d [unpackfsc] Unsquash is implemented 2022-05-17 12:21:52 +02:00
Adriaan de Groot
1344880f2e [unpackfsc] Add fsarchiver "fs" mode
- Add "restfs" suppotr for fsarchiver
- Apply coding style from Calamares
2022-05-17 12:12:30 +02:00
Adriaan de Groot
5704e146a0 Changes: pre-release housekeeping 2021-11-16 13:10:36 +01:00
Adriaan de Groot
85eb434b57 Merge branch 'fsarchiver' into calamares 2021-11-16 13:07:14 +01:00
Adriaan de Groot
4b2ea61aa3 [unpackfs] Document that fsarchiver uses savedir/restdir 2021-11-16 13:04:54 +01:00
Adriaan de Groot
39d25eef1c [unpackfsc] Reduce number of status updates in fsarchiver 2021-11-16 13:00:00 +01:00
Adriaan de Groot
e82d32fe66 [unpackfsc] Use restdir instead of restfs 2021-11-16 12:58:17 +01:00
Adriaan de Groot
a50ab49c22 [unpackfsc] Add test-configs for fsarchiver-unpack 2021-11-16 12:52:34 +01:00
Adriaan de Groot
a66ab99ce8 [unpackfsc] Report fsarchiver progress, too 2021-11-16 12:24:33 +01:00
Adriaan de Groot
c3170a9dfb Changes: post-release housekeeping 2021-11-16 12:12:21 +01:00
Adriaan de Groot
88437d3612 CI: fix build-script with new-style version reporting 2021-11-16 01:47:36 +01:00
Adriaan de Groot
6db52a904e [unpackfsc] Fix build with old Qt 2021-11-16 01:41:00 +01:00
Adriaan de Groot
8f0403a3f8 [unpackfsc] Report progress through status message 2021-11-16 00:50:19 +01:00
Adriaan de Groot
caf231b0d1 [unpackfsc] strip() is Python, I guess 2021-11-16 00:46:42 +01:00
Adriaan de Groot
9ad86f81bb [unpackfsc] Avoid newlines in filenames 2021-11-16 00:42:36 +01:00
Adriaan de Groot
4d601f2e6a [unpackfs] Don't reformat table of enum names 2021-11-16 00:36:58 +01:00
Adriaan de Groot
69f68d82e6 [unpackfsc] Get the total number of inodes
- Call run() to actually run the command we've set up; in the
  advice "have you tried turning if off and on again", the "on"
  bit is actually kind of important.
2021-11-16 00:29:18 +01:00
Adriaan de Groot
572a94e493 [unpackfsc] Map destination path to target system 2021-11-15 23:47:18 +01:00
Adriaan de Groot
90454be1b9 [unpackfs] Include a message in progress reports 2021-11-15 23:42:11 +01:00
Adriaan de Groot
6144404bd0 [unpackfsc] Remove unreachable progress 2021-11-15 23:24:56 +01:00
Adriaan de Groot
8f4d8d119c [unpackfsc] Hook up progress 2021-11-15 23:22:52 +01:00
Adriaan de Groot
e8c870205d [unpackfsc] Implement most of unsquash 2021-11-15 23:19:08 +01:00
Adriaan de Groot
081d7d47d1 [unpackfsc] Factor out command-name, it doesn't need translation 2021-11-15 23:19:08 +01:00
Adriaan de Groot
e92ecea3f5 Remove filekeeper job
- the filekeeper was never fully implemented, while core Calamares
  has a preservefiles module that does do all the same things.
2021-11-15 17:20:33 +01:00
Adriaan de Groot
e158402478 [unpackfsc] Use libcalamares version of Runner now 2021-11-09 23:42:45 +01:00
Adriaan de Groot
d242d077db CMake: bump version and Calamares-requirement
This is prep-work for a new release that uses the most
recent Calamares internals.
2021-11-09 23:29:42 +01:00
Adriaan de Groot
5f9b46a820 [os-nixos] Avoid None-concatenate-with-str
When running tests, with no configurations, GS contains none
of the keys the Nix configuration is looking for. Avoid
TypeErrors (but still end up failing the test because we
can't run the nix-config tool at the end).
2021-10-20 15:29:53 +02:00
Adriaan de Groot
9151d0fcee [refind] Fix warning function name 2021-10-20 15:25:43 +02:00
Adriaan de Groot
282f5bfade [filekeeper] Load part of the config file 2021-10-20 15:15:15 +02:00
Adriaan de Groot
eeb264f32e [mobile] Keep the tests happy 2021-10-20 14:58:25 +02:00
Adriaan de Groot
83606aaf9d CI: update to core calamares version of scripts 2021-10-20 14:58:25 +02:00
Adriaan de Groot
322a7a212f [refind] Fix tests
- the file "refind.conf" was interpreted as a **Calamares** configuration
  file for the module; it isn't, it's for rEFInd, so rename it. The
  code does not use it yet, anyway.
- Mark the module as "noconfig" since it only consumes settings from
  the partition module and nothing else.
2021-10-20 14:58:25 +02:00
Adriaan de Groot
40c7496f85 CMake: enable tests
- needs to have loadmodule and test_conf in PATH, which
  means some tweaking is needed to actually *run* them.
2021-10-20 14:58:25 +02:00
Adriaan de Groot
f1fd52e790 [refind] Add module to build 2021-10-20 14:58:25 +02:00
Adriaan de Groot
12f6068622 Merge pull request #17 from Otus9051/patch-1
[os-nixos] Test data - Kolkata is not in Europe
2021-10-20 14:42:44 +02:00
Adriaan de Groot
16a3e2edb2 [refind] Remove checks
- The checks use string manipulations and will always get
  a non-empty string, so the checks can't fail
- Refactor the check that there is an EFI partition mounted
- The refind-install command does not use any of the values
  computed by the checks.
2021-10-20 14:27:55 +02:00
Adriaan de Groot
8fc9b907af [refind] Python stylings
- Use None for unset
- Use logging functions instead of print()
2021-10-20 14:17:43 +02:00
Adriaan de Groot
ecaf2c3076 [refind] Improve device-detection warnings
- Possibly support something other that /boot
- Error message mismatch with what it actually checks
2021-10-20 14:06:55 +02:00
Adriaan de Groot
ea97927997 [refind] Python stylings
- no need for explicit close() in file context
- simplify returns
2021-10-20 14:01:40 +02:00
Adriaan de Groot
6795190216 [refind] kernel_params is a list (converted to space-separated later) 2021-10-20 14:00:50 +02:00
Adriaan de Groot
51149e34d7 Merge pull request #16 from demmm/calamares
[refind] adding a simple rEFInd module
2021-10-20 13:58:21 +02:00
Otus9051
a2674c652d How is Kolkata in Europe? 2021-10-07 08:49:41 +05:30
demmm
d4d6d17efe [refind] apply requested changes
tested on new ISO & installed with refind option
2021-10-04 16:46:38 +02:00
demmm
b74d77a9c5 [refind] adding a simple rEFInd module
takes just 2 options, install in /boot as EFI partition and
uses the refind-install option to install & create needed conf files
no work done to make EFI partiton configurable or choose a manual
option to install.  Those can be added, if there is any interest
2021-09-29 13:36:36 +02:00
Adriaan de Groot
5074bebd32 [os-nixos] Example global-config file, for tests 2021-09-27 14:07:48 +02:00
Adriaan de Groot
76be9988fe SPDX: tag modules 2021-09-27 14:07:27 +02:00
Adriaan de Groot
c85daf8f7b SPDX: tag various 2021-09-27 13:58:33 +02:00
Adriaan de Groot
13e8b1e9b5 SPDX: add dep5-blanket for build artifacts and GH cruft 2021-09-27 13:54:38 +02:00
Adriaan de Groot
e6ddf30512 SPDX: wrangling
- remove / add license texts that are actually used
- the CMakeLists are supposed to be BSD-2-clause
2021-09-27 13:51:41 +02:00
Adriaan de Groot
9938d11ad1 [os-nixos] Replace useless stub implementation
The existing stub just did the same as slow-python, logging
some values and doing nothing useful. It didn't even act
as an example of the original os-nixos plan, which was
to write the config file.

Do a very minimal effort example with variable substitution
from GS.
2021-09-27 13:46:40 +02:00
Adriaan de Groot
def4ee5c7e SPDX: add license texts 2021-09-27 13:44:51 +02:00
Adriaan de Groot
718f73c9c8 CMake: adjust extensions to changes in Calamares core 2021-09-09 10:57:21 +02:00
Adriaan de Groot
720415d8b7 Docs: do not use freenode 2021-06-15 11:26:30 +02:00
Adriaan de Groot
0f0b9aa776 Docs: IRC links to Libera.Chat 2021-05-31 10:43:44 +02:00
Adriaan de Groot
19d508ca2e CI: chase new tag in actions repo 2021-05-28 14:41:07 +02:00
Adriaan de Groot
7af1aeb132 CI: factor out prepatation, chase update to build 2021-05-28 14:35:29 +02:00
Adriaan de Groot
3f7cabe832 CI: use generic-build action instead of expanding it here 2021-05-28 13:39:29 +02:00
Adriaan de Groot
341c9f4a7f CI: switch to shared Calamares actions entirely 2021-05-28 12:50:44 +02:00
Adriaan de Groot
03260efb3a CI: try to use (shared) Calamares notification action 2021-05-28 12:11:12 +02:00
Adriaan de Groot
18060db82d CI: try to use (shared) Calamares notification action 2021-05-28 12:00:10 +02:00
Adriaan de Groot
b978f03618 CI: update to current matrix-notifications 2021-05-26 16:53:23 +02:00
Adriaan de Groot
a4a6194a5d CI: Add Matrix notification action 2021-05-25 16:31:13 +02:00
Adriaan de Groot
93cf06da82 CI: restore the success/fail check, silence curl output 2021-05-21 16:21:24 +02:00
Adriaan de Groot
901211c12d CI: repair the action YAML 2021-05-21 16:12:50 +02:00
Adriaan de Groot
e89a58a34b Merge branch 'add-unpackfsc' into calamares 2021-05-21 15:02:38 +02:00
Adriaan de Groot
fb36765982 Merge branch 'update-release-reporting' into calamares 2021-05-15 17:40:59 +02:00
Adriaan de Groot
f1d2bfacce CI: switch to matrix notifications 2021-05-15 17:40:46 +02:00
Adriaan de Groot
9703bd9058 CMake: bump required Calamares version
We need the latest version with new failure-enum values.
2021-05-15 17:12:15 +02:00
Adriaan de Groot
0e2a178436 Docs: badge for Matrix 2021-05-19 15:21:21 +02:00
Adriaan de Groot
fb70e29e29 Docs: add a CONTRIBUTING document
- just copy the Calamares one and point to it for all technicalities
2021-05-19 14:31:48 +02:00
Adriaan de Groot
fbfa9d8038 Docs: mention the communication channels 2021-05-19 14:28:06 +02:00
Adriaan de Groot
59ab728502 [unpackfsc] Build a proxy for handling process output
- Rearrange sources, give implementation classes their own header
- Add a *RunCommand* class to run the commands for the tools.
  Existing code in System::runCommand() does not expose any
  of the output until the process is done. This means it's
  not useful for reporting progress information. New code
  **does** do that.

Rearrange sources, split to multiple
2021-05-17 14:55:46 +02:00
Adriaan de Groot
476224d34e [unpackfsc] Support functions for Runners 2021-05-17 14:12:02 +02:00
Adriaan de Groot
2aaf440fec [unpackfsc] Stub of the Unsquash runner 2021-05-17 14:12:02 +02:00
Adriaan de Groot
b416caa462 [unpackfsc] Apply coding style 2021-05-17 11:04:34 +02:00
Adriaan de Groot
0c11b864e5 [unpackfsc] Start implementing fsarchive runner 2021-05-15 01:08:05 +02:00
Adriaan de Groot
0f9ffb9e35 CMake: Calamares is C++17, use it in extensions as well. 2021-05-15 00:13:40 +02:00
Adriaan de Groot
813d26ec4e [unpackfsc] Add stubs for calling the external tools.
This isn't the right way to do it: there is no possibility to
report progress or let the tool run asynchronously.
2021-05-15 00:12:48 +02:00
Adriaan de Groot
5d44118b4b [unpackfsc] Simplify to **one** unpack action
The whole getup with multiple unpack actions makes the module
more complicated; instead, use one unpack action only. If there
are more of them, use multiple instances.

Similarly, remove the Config object: The **view** modules need a
Config object, because the config can be used by multiple UIs, and
we don't want to mix UI with business logic. For a single job,
that's different. Just put the job config in the job itself.
2021-05-15 00:11:16 +02:00
Adriaan de Groot
833e33f011 CMake: Calamares is C++17, use it in extensions as well. 2021-05-15 00:11:16 +02:00
Adriaan de Groot
ea3a03a8cc CI: sign release tarball after it's made
- replace this manual step with signing automatically;
  the tag is signes as well so the gpg-agent likely
  still has the key.
2021-05-15 00:02:45 +02:00
Adriaan de Groot
05bbeae2c3 CI: Use new versioning support from CMakeLists.txt 2021-05-15 00:02:45 +02:00
Adriaan de Groot
b9b923e69c CMake: update versioning infrastructure
Be smarter with versioning information: we don't
need to generate a whole build system to query
the version number set in CMake. Use script mode,
and print the version. Unfortunately, `project()`
isn't valid in script mode so we need to lift the
version into a variable and stick in some boilerplate
for printing the (full) version.
2021-05-14 23:59:36 +02:00
Adriaan de Groot
ff3391f67b Docs: add release announcement 2021-05-10 13:50:13 +02:00
Adriaan de Groot
b60b21b680 CMake: bump version 2021-05-10 13:45:31 +02:00
Adriaan de Groot
8a533d22be [mobile] Consistent initialization-expressions in declaration of Config 2021-05-11 11:49:06 +02:00
Adriaan de Groot
6f32c18ef9 Merge pull request #14 from ollieparanoid/non-numeric-pass
[mobile] support non-numeric passwords
2021-05-11 11:42:01 +02:00
Oliver Smith
f36f21c55c mobile.qml: refactor navNextFeature()
Make it more readable as for-loop.
2021-05-09 19:01:09 +02:00
Oliver Smith
bcacab531f [mobile] navNextFeature: fix skipping > 1 feature
Fix the condition in the while loop, so that it can actually continue
with the "continue" keyword. Without this patch, having the sshd
feature disabled would result in the fsType feature getting displayed,
even if it was disabled.
2021-05-02 17:54:18 +02:00
Oliver Smith
85586293c8 [mobile] tweak invalid chars msg/related comments
Change "cannot be typed in at boot time" to "can possibly not be typed
in after installation" and explain that the same check is now used for
the user password too.
2021-05-02 17:54:17 +02:00
Oliver Smith
e71eb01feb [mobile] add option userPasswordNumeric
Related: https://gitlab.com/postmarketOS/postmarketos-ondev/-/issues/46
2021-05-02 17:54:15 +02:00
Oliver Smith
3eb3e9c98d [mobile] rename default_pin -> user_pass
Prepare to add a config option to allow non-numeric passwords, by giving
the existing default_pin screen the more generic name user_pass.
Adjust the title of the screen too.

"default" in the file name was referring to configuring the default
user's PIN/password as opposed to the SSH user. However, I think
replacing it with "user" makes it more intuitive.
2021-05-02 17:54:13 +02:00
Oliver Smith
2bf5706f73 [mobile] add option builtinVirtualKeyboard
Allow to always hide the built-in qtvirtualkeyboard to support using a
different keyboard.

Related: https://gitlab.com/postmarketOS/postmarketos-ondev/-/issues/47
2021-05-02 17:54:05 +02:00
Adriaan de Groot
809491c969 [unpackfsc] Load and log configuration 2021-04-06 14:22:06 +02:00
Adriaan de Groot
febeb3281e [unpackfsc] Stub of unpackfsc
This module is supposed to use fsarchiver (or similar tools)
to unpack a filesystem to the target. In many ways it is like
the rawfs module.
2021-04-06 13:23:05 +02:00
Adriaan de Groot
76885c7fe1 [mobile] Don't need to list headers as source 2021-04-06 12:00:21 +02:00
Adriaan de Groot
fad0575e4d Merge pull request #13 from ollieparanoid/readme-update
Docs: README: update mobile description
2021-04-03 00:01:12 +02:00
Adriaan de Groot
550f7d40ef CMake: collect and report the skipped modules
Use the newly-added methods for reporting skipped modules.
This requires a post-3.2.39 Calamares, which the CMakeLists.txt
does not yet enforce.
2021-03-23 01:50:59 +01:00
Adriaan de Groot
6c07d39374 [os-freebsd] Remove superfluous linking
For external modules, calamares_add_plugin() does the right thing,
and links to Calamares::calamares; don't use the internal name
from the Calamares repository, and don't link redundantly
to it anyway.
2021-03-22 14:09:28 +01:00
Adriaan de Groot
4285ccebd7 [os-freebsd] Until there's a config file, mark it explicitly without 2021-03-22 13:38:39 +01:00
Adriaan de Groot
320d67a5ba CMake: bail out on broken CMake folders
On a developer's system, the CMake configuration from the **build**
can be found in cache, and that means that <srcdir>/build/CalamaresConfig.cmake
is read instead of an installed version; that doesn't work, so
bail out early (after a half-dozen errors from CalamaresConfig internals).
2021-03-22 13:04:08 +01:00
Adriaan de Groot
6a80ce6dab Add os-* modules to the build
Document SKIP_MODULES and USE_os, although they don't **work**
yet; this should be the same machinery as in the core repo.
2021-03-22 12:33:54 +01:00
Adriaan de Groot
33678a6a16 Import os-* modules from the main repo
The os-* branch was started to add some os-specific modules
to the main Calamares repo. Now that calamares-extensions
has its own release cycle and a reason-to-be beyond examples,
those modules are more suitable here than in the main repo.

Both modules are stubs; development did not get very far.
2021-03-22 12:02:19 +01:00
Oliver Smith
2c91dc4664 Docs: README: update mobile description 2021-02-23 21:13:01 +01:00
Adriaan de Groot
b3cda8d7cb Docs: polish the README to match new additions 2021-02-23 12:20:55 +01:00
Adriaan de Groot
4611f2f9ea CI: note that the release-script is Linux-only 2021-02-23 12:20:28 +01:00
Adriaan de Groot
7acd4bb3d6 Changes: pre-release housekeeping 2021-02-23 11:41:06 +01:00
Adriaan de Groot
41b4b59a5e [mobile] Apply coding style 2021-02-23 11:37:55 +01:00
Adriaan de Groot
077320dc19 CI: add the style tool from calamares 2021-02-23 11:37:16 +01:00
Adriaan de Groot
c5eeeb7a50 Merge pull request #8 from Undef-a/upstream/fs_selection
[mobile] Allow user to configure filesystem type
2021-02-23 11:17:03 +01:00
undef
52882df5ee [mobile] bugfix: initialise externalToInternal
This variable was uninitialised, leading to an undefined action occurring
when the install target screen is skipped.
2021-02-21 21:13:30 +00:00
undef
616e51b3ee [mobile] Allow user to configure filesystem type
The user can now configure the target root filesystem type. Options
provided are ext4 (default), f2fs and btrfs.

OS Maintainers are required to provide their own BTRFS install script
which configures default submodules as required.

Allow disabling selection of fstype
This setting is disabled by default and defaults to ext4. Enabling it
will provide the user with a choice of ext4, f2fs and btrfs for the root
filesystem.

Add FS specific mkfs configuration options
This allows distribution maintainers to set the command/script used for
configuring the root filesystem on a per-fs level. Two examples of its
use are:
* F2FS: mkfs.f2fs uses -l for label, rather than -L like other filesystems.
* BTRFS: Distributions can provide a script which will configure submodules
         as requiref for snapshots to work correctly.

Use of QStringList rather than a Model:
We don't need the complexities of a full model here for a list of strings
and labelModel was the wrong one anyway. At least going down that path means
the back and forward buttons work.
2021-02-21 21:12:21 +00:00
Adriaan de Groot
f2e59e611f [image-slideshow] Add an example QML slideshow for images
The default slideshow is rather complicated for a "simple" slideshow
with images, so add an extensively-documented images-only slideshow
in QML. This can easily be extended with more images, or with
text and fanciness if the designer wants to flex more QML.
2021-02-17 21:53:44 +01:00
Adriaan de Groot
a532b309b7 CI: massage message a bit and adjust naming scheme 2021-02-17 10:43:38 +01:00
Adriaan de Groot
45ae6084d8 Docs: mention the *mobile* module 2021-02-16 23:38:52 +01:00
Adriaan de Groot
e0cd044e5b CI: extract the tarball from calamares first 2021-02-16 15:34:29 +01:00
Adriaan de Groot
2c1cbb7f85 CI: try to run CI for the extensions, too 2021-02-16 15:13:12 +01:00
Adriaan de Groot
bf5e3d857a Merge pull request #11 from ollieparanoid/wait-2min
[mobile] wait screen: reword 20s -> 2 min
2021-02-16 10:53:27 +01:00
Oliver Smith
e78bd936a3 [mobile] wait screen: reword 20s -> 2 min
Change the message in the wait screen from "This may take up to 20
seconds" to "This may take up to two minutes". That's what Mobian needs,
and with "up to" in the sentence, it's not wrong, even if the actual
time is much shorter.

Creating a config option was considered (#10), but would only add
technical debt since the wait screen is already a hack (as described in
the big comment in runPartitionJobThenLeave()).
2021-02-08 22:34:16 +01:00
Adriaan de Groot
a53f9359f4 Changes: post-release housekeeping 2021-01-04 11:53:40 +01:00
147 changed files with 6678 additions and 2391 deletions

View File

@@ -8,6 +8,7 @@ AlignEscapedNewlines: DontAlign
AllowAllParametersOfDeclarationOnNextLine: "false"
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: "false"
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: "false"
AlwaysBreakAfterReturnType: TopLevelDefinitions
AlwaysBreakTemplateDeclarations: Yes
@@ -28,7 +29,6 @@ ReflowComments: "false"
SortIncludes: "true"
SpaceAfterCStyleCast: "false"
SpacesBeforeTrailingComments: "2"
# SpaceInEmptyBlock: "true"
SpacesInAngles: "true"
SpacesInParentheses: "true"
SpacesInSquareBrackets: "true"

35
.clang-format.base Normal file
View File

@@ -0,0 +1,35 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
---
BasedOnStyle: WebKit
AlignAfterOpenBracket: Align
AlignEscapedNewlines: DontAlign
AllowAllParametersOfDeclarationOnNextLine: "false"
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: "false"
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: "false"
AlwaysBreakAfterReturnType: TopLevelDefinitions
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: "false"
BinPackParameters: "false"
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: "true"
BreakConstructorInitializers: BeforeComma
ColumnLimit: 120
Cpp11BracedListStyle: "false"
FixNamespaceComments: "true"
IncludeBlocks: Preserve
IndentWidth: "4"
MaxEmptyLinesToKeep: "2"
NamespaceIndentation: None
PointerAlignment: Left
ReflowComments: "false"
SortIncludes: "true"
SpaceAfterCStyleCast: "false"
SpacesBeforeTrailingComments: "2"
SpacesInAngles: "true"
SpacesInParentheses: "true"
SpacesInSquareBrackets: "true"
Standard: Cpp11

60
.github/workflows/nightly-neon.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: nightly-neon-xtn
on:
schedule:
- cron: "52 1 * * *"
workflow_dispatch:
env:
BUILDDIR: /build
SRCDIR: ${{ github.workspace }}
CMAKE_ARGS: |
-DWEBVIEW_FORCE_WEBKIT=1
-DKDE_INSTALL_USE_QT_SYS_PATHS=ON
-DWITH_PYTHONQT=OFF"
-DCMAKE_BUILD_TYPE=Debug
jobs:
build:
runs-on: ubuntu-latest
container:
image: docker://kdeneon/plasma:user
options: --tmpfs /build:rw --user 0:0
steps:
- name: "fetch artifacts"
uses: dawidd6/action-download-artifact@v2
with:
workflow: nightly-neon.yml
workflow_conclusion: success
branch: calamares
name: calamares-tarball
path: ${{ env.BUILDDIR }}
repo: calamares/calamares
- name: "prepare env"
uses: calamares/actions/prepare-neon@v2
- name: "prepare source"
uses: actions/checkout@v2
- name: "prepare artifacts"
run: tar xvzf "$BUILDDIR/calamares.tar.gz" -C / --strip-components 1
- name: "build"
id: build
uses: calamares/actions/generic-build@v2
- name: "notify: ok"
if: ${{ success() && github.repository == 'calamares/calamares-extensions' }}
uses: calamares/actions/matrix-notify@v2
with:
token: ${{ secrets.MATRIX_TOKEN }}
room: ${{ secrets.MATRIX_ROOM }}
message: "OK ${{ github.workflow }} in ${{ github.repository }} ${{ steps.build.outputs.git_summary }}"
- name: "notify: fail"
if: ${{ failure() && github.repository == 'calamares/calamares' }}
uses: calamares/actions/matrix-notify@v2
with:
token: ${{ secrets.MATRIX_TOKEN }}
room: ${{ secrets.MATRIX_ROOM }}
message: "FAIL ${{ github.workflow }} in ${{ github.repository }} ${{ steps.build.outputs.git_summary}}"

66
.github/workflows/push.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
name: ci-push-xtn
on:
push:
branches:
- calamares
pull_request:
types:
- opened
- reopened
- synchronize
workflow_dispatch:
env:
BUILDDIR: /build
SRCDIR: ${{ github.workspace }}
CMAKE_ARGS: |
-DWEBVIEW_FORCE_WEBKIT=1
-DKDE_INSTALL_USE_QT_SYS_PATHS=ON
-DWITH_PYTHONQT=OFF"
-DCMAKE_BUILD_TYPE=Debug
jobs:
build:
runs-on: ubuntu-latest
container:
image: docker://kdeneon/plasma:user
options: --tmpfs /build:rw --user 0:0
steps:
- name: "fetch artifacts"
uses: dawidd6/action-download-artifact@v2
with:
workflow: nightly-neon.yml
workflow_conclusion: success
branch: calamares
name: calamares-tarball
path: ${{ env.BUILDDIR }}
repo: calamares/calamares
- name: "prepare env"
uses: calamares/actions/prepare-neon@v2
- name: "prepare source"
uses: actions/checkout@v2
- name: "prepare artifacts"
run: tar xvzf "$BUILDDIR/calamares.tar.gz" -C / --strip-components 1
- name: "build"
id: build
uses: calamares/actions/generic-build@v2
- name: "notify: ok"
if: ${{ success() && github.repository == 'calamares/calamares-extensions' }}
uses: calamares/actions/matrix-notify@v2
with:
token: ${{ secrets.MATRIX_TOKEN }}
room: ${{ secrets.MATRIX_ROOM }}
message: |
OK ${{ github.workflow }} in ${{ github.repository }} by ${{ github.actor }} on ${{ github.event.ref }}
.. ${{ steps.build.outputs.git-summary }}
- name: "notify: fail"
if: ${{ failure() && github.repository == 'calamares/calamares-extensions' }}
uses: calamares/actions/matrix-notify@v2
with:
token: ${{ secrets.MATRIX_TOKEN }}
room: ${{ secrets.MATRIX_ROOM }}
message: |
FAIL ${{ github.workflow }} in ${{ github.repository }} by ${{ github.actor }} on ${{ github.event.ref }}
.. ${{ steps.build.outputs.git-summary }}
.. ${{ github.event.compare }}

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
.kdev4/
build/
*.kdev4

23
.reuse/dep5 Normal file
View File

@@ -0,0 +1,23 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Calamares-Extensions
Source: https://github.com/calamares/calamares-extensions.git
### BUILD ARTIFACTS / NOT SOURCE
#
# QRC Files are basically build artifacts
#
FILES: modules/*/*.qrc
License: CC0-1.0
Copyright: no
# GitHub issue templates are not part of the source
#
Files: .github/ISSUE_TEMPLATE/*
License: CC0-1.0
Copyright: no
# GitHub actions are not part of the source
Files: .github/workflows/*.yml
License: CC0-1.0
Copyright: no

73
CHANGES
View File

@@ -6,8 +6,77 @@ This is the changelog for Calamares-Extensions. For each release, the major
changes and contributors are listed. Note that Calamares-Extensions does not
have a historical changelog -- this log starts with version 1.0.0.
# 1.3.2 (2023-08-28)
# 1.1.0 (2021-01-04) #
We skipped a couple of releases in the release-notes, then tagged
1.3.1 without a version bump or release-notes. So 1.3.2 brings us
back to "regular releases".
This release contains contributions from (alphabetically by first name):
- Anke Boersma
- Nathan Schulte
- Oliver Smith
- stravanpannala
- undef
Changes and new modules in this release:
- *mobile* Has new configuration options. (Thanks Nathan, Oliver)
- *unpackfsc* Uses a more portable invocation of tar. (Thanks sravanpannala)
# 1.2.1 (2021-11-16)
The 1.2.0 release had no release-notes for that version, and failed to
credit Anke and Otus.
Changes and new modules in this release:
- *unpackfsc* can use `fsarchiver` and unpack that, instead of squashfs;
a distro might choose one tool or the other. Currently, only *savedir* /
*restdir* mode (i.e. directories, not block-devices) are supported.
# 1.2.0 (2021-11-16)
This release contains contributions from (alphabetically by first name):
- Anke Boersma
- Otus9051
Changes and new modules in this release:
- *refind* is a new module that installs the rEFInd bootloader. It can
be used instead of the *bootloader* module from core Calamares. (Thanks Anke)
- *unpackfsc* is a new module that uses `unsquashfs` directly. This may
be faster or more convenient than the *unpackfs* module from core Calamares.
The configuration file supports only one entry, but is otherwise easy to
adapt from an existing `unpackfs.conf`.
# 1.1.2 (2021-05-14)
This release contains contributions from (alphabetically by first name):
- Oliver Smith
Changes and new modules in this release:
- New *os-* modules are intended for OS-specific work. They don't
do anything concrete yet, though.
- The *mobile* module has new features thanks to Oliver, with
keyboard selection (numeric / alpha) for PIN / password entry among them.
# 1.1.1 (2021-02-23)
This release contains contributions from (alphabetically by first name):
- Oliver Smith
- Undef
Changes and new modules in this release:
- Branding has a new example, *image-slideshow*, for a QML-based
slideshow with only images.
- CI now builds the extensions against a recent Calamares build.
- *mobile* add ability to select target filesystem.
- *mobile* rename `cmdMkfsRoot` to `cmdMkfsRootExt4`.
- *mobile* wait screen has been re-worded.
# 1.1.0 (2021-01-04)
This release contains contributions from (alphabetically by first name):
- Oliver Smith
@@ -17,7 +86,7 @@ Changes and new modules in this release:
- *mobile* module SSH daemon can be disabled
# 1.0.0 (2020-12-05) #
# 1.0.0 (2020-12-05)
This release contains contributions from (alphabetically by first name):
- Oliver Smith

View File

@@ -1,20 +1,15 @@
# === 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.
# SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# 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/>.
# Calamares-Examples is Free Software: see the License-Identifier above.
#
# SPDX-License-Identifier: GPL-3.0+
# License-Filename: LICENSE
# Individual files may have different licenses (like the CMake
# infrastructure, which is BSD-2-Clause licensed). Check the SPDX
# identifiers in each file.
#
###
#
@@ -24,15 +19,79 @@
# distro can use an unmodified (upstream) Calamares package and a local
# customisation package in tandem.
#
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
# Besides being an example repository, it is also a collection of modules
# and branding that is usable in its own right.
#
### CONFIGURING
#
# By default, all the branding examples and all the modules are built.
# This can be influenced through:
# SKIP_MODULES : a space or semicolon-separated list of directory names
# under src/modules that should not be built.
# USE_* : fills in SKIP_MODULES for modules called *-<something>
# In this repository, there is just one "group" to which USE_* applies:
# USE_os : operating-system-specific modules.
#
### NOTES
#
# Call this CMake file in script mode, e.g. `cmake -P CMakeLists.txt`
# to print out version information. Use `cmake -DVERSION_STYLE=short`
# to get just the short versioning.
#
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set( CALAMARES_EXTENSIONS_VERSION 1.4.0 )
include( ${CMAKE_CURRENT_LIST_DIR}/CMakeModules/ExtendedVersion.cmake )
if ( CMAKE_SCRIPT_MODE_FILE )
report_version( ${CALAMARES_EXTENSIONS_VERSION} ${CMAKE_CURRENT_LIST_DIR} )
return()
endif()
project(calamares-extensions
VERSION 1.1.0
VERSION ${CALAMARES_EXTENSIONS_VERSION}
LANGUAGES CXX
)
find_package(Calamares 3.2.26 REQUIRED)
set( CMAKE_CXX_STANDARD 17 )
set( CMAKE_CXX_STANDARD_REQUIRED ON )
find_package(YAMLCPP REQUIRED) # Needed to untangle some dependencies before Calamares 3.2.36
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
# installed inside it).
set( CALAMARES_VERSION_REQUIRED 3.3.0 )
find_package(Calamares ${CALAMARES_VERSION_REQUIRED} NO_CMAKE_PACKAGE_REGISTRY)
if (NOT TARGET Calamares::calamares OR NOT TARGET Calamares::calamaresui)
find_package(Calamares ${CALAMARES_VERSION_REQUIRED} REQUIRED)
endif()
message(STATUS "Found Calamares version ${Calamares_VERSION}")
message(STATUS " libraries ${Calamares_LIB_DIRS}")
message(STATUS "")
### CMAKE SETUP
#
@@ -52,6 +111,7 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.10.0")
"K_EXPORT_PLASMA_RUNNER"
)
endif()
include( CTest )
### BRANDING
@@ -59,45 +119,45 @@ endif()
# 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
#
# Add one of more modules, either C++ or Python.
#
set(SKIPPED_MODULES "")
set(LIST_SKIPPED_MODULES "")
calamares_add_module_subdirectory( modules/filekeeper ) # C++ job
calamares_add_module_subdirectory( modules/freebsddisk ) # C++ viewmodule
calamares_add_module_subdirectory( modules/mobile )
calamares_add_module_subdirectory( modules/slowpython ) # Python job
# 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}")
# If modules cannot be built, they usually call a macro
# which builds a list of explanations; show that list.
calamares_explain_skipped_modules( ${SKIPPED_MODULES} )
### RELEASE SUPPORT
#
#
set( CALAMARES_VERSION ${calamares-extensions_VERSION_MAJOR}.${calamares-extensions_VERSION_MINOR}.${calamares-extensions_VERSION_PATCH} )
# In rare cases we have hotfix-releases with a tweak
if( calamares-extensions_VERSION_TWEAK )
set( CALAMARES_VERSION "${calamares-extensions_VERSION}.${calamares-extensions_VERSION_TWEAK}" )
endif()
set( CALAMARES_VERSION_SHORT "${CALAMARES_VERSION}" )
add_custom_target(show-version
${CMAKE_COMMAND} -E echo CALAMARES_VERSION=${CALAMARES_VERSION_SHORT}
USES_TERMINAL
)
calamares_explain_skipped_modules( ${LIST_SKIPPED_MODULES} )

View File

@@ -0,0 +1,90 @@
# === This file is part of Calamares - <https://calamares.io> ===
#
# SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
# SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
#
###
#
# This file defines one function for extending a VERSION-like value
# with date and git information (if desired).
#
# - extend_version( version-string short_var long_var )
# Calling this function will copy *version-string* (which would typically
# be a semver-style string, like "3.2.40") into the variable *short_var*.
# The *version-string* plus date and git information (if git is available),
# is copied into the varialbe *long_var*, in the format {version}-{date}-{hash}
#
# A helper function that may be used independently:
#
# - get_git_version_info( out_var )
# If relevant and possible (e.g. it is a git checkout and git is availablle
# in the environment), put git versioning information in *out_var*.
#
# A convenience function for use from script-mode for version reporting:
#
# - report_version( version top_dir )
# Call this with an intended version string (e.g. "1.1") and
# the top-level source directory (e.g. `${CMAKE_CURRENT_LIST_DIR}`
# or `${CMAKE_SOURCE_DIR}` .. in script mode, the latter is not defined).
#
function( get_git_version_info out_var )
set(CMAKE_VERSION_SOURCE "")
if(EXISTS ${CMAKE_SOURCE_DIR}/.git/HEAD)
find_program(GIT_EXECUTABLE NAMES git git.cmd)
mark_as_advanced(GIT_EXECUTABLE)
if(GIT_EXECUTABLE)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --verify -q --short=8 HEAD
OUTPUT_VARIABLE head
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
if(head)
set(CMAKE_VERSION_SOURCE "${head}")
execute_process(
COMMAND ${GIT_EXECUTABLE} update-index -q --refresh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
execute_process(
COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD --
OUTPUT_VARIABLE dirty
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
if(dirty)
set(CMAKE_VERSION_SOURCE "${CMAKE_VERSION_SOURCE}-dirty")
endif()
endif()
endif()
endif()
set( ${out_var} "${CMAKE_VERSION_SOURCE}" PARENT_SCOPE )
endfunction()
function( extend_version version short_var long_var )
set( ${short_var} "${version}" PARENT_SCOPE )
# Additional info for non-release builds which want "long" version info
# with date and git information (commit, dirty status).
set( _v "${version}" )
string( TIMESTAMP CALAMARES_VERSION_DATE "%Y%m%d" )
if( CALAMARES_VERSION_DATE GREATER 0 )
set( _v ${_v}.${CALAMARES_VERSION_DATE} )
endif()
get_git_version_info( _gitv )
if( _gitv )
set( _v "${_v}-${_gitv}" )
endif()
set( ${long_var} "${_v}" PARENT_SCOPE )
endfunction()
function( report_version version top_dir )
set( CMAKE_SOURCE_DIR ${top_dir} )
extend_version( ${version} _vshort _vlong )
if ( "x${VERSION_STYLE}" STREQUAL "xshort" )
message( "${_vshort}" )
else()
message( "${_vlong}" )
endif()
endfunction()

40
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,40 @@
<!-- SPDX-FileCopyrightText: no
SPDX-License-Identifier: CC0-1.0
-->
# Contributing to Calamares Extensions
Welcome to Calamares! We're happy that you would like to add
something to Calamares -- by extending it!
This contribution guide is minimal:
all the **technical** parts of contributing to
Calamares Extensions are the same as [contributing to Calamares](https://github.com/calamares/calamares/CONTRIBUTING.md).
## Code of Conduct
The Calamares community -- of developers, translators, and downstream (distro) users --
aims to be courteous, professional, and inclusive. Harrassment, discriminatory
statements and abuse are not tolerated. In general, we apply the
[KDE Code of Conduct](https://www.kde.org/code-of-conduct/) and the
[GNOME Code of Conduct](https://wiki.gnome.org/Foundation/CodeOfConduct) (the
rules of decent behavior in both communities are pretty much the same).
> See the [CoC section on the wiki](https://github.com/calamares/calamares/wiki#code-of-conduct)
> for a longer text. To report a problem, please contact the maintainer,
> Adriaan de Groot, or the KDE Community Working Group.
## Join the Conversation
GitHub Issues are **one** place for discussing Calamares and its extensions if there are concrete
problems or a new feature to discuss.
Issues are not a help channel.
Visit Matrix for help with configuration or compilation.
Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/)
room, `#calamares:kde.org`. Responsiveness is best during the day
in Europe, but feel free to idle.
Matrix is persistent, and we'll see your message eventually.
* [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org)

24
LICENSES/BSD-2-Clause.txt Normal file
View File

@@ -0,0 +1,24 @@
Copyright 2019 Adriaan de Groot <groot@kde.org>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

119
LICENSES/CC0-1.0.txt Normal file
View File

@@ -0,0 +1,119 @@
Creative Commons Legal Code
CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES
NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE
AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION
ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE
OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS
LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION
OR WORKS PROVIDED HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer exclusive
Copyright and Related Rights (defined below) upon the creator and subsequent
owner(s) (each and all, an "owner") of an original work of authorship and/or
a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later claims
of infringement build upon, modify, incorporate in other works, reuse and
redistribute as freely as possible in any form whatsoever and for any purposes,
including without limitation commercial purposes. These owners may contribute
to the Commons to promote the ideal of a free culture and the further production
of creative, cultural and scientific works, or to gain reputation or greater
distribution for their Work in part through the use and efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with
a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or
her Copyright and Related Rights in the Work and the meaning and intended
legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be protected
by copyright and related or neighboring rights ("Copyright and Related Rights").
Copyright and Related Rights include, but are not limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work, subject
to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal protection
of databases, and under any national implementation thereof, including any
amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time extensions),
(iii) in any current or future medium and for any number of copies, and (iv)
for any purpose whatsoever, including without limitation commercial, advertising
or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the
benefit of each member of the public at large and to the detriment of Affirmer's
heirs and successors, fully intending that such Waiver shall not be subject
to revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account Affirmer's
express Statement of Purpose. In addition, to the extent the Waiver is so
judged Affirmer hereby grants to each affected person a royalty-free, non
transferable, non sublicensable, non exclusive, irrevocable and unconditional
license to exercise Affirmer's Copyright and Related Rights in the Work (i)
in all territories worldwide, (ii) for the maximum duration provided by applicable
law or treaty (including future time extensions), (iii) in any current or
future medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional purposes
(the "License"). The License shall be deemed effective as of the date CC0
was applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder of
the License, and in such case Affirmer hereby affirms that he or she will
not (i) exercise any of his or her remaining Copyright and Related Rights
in the Work or (ii) assert any associated claims and causes of action with
respect to the Work, in either case contrary to Affirmer's express Statement
of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered,
licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or other
defects, accuracy, or the present or absence of errors, whether or not discoverable,
all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims
responsibility for obtaining any necessary consents, permissions or other
rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a party
to this document and has no duty or obligation with respect to this CC0 or
use of the Work.

100
README.md
View File

@@ -1,10 +1,15 @@
<!-- SPDX-FileCopyrightText: no
SPDX-License-Identifier: CC0-1.0
-->
# Calamares Branding and Module Examples
> A *branding component* in Calamares is a description of the
> produce (i.e. distribution) being installed along with a "slideshow"
> product (i.e. distribution) being installed along with a "slideshow"
> that is displayed during the installation phase of Calamares.
> This shapes the **look** of your installation.
>
> A *module* adds functionality to Calamares; modules may be written
> A *module* adds **functionality** to Calamares; modules may be written
> in C++ or Python, using Qt Widgets or QML for the UI (with C++)
> if there is one. Both C++ and Python allow a full control over the
> target system during the installation.
@@ -13,13 +18,9 @@ This repository contains complete examples of branding and some
modules for Calamares.
- [Branding](#branding) documentation
- [default](branding/default/branding.desc) branding example
- [fancy](branding/fancy/branding.desc) branding example
- [KaOS](branding/kaos_branding/branding.desc) branding example
- [SameGame](branding/samegame/branding.desc) branding example
- [Module](#module) documentation
## Branding
- [Module](#modules) documentation
## Branding
> Branding shapes the **look** of Calamares to your distro
@@ -28,11 +29,23 @@ can be used for testing. The examples here show what can be done
with QML in the context of Calamares branding, and provide examples
and documentation for the framework that Calamares ships with.
- `default/` is a copy of the default branding included with Calamares.
- `fancy/` has navigation buttons and a slide counter.
- `kaos_branding/` is a copy of the KaOS branding component, which
has translations and a bunch of fancy graphics.
- `samegame/` is a copy of the Qt Company "Same Game" QML demo. It
- [`default/`](branding/default/branding.desc)
is a copy of the default branding included with Calamares.
- [`fancy/`](branding/fancy/branding.desc)
has navigation buttons and a slide counter.
- [`image-slideshow/`](branding/image-slideshow/branding.desc)
is a variant of the *default* branding that implements its
own slide element for QML that supports a single image.
This is useful for straightforward images-only slideshows
(probably moreso than the default slideshow).
- [`kaos_branding/`](branding/kaos_branding/branding.desc)
is a copy of the KaOS branding component, which
has translations and a bunch of fancy graphics for the
slideshow. Plus it includes examples of using different
QML options for a vertical navigation bar and horizontal
sidebar.
- [`samegame/` ](branding/default/branding.desc)
is a copy of the Qt Company "Same Game" QML demo. It
shows that **any** QML can be used for branding purposes.
### Writing your own Branding
@@ -62,7 +75,7 @@ and documentation for the framework that Calamares ships with.
parts of Calamares.
See the [styling paragraph](https://github.com/calamares/calamares/wiki/Deploy-Guide#styling-calamares) of the deployment guide for more details.
### Testing a Branding Component
If Calamares is installed, then the Calamares QML support files
@@ -72,7 +85,7 @@ branding component is free to do whatever is interesting in QML.
The tool for quickly viewing QML files is `qmlscene`, which is
included with the Qt development tools. It can be used to
preview a Calamares branding component (slideshow) without starting
preview a Calamares branding component (slideshow) without starting
Calamares. If the component uses translations, you will need to
build the translations first (using Qt Linguist `lrelease`, or by
using the normal build system for branding components).
@@ -87,7 +100,7 @@ qmlscene \
-translation build/calamares-fancy_nl.qm \
-I /usr/local/share/calamares/qml \
-geometry 600x400 \
fancy/show.qml
fancy/show.qml
```
This starts the viewer with the Dutch (nl) translation, using the
@@ -104,25 +117,39 @@ every time.
### Calamares Branding API
The slideshow which is configured in the branding files can have
one of two "API styles".
one of two "API styles".
- Version 1 is loaded when the slideshow starts. If the slideshow is
large, or contains remote content, then this may be slow.
The loading time may be visible as a "white flash" as the
QML component is displayed with no background until the
slideshow is loaded.
- Version 2 is loaded asynchronously from the moment Calamares is
started. This may delay startup a little, but may appear more
started. This may delay startup a little, but appears more
responsive overall.
If the slideshow QML defines functions
`onActivate()` and `onLeave()` then those functions
are called when the slideshow becomes visible and when the installation is finished.
These can be used to start and stop timers or sound effects or
whatever.
whatever.
In addition, if the slideshow QML defines a property
`activatedInCalamares` then it is set to `true`
when the slideshow becomes visible, and to `false` when
when the slideshow becomes visible, and to `false` when
the installation is finished. This can also be used to
start timers, etc.
start timers, etc. The standard `Presentation.qml` included with
Calamares has such a property.
A slideshow (`show.qml`) can be entirely independent, with bespoke code,
or it can make use of files shipped as part of Calamares: a *Presentation* and
a *Slide* element (and some others). There are also Calamares internals which
can be used from QML:
- `import calamares.slideshow 1.0` for the standard QML slideshow (e.g. *Presentation* element;
use *Slide* with this or write an API-equivalent element such as the one in `image-slideshow/`).
- `import io.calamares.ui 1.0` for a *Branding* object which has an API to
get colors and strings that are used elsewhere in Calamares (e.g. to make the
slide background the same as the background defined in `branding.desc`).
## Modules
@@ -135,6 +162,8 @@ listed in the *show* phase, and may run jobs if listed in the
*exec* phase) and a Python job module (no UI, runs in the *exec*
phase).
### Example Modules
- [filekeeper](modules/filekeeper/CMakeLists.txt) is a C++ **job** module
to copy files from the host (live) system to the target system at
the end of installation, like logfiles. (This module is made obsolete
@@ -146,6 +175,18 @@ phase).
module that just serves to slow down an installation by delaying
a configurable (default 30 seconds) amount of time.
### Functional Modules
- [mobile](modules/mobile/CMakeLists.txt) is a QML **view** that
takes over a number of other view steps. It is specific to
mobile phone use, and as of writing used by
[postmarketOS](https://postmarketos.org) and
[Mobian](https://mobian-project.org/). Among other things, it
allows to set up full disk encryption and to configure the
default user's password. Read the
[on-device installer](https://wiki.postmarketos.org/wiki/On-device_installer)
article for more information.
### CMake Preparation
The single macro `calamares_add_module_subdirectory()`
@@ -173,3 +214,18 @@ usually called `main.py` which defines a `run()` function.
The API is loosely documented in the
[developer guide](https://github.com/calamares/calamares/wiki/Develop-Guide).
# Join the Conversation
GitHub Issues are **one** place for discussing Calamares (and Calamares Extensions)
if there are concrete
problems or a new feature to discuss.
Issues are not a help channel.
Visit Matrix for help with configuration or compilation.
Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/)
room, `#calamares:kde.org`. Responsiveness is best during the day
in Europe, but feel free to idle.
Matrix is persistent, and we'll see your message eventually.
* [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org)

BIN
branding/artix/artix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
branding/artix/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 KiB

BIN
branding/artix/desktops.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

BIN
branding/artix/office.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

BIN
branding/artix/rolling.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

178
branding/artix/show.qml Normal file
View 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
}
}
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
branding/artix/tools.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

BIN
branding/artix/web.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@@ -0,0 +1,64 @@
/* === This file is part of Calamares Extensions - <http://github.com/calamares-extensions> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: BSD-2-Clause
*/
/* An *ImageSlide* is a *Slide* (it offers the API that *Presentation*
* expects) while displaying only a single image. This is useful
* for presentations that are all images, with no interaction or text.
*/
import QtQuick 2.5
/* To use an *ImageSlide*, instantiate it inside your *Presentation*
* and set the *src* property to a path to an image file in a supported
* format. Relative paths are ok.
*/
Item {
id: imageslide
/* Slides should be non-visible at the start; the *Presentation*
* handles visibility (so that one slide at a time is visible).
*/
visible: false
/* Make this item fill up the parent, so that alignment of the
* image (below) works out to "middle of the parent".
*/
anchors.fill: parent
/* The *Presentation* manages visibility of children that have
* attribute *isSlide* and *isSlide* is set to *true*. Other
* children are ignored, so we need to set this so that the
* *ImageSlide* elements are treated like slides.
*/
property bool isSlide: true;
/* The *Presentation* allows slides to have notes, so just leave
* an empty string here.
*/
property string notes;
/* This is the important property for *ImageSlide*: the path to the
* image to display. When instantiating *ImageSlide*, set this for
* each instance. Relative paths are ok.
*/
property string src;
/* The image itself. It has fixed sizes (300x150px). You could set
* an aspect ratio here (e.g. `height: width / 2`) as well.
*
* This binds the image source (filename) to the string *src*
* in the *ImageSlide* element, for convenience in setting things
* up in the overall slideshow. If you want to make width and
* height configurable, add a property above and then bind to
* them from the Image element.
*/
Image {
id: image
source: src
width: 300
height: 150
anchors.centerIn: parent
}
}

View File

@@ -0,0 +1,40 @@
# SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
---
componentName: default
# image-slideshow
# Branding must define some strings for the welcome page,
# even though this example is about the slideshow, not the rest.
strings:
productName: Mirror Linux
shortProductName: Mirror
version:
shortVersion:
versionedName: Mirror Linux 1.0
shortVersionedName: Mirror 1.0
bootloaderEntryName: Mirror
# These images do not exist in this branding example.
images:
productLogo: "logo.png"
productIcon: "logo.png"
productWelcome: "languages.png"
# Dark-mode for Calamares. The slideshow can access these color values
# through the Branding object which is always available to QML inside Calamares.
style:
sidebarBackground: "#36393e"
sidebarText: "#efefef"
sidebarTextSelect: "#2eb69b"
sidebarTextHighlight: "#313338"
# The actual slideshow. API version 2 means that the QML is loaded at
# startup. This is **slightly** slower at startup, but means that by
# the time we reach the slideshow, it is loaded and ready-to-go.
#
# A v2 slideshow **may** have an onActivate() and onLeave() function,
# although Calamares will complain if it does not.
slideshow: "show.qml"
slideshowAPI: 2

View File

@@ -0,0 +1,103 @@
/* === This file is part of Calamares Extensions - <http://github.com/calamares-extensions> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: BSD-2-Clause
*/
/* This is a simple slideshow for use during the *exec* phase of
* installation, that displays a handful of slides. It uses
* the *Presentation* QML components -- this allows, for instance,
* notes to be added to slides, and for arrow navigation to be
* used. But at its core it's just a bunch of images, repeating.
*
* For this kind of limited functionality, it may be better to
* use the "plain images" slideshow format in Calamares, although
* then you don't have any say in how things are animated.
*
* This slideshow is written for *slideshowAPI* version 1, so in
* `branding.desc` set that appropriately.
*/
import QtQuick 2.0 // Basic QML
import calamares.slideshow 1.0 // Calamares slideshow: Presentation
import io.calamares.ui 1.0 // Calamares internals: Branding
/* *Presentation* comes from the pre-installed calamares.slideshow
* that comes with Calamares itself. See `Presentation.qml` in the
* Calamares repository for details and documentation.
*
* The important parts of presentation are:
* - it has a property *activatedInCalamares* which is set to *true*
* when the slideshow becomes visible, *false* afterwards.
* - it expects one or more children with a property *isSlide*
* set to *true*.
* - it has a function *goToNextSlide()* to do just that (where
* "slides" is the sequence of children that have property
* *isSlide* set to *true*.
*
*/
Presentation
{
id: presentation
/* This timer ticks once per second (1000ms, set in *interval*)
* and calls *goToNextSlide()* each time. Note that it needs
* to know the *id* of the presentation, so keep *id* (above)
* matched with the function call.
*
* The timer starts when the presentation is activated; you could
* also set *running* to true, but that might cost extra resources.
*/
Timer {
interval: 1000
running: presentation.activatedInCalamares
repeat: true
onTriggered: presentation.goToNextSlide()
}
/* These functions are called when the presentation starts and
* ends, respectively. They could be used to start the timer,
* but that is done automatically through *activatedInCalamares*,
* so there's nothing **to** do.
*
* Leaving these functions out is fine, although Calamares will
* complain that they are missing, then.
*/
function onActivate() { }
function onLeave() { }
/* A presentation is an Item: it has no visual appearance at all.
* Give it a background, which fills the whole area of the presentation.
* Setting *z* to a low value places this rectangle **behind** other
* things in the presentation -- which is correct for a background.
*
* This uses the background set in the styles section of `branding.desc`.
*/
Rectangle {
id: mybackground
anchors.fill: parent
color: Branding.styleString(Branding.SidebarBackground)
z: -1
}
/* The *ImageSlide* is a component unique to this branding directory.
* The QML file `ImageSlide.qml` can be stored alongside `show.qml`
* and it will be loaded on-demand. See the documentation in that
* file for details, but it comes down to this: for each *ImageSlide*,
* set *src* to a suitable value (an image path in this directory)
* and that will be displayed.
*/
ImageSlide {
src: "slide1.png"
}
ImageSlide {
src: "slide2.png"
}
ImageSlide {
src: "slide3.png"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,101 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2020 2022 Anke Boersma <demm@kaosx.us>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
import io.calamares.core 1.0
import io.calamares.ui 1.0
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.3
ApplicationWindow {
id: about
visible: true
width: 760
height: 400
title: qsTr("About Calamares")
property var appName: "Calamares"
property var appVersion: "3.3 RC"
Rectangle {
id: textArea
anchors.fill: parent
color: "#f2f2f2"
Column {
id: column
anchors.centerIn: parent
Rectangle {
width: 560
height: 250
radius: 10
border.width: 0
Text {
width: 400
height: 250
anchors.centerIn: parent
text: qsTr("<h1>%1</h1><br/>
<strong>%2<br/>
for %3</strong><br/><br/>
Copyright 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>
Copyright 2017-2022 Adriaan de Groot &lt;groot@kde.org&gt;<br/>
Thanks to <a href='https://calamares.io/team/'>the Calamares team</a>
and the <a href=\"https://www.transifex.com/kaos/kaos/\">KaOS
translators team</a>.<br/><br/>
<a href='https://calamares.io/'>Calamares</a>
development is sponsored by <br/>
<a href='http://www.blue-systems.com/'>Blue Systems</a> -
Liberating Software." )
.arg(appName)
.arg(appVersion)
.arg(Branding.string(Branding.VersionedName))
onLinkActivated: Qt.openUrlExternally(link)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
font.pointSize: 10
anchors.verticalCenterOffset: 10
anchors.horizontalCenterOffset: 40
wrapMode: Text.WordWrap
}
Image {
id: image
x: 8
y: 12
height: 100
fillMode: Image.PreserveAspectFit
source: "squid.png"
}
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
icon.name: "window-close"
text: qsTr("Close")
hoverEnabled: true
onClicked: about.close();
}
}
}

View File

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

View File

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

View File

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

View File

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

After

Width:  |  Height:  |  Size: 147 B

View File

@@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
foreach( lang ast ca cs de en es es_AR fr hu id_ID nl_NL pl pt_BR pt_PT ro_RO ru sr_RS tr_TR zh_CN )
list( APPEND TS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/calamares-${COMPONENT_NAME}_${lang}.ts" )
endforeach()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

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

After

Width:  |  Height:  |  Size: 896 B

View File

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

After

Width:  |  Height:  |  Size: 898 B

View File

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

After

Width:  |  Height:  |  Size: 902 B

View File

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

View File

@@ -10,6 +10,7 @@
# NOTE: this is largely a copy of the release script for Calamares,
# with not-applicable parts (such as translation-freeze) either
# commented-out, or skipped with if(false).
# NOTE: this script may contain Linuxisms
#
# This attempts to perform the different steps of the RELEASE.md
# document automatically. It's not tested on other machines or
@@ -31,6 +32,7 @@
# * `-B` do not build (before tagging)
# * `-P` do not package (tag, sign, tarball)
# * `-T` do not respect string freeze
# * `-C <args>` set extra arguments to pass to CMake
#
# The build / package settings can be influenced via environment variables:
# * BUILD_DEFAULT set to `false` to avoid first build with gcc
@@ -91,7 +93,7 @@ fi
### Setup
#
#
BUILDDIR=$(mktemp -d --suffix=-build --tmpdir=.)
BUILDDIR=$(mktemp -d -p . -t build.XXXXX)
### Build with default compiler
#
@@ -138,30 +140,34 @@ fi
### Get version number for this release
#
#
V=$( cd "$BUILDDIR" && make show-version | grep ^CALAMARES_VERSION | sed s/^[A-Z_]*=// )
V=$( cmake -DVERSION_STYLE=short -P CMakeLists.txt 2>&1 )
test -n "$V" || { echo "Could not obtain version in $BUILDDIR ." ; exit 1 ; }
### Create signed tag
#
# This is the signing key ID associated with the GitHub account adriaandegroot,
# which is used to create all "verified" tags in the Calamares repo.
KEY_ID="CFDDC96F12B1915C"
#
KEY_ID="328D742D8807A435"
git tag -u "$KEY_ID" -m "Release v$V" "v$V" || { echo "Could not sign tag v$V." ; exit 1 ; }
### Create the tarball
#
# Create the tarball, compute SHA256 for later reporting, and
# sign the tarball so the signature can be uploaded separately.
#
TAR_V="$TARBALL_PREFIX-$V"
TAR_FILE="$TAR_V.tar.gz"
git archive -o "$TAR_FILE" --prefix "$TAR_V/" "v$V" || { echo "Could not create tarball." ; exit 1 ; }
test -f "$TAR_FILE" || { echo "Tarball was not created." ; exit 1 ; }
SHA256=$(sha256sum "$TAR_FILE" | cut -d" " -f1)
gpg -s -u $KEY_ID --detach --armor $TAR_FILE # Sign the tarball
### Build the tarball
#
#
D=$(date +%Y%m%d-%H%M%S)
TMPDIR=$(mktemp -d --suffix="-calamares-$D")
TMPDIR=$(mktemp -d -p . -t calamares.XXXXX)
test -d "$TMPDIR" || { echo "Could not create tarball-build directory." ; exit 1 ; }
tar xzf "$TAR_FILE" -C "$TMPDIR" || { echo "Could not unpack tarball." ; exit 1 ; }
test -d "$TMPDIR/$TAR_V" || { echo "Tarball did not contain source directory." ; exit 1 ; }
@@ -181,7 +187,6 @@ rm -rf "$TMPDIR" # From tarball
cat <<EOF
# Next steps for this release:
git push origin v$V
gpg -s -u $KEY_ID --detach --armor $TAR_FILE # Sign the tarball
# Upload tarball $TAR_FILE and the signature $TAR_FILE.asc
# Announce via https://github.com/calamares/$PROJECT_NAME/releases/new
# SHA256: $SHA256

17
ci/astylerc Normal file
View File

@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
# SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
# Do not create a backup file
suffix=none
indent=spaces=4
# Brackets
style=break
add-brackets
# Spaces
pad-paren-in
pad-header
align-pointer=type

105
ci/calamaresstyle Executable file
View File

@@ -0,0 +1,105 @@
#!/bin/sh
#
# SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
# SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
#
# Calls astyle with settings matching Calamares coding style
# Requires astyle >= 2.04 and clang-format-8 or later
#
# You can pass in directory names, in which case the files
# in that directory (NOT below it) are processed.
#
LANG=C
LC_ALL=C
LC_NUMERIC=C
export LANG LC_ALL LC_NUMERIC
BASEDIR=$(dirname $0)
TOPDIR=$( cd $BASEDIR/.. && pwd -P )
test -d "$BASEDIR" || { echo "! Could not determine base for $0" ; exit 1 ; }
test -d "$TOPDIR" || { echo "! Cound not determine top-level source dir" ; exit 1 ; }
test -f "$TOPDIR/.clang-format.base" || { echo "! No .clang-format support files in $TOPDIR" ; exit 1 ; }
AS=$( which astyle )
# Allow specifying CF_VERSIONS outside already
CF_VERSIONS="$CF_VERSIONS clang-format-8 clang-format80 clang-format90 clang-format-9.0.1 clang-format"
for _cf in $CF_VERSIONS
do
# Not an error if this particular clang-format isn't found
CF=$( which $_cf 2> /dev/null || true )
test -n "$CF" && break
done
test -n "$AS" || { echo "! No astyle found in PATH"; exit 1 ; }
test -n "$CF" || { echo "! No clang-format ($CF_VERSIONS) found in PATH"; exit 1 ; }
test -x "$AS" || { echo "! $AS is not executable."; exit 1 ; }
test -x "$CF" || { echo "! $CF is not executable."; exit 1 ; }
### CLANG-FORMAT-WRANGLING
#
# Version 7 and earlier doesn't understand all the options we would like
# Version 8 is ok
# Version 9 is ok
# Later versions change some defaults so need extra wrangling.
# .. there are extra files that are appended to the settings, per
# .. clang-format version.
format_version=`"$CF" --version | tr -dc '[^.0-9]' | cut -d . -f 1`
case "$format_version" in
[0-7] )
echo "! Clang-format version 8+ required"
exit 1
;;
[89] )
:
;;
* )
echo "! Clang-format version '$format_version' unsupported."
exit 1
;;
esac
_fmt="$TOPDIR/.clang-format"
cp "$_fmt.base" "$_fmt"
for f in "$extra_settings" ; do
test -f "$_fmt.$f" && cat "$_fmt.$f" >> "$_fmt"
done
### FILE PROCESSING
#
#
set -e
any_dirs=no
for d in "$@"
do
test -d "$d" && any_dirs=yes
done
style_some()
{
if test -n "$*" ; then
$AS --options=$BASEDIR/astylerc --quiet "$@"
$CF -i -style=file "$@"
fi
}
if test "x$any_dirs" = "xyes" ; then
for d in "$@"
do
if test -d "$d" ; then
style_some $( find "$d" -maxdepth 1 -type f -name '*.cpp' -o -name '*.h' )
else
style_some "$d"
fi
done
else
style_some "$@"
fi
### CLANG-FORMAT-WRANGLING
#
# Restore the original .clang-format
cp "$_fmt.base" "$_fmt"

View 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
View 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

View File

@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
---
type: "job"
name: "basestrap"
interface: "python"
script: "main.py"

View File

@@ -1,11 +0,0 @@
# The FileKeeper plugin preserves files from the live system
# to the target system. It is particularly suited for copying
# the log file(s) for post-installation or post-mortem examination.
calamares_add_plugin( filekeeper
TYPE job
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
FileKeeper.cpp
SHARED_LIB
)

View File

@@ -1,58 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "FileKeeper.h"
#include <QDateTime>
#include <QProcess>
#include <QThread>
#include <GlobalStorage.h>
#include <JobQueue.h>
#include <utils/Logger.h>
FileKeeperJob::FileKeeperJob( QObject* parent )
: Calamares::CppJob( parent )
{
}
FileKeeperJob::~FileKeeperJob() {}
QString
FileKeeperJob::prettyName() const
{
return tr( "File keeper Job" );
}
Calamares::JobResult
FileKeeperJob::exec()
{
return Calamares::JobResult::ok();
}
void
FileKeeperJob::setConfigurationMap( const QVariantMap& configurationMap )
{
Q_UNUSED( configurationMap );
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( FileKeeperJobFactory, registerPlugin< FileKeeperJob >(); )

View File

@@ -1,46 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FILEKEEPER_H
#define FILEKEEPER_H
#include <CppJob.h>
#include <DllMacro.h>
#include <utils/PluginFactory.h>
#include <QObject>
#include <QVariantMap>
class PLUGINDLLEXPORT FileKeeperJob : public Calamares::CppJob
{
Q_OBJECT
public:
explicit FileKeeperJob( QObject* parent = nullptr );
virtual ~FileKeeperJob() override;
QString prettyName() const override;
Calamares::JobResult exec() override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( FileKeeperJobFactory )
#endif

View File

@@ -1,14 +0,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
)

View File

@@ -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 >(); )

View File

@@ -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

View File

@@ -1,7 +0,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

View File

@@ -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."
}
}

View File

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

View File

@@ -1,19 +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
Config.h
MobileQmlViewStep.cpp
MobileQmlViewStep.h
PartitionJob.cpp
PartitionJob.h
UsersJob.cpp
UsersJob.h
RESOURCES
mobile.qrc
SHARED_LIB
)

View File

@@ -1,145 +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/Variant.h"
#include <QVariant>
Config::Config( QObject* parent )
: QObject( parent )
{
}
void
Config::setConfigurationMap( const QVariantMap& cfgMap )
{
using namespace CalamaresUtils;
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_username = getString( cfgMap, "username", "user" );
m_featureSshd = getBool( cfgMap, "featureSshd", true );
m_cmdLuksFormat = getString( cfgMap, "cmdLuksFormat", "cryptsetup luksFormat --use-random" );
m_cmdLuksOpen = getString( cfgMap, "cmdLuksOpen", "cryptsetup luksOpen" );
m_cmdMkfsRoot = getString( cfgMap, "cmdMkfsRoot", "mkfs.ext4 -L 'unknownOS_root'" );
m_cmdMount = getString( cfgMap, "cmdMount", "mount" );
m_targetDeviceRoot = getString( cfgMap, "targetDeviceRoot", "/dev/unknown" );
m_targetDeviceRootInternal = getString( cfgMap, "targetDeviceRootInternal", "" );
m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" );
m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" );
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,
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 )
{
/* 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,
m_cmdMkfsRoot,
m_cmdMount,
m_targetDeviceRoot,
m_targetDeviceRootInternal,
m_installFromExternalToInternal,
m_isFdeEnabled,
m_fdePassword );
Calamares::JobResult res = j->exec();
Calamares::ViewManager* v = Calamares::ViewManager::instance();
if ( res )
{
v->next();
}
else
{
v->onInstallationFailed( res.message(), res.details() );
}
}
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;
}

View File

@@ -1,151 +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
/* 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 )
/* default user */
Q_PROPERTY( QString username READ username CONSTANT FINAL )
Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged )
/* 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 )
/* 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 cmdMkfsRoot READ cmdMkfsRoot 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();
/* 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; }
/* default user */
QString username() const { return m_username; }
QString userPassword() const { return m_userPassword; }
void setUserPassword( const QString& userPassword );
/* ssh server + credetials */
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 );
/* 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 cmdMkfsRoot() const { return m_cmdMkfsRoot; }
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 cmdSshdEnable() const { return m_cmdSshdEnable; }
QString cmdSshdDisable() const { return m_cmdSshdDisable; }
QString cmdSshdUseradd() const { return m_cmdSshdUseradd; }
private:
/* welcome */
QString m_osName;
QString m_arch;
QString m_device;
QString m_userInterface;
QString m_version;
/* default user */
QString m_username;
QString m_userPassword;
/* ssh server + credetials */
bool m_featureSshd;
QString m_sshdUsername;
QString m_sshdPassword;
bool m_isSshEnabled;
/* full disk encryption */
QString m_fdePassword = "";
bool m_isFdeEnabled = false;
/* partition job */
QString m_cmdInternalStoragePrepare;
QString m_cmdLuksFormat;
QString m_cmdLuksOpen;
QString m_cmdMkfsRoot;
QString m_cmdMount;
QString m_targetDeviceRoot;
QString m_targetDeviceRootInternal;
bool m_installFromExternalToInternal;
/* users job */
QString m_cmdPasswd;
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 );
/* ssh server + credentials */
void sshdUsernameChanged( QString sshdUsername );
void sshdPasswordChanged( QString sshdPassword );
/* full disk encryption */
void fdePasswordChanged( QString fdePassword );
};

View File

@@ -1,75 +0,0 @@
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
* SPDX-License-Identifier: GPL-3.0-or-later */
#include "MobileQmlViewStep.h"
#include "GlobalStorage.h"
#include "locale/LabelModel.h"
#include "utils/Dirs.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
#include "Branding.h"
#include "modulesystem/ModuleManager.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;
}

View File

@@ -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

View File

@@ -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( 120 ) );
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();
}

View File

@@ -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;
};

View File

@@ -1,88 +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& 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_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_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();
}

View File

@@ -1,36 +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& 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_cmdSshd;
QString m_cmdSshdUseradd;
bool m_isSshEnabled;
QString m_username;
QString m_password;
QString m_sshdUsername;
QString m_sshdPassword;
};

View File

@@ -1,95 +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: description
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 30
wrapMode: Text.WordWrap
text: "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."
width: 500
}
TextField {
id: userPin
anchors.top: description.bottom
placeholderText: qsTr("PIN")
echoMode: TextInput.Password
onTextChanged: validatePin(userPin, userPinRepeat, errorText)
text: config.userPassword
/* Let the virtual keyboard change to digits only */
inputMethodHints: Qt.ImhDigitsOnly
onActiveFocusChanged: {
if(activeFocus) {
Qt.inputMethod.update(Qt.ImQueryInput)
}
}
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50
width: 500
}
TextField {
id: userPinRepeat
anchors.top: userPin.bottom
placeholderText: qsTr("PIN (repeat)")
inputMethodHints: Qt.ImhDigitsOnly
echoMode: TextInput.Password
onTextChanged: validatePin(userPin, userPinRepeat, errorText)
text: config.userPassword
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50
width: 500
}
Text {
anchors.top: userPinRepeat.bottom
id: errorText
visible: false
wrapMode: Text.WordWrap
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50
width: 500
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: errorText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Continue")
onClicked: {
if (validatePin(userPin, userPinRepeat, errorText)) {
config.userPassword = userPin.text;
navNext();
}
}
}
}

View File

@@ -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: 30
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: 500
}
Button {
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Enable")
onClicked: {
config.isFdeEnabled = true;
navNext();
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Disable")
onClicked: {
config.isFdeEnabled = false;
navNextFeature();
}
}
}

View File

@@ -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: 500
}
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: 50
width: 500
}
Text {
id: errorText
anchors.top: passwordRepeat.bottom
visible: false
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50
width: 500
wrapMode: Text.WordWrap
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: errorText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Continue")
onClicked: {
if (validatePassword(password, passwordRepeat, errorText)) {
config.fdePassword = password.text;
navNext();
}
}
}
}

View File

@@ -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: 30
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><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: 500
}
Button {
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Install")
onClicked: navFinish()
}
}

View File

@@ -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: 30
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: 500
}
Button {
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Internal (eMMC)")
onClicked: {
config.installFromExternalToInternal = true;
navNext();
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom
anchors.topMargin: 40
width: 500
text: qsTr("External (SD card)")
onClicked: {
config.installFromExternalToInternal = false;
navNextFeature();
}
}
}

View File

@@ -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: 30
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: 500
}
Button {
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Yes")
onClicked: {
navNext();
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom
anchors.topMargin: 40
width: 500
text: qsTr("No")
onClicked: {
navBack();
}
}
}

View File

@@ -1,100 +0,0 @@
# Commented out values are defaults.
# All commands are running with 'sh -c'.
---
#######
### Target OS information
#######
## Operating System Name
# osName: "(unknown)"
## User Interface name (e.g. Plasma Mobile)
# userInterface: "(unknown)"
## OS version
# version: "(unknown)"
## Default username (for which the password will be set)
# username: "user"
#######
### 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
#######
### 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>
# cmdMkfsRoot: "mkfs.ext4 -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)
#######
## 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"

View File

@@ -1,409 +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",
"default_pin": "Lockscreen PIN",
"ssh_confirm": "SSH server",
"ssh_credentials": "SSH credentials",
"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": "userPin",
"screens": ["default_pin"]},
{"name": "sshd",
"screens": ["ssh_confirm", "ssh_credentials"]},
{"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, "default_pin": 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 the initramfs on-screen keyboard
* (osk-sdl: see src/keyboard.cpp). FIXME: make configurable, but keep this as default? */
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: 60
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: 32
implicitHeight: 30
border.color: "#c1bab5"
border.width: 1
radius: 4
color: mobileBack.down ? "#dbdbdb" : "#f2f2f2"
}
onClicked: navBack()
}
Rectangle {
implicitHeight: 30
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
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 = featureIdByScreen[screen] + 1;
/* Skip disabled features */
do {
/* 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);
id += 1;
continue;
}
/* Check skipFeature<Name>() */
var funcName = "skipFeature" + nameUp;
if (eval("typeof " + funcName) === "function"
&& eval(funcName + "()")) {
console.log("Skipping feature (skip function): " + name);
id += 1;
continue;
}
} while(false);
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 (default_pin, 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 validateSshdUsername(username, errorText) {
var name = username.text;
var reserved = [ /* FIXME: make configurable */
config.username,
"adm",
"at ",
"bin",
"colord",
"cron",
"cyrus",
"daemon",
"ftp",
"games",
"geoclue",
"guest",
"halt",
"lightdm",
"lp",
"mail",
"man",
"messagebus",
"news",
"nobody",
"ntp",
"operator",
"polkitd",
"postmaster",
"pulse",
"root",
"shutdown",
"smmsp",
"squid",
"sshd",
"sync",
"uucp",
"vpopmail",
"xfs",
]
/* Validate characters */
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 validateSshdPassword(password, passwordRepeat, errorText) {
var pass = password.text;
var repeat = passwordRepeat.text;
if (pass == "")
return validationFailure(errorText);
if (pass.length < 8)
return validationFailure(errorText,
"Too short: needs at least 8" +
" characters.");
if (repeat == "")
return validationFailure(errorText);
if (pass != repeat)
return validationFailure(errorText, "Passwords don't match.");
return validationFailureClear(errorText);
}
/* Input validation: fde_pass */
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);
if (!check_chars(pass))
return validationFailure(errorText,
"The password must only contain" +
" these characters, others cannot be" +
" typed in at boot time:\n" +
"\n" +
allowed_chars_multiline());
if (pass.length < 8)
return validationFailure(errorText,
"Too short: needs at least 8" +
" characters.");
if (repeat == "")
return validationFailure(errorText);
if (pass != repeat)
return validationFailure(errorText, "Passwords don't match.");
return validationFailureClear(errorText);
}
}

View File

@@ -1,20 +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>default_pin.qml</file> <!-- default user: pin -->
<file>ssh_confirm.qml</file> <!-- sshd: enable or not? -->
<file>ssh_credentials.qml</file> <!-- sshd user: username, password -->
<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>

View File

@@ -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: 500
}
Button {
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Enable")
onClicked: {
config.isSshEnabled = true;
navNext();
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Disable")
onClicked: {
config.isSshEnabled = false;
navNextFeature();
}
}
}

View File

@@ -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: 500
}
Text {
id: errorTextUsername
anchors.top: username.bottom
visible: false
wrapMode: Text.WordWrap
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50
width: 500
}
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: 500
}
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: 500
}
Text {
id: errorTextPassword
anchors.top: passwordRepeat.bottom
visible: false
wrapMode: Text.WordWrap
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50
width: 500
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: errorTextPassword.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Continue")
onClicked: {
if (validateSshdUsername(username, errorTextUsername) &&
validateSshdPassword(password, passwordRepeat,
errorTextPassword)) {
config.sshdUsername = username.text;
config.sshdPassword = password.text;
navNext();
}
}
}
}

View File

@@ -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: 50
height: 250
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: 150
wrapMode: Text.WordWrap
text: "Formatting and mounting target partition. This may" +
" take up to 20 seconds, please be patient."
width: 500
}
}
}

View File

@@ -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: 50
height: 250
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: 50
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 " +
"<b>" + config.device + "</b><br>"
width: 500
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom
anchors.topMargin: 50
width: 500
text: qsTr("Continue")
onClicked: navNext()
}
}
}
}

View File

@@ -0,0 +1,68 @@
# === This file is part of Calamares - <https://calamares.io> ===
#
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
#
find_package(${qtname} COMPONENTS Core Gui Widgets REQUIRED)
set(_extra_libraries "")
set(_extra_src "")
### 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 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 ItemAppStream.cpp)
endif()
endif()
calamares_add_plugin(packagechooser
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
Config.cpp
PackageChooserPage.cpp
PackageChooserViewStep.cpp
PackageModel.cpp
${_extra_src}
RESOURCES
packagechooser.qrc
UI
page_package.ui
LINK_PRIVATE_LIBRARIES
${_extra_libraries}
SHARED_LIB
)
# include(CalamaresAddTest)
# calamares_add_test(
# packagechoosertest
# GUI
# SOURCES Tests.cpp
# LIBRARIES calamares_viewmodule_packagechooser ${_extra_libraries}
# )

View File

@@ -0,0 +1,361 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 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 "Config.h"
#ifdef HAVE_APPDATA
#include "ItemAppData.h"
#endif
#ifdef HAVE_APPSTREAM_VERSION
#include "ItemAppStream.h"
#include <AppStreamQt/pool.h>
#include <memory>
#endif
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "compat/Variant.h"
#include "packages/Globals.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
/** @brief This removes any values from @p groups that match @p source
*
* This is used to remove duplicates from the netinstallAdd structure
* It iterates over @p groups and for each map in the list, if the
* "source" element matches @p source, it is removed from the returned
* list.
*/
static QVariantList
pruneNetinstallAdd( const QString& source, const QVariant& groups )
{
QVariantList newGroupList;
const QVariantList groupList = groups.toList();
for ( const QVariant& group : groupList )
{
QVariantMap groupMap = group.toMap();
if ( groupMap.value( "source", "" ).toString() != source )
{
newGroupList.append( groupMap );
}
}
return newGroupList;
}
const NamedEnumTable< PackageChooserMode >&
packageChooserModeNames()
{
static const NamedEnumTable< PackageChooserMode > names {
{ "optional", PackageChooserMode::Optional },
{ "required", PackageChooserMode::Required },
{ "optionalmultiple", PackageChooserMode::OptionalMultiple },
{ "requiredmultiple", PackageChooserMode::RequiredMultiple },
// and a bunch of aliases
{ "zero-or-one", PackageChooserMode::Optional },
{ "radio", PackageChooserMode::Required },
{ "one", PackageChooserMode::Required },
{ "set", PackageChooserMode::OptionalMultiple },
{ "zero-or-more", PackageChooserMode::OptionalMultiple },
{ "multiple", PackageChooserMode::RequiredMultiple },
{ "one-or-more", PackageChooserMode::RequiredMultiple }
};
return names;
}
const NamedEnumTable< PackageChooserMethod >&
PackageChooserMethodNames()
{
static const NamedEnumTable< PackageChooserMethod > names {
{ "legacy", PackageChooserMethod::Legacy },
{ "custom", PackageChooserMethod::Legacy },
{ "contextualprocess", PackageChooserMethod::Legacy },
{ "packages", PackageChooserMethod::Packages },
{ "netinstall-add", PackageChooserMethod::NetAdd },
{ "netinstall-select", PackageChooserMethod::NetSelect },
};
return names;
}
Config::Config( QObject* parent )
: Calamares::ModuleSystem::Config( parent )
, m_model( new PackageListModel( this ) )
, m_mode( PackageChooserMode::Required )
{
}
Config::~Config() {}
const PackageItem&
Config::introductionPackage() const
{
for ( int i = 0; i < m_model->packageCount(); ++i )
{
const auto& package = m_model->packageData( i );
if ( package.isNonePackage() )
{
return package;
}
}
static PackageItem* defaultIntroduction = nullptr;
if ( !defaultIntroduction )
{
const auto name = QT_TR_NOOP( "Package Selection" );
const auto description
= QT_TR_NOOP( "Please pick a product from the list. The selected product will be installed." );
defaultIntroduction = new PackageItem( QString(), name, description );
defaultIntroduction->screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) );
defaultIntroduction->name = Calamares::Locale::TranslatedString( name, metaObject()->className() );
defaultIntroduction->description
= Calamares::Locale::TranslatedString( description, metaObject()->className() );
}
return *defaultIntroduction;
}
static inline QString
make_gs_key( const Calamares::ModuleSystem::InstanceKey& key )
{
return QStringLiteral( "packagechooser_" ) + key.id();
}
void
Config::updateGlobalStorage( const QStringList& selected ) const
{
if ( m_packageChoice.has_value() )
{
cWarning() << "Inconsistent package choices -- both model and single-selection QML";
}
if ( m_method == PackageChooserMethod::Legacy )
{
QString value = selected.join( ',' );
Calamares::JobQueue::instance()->globalStorage()->insert( make_gs_key( m_defaultId ), value );
cDebug() << m_defaultId << "selected" << value;
}
else if ( m_method == PackageChooserMethod::Packages )
{
QStringList packageNames = m_model->getInstallPackagesForNames( selected );
cDebug() << m_defaultId << "packages to install" << packageNames;
Calamares::Packages::setGSPackageAdditions(
Calamares::JobQueue::instance()->globalStorage(), m_defaultId, packageNames );
}
else if ( m_method == PackageChooserMethod::NetAdd )
{
QVariantList netinstallDataList = m_model->getNetinstallDataForNames( selected );
if ( netinstallDataList.isEmpty() )
{
cWarning() << "No netinstall information found for " << selected;
}
else
{
// If an earlier packagechooser instance added this data to global storage, combine them
auto* gs = Calamares::JobQueue::instance()->globalStorage();
if ( gs->contains( "netinstallAdd" ) )
{
netinstallDataList
+= pruneNetinstallAdd( QStringLiteral( "packageChooser" ), gs->value( "netinstallAdd" ) );
}
gs->insert( "netinstallAdd", netinstallDataList );
}
}
else if ( m_method == PackageChooserMethod::NetSelect )
{
cDebug() << m_defaultId << "groups to select in netinstall" << selected;
QStringList newSelected = selected;
auto* gs = Calamares::JobQueue::instance()->globalStorage();
// If an earlier packagechooser instance added this data to global storage, combine them
if ( gs->contains( "netinstallSelect" ) )
{
auto selectedOrig = gs->value( "netinstallSelect" );
if ( selectedOrig.canConvert< QStringList >() )
{
newSelected += selectedOrig.toStringList();
}
else
{
cWarning() << "Invalid NetinstallSelect data in global storage. Earlier selections purged";
}
gs->remove( "netinstallSelect" );
}
gs->insert( "netinstallSelect", newSelected );
}
else
{
cWarning() << "Unknown packagechooser method" << smash( m_method );
}
}
void
Config::updateGlobalStorage() const
{
if ( m_model->packageCount() > 0 )
{
cWarning() << "Inconsistent package choices -- both model and single-selection QML";
}
if ( m_method == PackageChooserMethod::Legacy )
{
auto* gs = Calamares::JobQueue::instance()->globalStorage();
if ( m_packageChoice.has_value() )
{
gs->insert( make_gs_key( m_defaultId ), m_packageChoice.value() );
}
else
{
gs->remove( make_gs_key( m_defaultId ) );
}
}
else if ( m_method == PackageChooserMethod::Packages )
{
cWarning() << "Unsupported single-selection packagechooser method 'Packages'";
}
else
{
cWarning() << "Unknown packagechooser method" << smash( m_method );
}
}
void
Config::setPackageChoice( const QString& packageChoice )
{
if ( packageChoice.isEmpty() )
{
m_packageChoice.reset();
}
else
{
m_packageChoice = packageChoice;
}
emit packageChoiceChanged( m_packageChoice.value_or( QString() ) );
}
QString
Config::prettyName() const
{
return m_stepName ? m_stepName->get() : tr( "Packages" );
}
QString
Config::prettyStatus() const
{
return tr( "Install option: <strong>%1</strong>" ).arg( m_packageChoice.value_or( tr( "None" ) ) );
}
static void
fillModel( PackageListModel* model, const QVariantList& items )
{
if ( items.isEmpty() )
{
cWarning() << "No *items* for PackageChooser module.";
return;
}
#ifdef HAVE_APPSTREAM_VERSION
std::unique_ptr< AppStream::Pool > pool;
bool poolOk = false;
#endif
cDebug() << "Loading PackageChooser model items from config";
int item_index = 0;
for ( const auto& item_it : items )
{
++item_index;
QVariantMap item_map = item_it.toMap();
if ( item_map.isEmpty() )
{
cWarning() << "PackageChooser entry" << item_index << "is not valid.";
continue;
}
if ( item_map.contains( "appdata" ) )
{
#ifdef HAVE_XML
model->addPackage( fromAppData( item_map ) );
#else
cWarning() << "Loading AppData XML is not supported.";
#endif
}
else if ( item_map.contains( "appstream" ) )
{
#ifdef HAVE_APPSTREAM_VERSION
if ( !pool )
{
pool = std::make_unique< AppStream::Pool >();
pool->setLocale( QStringLiteral( "ALL" ) );
poolOk = pool->load();
}
if ( pool && poolOk )
{
model->addPackage( fromAppStream( *pool, item_map ) );
}
#else
cWarning() << "Loading AppStream data is not supported.";
#endif
}
else
{
model->addPackage( PackageItem( item_map ) );
}
}
cDebug() << Logger::SubEntry << "Loaded PackageChooser with" << model->packageCount() << "entries.";
}
void
Config::setConfigurationMap( const QVariantMap& configurationMap )
{
m_mode = packageChooserModeNames().find( Calamares::getString( configurationMap, "mode" ),
PackageChooserMode::Required );
m_method = PackageChooserMethodNames().find( Calamares::getString( configurationMap, "method" ),
PackageChooserMethod::Legacy );
if ( m_method == PackageChooserMethod::Legacy )
{
cDebug() << "Using module ID" << m_defaultId;
}
if ( configurationMap.contains( "items" ) )
{
fillModel( m_model, configurationMap.value( "items" ).toList() );
QString default_item_id = Calamares::getString( configurationMap, "default" );
if ( !default_item_id.isEmpty() )
{
for ( int item_n = 0; item_n < m_model->packageCount(); ++item_n )
{
QModelIndex item_idx = m_model->index( item_n, 0 );
QVariant item_id = m_model->data( item_idx, PackageListModel::IdRole );
if ( item_id.toString() == default_item_id )
{
m_defaultModelIndex = item_idx;
break;
}
}
}
}
else
{
setPackageChoice( Calamares::getString( configurationMap, "packageChoice" ) );
if ( m_method != PackageChooserMethod::Legacy )
{
cWarning() << "Single-selection QML module must use 'Legacy' method.";
}
}
bool labels_ok = false;
auto labels = Calamares::getSubMap( configurationMap, "labels", labels_ok );
if ( labels_ok )
{
if ( labels.contains( "step" ) )
{
m_stepName = new Calamares::Locale::TranslatedString( labels, "step" );
}
}
}

View File

@@ -0,0 +1,128 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 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 PACKAGECHOOSER_CONFIG_H
#define PACKAGECHOOSER_CONFIG_H
#include "PackageModel.h"
#include "modulesystem/Config.h"
#include "modulesystem/InstanceKey.h"
#include <memory>
#include <optional>
enum class PackageChooserMode
{
Optional, // zero or one
Required, // exactly one
OptionalMultiple, // zero or more
RequiredMultiple // one or more
};
const NamedEnumTable< PackageChooserMode >& packageChooserModeNames();
enum class PackageChooserMethod
{
Legacy, // use contextualprocess or other custom
Packages, // use the packages module
NetAdd, // adds packages to the netinstall module
NetSelect, // makes selections in the netinstall module
};
const NamedEnumTable< PackageChooserMethod >& PackageChooserMethodNames();
class Config : public Calamares::ModuleSystem::Config
{
Q_OBJECT
/** @brief This is the single-select package-choice
*
* For (QML) modules that support only a single selection and
* just want to do things in a straightforward pick-this-one
* way, the packageChoice property is a (the) way to go.
*
* Writing to this property means that any other form of package-
* choice or selection is ignored.
*/
Q_PROPERTY( QString packageChoice READ packageChoice WRITE setPackageChoice NOTIFY packageChoiceChanged )
Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL )
public:
Config( QObject* parent = nullptr );
~Config() override;
/** @brief Sets the default Id for this Config
*
* The default Id is the (owning) module identifier for the config,
* and it is used when the Id read from the config file is empty.
* The **usual** configuration when using method *packages* is
* to rely on the default Id.
*/
void setDefaultId( const Calamares::ModuleSystem::InstanceKey& defaultId ) { m_defaultId = defaultId; }
void setConfigurationMap( const QVariantMap& ) override;
PackageChooserMode mode() const { return m_mode; }
PackageListModel* model() const { return m_model; }
QModelIndex defaultSelectionIndex() const { return m_defaultModelIndex; }
/** @brief Returns an "introductory package" which describes packagechooser
*
* If the model contains a "none" package, returns that one on
* the assumption that it is one to describe the whole; otherwise
* returns a totally generic description.
*/
const PackageItem& introductionPackage() const;
/** @brief Write selection to global storage
*
* Updates the GS keys for this packagechooser, marking all
* (and only) the packages in @p selected as selected.
*/
void updateGlobalStorage( const QStringList& selected ) const;
/** @brief Write selection to global storage
*
* Updates the GS keys for this packagechooser, marking **only**
* the package choice as selected. This assumes that the single-
* selection QML code is in use.
*/
void updateGlobalStorage() const;
QString packageChoice() const { return m_packageChoice.value_or( QString() ); }
void setPackageChoice( const QString& packageChoice );
QString prettyName() const;
QString prettyStatus() const;
signals:
void packageChoiceChanged( QString packageChoice );
void prettyStatusChanged();
private:
PackageListModel* m_model = nullptr;
QModelIndex m_defaultModelIndex;
/// Selection mode for this module
PackageChooserMode m_mode = PackageChooserMode::Optional;
/// How this module stores to GS
PackageChooserMethod m_method = PackageChooserMethod::Legacy;
/// Value to use for id if none is set in the config file
Calamares::ModuleSystem::InstanceKey m_defaultId;
/** @brief QML selection (for single-selection approaches)
*
* If there is no value, then there has been no selection.
* Reading the property will return an empty QString.
*/
std::optional< QString > m_packageChoice;
Calamares::Locale::TranslatedString* m_stepName; // As it appears in the sidebar
};
#endif

View File

@@ -0,0 +1,225 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
/** @brief Loading items from AppData XML files.
*
* Only used if QtXML is found, implements PackageItem::fromAppData().
*/
#include "PackageModel.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
#include <QDomDocument>
#include <QDomNodeList>
#include <QFile>
/** @brief try to load the given @p fileName XML document
*
* Returns a QDomDocument, which will be valid iff the file can
* be read and contains valid XML data.
*/
static inline QDomDocument
loadAppData( const QString& fileName )
{
QFile file( fileName );
if ( !file.open( QIODevice::ReadOnly ) )
{
return QDomDocument();
}
QDomDocument doc( "AppData" );
if ( !doc.setContent( &file ) )
{
file.close();
return QDomDocument();
}
file.close();
return doc;
}
/** @brief gets the text of child element @p tagName
*/
static inline QString
getChildText( const QDomNode& n, const QString& tagName )
{
QDomElement e = n.firstChildElement( tagName );
return e.isNull() ? QString() : e.text();
}
/** @brief Gets a suitable screenshot path
*
* The <screenshots> element contains zero or more <screenshot>
* elements, which can have a *type* associated with them.
* Scan the screenshot elements, return the <image> path
* for the one labeled with type=default or, if there is no
* default, the first element.
*/
static inline QString
getScreenshotPath( const QDomNode& n )
{
QDomElement shotsNode = n.firstChildElement( "screenshots" );
if ( shotsNode.isNull() )
{
return QString();
}
const QDomNodeList shotList = shotsNode.childNodes();
int firstScreenshot = -1; // Use which screenshot node?
for ( int i = 0; i < shotList.count(); ++i )
{
if ( !shotList.at( i ).isElement() )
{
continue;
}
QDomElement e = shotList.at( i ).toElement();
if ( e.tagName() != "screenshot" )
{
continue;
}
// If none has the "type=default" attribute, use the first one
if ( firstScreenshot < 0 )
{
firstScreenshot = i;
}
// But type=default takes precedence.
if ( e.hasAttribute( "type" ) && e.attribute( "type" ) == "default" )
{
firstScreenshot = i;
break;
}
}
if ( firstScreenshot >= 0 )
{
return shotList.at( firstScreenshot ).firstChildElement( "image" ).text();
}
return QString();
}
/** @brief Returns language of the given element @p e
*
* Transforms the attribute value for xml:lang to something
* suitable for TranslatedString (e.g. [lang]).
*/
static inline QString
getLanguage( const QDomElement& e )
{
QString language = e.attribute( "xml:lang" );
if ( !language.isEmpty() )
{
language.replace( '-', '_' );
language.prepend( '[' );
language.append( ']' );
}
return language;
}
/** @brief Scan the list of @p children for @p tagname elements and add them to the map
*
* Uses @p mapname instead of @p tagname for the entries in map @p m
* to allow renaming from XML to map keys (in particular for
* TranslatedString). Also transforms xml:lang attributes to suitable
* key-decorations on @p mapname.
*/
static inline void
fillMap( QVariantMap& m, const QDomNodeList& children, const QString& tagname, const QString& mapname )
{
for ( int i = 0; i < children.count(); ++i )
{
if ( !children.at( i ).isElement() )
{
continue;
}
QDomElement e = children.at( i ).toElement();
if ( e.tagName() != tagname )
{
continue;
}
m[ mapname + getLanguage( e ) ] = e.text();
}
}
/** @brief gets the <name> and <description> elements
*
* Builds up a map of the <name> elements (which may have a *lang*
* attribute to indicate translations and paragraphs of the
* <description> element (also with lang). Uses the <summary>
* elements to supplement the description if no description
* is available for a given language.
*
* Returns a map with keys suitable for use by TranslatedString.
*/
static inline QVariantMap
getNameAndSummary( const QDomNode& n )
{
QVariantMap m;
const QDomNodeList children = n.childNodes();
fillMap( m, children, "name", "name" );
fillMap( m, children, "summary", "description" );
const QDomElement description = n.firstChildElement( "description" );
if ( !description.isNull() )
{
fillMap( m, description.childNodes(), "p", "description" );
}
return m;
}
PackageItem
fromAppData( const QVariantMap& item_map )
{
QString fileName = Calamares::getString( item_map, "appdata" );
if ( fileName.isEmpty() )
{
cWarning() << "Can't load AppData without a suitable key.";
return PackageItem();
}
cDebug() << "Loading AppData XML from" << fileName;
QDomDocument doc = loadAppData( fileName );
if ( doc.isNull() )
{
return PackageItem();
}
QDomElement componentNode = doc.documentElement();
if ( !componentNode.isNull() && componentNode.tagName() == "component" )
{
// An "id" entry in the Calamares config overrides ID in the AppData
QString id = Calamares::getString( item_map, "id" );
if ( id.isEmpty() )
{
id = getChildText( componentNode, "id" );
}
if ( id.isEmpty() )
{
return PackageItem();
}
// A "screenshot" entry in the Calamares config overrides AppData
QString screenshotPath = Calamares::getString( item_map, "screenshot" );
if ( screenshotPath.isEmpty() )
{
screenshotPath = getScreenshotPath( componentNode );
}
QVariantMap map = getNameAndSummary( componentNode );
map.insert( "id", id );
map.insert( "screenshot", screenshotPath );
return PackageItem( map );
}
return PackageItem();
}

View File

@@ -0,0 +1,28 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef ITEMAPPDATA_H
#define ITEMAPPDATA_H
#include "PackageModel.h"
/** @brief Loads an AppData XML file and returns a PackageItem
*
* The @p map must have a key *appdata*. That is used as the
* primary source of information, but keys *id* and *screenshotPath*
* may be used to override parts of the AppData -- so that the
* ID is under the control of Calamares, and the screenshot can be
* forced to a local path available on the installation medium.
*
* Requires XML support in libcalamares, if not present will
* return invalid PackageItems.
*/
PackageItem fromAppData( const QVariantMap& map );
#endif

View File

@@ -0,0 +1,169 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
/** @brief Loading items from AppData XML files.
*
* Only used if QtXML is found, implements PackageItem::fromAppData().
*/
#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 )
{
return static_cast<quint64>(size.width()) * static_cast<quint64>(size.height());
}
/// @brief Sets a screenshot in @p map from @p screenshot, if a usable one is found
static void
setScreenshot( QVariantMap& map, const AppStream::Screenshot& screenshot )
{
if ( screenshot.images().count() < 1 )
{
return;
}
// Pick the smallest
QUrl url;
quint64 size = sizeOrder( screenshot.images().first().size() );
for ( const auto& img : screenshot.images() )
{
if ( sizeOrder( img.size() ) <= size )
{
url = img.url();
}
}
if ( url.isValid() )
{
map.insert( "screenshot", url.toString() );
}
}
/// @brief Interpret an AppStream Component
static PackageItem
fromComponent( AppStream::Pool& pool, AppStream::Component& component )
{
#if HAVE_APPSTREAM_VERSION == 0
auto setActiveLocale = [&component](const QString & locale)
{
component.setActiveLocale( locale );
};
#else
auto setActiveLocale = [&pool](const QString & locale)
{
pool.setLocale( locale );
};
#endif
QVariantMap map;
map.insert( "id", component.id() );
map.insert( "package", component.packageNames().join( "," ) );
// Assume that the pool has loaded "ALL" locales, but it might be set
// to any of them; get the en_US locale as "untranslated" and then
// loop over Calamares locales (since there is no way to query for
// available locales in the Component) to see if there's anything else.
setActiveLocale( QStringLiteral( "en_US" ) );
QString en_name = component.name();
QString en_description = component.description();
map.insert( "name", en_name );
map.insert( "description", en_description );
for ( const QString& locale : Calamares::Locale::availableTranslations()->localeIds() )
{
setActiveLocale( locale );
QString name = component.name();
if ( name != en_name )
{
map.insert( QStringLiteral( "name[%1]" ).arg( locale ), name );
}
QString description = component.description();
if ( description != en_description )
{
map.insert( QStringLiteral( "description[%1]" ).arg( locale ), description );
}
}
#if HAVE_APPSTREAM_VERSION == 0
auto screenshots = component.screenshots();
#else
auto screenshots = component.screenshotsAll();
#endif
if ( screenshots.count() > 0 )
{
bool done = false;
for ( const auto& s : screenshots )
{
if ( s.isDefault() )
{
setScreenshot( map, s );
done = true;
break;
}
}
if ( !done )
{
setScreenshot( map, screenshots.first() );
}
}
return PackageItem( map );
}
PackageItem
fromAppStream( AppStream::Pool& pool, const QVariantMap& item_map )
{
QString appstreamId = Calamares::getString( item_map, "appstream" );
if ( appstreamId.isEmpty() )
{
cWarning() << "Can't load AppStream without a suitable appstreamId.";
return PackageItem();
}
cDebug() << "Loading AppStream data for" << appstreamId;
#if HAVE_APPSTREAM_VERSION == 0
auto itemList = pool.componentsById( appstreamId );
#else
auto itemList = pool.componentsById( appstreamId ).toList();
#endif
if ( itemList.count() < 1 )
{
cWarning() << "No AppStream data for" << appstreamId;
return PackageItem();
}
if ( itemList.count() > 1 )
{
cDebug() << "Multiple AppStream data for" << appstreamId << "using first.";
}
auto r = fromComponent( pool, itemList.first() );
if ( r.isValid() )
{
QString id = Calamares::getString( item_map, "id" );
QString screenshotPath = Calamares::getString( item_map, "screenshot" );
if ( !id.isEmpty() )
{
r.id = id;
}
if ( !screenshotPath.isEmpty() )
{
r.screenshot = screenshotPath;
}
}
return r;
}

View File

@@ -0,0 +1,34 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef ITEMAPPSTREAM_H
#define ITEMAPPSTREAM_H
#include "PackageModel.h"
namespace AppStream
{
class Pool;
} // namespace AppStream
/** @brief Loads an item from AppStream data.
*
* The @p map must have a key *appstream*. That is used as the
* primary source of information from the AppStream cache, but
* keys *id* and *screenshotPath* may be used to override parts
* of the AppStream data -- so that the ID is under the control
* of Calamares, and the screenshot can be forced to a local path
* available on the installation medium.
*
* Requires AppStreamQt, if not present will return invalid
* PackageItems.
*/
PackageItem fromAppStream( AppStream::Pool& pool, const QVariantMap& map );
#endif

View File

@@ -0,0 +1,145 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "PackageChooserPage.h"
#include "ui_page_package.h"
#include "utils/Gui.h"
#include "utils/Logger.h"
#include "utils/Retranslator.h"
#include <QLabel>
PackageChooserPage::PackageChooserPage( PackageChooserMode mode, QWidget* parent )
: QWidget( parent )
, ui( new Ui::PackageChooserPage )
, m_introduction( QString(),
QString(),
tr( "Package Selection" ),
tr( "Please pick a product from the list. The selected product will be installed." ) )
{
m_introduction.screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) );
ui->setupUi( this );
CALAMARES_RETRANSLATE( updateLabels(); );
switch ( mode )
{
case PackageChooserMode::Optional:
[[fallthrough]];
case PackageChooserMode::Required:
ui->products->setSelectionMode( QAbstractItemView::SingleSelection );
break;
case PackageChooserMode::OptionalMultiple:
[[fallthrough]];
case PackageChooserMode::RequiredMultiple:
ui->products->setSelectionMode( QAbstractItemView::ExtendedSelection );
}
ui->products->setMinimumWidth( 10 * Calamares::defaultFontHeight() );
}
void
PackageChooserPage::currentChanged( const QModelIndex& index )
{
if ( !index.isValid() || !ui->products->selectionModel()->hasSelection() )
{
ui->productName->setText( m_introduction.name.get() );
ui->productScreenshot->setPixmap( m_introduction.screenshot );
ui->productDescription->setText( m_introduction.description.get() );
}
else
{
const auto* model = ui->products->model();
ui->productName->setText( model->data( index, PackageListModel::NameRole ).toString() );
ui->productDescription->setText( model->data( index, PackageListModel::DescriptionRole ).toString() );
QPixmap currentScreenshot = model->data( index, PackageListModel::ScreenshotRole ).value< QPixmap >();
if ( currentScreenshot.isNull() )
{
ui->productScreenshot->setPixmap( m_introduction.screenshot );
}
else
{
ui->productScreenshot->setPixmap( currentScreenshot );
}
}
}
void
PackageChooserPage::updateLabels()
{
if ( ui && ui->products && ui->products->selectionModel() )
{
currentChanged( ui->products->selectionModel()->currentIndex() );
}
else
{
currentChanged( QModelIndex() );
}
emit selectionChanged();
}
void
PackageChooserPage::setModel( QAbstractItemModel* model )
{
ui->products->setModel( model );
currentChanged( QModelIndex() );
connect( ui->products->selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
&PackageChooserPage::updateLabels );
}
void
PackageChooserPage::setSelection( const QModelIndex& index )
{
if ( index.isValid() )
{
ui->products->selectionModel()->select( index, QItemSelectionModel::Select );
}
currentChanged( index );
}
bool
PackageChooserPage::hasSelection() const
{
return ui && ui->products && ui->products->selectionModel() && ui->products->selectionModel()->hasSelection();
}
QStringList
PackageChooserPage::selectedPackageIds() const
{
if ( !( ui && ui->products && ui->products->selectionModel() ) )
{
return QStringList();
}
const auto* model = ui->products->model();
QStringList ids;
for ( const auto& index : ui->products->selectionModel()->selectedIndexes() )
{
QString pid = model->data( index, PackageListModel::IdRole ).toString();
if ( !pid.isEmpty() )
{
ids.append( pid );
}
}
return ids;
}
void
PackageChooserPage::setIntroduction( const PackageItem& item )
{
m_introduction.name = item.name;
m_introduction.description = item.description;
m_introduction.screenshot = item.screenshot;
}

View File

@@ -0,0 +1,57 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef PACKAGECHOOSERPAGE_H
#define PACKAGECHOOSERPAGE_H
#include "Config.h"
#include "PackageModel.h"
#include <QAbstractItemModel>
#include <QWidget>
namespace Ui
{
class PackageChooserPage;
} // namespace Ui
class PackageChooserPage : public QWidget
{
Q_OBJECT
public:
explicit PackageChooserPage( PackageChooserMode mode, QWidget* parent = nullptr );
/// @brief Sets the data model for the listview
void setModel( QAbstractItemModel* model );
/// @brief Sets the introductory (no-package-selected) texts
void setIntroduction( const PackageItem& item );
/// @brief Selects a listview item
void setSelection( const QModelIndex& index );
/// @brief Is anything selected?
bool hasSelection() const;
/** @brief Get the list of selected ids
*
* This list may be empty (if none is selected).
*/
QStringList selectedPackageIds() const;
public slots:
void currentChanged( const QModelIndex& index );
void updateLabels();
signals:
void selectionChanged();
private:
Ui::PackageChooserPage* ui;
PackageItem m_introduction;
};
#endif // PACKAGECHOOSERPAGE_H

View File

@@ -0,0 +1,158 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "PackageChooserViewStep.h"
#include "Config.h"
#include "PackageChooserPage.h"
#include "PackageModel.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "locale/TranslatableConfiguration.h"
#include "utils/Logger.h"
#include "utils/System.h"
#include "utils/Variant.h"
#include <QDesktopServices>
#include <QVariantMap>
CALAMARES_PLUGIN_FACTORY_DEFINITION( PackageChooserViewStepFactory, registerPlugin< PackageChooserViewStep >(); )
PackageChooserViewStep::PackageChooserViewStep( QObject* parent )
: Calamares::ViewStep( parent )
, m_config( new Config( this ) )
, m_widget( nullptr )
{
emit nextStatusChanged( false );
}
PackageChooserViewStep::~PackageChooserViewStep()
{
if ( m_widget && m_widget->parent() == nullptr )
{
m_widget->deleteLater();
}
}
QString
PackageChooserViewStep::prettyName() const
{
return m_config->prettyName();
}
QWidget*
PackageChooserViewStep::widget()
{
if ( !m_widget )
{
m_widget = new PackageChooserPage( m_config->mode(), nullptr );
connect( m_widget,
&PackageChooserPage::selectionChanged,
[ = ]() { emit nextStatusChanged( this->isNextEnabled() ); } );
hookupModel();
}
return m_widget;
}
bool
PackageChooserViewStep::isNextEnabled() const
{
if ( !m_widget )
{
// No way to have changed anything
return true;
}
switch ( m_config->mode() )
{
case PackageChooserMode::Optional:
case PackageChooserMode::OptionalMultiple:
// zero or one OR zero or more
return true;
case PackageChooserMode::Required:
case PackageChooserMode::RequiredMultiple:
// exactly one OR one or more
return m_widget->hasSelection();
}
__builtin_unreachable();
}
bool
PackageChooserViewStep::isBackEnabled() const
{
return true;
}
bool
PackageChooserViewStep::isAtBeginning() const
{
return true;
}
bool
PackageChooserViewStep::isAtEnd() const
{
return true;
}
void
PackageChooserViewStep::onActivate()
{
if ( !m_widget->hasSelection() )
{
m_widget->setSelection( m_config->defaultSelectionIndex() );
}
}
void
PackageChooserViewStep::onLeave()
{
m_config->updateGlobalStorage( m_widget->selectedPackageIds() );
}
Calamares::JobList
PackageChooserViewStep::jobs() const
{
Calamares::JobList l;
return l;
}
void
PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
m_config->setDefaultId( moduleInstanceKey() );
m_config->setConfigurationMap( configurationMap );
if ( m_widget )
{
hookupModel();
}
}
void
PackageChooserViewStep::hookupModel()
{
if ( !m_config->model() || !m_widget )
{
cError() << "Can't hook up model until widget and model both exist.";
return;
}
m_widget->setModel( m_config->model() );
m_widget->setIntroduction( m_config->introductionPackage() );
}

View File

@@ -0,0 +1,57 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef PACKAGECHOOSERVIEWSTEP_H
#define PACKAGECHOOSERVIEWSTEP_H
#include "DllMacro.h"
#include "locale/TranslatableConfiguration.h"
#include "utils/PluginFactory.h"
#include "viewpages/ViewStep.h"
#include <QVariantMap>
class Config;
class PackageChooserPage;
class PLUGINDLLEXPORT PackageChooserViewStep : public Calamares::ViewStep
{
Q_OBJECT
public:
explicit PackageChooserViewStep( QObject* parent = nullptr );
~PackageChooserViewStep() override;
QString prettyName() const override;
QWidget* widget() 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;
private:
void hookupModel();
Config* m_config;
PackageChooserPage* m_widget;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory )
#endif // PACKAGECHOOSERVIEWSTEP_H

Some files were not shown because too many files have changed in this diff Show More