:Merge branch 'calamares' of https://github.com/calamares/calamares into development
This commit is contained in:
commit
5aee4d195d
27
CHANGES
27
CHANGES
@ -6,14 +6,33 @@ website will have to do for older versions.
|
||||
# 3.2.28 (unreleased) #
|
||||
|
||||
This release contains contributions from (alphabetically by first name):
|
||||
- No external contributors yet
|
||||
- Anke Boersma
|
||||
- apt-ghetto
|
||||
|
||||
## Core ##
|
||||
- No core changes yet
|
||||
- A new object *Network* is available to QML modules in `io.calamares.core`.
|
||||
It exposes network status through the *hasInternet* property.
|
||||
- Welcome to Tajik translations. This has reached sufficient completion
|
||||
to be included in the drop-down list of languages on the welcome page.
|
||||
- Welcome to [Interlingue](https://en.wikipedia.org/wiki/Interlingue).
|
||||
The translation is at an early stage.
|
||||
|
||||
## Modules ##
|
||||
- No module changes yet
|
||||
|
||||
- The *locale* module has been completely redone on the inside.
|
||||
Users should see no changes. #1391
|
||||
- The *localeq* module uses the redone internals of the locale module.
|
||||
It can now be used to set timezone, language and locale information
|
||||
and is a suitable alternative module. Thanks to Anke Boersma who did
|
||||
the work of figuring out maps. Note that the map uses several GeoIP
|
||||
and GeoData providers and you may need to configure the URLs
|
||||
with suitable usernames for those services. #1426
|
||||
- Both *locale* and *localeq* can now be configured to use the system's
|
||||
timezone setting -- this can be useful to avoid both hard-coding an
|
||||
initial zone and doing extra GeoIP lookups, in the case where the
|
||||
live system already does so. #1391
|
||||
- The *users* module no longer accepts `root` as a username. #1462
|
||||
- The *keyboardq* module is now more inline with the look of the rest
|
||||
of the Calamares modules, use of a background image is removed.
|
||||
|
||||
# 3.2.27 (2020-07-11) #
|
||||
|
||||
|
@ -147,14 +147,15 @@ set( CALAMARES_DESCRIPTION_SUMMARY
|
||||
# copy these four lines to four backup lines, add "p", and then update
|
||||
# the original four lines with the current translations).
|
||||
#
|
||||
# Total 66 languages
|
||||
set( _tx_complete az az_AZ ca he hi hr ja sq tr_TR uk zh_TW )
|
||||
set( _tx_good as ast be cs_CZ da de es fi_FI fr hu it_IT ko lt ml
|
||||
nl pt_BR pt_PT ru sk sv zh_CN )
|
||||
# Total 68 languages
|
||||
set( _tx_complete az az_AZ ca da fi_FI he hi hr ja pt_BR sq tr_TR
|
||||
uk zh_TW )
|
||||
set( _tx_good as ast be cs_CZ de es fr hu it_IT ko lt ml nl pt_PT
|
||||
ru sk sv zh_CN )
|
||||
set( _tx_ok ar bg el en_GB es_MX es_PR et eu fa gl id is mr nb pl
|
||||
ro sl sr sr@latin th )
|
||||
set( _tx_incomplete bn ca@valencia eo fr_CH gu kk kn lo lv mk ne_NP
|
||||
ur uz )
|
||||
ro sl sr sr@latin tg th )
|
||||
set( _tx_incomplete bn ca@valencia eo fr_CH gu ie kk kn lo lv mk
|
||||
ne_NP ur uz )
|
||||
|
||||
### Required versions
|
||||
#
|
||||
@ -355,23 +356,33 @@ set_package_properties(
|
||||
URL "https://python.org"
|
||||
PURPOSE "Python 3 interpreter for certain tests."
|
||||
)
|
||||
|
||||
set( _schema_explanation "" )
|
||||
if ( PYTHONINTERP_FOUND )
|
||||
message(STATUS "Found Python 3 interpreter ${PYTHON_EXECUTABLE}")
|
||||
if ( BUILD_SCHEMA_TESTING )
|
||||
# The configuration validator script has some dependencies,
|
||||
# and if they are not installed, don't run. If errors out
|
||||
# with exit(1) on missing dependencies.
|
||||
if ( CALAMARES_CONFIGVALIDATOR_CHECKED )
|
||||
set( _validator_deps ${CALAMARES_CONFIGVALIDATOR_RESULT} )
|
||||
else()
|
||||
exec_program( ${PYTHON_EXECUTABLE} ARGS "${CMAKE_SOURCE_DIR}/ci/configvalidator.py" -x RETURN_VALUE _validator_deps )
|
||||
set( CALAMARES_CONFIGVALIDATOR_CHECKED TRUE CACHE INTERNAL "Dependencies for configvalidator checked" )
|
||||
set( CALAMARES_CONFIGVALIDATOR_RESULT ${_validator_deps} CACHE INTERNAL "Result of configvalidator dependency check" )
|
||||
endif()
|
||||
# It should never succeed, but only returns 1 when the imports fail
|
||||
if ( _validator_deps EQUAL 1 )
|
||||
message(STATUS "BUILD_SCHEMA_TESTING dependencies are missing." )
|
||||
set( _schema_explanation " Missing dependencies for configvalidator.py." )
|
||||
set( BUILD_SCHEMA_TESTING OFF )
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# Can't run schema tests without Python3.
|
||||
set( _schema_explanation " Missing Python3." )
|
||||
set( BUILD_SCHEMA_TESTING OFF )
|
||||
endif()
|
||||
add_feature_info( yaml-schema BUILD_SCHEMA_TESTING "Validate YAML (config files) with schema.${_schema_explanation}" )
|
||||
|
||||
find_package( PythonLibs ${PYTHONLIBS_VERSION} )
|
||||
set_package_properties(
|
||||
PythonLibs PROPERTIES
|
||||
@ -536,7 +547,11 @@ if( CALAMARES_VERSION_RC )
|
||||
set( CALAMARES_VERSION ${CALAMARES_VERSION}rc${CALAMARES_VERSION_RC} )
|
||||
endif()
|
||||
|
||||
# additional info for non-release builds
|
||||
# Additional info for non-release builds. The "extended" version information
|
||||
# with date and git information (commit, dirty status) is used only
|
||||
# by CalamaresVersionX.h, which is included by consumers that need a full
|
||||
# version number with all that information; normal consumers can include
|
||||
# CalamaresVersion.h with more stable numbers.
|
||||
if( NOT BUILD_RELEASE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git/" )
|
||||
include( CMakeDateStamp )
|
||||
set( CALAMARES_VERSION_DATE "${CMAKE_DATESTAMP_YEAR}${CMAKE_DATESTAMP_MONTH}${CMAKE_DATESTAMP_DAY}" )
|
||||
|
@ -62,10 +62,8 @@ function(calamares_add_library)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# add resources from current dir
|
||||
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_RESOURCES}")
|
||||
qt5_add_resources(LIBRARY_RC_SOURCES "${LIBRARY_RESOURCES}")
|
||||
list(APPEND LIBRARY_SOURCES ${LIBRARY_RC_SOURCES})
|
||||
unset(LIBRARY_RC_SOURCES)
|
||||
if(LIBRARY_RESOURCES)
|
||||
list(APPEND LIBRARY_SOURCES ${LIBRARY_RESOURCES})
|
||||
endif()
|
||||
|
||||
# add target
|
||||
@ -81,6 +79,9 @@ function(calamares_add_library)
|
||||
if(LIBRARY_UI)
|
||||
calamares_autouic(${target} ${LIBRARY_UI})
|
||||
endif()
|
||||
if(LIBRARY_RESOURCES)
|
||||
calamares_autorcc(${target} ${LIBRARY_RESOURCES})
|
||||
endif()
|
||||
|
||||
if(LIBRARY_EXPORT_MACRO)
|
||||
set_target_properties(${target} PROPERTIES COMPILE_DEFINITIONS ${LIBRARY_EXPORT_MACRO})
|
||||
|
@ -22,50 +22,15 @@
|
||||
|
||||
include( CMakeParseArguments )
|
||||
|
||||
if( NOT _rcc_version_support_checked )
|
||||
set( _rcc_version_support_checked TRUE )
|
||||
|
||||
# Extract the executable name
|
||||
get_property( _rcc_executable
|
||||
TARGET ${Qt5Core_RCC_EXECUTABLE}
|
||||
PROPERTY IMPORTED_LOCATION
|
||||
)
|
||||
if( NOT _rcc_executable )
|
||||
# Weird, probably now uses Qt5::rcc which is wrong too
|
||||
set( _rcc_executable ${Qt5Core_RCC_EXECUTABLE} )
|
||||
endif()
|
||||
|
||||
# Try an empty RCC file with explicit format-version
|
||||
execute_process(
|
||||
COMMAND echo "<RCC version='1.0'></RCC>"
|
||||
COMMAND ${Qt5Core_RCC_EXECUTABLE} --format-version 1 --list -
|
||||
RESULT_VARIABLE _rcc_version_rv
|
||||
ERROR_VARIABLE _rcc_version_dump
|
||||
)
|
||||
if ( _rcc_version_rv EQUAL 0 )
|
||||
# Supported: force to the reproducible version
|
||||
set( _rcc_version_support --format-version 1 )
|
||||
else()
|
||||
# Older Qt versions (5.7, 5.8) don't support setting the
|
||||
# rcc format-version, so won't be reproducible if they
|
||||
# default to version 2.
|
||||
set( _rcc_version_support "" )
|
||||
endif()
|
||||
unset( _rcc_version_rv )
|
||||
unset( _rcc_version_dump )
|
||||
endif()
|
||||
|
||||
|
||||
# Internal macro for adding the C++ / Qt translations to the
|
||||
# build and install tree. Should be called only once, from
|
||||
# src/calamares/CMakeLists.txt.
|
||||
macro(add_calamares_translations language)
|
||||
list( APPEND CALAMARES_LANGUAGES ${ARGV} )
|
||||
|
||||
set( calamares_i18n_qrc_content "<!DOCTYPE RCC><RCC version=\"1.0\">\n" )
|
||||
set( calamares_i18n_qrc_content "" )
|
||||
|
||||
# calamares and qt language files
|
||||
set( calamares_i18n_qrc_content "${calamares_i18n_qrc_content}<qresource prefix=\"/lang\">\n" )
|
||||
foreach( lang ${CALAMARES_LANGUAGES} )
|
||||
foreach( tlsource "calamares_${lang}" "tz_${lang}" )
|
||||
if( EXISTS "${CMAKE_SOURCE_DIR}/lang/${tlsource}.ts" )
|
||||
@ -75,31 +40,19 @@ macro(add_calamares_translations language)
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
set( calamares_i18n_qrc_content "${calamares_i18n_qrc_content}</qresource>\n" )
|
||||
set( calamares_i18n_qrc_content "${calamares_i18n_qrc_content}</RCC>\n" )
|
||||
|
||||
file( WRITE ${CMAKE_BINARY_DIR}/lang/calamares_i18n.qrc "${calamares_i18n_qrc_content}" )
|
||||
|
||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||
|
||||
## HACK HACK HACK - around rcc limitations to allow out of source-tree building
|
||||
set( trans_file calamares_i18n )
|
||||
set( trans_srcfile ${CMAKE_BINARY_DIR}/lang/${trans_file}.qrc )
|
||||
set( trans_infile ${CMAKE_CURRENT_BINARY_DIR}/${trans_file}.qrc )
|
||||
set( trans_outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${trans_file}.cxx )
|
||||
|
||||
# Copy the QRC file to the output directory
|
||||
add_custom_command(
|
||||
OUTPUT ${trans_infile}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${trans_srcfile} ${trans_infile}
|
||||
MAIN_DEPENDENCY ${trans_srcfile}
|
||||
)
|
||||
configure_file( ${CMAKE_SOURCE_DIR}/lang/calamares_i18n.qrc.in ${trans_infile} @ONLY )
|
||||
|
||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||
|
||||
# Run the resource compiler (rcc_options should already be set)
|
||||
add_custom_command(
|
||||
OUTPUT ${trans_outfile}
|
||||
COMMAND "${Qt5Core_RCC_EXECUTABLE}"
|
||||
ARGS ${rcc_options} ${_rcc_version_support} -name ${trans_file} -o ${trans_outfile} ${trans_infile}
|
||||
ARGS ${rcc_options} --format-version 1 -name ${trans_file} -o ${trans_outfile} ${trans_infile}
|
||||
MAIN_DEPENDENCY ${trans_infile}
|
||||
DEPENDS ${QM_FILES}
|
||||
)
|
||||
|
@ -18,8 +18,10 @@
|
||||
#
|
||||
###
|
||||
#
|
||||
# Helper function for doing automoc on a target, and autoui on a .ui file.
|
||||
# Helper function for doing automoc, autouic, autorcc on targets,
|
||||
# and on the corresponding .ui or .rcc files.
|
||||
#
|
||||
# calamares_automoc(target)
|
||||
# Sets AUTOMOC TRUE for a target.
|
||||
#
|
||||
# If the global variable CALAMARES_AUTOMOC_OPTIONS is set, uses that
|
||||
@ -27,8 +29,17 @@
|
||||
# libcalamares/utils/moc-warnings.h file to the moc, which in turn
|
||||
# reduces compiler warnings in generated MOC code.
|
||||
#
|
||||
# calamares_autouic(target [uifile ..])
|
||||
# Sets AUTOUIC TRUE for a target.
|
||||
#
|
||||
# If the global variable CALAMARES_AUTOUIC_OPTIONS is set, adds that
|
||||
# to the options passed to uic.
|
||||
# to the options passed to uic for each of the named uifiles.
|
||||
#
|
||||
# calamares_autorcc(target [rcfile ..])
|
||||
# Sets AUTOUIC TRUE for a target.
|
||||
#
|
||||
# If the global variable CALAMARES_AUTORCC_OPTIONS is set, adds that
|
||||
# to the options passed to rcc for each of the named rcfiles.
|
||||
|
||||
function(calamares_automoc TARGET)
|
||||
set_target_properties( ${TARGET} PROPERTIES AUTOMOC TRUE )
|
||||
@ -45,3 +56,12 @@ function(calamares_autouic TARGET)
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(calamares_autorcc TARGET)
|
||||
set_target_properties( ${TARGET} PROPERTIES AUTORCC TRUE )
|
||||
if ( CALAMARES_AUTORCC_OPTIONS )
|
||||
foreach(S ${ARGN})
|
||||
set_property(SOURCE ${S} PROPERTY AUTORCC_OPTIONS "${CALAMARES_AUTORCC_OPTIONS}")
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
@ -94,6 +94,10 @@ Name[hr]=Instaliraj sustav
|
||||
Icon[hr]=calamares
|
||||
GenericName[hr]=Instalacija sustava
|
||||
Comment[hr]=Calamares — Instalacija sustava
|
||||
Name[ie]=Installar li sistema
|
||||
Icon[ie]=calamares
|
||||
GenericName[ie]=Installator del sistema
|
||||
Comment[ie]=Calamares — Installator del sistema
|
||||
Name[hu]=Rendszer telepítése
|
||||
Icon[hu]=calamares
|
||||
GenericName[hu]=Rendszertelepítő
|
||||
@ -184,6 +188,10 @@ Name[sv]=Installera system
|
||||
Icon[sv]=calamares
|
||||
GenericName[sv]=Systeminstallerare
|
||||
Comment[sv]=Calamares — Systeminstallerare
|
||||
Name[tg]=Насбкунии низом
|
||||
Icon[tg]=calamares
|
||||
GenericName[tg]=Насбкунандаи низом
|
||||
Comment[tg]=Calamares — Насбкунандаи низом
|
||||
Name[th]=ติดตั้งระบบ
|
||||
Name[uk]=Встановити Систему
|
||||
Icon[uk]=calamares
|
||||
|
112
ci/RELEASE.md
112
ci/RELEASE.md
@ -1,21 +1,13 @@
|
||||
# Calamares Release Process
|
||||
|
||||
> Calamares releases are now rolling when-they-are-ready releases.
|
||||
> Releases are made from *master* and tagged there. When, in future,
|
||||
> Releases are made from *calamares* and tagged there. When, in future,
|
||||
> LTS releases resume, these steps may be edited again.
|
||||
>
|
||||
> Most things are automated through the release script [RELEASE.sh](RELEASE.sh)
|
||||
|
||||
## (0) A week in advance
|
||||
## (1) Preparation
|
||||
|
||||
* Run [Coverity scan][coverity], fix what's relevant. The Coverity scan runs
|
||||
automatically once a week on master. The badge is displayed on the
|
||||
project front page and in the wiki.
|
||||
* Build with clang -Weverything, fix what's relevant.
|
||||
```
|
||||
rm -rf build ; mkdir build ; cd build
|
||||
CC=clang CXX=clang++ cmake .. && make
|
||||
```
|
||||
* Make sure all tests pass.
|
||||
```
|
||||
make
|
||||
@ -25,16 +17,6 @@
|
||||
an additional environment variable to be set for some tests, which will
|
||||
destroy an attached disk. This is not always desirable. There are some
|
||||
sample config-files that are empty and which fail the config-tests.
|
||||
* Notify [translators][transifex]. In the dashboard there is an *Announcements*
|
||||
link that you can use to send a translation announcement. Note that regular
|
||||
use of `txpush.sh` will notify translators as well of any changes.
|
||||
|
||||
[coverity]: https://scan.coverity.com/projects/calamares-calamares?tab=overview
|
||||
[transifex]: https://www.transifex.com/calamares/calamares/dashboard/
|
||||
|
||||
|
||||
## (1) Preparation
|
||||
|
||||
* Pull latest translations from Transifex. We only push / pull translations
|
||||
from master, so longer-lived branches (e.g. 3.1.x) don't get translation
|
||||
updates. This is to keep the translation workflow simple. The script
|
||||
@ -47,13 +29,8 @@
|
||||
fairly complete translations, or use `ci/txstats.py` for an automated
|
||||
suggestion. If there are changes, commit them.
|
||||
* Push the changes.
|
||||
* Drop the RC variable to 0 in `CMakeLists.txt`, *CALAMARES_VERSION_RC*.
|
||||
* Check `README.md` and the
|
||||
[Coding Guide](https://github.com/calamares/calamares/wiki/Develop-Code),
|
||||
make sure it's all still
|
||||
relevant. Run `ci/calamaresstyle` to check the C++ code style.
|
||||
Run pycodestyle on recently-modified Python modules, fix what makes sense.
|
||||
* Check defaults in `settings.conf` and other configuration files.
|
||||
* Drop the RC variable to 0 in `CMakeLists.txt`, *CALAMARES_VERSION_RC*.
|
||||
* Edit `CHANGES` and set the date of the release.
|
||||
* Commit both. This is usually done with commit-message
|
||||
*Changes: pre-release housekeeping*.
|
||||
@ -73,44 +50,16 @@
|
||||
On success, it prints out a suitable signature- and SHA256 blurb
|
||||
for use in the release announcement.
|
||||
|
||||
### (2.1) Buld and Test
|
||||
## (3) Release
|
||||
|
||||
* Build with gcc. If available, build again with Clang and double-check
|
||||
any warnings Clang produces.
|
||||
* Run the tests; `make test` in the build directory should have no
|
||||
failures (or if there are, know why they are there).
|
||||
Follow the instructions printed by the release script.
|
||||
|
||||
### (2.2) Tag
|
||||
|
||||
* `git tag -s v1.1.0` Make sure the signing key is known in GitHub, so that the
|
||||
tag is shown as a verified tag. Do not sign -rc tags.
|
||||
You can use `make show-version` in the build directory to get the right
|
||||
version number -- this will fail if you didn't follow step (1).
|
||||
|
||||
### (2.3) Tarball
|
||||
|
||||
* Create tarball: `git-archive-all -v calamares-1.1-rc1.tar.gz` or without
|
||||
the helper script,
|
||||
```
|
||||
V=calamares-3.1.5
|
||||
git archive -o $V.tar.gz --prefix $V/ master
|
||||
```
|
||||
Double check that the tarball matches the version number.
|
||||
* Test tarball (e.g. unpack somewhere else and run the tests from step 0).
|
||||
|
||||
|
||||
## (3) Housekeeping
|
||||
|
||||
* Generate MD5 and SHA256 checksums.
|
||||
* Upload tarball.
|
||||
* Announce on mailing list, notify packagers.
|
||||
* Write release article.
|
||||
* Publish tarball.
|
||||
* Update download page.
|
||||
* Push the tags.
|
||||
* Create a new release on GitHub.
|
||||
* Upload tarball and signature.
|
||||
* Publish release article on `calamares.io`.
|
||||
* Publicize on social networks.
|
||||
* Close associated milestone on GitHub if this is the actual release.
|
||||
* Publish blog post.
|
||||
* Close associated milestone on GitHub if it's entirely done.
|
||||
* Update topic on #calamares IRC channel.
|
||||
|
||||
## (4) Post-Release
|
||||
|
||||
@ -133,3 +82,44 @@ This release contains contributions from (alphabetically by first name):
|
||||
## Modules ##
|
||||
- No module changes yet
|
||||
```
|
||||
|
||||
# Related Material
|
||||
|
||||
> This section isn't directly related to any specific release,
|
||||
> but bears on all releases.
|
||||
|
||||
## GPG Key Maintainence
|
||||
|
||||
Calamares uses GPG Keys for signing the tarballs and some commits
|
||||
(tags, mostly). Calamares uses the **maintainer's** personal GPG
|
||||
key for this. This section details some GPG activities that the
|
||||
maintainer should do with those keys.
|
||||
|
||||
- Signing sub-key. It's convenient to use a signing sub-key specifically
|
||||
for the signing of Calamares. To do so, add a key to the private key.
|
||||
It's recommended to use key expiry, and to update signing keys periodically.
|
||||
- Run `gpg -K` to find the key ID of your personal GPG secret key.
|
||||
- Run `gpg --edit-key <keyid>` to edit that personal GPG key.
|
||||
- In gpg edit-mode, use `addkey`, then pick a key type that is *sign-only*
|
||||
(e.g. type 4, *RSA (sign only)*), then pick a keysize (3072 seems ok
|
||||
as of 2020) and set a key expiry time, (e.g. in 18 months time).
|
||||
- After generation, the secret key information is printed again, now
|
||||
including the new signing subkey:
|
||||
```
|
||||
ssb rsa3072/0xCFDDC96F12B1915C
|
||||
created: 2020-07-11 expires: 2022-01-02 usage: S
|
||||
```
|
||||
- Update the `RELEASE.sh` script with a new signing sub-key ID when a new
|
||||
one is generated. Also announce the change of signing sub-key (e.g. on
|
||||
the Calmares site or as part of a release announcement).
|
||||
- Send the updated key to keyservers with `gpg --send-keys <keyid>`
|
||||
- Optional: sanitize the keyring for use in development machines.
|
||||
Export the current subkeys of the master key and keep **only** those
|
||||
secret keys around. There is documentation
|
||||
[here](https://blog.tinned-software.net/create-gnupg-key-with-sub-keys-to-sign-encrypt-authenticate/)
|
||||
but be careful.
|
||||
- Export the public key material with `gpg --export --armor <keyid>`,
|
||||
possibly also setting an output file.
|
||||
- Upload that public key to the relevant GitHub profile.
|
||||
- Upload that public key to the Calamares site.
|
||||
|
||||
|
@ -781,7 +781,7 @@ Instalační program bude ukončen a všechny změny ztraceny.</translation>
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="245"/>
|
||||
<source><h1>Welcome to the Calamares setup program for %1</h1></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><h1>Vítejte v Calamares instalačním programu pro %1.</h1></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="246"/>
|
||||
@ -796,7 +796,7 @@ Instalační program bude ukončen a všechny změny ztraceny.</translation>
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="252"/>
|
||||
<source><h1>Welcome to the %1 installer</h1></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><h1>Vítejte v instalátoru %1.</h1></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3424,7 +3424,7 @@ Výstup:
|
||||
<message>
|
||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="202"/>
|
||||
<source>KDE user feedback</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zpětná vazba uživatele KDE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="214"/>
|
||||
@ -3445,7 +3445,7 @@ Výstup:
|
||||
<message>
|
||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="243"/>
|
||||
<source>Could not configure KDE user feedback correctly, Calamares error %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nepodařilo se správně nastavit zpětnou vazbu KDE uživatele, chyba Calamares %1.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -792,7 +792,7 @@ Dies wird das Installationsprogramm beenden und alle Änderungen gehen verloren.
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="252"/>
|
||||
<source><h1>Welcome to the %1 installer</h1></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><h1>Willkommen zum %1 Installationsprogramm</h1></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
5
lang/calamares_i18n.qrc.in
Normal file
5
lang/calamares_i18n.qrc.in
Normal file
@ -0,0 +1,5 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="/lang">
|
||||
@calamares_i18n_qrc_content@
|
||||
</qresource>
|
||||
</RCC>
|
3941
lang/calamares_ie.ts
Normal file
3941
lang/calamares_ie.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -776,22 +776,22 @@ Il programma d'installazione sarà terminato e tutte le modifiche andranno perse
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="245"/>
|
||||
<source><h1>Welcome to the Calamares setup program for %1</h1></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Benvenuto nel programma di installazione Calamares di %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="246"/>
|
||||
<source><h1>Welcome to %1 setup</h1></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Benvenuto nell'installazione di %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="251"/>
|
||||
<source><h1>Welcome to the Calamares installer for %1</h1></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Benvenuto nel programma di installazione Calamares di %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="252"/>
|
||||
<source><h1>Welcome to the %1 installer</h1></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Benvenuto nel programma di installazione di %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1780,7 +1780,7 @@ Il programma d'installazione sarà terminato e tutte le modifiche andranno perse
|
||||
<source>Please select your preferred location on the map so the installer can suggest the locale
|
||||
and timezone settings for you. You can fine-tune the suggested settings below. Search the map by dragging
|
||||
to move and using the +/- buttons to zoom in/out or use mouse scrolling for zooming.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Seleziona la tua posizione sulla mappa in modo che il programma di installazione possa suggerirti la localizzazione e le impostazioni del fuso orario. Puoi modificare le impostazioni suggerite nella parte in basso. Trascina la mappa per spostarti e usa i pulsanti +/- oppure la rotella del mouse per ingrandire o rimpicciolire.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1926,12 +1926,12 @@ Il programma d'installazione sarà terminato e tutte le modifiche andranno perse
|
||||
<message>
|
||||
<location filename="../src/modules/localeq/Offline.qml" line="62"/>
|
||||
<source>Timezone: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fuso orario: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/localeq/Offline.qml" line="77"/>
|
||||
<source>To be able to select a timezone, make sure you are connected to the internet. Restart the installer after connecting. You can fine-tune Language and Locale settings below.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Per selezionare un fuso orario, assicurati di essere collegato ad Internet. Riavvia il programma di installazione dopo esserti collegato. Puoi modificare le impostazioni relative alla lingua e alla posizione nella parte in basso.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2836,7 +2836,7 @@ Output:
|
||||
<location filename="../src/modules/welcomeq/Recommended.qml" line="49"/>
|
||||
<source><p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/>
|
||||
Setup can continue, but some features might be disabled.</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Questo computer non soddisfa alcuni requisiti raccomandati per poter installare %1. L'installazione può continuare, ma alcune funzionalità potrebbero essere disabilitate. </translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2947,13 +2947,13 @@ Output:
|
||||
<location filename="../src/modules/welcomeq/Requirements.qml" line="47"/>
|
||||
<source><p>This computer does not satisfy the minimum requirements for installing %1.<br/>
|
||||
Installation cannot continue.</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Questo computer non soddisfa i requisiti minimi per poter installare %1. L'installazione non può continuare.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/welcomeq/Requirements.qml" line="49"/>
|
||||
<source><p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/>
|
||||
Setup can continue, but some features might be disabled.</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Questo computer non soddisfa alcuni requisiti raccomandati per poter installare %1. L'installazione può continuare, ma alcune funzionalità potrebbero essere disabilitate. </translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3419,12 +3419,12 @@ Output:
|
||||
<message>
|
||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="202"/>
|
||||
<source>KDE user feedback</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Riscontro dell'utente di KDE </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="214"/>
|
||||
<source>Configuring KDE user feedback.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Sto configurando il riscontro dell'utente di KDE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="236"/>
|
||||
@ -3865,7 +3865,7 @@ Output:
|
||||
<message>
|
||||
<location filename="../src/modules/localeq/localeq.qml" line="98"/>
|
||||
<source>System language set to %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Lingua di sistema impostata su %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/localeq/localeq.qml" line="106"/>
|
||||
|
@ -245,7 +245,7 @@
|
||||
<source>(%n second(s))</source>
|
||||
<translation>
|
||||
<numerusform>(%n seconde)</numerusform>
|
||||
<numerusform>(%n seconden)</numerusform>
|
||||
<numerusform>(%n seconde(n))</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
@ -528,7 +528,7 @@ Het installatieprogramma zal afsluiten en alle wijzigingen zullen verloren gaan.
|
||||
<message>
|
||||
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="334"/>
|
||||
<source><strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. Having a GPT partition table and <strong>fat32 512Mb /boot partition is a must for UEFI installs</strong>, either use an existing without formatting or create one.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><strong>Handmatige partitionering</strong><br/>Je kunt zelf de partities creëren of wijzigen. Het hebben van een GPT partititie tabel and <strong>een FAT-32 512 MB /boot partitie is belangrijk voor UEFI installaties</strong>. Gebruik een bestaande zonder formatteren of maak een nieuwe aan.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="833"/>
|
||||
@ -1426,7 +1426,7 @@ Het installatieprogramma zal afsluiten en alle wijzigingen zullen verloren gaan.
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/checker/GeneralRequirements.cpp" line="214"/>
|
||||
<source>The screen is too small to display the installer.</source>
|
||||
<translation>Het schem is te klein on het installatieprogramma te vertonen.</translation>
|
||||
<translation>Het scherm is te klein on het installatieprogramma te laten zien.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1655,12 +1655,12 @@ Het installatieprogramma zal afsluiten en alle wijzigingen zullen verloren gaan.
|
||||
<message>
|
||||
<location filename="../src/modules/license/LicenseWidget.cpp" line="194"/>
|
||||
<source>Hide license text</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Verberg licentietekst</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/license/LicenseWidget.cpp" line="194"/>
|
||||
<source>Show the license text</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Toon licentietekst</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/license/LicenseWidget.cpp" line="198"/>
|
||||
@ -2268,7 +2268,7 @@ Het installatieprogramma zal afsluiten en alle wijzigingen zullen verloren gaan.
|
||||
<message>
|
||||
<location filename="../src/modules/users/page_usersetup.ui" line="51"/>
|
||||
<source>Your Full Name</source>
|
||||
<translation>Volledige Naam</translation>
|
||||
<translation>Volledige naam</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/users/page_usersetup.ui" line="120"/>
|
||||
@ -2278,7 +2278,7 @@ Het installatieprogramma zal afsluiten en alle wijzigingen zullen verloren gaan.
|
||||
<message>
|
||||
<location filename="../src/modules/users/page_usersetup.ui" line="144"/>
|
||||
<source>login</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Gebruikersnaam</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/users/page_usersetup.ui" line="219"/>
|
||||
@ -2310,13 +2310,13 @@ Het installatieprogramma zal afsluiten en alle wijzigingen zullen verloren gaan.
|
||||
<location filename="../src/modules/users/page_usersetup.ui" line="351"/>
|
||||
<location filename="../src/modules/users/page_usersetup.ui" line="521"/>
|
||||
<source>Password</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Wachtwoord</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/users/page_usersetup.ui" line="376"/>
|
||||
<location filename="../src/modules/users/page_usersetup.ui" line="546"/>
|
||||
<source>Repeat Password</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Herhaal wachtwoord</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/users/page_usersetup.ui" line="451"/>
|
||||
|
@ -3549,7 +3549,7 @@ Saída:
|
||||
<message>
|
||||
<location filename="../src/modules/users/UsersPage.cpp" line="402"/>
|
||||
<source>Your username must start with a lowercase letter or underscore.</source>
|
||||
<translation>Seu nome de usuário deve começar com uma letra maiúscula ou com um sublinhado.</translation>
|
||||
<translation>Seu nome de usuário deve começar com uma letra minúscula ou com um sublinhado.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/users/UsersPage.cpp" line="409"/>
|
||||
|
3947
lang/calamares_tg.ts
Normal file
3947
lang/calamares_tg.ts
Normal file
File diff suppressed because it is too large
Load Diff
BIN
lang/python/ie/LC_MESSAGES/python.mo
Normal file
BIN
lang/python/ie/LC_MESSAGES/python.mo
Normal file
Binary file not shown.
339
lang/python/ie/LC_MESSAGES/python.po
Normal file
339
lang/python/ie/LC_MESSAGES/python.po
Normal file
@ -0,0 +1,339 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-06-18 15:42+0200\n"
|
||||
"PO-Revision-Date: 2017-08-09 10:34+0000\n"
|
||||
"Language-Team: Interlingue (https://www.transifex.com/calamares/teams/20061/ie/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ie\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/modules/grubcfg/main.py:37
|
||||
msgid "Configure GRUB."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mount/main.py:38
|
||||
msgid "Mounting partitions."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mount/main.py:150 src/modules/initcpiocfg/main.py:205
|
||||
#: src/modules/initcpiocfg/main.py:209
|
||||
#: src/modules/luksopenswaphookcfg/main.py:95
|
||||
#: src/modules/luksopenswaphookcfg/main.py:99 src/modules/rawfs/main.py:173
|
||||
#: src/modules/initramfscfg/main.py:94 src/modules/initramfscfg/main.py:98
|
||||
#: src/modules/openrcdmcryptcfg/main.py:78
|
||||
#: src/modules/openrcdmcryptcfg/main.py:82 src/modules/fstab/main.py:332
|
||||
#: src/modules/fstab/main.py:338 src/modules/localecfg/main.py:144
|
||||
#: src/modules/networkcfg/main.py:48
|
||||
msgid "Configuration Error"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mount/main.py:151 src/modules/initcpiocfg/main.py:206
|
||||
#: src/modules/luksopenswaphookcfg/main.py:96 src/modules/rawfs/main.py:174
|
||||
#: src/modules/initramfscfg/main.py:95 src/modules/openrcdmcryptcfg/main.py:79
|
||||
#: src/modules/fstab/main.py:333
|
||||
msgid "No partitions are defined for <pre>{!s}</pre> to use."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:35
|
||||
msgid "Configure systemd services"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:68
|
||||
#: src/modules/services-openrc/main.py:102
|
||||
msgid "Cannot modify service"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:69
|
||||
msgid ""
|
||||
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:72
|
||||
#: src/modules/services-systemd/main.py:76
|
||||
msgid "Cannot enable systemd service <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:74
|
||||
msgid "Cannot enable systemd target <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:78
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:80
|
||||
msgid "Cannot mask systemd unit <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:82
|
||||
msgid ""
|
||||
"Unknown systemd commands <code>{command!s}</code> and "
|
||||
"<code>{suffix!s}</code> for unit {name!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/umount/main.py:40
|
||||
msgid "Unmount file systems."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:44
|
||||
msgid "Filling up filesystems."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:257
|
||||
msgid "rsync failed with error code {}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:302
|
||||
msgid "Unpacking image {}/{}, file {}/{}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:317
|
||||
msgid "Starting to unpack {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:326 src/modules/unpackfs/main.py:448
|
||||
msgid "Failed to unpack image \"{}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:415
|
||||
msgid "No mount point for root partition"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:416
|
||||
msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:421
|
||||
msgid "Bad mount point for root partition"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:422
|
||||
msgid "rootMountPoint is \"{}\", which does not exist, doing nothing"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:438 src/modules/unpackfs/main.py:442
|
||||
#: src/modules/unpackfs/main.py:462
|
||||
msgid "Bad unsquash configuration"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:439
|
||||
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:443
|
||||
msgid "The source filesystem \"{}\" does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:449
|
||||
msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:463
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:523
|
||||
msgid "Cannot write KDM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:524
|
||||
msgid "KDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:585
|
||||
msgid "Cannot write LXDM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:586
|
||||
msgid "LXDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:669
|
||||
msgid "Cannot write LightDM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:670
|
||||
msgid "LightDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:744
|
||||
msgid "Cannot configure LightDM"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:745
|
||||
msgid "No LightDM greeter installed."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:776
|
||||
msgid "Cannot write SLIM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:777
|
||||
msgid "SLIM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:903
|
||||
msgid "No display managers selected for the displaymanager module."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:904
|
||||
msgid ""
|
||||
"The displaymanagers list is empty or undefined in bothglobalstorage and "
|
||||
"displaymanager.conf."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:986
|
||||
msgid "Display manager configuration was incomplete"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initcpiocfg/main.py:37
|
||||
msgid "Configuring mkinitcpio."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initcpiocfg/main.py:210
|
||||
#: src/modules/luksopenswaphookcfg/main.py:100
|
||||
#: src/modules/initramfscfg/main.py:99 src/modules/openrcdmcryptcfg/main.py:83
|
||||
#: src/modules/fstab/main.py:339 src/modules/localecfg/main.py:145
|
||||
#: src/modules/networkcfg/main.py:49
|
||||
msgid "No root mount point is given for <pre>{!s}</pre> to use."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/luksopenswaphookcfg/main.py:35
|
||||
msgid "Configuring encrypted swap."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rawfs/main.py:35
|
||||
msgid "Installing data."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:38
|
||||
msgid "Configure OpenRC services"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:66
|
||||
msgid "Cannot add service {name!s} to run-level {level!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:68
|
||||
msgid "Cannot remove service {name!s} from run-level {level!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:70
|
||||
msgid ""
|
||||
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
||||
"level {level!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:103
|
||||
msgid ""
|
||||
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:110
|
||||
msgid "Target runlevel does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:111
|
||||
msgid ""
|
||||
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:119
|
||||
msgid "Target service does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:120
|
||||
msgid ""
|
||||
"The path for service {name!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/plymouthcfg/main.py:36
|
||||
msgid "Configure Plymouth theme"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:59 src/modules/packages/main.py:68
|
||||
#: src/modules/packages/main.py:78
|
||||
msgid "Install packages."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:66
|
||||
#, python-format
|
||||
msgid "Processing packages (%(count)d / %(total)d)"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:71
|
||||
#, python-format
|
||||
msgid "Installing one package."
|
||||
msgid_plural "Installing %(num)d packages."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/modules/packages/main.py:74
|
||||
#, python-format
|
||||
msgid "Removing one package."
|
||||
msgid_plural "Removing %(num)d packages."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/modules/bootloader/main.py:51
|
||||
msgid "Install bootloader."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/hwclock/main.py:35
|
||||
msgid "Setting hardware clock."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dracut/main.py:36
|
||||
msgid "Creating initramfs with dracut."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dracut/main.py:58
|
||||
msgid "Failed to run dracut on the target"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dracut/main.py:59
|
||||
msgid "The exit code was {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initramfscfg/main.py:41
|
||||
msgid "Configuring initramfs."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/openrcdmcryptcfg/main.py:34
|
||||
msgid "Configuring OpenRC dmcrypt service."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/fstab/main.py:38
|
||||
msgid "Writing fstab."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dummypython/main.py:44
|
||||
msgid "Dummy python job."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dummypython/main.py:46 src/modules/dummypython/main.py:102
|
||||
#: src/modules/dummypython/main.py:103
|
||||
msgid "Dummy python step {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/localecfg/main.py:39
|
||||
msgid "Configuring locales."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/networkcfg/main.py:37
|
||||
msgid "Saving network configuration."
|
||||
msgstr ""
|
BIN
lang/python/tg/LC_MESSAGES/python.mo
Normal file
BIN
lang/python/tg/LC_MESSAGES/python.mo
Normal file
Binary file not shown.
343
lang/python/tg/LC_MESSAGES/python.po
Normal file
343
lang/python/tg/LC_MESSAGES/python.po
Normal file
@ -0,0 +1,343 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Victor Ibragimov <victor.ibragimov@gmail.com>, 2020
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-06-18 15:42+0200\n"
|
||||
"PO-Revision-Date: 2017-08-09 10:34+0000\n"
|
||||
"Last-Translator: Victor Ibragimov <victor.ibragimov@gmail.com>, 2020\n"
|
||||
"Language-Team: Tajik (https://www.transifex.com/calamares/teams/20061/tg/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: tg\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/modules/grubcfg/main.py:37
|
||||
msgid "Configure GRUB."
|
||||
msgstr "Танзимоти GRUB."
|
||||
|
||||
#: src/modules/mount/main.py:38
|
||||
msgid "Mounting partitions."
|
||||
msgstr "Васлкунии қисмҳои диск."
|
||||
|
||||
#: src/modules/mount/main.py:150 src/modules/initcpiocfg/main.py:205
|
||||
#: src/modules/initcpiocfg/main.py:209
|
||||
#: src/modules/luksopenswaphookcfg/main.py:95
|
||||
#: src/modules/luksopenswaphookcfg/main.py:99 src/modules/rawfs/main.py:173
|
||||
#: src/modules/initramfscfg/main.py:94 src/modules/initramfscfg/main.py:98
|
||||
#: src/modules/openrcdmcryptcfg/main.py:78
|
||||
#: src/modules/openrcdmcryptcfg/main.py:82 src/modules/fstab/main.py:332
|
||||
#: src/modules/fstab/main.py:338 src/modules/localecfg/main.py:144
|
||||
#: src/modules/networkcfg/main.py:48
|
||||
msgid "Configuration Error"
|
||||
msgstr "Хатои танзимкунӣ"
|
||||
|
||||
#: src/modules/mount/main.py:151 src/modules/initcpiocfg/main.py:206
|
||||
#: src/modules/luksopenswaphookcfg/main.py:96 src/modules/rawfs/main.py:174
|
||||
#: src/modules/initramfscfg/main.py:95 src/modules/openrcdmcryptcfg/main.py:79
|
||||
#: src/modules/fstab/main.py:333
|
||||
msgid "No partitions are defined for <pre>{!s}</pre> to use."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:35
|
||||
msgid "Configure systemd services"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:68
|
||||
#: src/modules/services-openrc/main.py:102
|
||||
msgid "Cannot modify service"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:69
|
||||
msgid ""
|
||||
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:72
|
||||
#: src/modules/services-systemd/main.py:76
|
||||
msgid "Cannot enable systemd service <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:74
|
||||
msgid "Cannot enable systemd target <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:78
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:80
|
||||
msgid "Cannot mask systemd unit <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:82
|
||||
msgid ""
|
||||
"Unknown systemd commands <code>{command!s}</code> and "
|
||||
"<code>{suffix!s}</code> for unit {name!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/umount/main.py:40
|
||||
msgid "Unmount file systems."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:44
|
||||
msgid "Filling up filesystems."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:257
|
||||
msgid "rsync failed with error code {}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:302
|
||||
msgid "Unpacking image {}/{}, file {}/{}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:317
|
||||
msgid "Starting to unpack {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:326 src/modules/unpackfs/main.py:448
|
||||
msgid "Failed to unpack image \"{}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:415
|
||||
msgid "No mount point for root partition"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:416
|
||||
msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:421
|
||||
msgid "Bad mount point for root partition"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:422
|
||||
msgid "rootMountPoint is \"{}\", which does not exist, doing nothing"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:438 src/modules/unpackfs/main.py:442
|
||||
#: src/modules/unpackfs/main.py:462
|
||||
msgid "Bad unsquash configuration"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:439
|
||||
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:443
|
||||
msgid "The source filesystem \"{}\" does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:449
|
||||
msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:463
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:523
|
||||
msgid "Cannot write KDM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:524
|
||||
msgid "KDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:585
|
||||
msgid "Cannot write LXDM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:586
|
||||
msgid "LXDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:669
|
||||
msgid "Cannot write LightDM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:670
|
||||
msgid "LightDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:744
|
||||
msgid "Cannot configure LightDM"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:745
|
||||
msgid "No LightDM greeter installed."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:776
|
||||
msgid "Cannot write SLIM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:777
|
||||
msgid "SLIM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:903
|
||||
msgid "No display managers selected for the displaymanager module."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:904
|
||||
msgid ""
|
||||
"The displaymanagers list is empty or undefined in bothglobalstorage and "
|
||||
"displaymanager.conf."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:986
|
||||
msgid "Display manager configuration was incomplete"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initcpiocfg/main.py:37
|
||||
msgid "Configuring mkinitcpio."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initcpiocfg/main.py:210
|
||||
#: src/modules/luksopenswaphookcfg/main.py:100
|
||||
#: src/modules/initramfscfg/main.py:99 src/modules/openrcdmcryptcfg/main.py:83
|
||||
#: src/modules/fstab/main.py:339 src/modules/localecfg/main.py:145
|
||||
#: src/modules/networkcfg/main.py:49
|
||||
msgid "No root mount point is given for <pre>{!s}</pre> to use."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/luksopenswaphookcfg/main.py:35
|
||||
msgid "Configuring encrypted swap."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rawfs/main.py:35
|
||||
msgid "Installing data."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:38
|
||||
msgid "Configure OpenRC services"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:66
|
||||
msgid "Cannot add service {name!s} to run-level {level!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:68
|
||||
msgid "Cannot remove service {name!s} from run-level {level!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:70
|
||||
msgid ""
|
||||
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
||||
"level {level!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:103
|
||||
msgid ""
|
||||
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:110
|
||||
msgid "Target runlevel does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:111
|
||||
msgid ""
|
||||
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:119
|
||||
msgid "Target service does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-openrc/main.py:120
|
||||
msgid ""
|
||||
"The path for service {name!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/plymouthcfg/main.py:36
|
||||
msgid "Configure Plymouth theme"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:59 src/modules/packages/main.py:68
|
||||
#: src/modules/packages/main.py:78
|
||||
msgid "Install packages."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:66
|
||||
#, python-format
|
||||
msgid "Processing packages (%(count)d / %(total)d)"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:71
|
||||
#, python-format
|
||||
msgid "Installing one package."
|
||||
msgid_plural "Installing %(num)d packages."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/modules/packages/main.py:74
|
||||
#, python-format
|
||||
msgid "Removing one package."
|
||||
msgid_plural "Removing %(num)d packages."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/modules/bootloader/main.py:51
|
||||
msgid "Install bootloader."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/hwclock/main.py:35
|
||||
msgid "Setting hardware clock."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dracut/main.py:36
|
||||
msgid "Creating initramfs with dracut."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dracut/main.py:58
|
||||
msgid "Failed to run dracut on the target"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dracut/main.py:59
|
||||
msgid "The exit code was {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initramfscfg/main.py:41
|
||||
msgid "Configuring initramfs."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/openrcdmcryptcfg/main.py:34
|
||||
msgid "Configuring OpenRC dmcrypt service."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/fstab/main.py:38
|
||||
msgid "Writing fstab."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dummypython/main.py:44
|
||||
msgid "Dummy python job."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dummypython/main.py:46 src/modules/dummypython/main.py:102
|
||||
#: src/modules/dummypython/main.py:103
|
||||
msgid "Dummy python step {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/localecfg/main.py:39
|
||||
msgid "Configuring locales."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/networkcfg/main.py:37
|
||||
msgid "Saving network configuration."
|
||||
msgstr ""
|
@ -34,9 +34,8 @@ include_directories(
|
||||
# Translations
|
||||
include( CalamaresAddTranslations )
|
||||
add_calamares_translations( ${CALAMARES_TRANSLATION_LANGUAGES} )
|
||||
qt5_add_resources( calamaresRc calamares.qrc )
|
||||
|
||||
add_executable( calamares_bin ${calamaresSources} ${calamaresRc} ${trans_outfile} )
|
||||
add_executable( calamares_bin ${calamaresSources} calamares.qrc ${trans_outfile} )
|
||||
target_include_directories( calamares_bin PRIVATE ${CMAKE_SOURCE_DIR} )
|
||||
set_target_properties(calamares_bin
|
||||
PROPERTIES
|
||||
@ -45,6 +44,7 @@ set_target_properties(calamares_bin
|
||||
)
|
||||
calamares_automoc( calamares_bin )
|
||||
calamares_autouic( calamares_bin )
|
||||
calamares_autorcc( calamares_bin )
|
||||
|
||||
target_link_libraries( calamares_bin
|
||||
PRIVATE
|
||||
|
@ -27,8 +27,10 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} )
|
||||
|
||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/CalamaresConfig.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/CalamaresConfig.h )
|
||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/../calamares/CalamaresVersion.h.in
|
||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/CalamaresVersion.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/CalamaresVersion.h )
|
||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/CalamaresVersionX.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/CalamaresVersionX.h )
|
||||
|
||||
set( OPTIONAL_PRIVATE_LIBRARIES "" )
|
||||
set( OPTIONAL_PUBLIC_LIBRARIES "" )
|
||||
@ -76,6 +78,7 @@ set( libSources
|
||||
utils/Dirs.cpp
|
||||
utils/Entropy.cpp
|
||||
utils/Logger.cpp
|
||||
utils/Permissions.cpp
|
||||
utils/PluginFactory.cpp
|
||||
utils/Retranslator.cpp
|
||||
utils/String.cpp
|
||||
|
@ -4,7 +4,7 @@
|
||||
#cmakedefine CALAMARES_ORGANIZATION_NAME "${CALAMARES_ORGANIZATION_NAME}"
|
||||
#cmakedefine CALAMARES_ORGANIZATION_DOMAIN "${CALAMARES_ORGANIZATION_DOMAIN}"
|
||||
#cmakedefine CALAMARES_APPLICATION_NAME "${CALAMARES_APPLICATION_NAME}"
|
||||
#cmakedefine CALAMARES_VERSION "${CALAMARES_VERSION}"
|
||||
#cmakedefine CALAMARES_VERSION "${CALAMARES_VERSION_SHORT}"
|
||||
#cmakedefine CALAMARES_VERSION_SHORT "${CALAMARES_VERSION_SHORT}"
|
||||
|
||||
#cmakedefine CALAMARES_VERSION_MAJOR "${CALAMARES_VERSION_MAJOR}"
|
13
src/libcalamares/CalamaresVersionX.h.in
Normal file
13
src/libcalamares/CalamaresVersionX.h.in
Normal file
@ -0,0 +1,13 @@
|
||||
// Same as CalamaresVersion.h, but with a full-git-extended VERSION
|
||||
// rather than the short (vM.m.p) semantic version.
|
||||
#ifndef CALAMARES_VERSION_H
|
||||
|
||||
// On purpose, do not define the guard, but let CalamaresVersion.h do it
|
||||
// #define CALAMARES_VERSION_H
|
||||
|
||||
#include "CalamaresVersion.h"
|
||||
|
||||
#undef CALAMARES_VERSION
|
||||
#cmakedefine CALAMARES_VERSION "${CALAMARES_VERSION}"
|
||||
|
||||
#endif // CALAMARES_VERSION_H
|
@ -36,8 +36,8 @@ using CalamaresUtils::operator""_MiB;
|
||||
namespace Calamares
|
||||
{
|
||||
|
||||
GlobalStorage::GlobalStorage()
|
||||
: QObject( nullptr )
|
||||
GlobalStorage::GlobalStorage( QObject* parent )
|
||||
: QObject( parent )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ class GlobalStorage : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GlobalStorage();
|
||||
explicit GlobalStorage( QObject* parent = nullptr );
|
||||
|
||||
//NOTE: thread safety is guaranteed by JobQueue, which executes jobs one by one.
|
||||
// If at any time jobs become concurrent, this class must be made thread-safe.
|
||||
|
@ -170,7 +170,7 @@ JobQueue::globalStorage() const
|
||||
JobQueue::JobQueue( QObject* parent )
|
||||
: QObject( parent )
|
||||
, m_thread( new JobThread( this ) )
|
||||
, m_storage( new GlobalStorage() )
|
||||
, m_storage( new GlobalStorage( this ) )
|
||||
{
|
||||
Q_ASSERT( !s_instance );
|
||||
s_instance = this;
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -15,8 +16,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
@ -88,7 +87,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief A pair of strings for timezone regions (e.g. "America")
|
||||
/** @brief Timezone regions (e.g. "America")
|
||||
*
|
||||
* A region has a key and a human-readable name, but also
|
||||
* a collection of associated timezone zones (TZZone, below).
|
||||
* This class is not usually constructed, but uses fromFile()
|
||||
* to load a complete tree structure of timezones.
|
||||
*/
|
||||
class TZRegion : public CStringPair
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -120,7 +125,12 @@ private:
|
||||
CStringPairList m_zones;
|
||||
};
|
||||
|
||||
/// @brief A pair of strings for specific timezone names (e.g. "New_York")
|
||||
/** @brief Specific timezone zones (e.g. "New_York", "New York")
|
||||
*
|
||||
* A timezone zone lives in a region, and has some associated
|
||||
* data like the country (used to map likely languages) and latitude
|
||||
* and longitude information.
|
||||
*/
|
||||
class TZZone : public CStringPair
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -168,7 +168,11 @@ Manager::checkHasInternet()
|
||||
{
|
||||
hasInternet = synchronousPing( d->m_hasInternetUrl );
|
||||
}
|
||||
if ( hasInternet != d->m_hasInternet )
|
||||
{
|
||||
d->m_hasInternet = hasInternet;
|
||||
emit hasInternetChanged( hasInternet );
|
||||
}
|
||||
return hasInternet;
|
||||
}
|
||||
|
||||
|
@ -98,9 +98,10 @@ struct RequestStatus
|
||||
|
||||
QDebug& operator<<( QDebug& s, const RequestStatus& e );
|
||||
|
||||
class DLLEXPORT Manager : QObject
|
||||
class DLLEXPORT Manager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY( bool hasInternet READ hasInternet NOTIFY hasInternetChanged FINAL )
|
||||
|
||||
Manager();
|
||||
|
||||
@ -133,6 +134,16 @@ public:
|
||||
|
||||
/// @brief Set the URL which is used for the general "is there internet" check.
|
||||
void setCheckHasInternetUrl( const QUrl& url );
|
||||
|
||||
/** @brief Do a network request asynchronously.
|
||||
*
|
||||
* Returns a pointer to the reply-from-the-request.
|
||||
* This may be a nullptr if an error occurs immediately.
|
||||
* The caller is responsible for cleaning up the reply (eventually).
|
||||
*/
|
||||
QNetworkReply* asynchronousGet( const QUrl& url, const RequestOptions& options = RequestOptions() );
|
||||
|
||||
public Q_SLOTS:
|
||||
/** @brief Do an explicit check for internet connectivity.
|
||||
*
|
||||
* This **may** do a ping to the configured check URL, but can also
|
||||
@ -148,13 +159,13 @@ public:
|
||||
*/
|
||||
bool hasInternet();
|
||||
|
||||
/** @brief Do a network request asynchronously.
|
||||
signals:
|
||||
/** @brief Indicates that internet connectivity status has changed
|
||||
*
|
||||
* Returns a pointer to the reply-from-the-request.
|
||||
* This may be a nullptr if an error occurs immediately.
|
||||
* The caller is responsible for cleaning up the reply (eventually).
|
||||
* The value is that returned from hasInternet() -- @c true when there
|
||||
* is connectivity, @c false otherwise.
|
||||
*/
|
||||
QNetworkReply* asynchronousGet( const QUrl& url, const RequestOptions& options = RequestOptions() );
|
||||
void hasInternetChanged( bool );
|
||||
|
||||
private:
|
||||
class Private;
|
||||
|
@ -3,6 +3,7 @@
|
||||
* SPDX-FileCopyrightText: 2010-2011 Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||
* SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
|
||||
* SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
@ -18,15 +19,12 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "CalamaresVersionX.h"
|
||||
#include "utils/Dirs.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
@ -35,10 +33,10 @@
|
||||
#include <QTime>
|
||||
#include <QVariant>
|
||||
|
||||
#include "CalamaresVersion.h"
|
||||
#include "utils/Dirs.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#define LOGFILE_SIZE 1024 * 256
|
||||
static constexpr const int LOGFILE_SIZE = 1024 * 256;
|
||||
|
||||
static std::ofstream logfile;
|
||||
static unsigned int s_threshold =
|
||||
|
124
src/libcalamares/utils/Permissions.cpp
Normal file
124
src/libcalamares/utils/Permissions.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2018 Scott Harvey <scott@spharvey.me>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Permissions.h"
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace CalamaresUtils
|
||||
{
|
||||
|
||||
Permissions::Permissions()
|
||||
: m_username()
|
||||
, m_group()
|
||||
, m_value( 0 )
|
||||
, m_valid( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Permissions::Permissions( QString const& p )
|
||||
: Permissions()
|
||||
{
|
||||
parsePermissions( p );
|
||||
}
|
||||
|
||||
void
|
||||
Permissions::parsePermissions( QString const& p )
|
||||
{
|
||||
|
||||
QStringList segments = p.split( ":" );
|
||||
|
||||
if ( segments.length() != 3 )
|
||||
{
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( segments[ 0 ].isEmpty() || segments[ 1 ].isEmpty() )
|
||||
{
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
int octal = segments[ 2 ].toInt( &ok, 8 );
|
||||
if ( !ok || octal == 0 )
|
||||
{
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_value = octal;
|
||||
}
|
||||
|
||||
// We have exactly three segments and the third is valid octal,
|
||||
// so we can declare the string valid and set the user and group names
|
||||
m_valid = true;
|
||||
m_username = segments[ 0 ];
|
||||
m_group = segments[ 1 ];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
Permissions::apply( const QString& path, int mode )
|
||||
{
|
||||
// We **don't** use QFile::setPermissions() here because it takes
|
||||
// a Qt flags object that subtlely does not align with POSIX bits.
|
||||
// The Qt flags are **hex** based, so 0x755 for rwxr-xr-x, while
|
||||
// our integer (mode_t) stores **octal** based flags.
|
||||
//
|
||||
// Call chmod(2) directly, that's what Qt would be doing underneath
|
||||
// anyway.
|
||||
int r = chmod( path.toUtf8().constData(), mode_t( mode ) );
|
||||
if ( r )
|
||||
{
|
||||
cDebug() << Logger::SubEntry << "Could not set permissions of" << path << "to" << QString::number( mode, 8 );
|
||||
}
|
||||
return r == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Permissions::apply( const QString& path, const CalamaresUtils::Permissions& p )
|
||||
{
|
||||
if ( !p.isValid() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool r = apply( path, p.value() );
|
||||
if ( r )
|
||||
{
|
||||
// We don't use chgrp(2) or chown(2) here because then we need to
|
||||
// go through the users list (which one, target or source?) to get
|
||||
// uid_t and gid_t values to pass to that system call.
|
||||
//
|
||||
// Do a lame cop-out and let the chown(8) utility do the heavy lifting.
|
||||
if ( QProcess::execute( "chown", { p.username() + ':' + p.group(), path } ) )
|
||||
{
|
||||
r = false;
|
||||
cDebug() << Logger::SubEntry << "Could not set owner of" << path << "to"
|
||||
<< ( p.username() + ':' + p.group() );
|
||||
}
|
||||
}
|
||||
if ( r )
|
||||
{
|
||||
/* NOTUSED */ apply( path, p.value() );
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
} // namespace CalamaresUtils
|
92
src/libcalamares/utils/Permissions.h
Normal file
92
src/libcalamares/utils/Permissions.h
Normal file
@ -0,0 +1,92 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2018 Scott Harvey <scott@spharvey.me>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LIBCALAMARES_PERMISSIONS_H
|
||||
#define LIBCALAMARES_PERMISSIONS_H
|
||||
|
||||
#include "DllMacro.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace CalamaresUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The Permissions class takes a QString @p in the form of
|
||||
* <user>:<group>:<permissions>, checks it for validity, and makes the three
|
||||
* components available indivdually.
|
||||
*/
|
||||
class DLLEXPORT Permissions
|
||||
{
|
||||
|
||||
public:
|
||||
/** @brief Constructor
|
||||
*
|
||||
* Splits the string @p at the colon (":") into separate elements for
|
||||
* <user>, <group>, and <value> (permissions), where <value> is interpreted
|
||||
* as an **octal** integer. That is, "root:wheel:755" will give
|
||||
* you an integer value of four-hundred-ninety-three (493),
|
||||
* corresponding to the UNIX file permissions rwxr-xr-x,
|
||||
* as one would expect from chmod and other command-line utilities.
|
||||
*/
|
||||
Permissions( QString const& p );
|
||||
|
||||
/// @brief Default constructor of an invalid Permissions.
|
||||
Permissions();
|
||||
|
||||
/// @brief Was the Permissions object constructed from valid data?
|
||||
bool isValid() const { return m_valid; }
|
||||
/// @brief The user (first component, e.g. "root" in "root:wheel:755")
|
||||
QString username() const { return m_username; }
|
||||
/// @brief The group (second component, e.g. "wheel" in "root:wheel:755")
|
||||
QString group() const { return m_group; }
|
||||
/** @brief The value (file permission) as an integer.
|
||||
*
|
||||
* Bear in mind that input is in octal, but integers are just integers;
|
||||
* naively printing them will get decimal results (e.g. 493 from the
|
||||
* input of "root:wheel:755"). This is suitable to pass to apply().
|
||||
*/
|
||||
int value() const { return m_value; }
|
||||
/** @brief The value (file permission) as octal string
|
||||
*
|
||||
* This is suitable for passing to chmod-the-program, or for
|
||||
* recreating the original Permissions string.
|
||||
*/
|
||||
QString octal() const { return QString::number( value(), 8 ); }
|
||||
|
||||
/** @brief Sets the file-access @p mode of @p path
|
||||
*
|
||||
* Pass a path that is relative (or absolute) in the **host** system.
|
||||
*/
|
||||
static bool apply( const QString& path, int mode );
|
||||
/** @brief Do both chmod and chown on @p path
|
||||
*
|
||||
* Note that interpreting user- and group- names for applying the
|
||||
* permissions can be different between the host system and the target
|
||||
* system; the target might not have a "live" user, for instance, and
|
||||
* the host won't have the user-entered username for the installation.
|
||||
*
|
||||
* For this call, the names are interpreted in the **host** system.
|
||||
* Pass a path that is relative (or absolute) in the **host** system.
|
||||
*/
|
||||
static bool apply( const QString& path, const Permissions& p );
|
||||
/// Convenience method for apply(const QString&, const Permissions& )
|
||||
bool apply( const QString& path ) const { return apply( path, *this ); }
|
||||
|
||||
private:
|
||||
void parsePermissions( QString const& p );
|
||||
|
||||
QString m_username;
|
||||
QString m_group;
|
||||
int m_value;
|
||||
bool m_valid;
|
||||
};
|
||||
|
||||
} // namespace CalamaresUtils
|
||||
|
||||
#endif // LIBCALAMARES_PERMISSIONS_H
|
@ -24,6 +24,7 @@
|
||||
#define UTILS_RAII_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalBlocker>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
@ -44,4 +45,21 @@ struct cqDeleter
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Sets a bool to @p value and resets to !value on destruction
|
||||
template < bool value >
|
||||
struct cBoolSetter
|
||||
{
|
||||
bool& m_b;
|
||||
|
||||
cBoolSetter( bool& b )
|
||||
: m_b( b )
|
||||
{
|
||||
m_b = value;
|
||||
}
|
||||
~cBoolSetter() { m_b = !value; }
|
||||
};
|
||||
|
||||
/// @brief Blocks signals on a QObject until destruction
|
||||
using cSignalBlocker = QSignalBlocker;
|
||||
|
||||
#endif
|
||||
|
@ -103,11 +103,37 @@ public:
|
||||
RequirementsModel* requirementsModel() { return m_requirementsModel; }
|
||||
|
||||
signals:
|
||||
/** @brief Emitted when all the module **configuration** has been read
|
||||
*
|
||||
* This indicates that all of the module.desc files have been
|
||||
* loaded; bad ones are silently skipped, so this just indicates
|
||||
* that the module manager is ready for the next phase (loading).
|
||||
*/
|
||||
void initDone();
|
||||
void modulesLoaded(); /// All of the modules were loaded successfully
|
||||
void modulesFailed( QStringList ); /// .. or not
|
||||
// Below, see RequirementsChecker documentation
|
||||
void requirementsComplete( bool );
|
||||
/** @brief Emitted when all the modules are loaded successfully
|
||||
*
|
||||
* Each module listed in the settings is loaded. Modules are loaded
|
||||
* only once, even when instantiated multiple times. If all of
|
||||
* the listed modules are successfully loaded, this signal is
|
||||
* emitted (otherwise, it isn't, so you need to wait for **both**
|
||||
* of the signals).
|
||||
*
|
||||
* If this is emitted (i.e. all modules have loaded) then the next
|
||||
* phase, requirements checking, can be started.
|
||||
*/
|
||||
void modulesLoaded();
|
||||
/** @brief Emitted if any modules failed to load
|
||||
*
|
||||
* Modules that failed to load (for any reason) are listed by
|
||||
* instance key (e.g. "welcome@welcome", "shellprocess@mycustomthing").
|
||||
*/
|
||||
void modulesFailed( QStringList );
|
||||
/** @brief Emitted after all requirements have been checked
|
||||
*
|
||||
* The bool @p canContinue indicates if all of the **mandatory** requirements
|
||||
* are satisfied (e.g. whether installation can continue).
|
||||
*/
|
||||
void requirementsComplete( bool canContinue );
|
||||
|
||||
private slots:
|
||||
void doInit();
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
#include "ViewManager.h"
|
||||
#include "network/Manager.h"
|
||||
#include "utils/Dirs.h"
|
||||
#include "utils/Logger.h"
|
||||
|
||||
@ -242,6 +243,10 @@ registerQmlModels()
|
||||
"io.calamares.core", 1, 0, "Global", []( QQmlEngine*, QJSEngine* ) -> QObject* {
|
||||
return Calamares::JobQueue::instance()->globalStorage();
|
||||
} );
|
||||
qmlRegisterSingletonType< CalamaresUtils::Network::Manager >(
|
||||
"io.calamares.core", 1, 0, "Network", []( QQmlEngine*, QJSEngine* ) -> QObject* {
|
||||
return &CalamaresUtils::Network::Manager::instance();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ ListView {
|
||||
|
||||
z: parent.z - 1
|
||||
anchors.fill: parent
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
color: "#BDC3C7"
|
||||
radius: 5
|
||||
opacity: 0.7
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ Page {
|
||||
width: 800 //parent.width
|
||||
height: 550 //parent.height
|
||||
|
||||
Kirigami.Theme.backgroundColor: "#fafafa"
|
||||
Kirigami.Theme.textColor: "#333"
|
||||
Kirigami.Theme.backgroundColor: "#FAFAFA"
|
||||
Kirigami.Theme.textColor: "#1F1F1F"
|
||||
|
||||
property string subtitle
|
||||
property string message
|
||||
@ -22,39 +22,6 @@ Page {
|
||||
default property alias content : _content.data
|
||||
property alias stackView: _stackView
|
||||
|
||||
background: Item {
|
||||
|
||||
id: _background
|
||||
|
||||
Image {
|
||||
|
||||
id: _wallpaper
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
|
||||
sourceSize.width: 800
|
||||
sourceSize.height: 550
|
||||
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
antialiasing: false
|
||||
smooth: false
|
||||
asynchronous: true
|
||||
cache: true
|
||||
|
||||
source: "keyboard.jpg"
|
||||
}
|
||||
|
||||
FastBlur {
|
||||
|
||||
id: fastBlur
|
||||
anchors.fill: parent
|
||||
source: _wallpaper
|
||||
radius: 32
|
||||
transparentBorder: false
|
||||
cached: true
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
id: _content
|
||||
@ -72,7 +39,7 @@ Page {
|
||||
wrapMode: Text.NoWrap
|
||||
elide: Text.ElideMiddle
|
||||
text: control.title
|
||||
color: "white"
|
||||
color: Kirigami.Theme.textColor
|
||||
font.bold: true
|
||||
font.weight: Font.Bold
|
||||
font.pointSize: 24
|
||||
@ -86,7 +53,7 @@ Page {
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideMiddle
|
||||
text: control.subtitle
|
||||
color: "white"
|
||||
color: Kirigami.Theme.textColor
|
||||
font.weight: Font.Light
|
||||
font.pointSize: 12
|
||||
}
|
||||
@ -99,7 +66,7 @@ Page {
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideMiddle
|
||||
text: control.message
|
||||
color: "white"
|
||||
color: Kirigami.Theme.textColor
|
||||
font.weight: Font.Light
|
||||
font.pointSize: 10
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 101 KiB |
@ -182,14 +182,15 @@ ResponsiveBase {
|
||||
Layout.maximumWidth: 500
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
color: control.Kirigami.Theme.textColor
|
||||
|
||||
background:Rectangle {
|
||||
|
||||
z: parent.z - 1
|
||||
anchors.fill: parent
|
||||
color: control.Kirigami.Theme.backgroundColor
|
||||
color: "#BDC3C7"
|
||||
radius: 5
|
||||
opacity: 0.8
|
||||
opacity: 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,5 @@
|
||||
<file>ListItemDelegate.qml</file>
|
||||
<file>ListViewTemplate.qml</file>
|
||||
<file>ResponsiveBase.qml</file>
|
||||
<file>keyboard.jpg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -1,7 +1,8 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright 2019-2020, Adriaan de Groot <groot@kde.org>
|
||||
* Copyright 2020, Camilo Higuita <milo.h@aol.com>
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -19,85 +20,43 @@
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "LCLocaleDialog.h"
|
||||
#include "SetTimezoneJob.h"
|
||||
#include "timezonewidget/timezonewidget.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include "locale/Label.h"
|
||||
#include "locale/TimeZone.h"
|
||||
#include "utils/CalamaresUtilsGui.h"
|
||||
#include "modulesystem/ModuleManager.h"
|
||||
#include "network/Manager.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Retranslator.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QTimeZone>
|
||||
|
||||
Config::Config( QObject* parent )
|
||||
: QObject( parent )
|
||||
, m_regionList( CalamaresUtils::Locale::TZRegion::fromZoneTab() )
|
||||
, m_regionModel( new CalamaresUtils::Locale::CStringListModel( m_regionList ) )
|
||||
, m_zonesModel( new CalamaresUtils::Locale::CStringListModel() )
|
||||
, m_blockTzWidgetSet( false )
|
||||
/** @brief Load supported locale keys
|
||||
*
|
||||
* If i18n/SUPPORTED exists, read the lines from that and return those
|
||||
* as supported locales; otherwise, try the file at @p localeGenPath
|
||||
* and get lines from that. Failing both, try the output of `locale -a`.
|
||||
*
|
||||
* This gives us a list of locale identifiers (e.g. en_US.UTF-8), which
|
||||
* are not particularly human-readable.
|
||||
*
|
||||
* Only UTF-8 locales are returned (even if the system claims to support
|
||||
* other, non-UTF-8, locales).
|
||||
*/
|
||||
static QStringList
|
||||
loadLocales( const QString& localeGenPath )
|
||||
{
|
||||
connect( m_regionModel, &CalamaresUtils::Locale::CStringListModel::currentIndexChanged, [&]() {
|
||||
m_zonesModel->setList( static_cast< const CalamaresUtils::Locale::TZRegion* >(
|
||||
m_regionModel->item( m_regionModel->currentIndex() ) )
|
||||
->zones() );
|
||||
updateLocaleLabels();
|
||||
} );
|
||||
|
||||
connect(
|
||||
m_zonesModel, &CalamaresUtils::Locale::CStringListModel::currentIndexChanged, [&]() { updateLocaleLabels(); } );
|
||||
}
|
||||
|
||||
Config::~Config()
|
||||
{
|
||||
qDeleteAll( m_regionList );
|
||||
}
|
||||
|
||||
CalamaresUtils::Locale::CStringListModel*
|
||||
Config::zonesModel() const
|
||||
{
|
||||
return m_zonesModel;
|
||||
}
|
||||
|
||||
CalamaresUtils::Locale::CStringListModel*
|
||||
Config::regionModel() const
|
||||
{
|
||||
return m_regionModel;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath )
|
||||
{
|
||||
using namespace CalamaresUtils::Locale;
|
||||
|
||||
cDebug() << "REGION MODEL SIZE" << initialRegion << initialZone;
|
||||
auto* region = m_regionList.find< TZRegion >( initialRegion );
|
||||
if ( region && region->zones().find< TZZone >( initialZone ) )
|
||||
{
|
||||
m_regionModel->setCurrentIndex( m_regionModel->indexOf( initialRegion ) );
|
||||
m_zonesModel->setList( region->zones() );
|
||||
m_zonesModel->setCurrentIndex( m_zonesModel->indexOf( initialZone ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_regionModel->setCurrentIndex( m_regionModel->indexOf( "America" ) );
|
||||
m_zonesModel->setList(
|
||||
static_cast< const TZRegion* >( m_regionModel->item( m_regionModel->currentIndex() ) )->zones() );
|
||||
m_zonesModel->setCurrentIndex( m_zonesModel->indexOf( "New_York" ) );
|
||||
}
|
||||
QStringList localeGenLines;
|
||||
|
||||
// Some distros come with a meaningfully commented and easy to parse locale.gen,
|
||||
// and others ship a separate file /usr/share/i18n/SUPPORTED with a clean list of
|
||||
// supported locales. We first try that one, and if it doesn't exist, we fall back
|
||||
// to parsing the lines from locale.gen
|
||||
m_localeGenLines.clear();
|
||||
localeGenLines.clear();
|
||||
QFile supported( "/usr/share/i18n/SUPPORTED" );
|
||||
QByteArray ba;
|
||||
|
||||
@ -109,7 +68,7 @@ Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone,
|
||||
const auto lines = ba.split( '\n' );
|
||||
for ( const QByteArray& line : lines )
|
||||
{
|
||||
m_localeGenLines.append( QString::fromLatin1( line.simplified() ) );
|
||||
localeGenLines.append( QString::fromLatin1( line.simplified() ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -150,11 +109,11 @@ Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone,
|
||||
continue;
|
||||
}
|
||||
|
||||
m_localeGenLines.append( lineString );
|
||||
localeGenLines.append( lineString );
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_localeGenLines.isEmpty() )
|
||||
if ( localeGenLines.isEmpty() )
|
||||
{
|
||||
cWarning() << "cannot acquire a list of available locales."
|
||||
<< "The locale and localecfg modules will be broken as long as this "
|
||||
@ -164,168 +123,388 @@ Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone,
|
||||
<< "* a well-formed"
|
||||
<< ( localeGenPath.isEmpty() ? QLatin1String( "/etc/locale.gen" ) : localeGenPath ) << "\n\tOR"
|
||||
<< "* a complete pre-compiled locale-gen database which allows complete locale -a output.";
|
||||
return; // something went wrong and there's nothing we can do about it.
|
||||
return localeGenLines; // something went wrong and there's nothing we can do about it.
|
||||
}
|
||||
|
||||
// Assuming we have a list of supported locales, we usually only want UTF-8 ones
|
||||
// because it's not 1995.
|
||||
for ( auto it = m_localeGenLines.begin(); it != m_localeGenLines.end(); )
|
||||
{
|
||||
if ( !it->contains( "UTF-8", Qt::CaseInsensitive ) && !it->contains( "utf8", Qt::CaseInsensitive ) )
|
||||
{
|
||||
it = m_localeGenLines.erase( it );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
auto notUtf8 = []( const QString& s ) {
|
||||
return !s.contains( "UTF-8", Qt::CaseInsensitive ) && !s.contains( "utf8", Qt::CaseInsensitive );
|
||||
};
|
||||
auto it = std::remove_if( localeGenLines.begin(), localeGenLines.end(), notUtf8 );
|
||||
localeGenLines.erase( it, localeGenLines.end() );
|
||||
|
||||
// We strip " UTF-8" from "en_US.UTF-8 UTF-8" because it's redundant redundant.
|
||||
for ( auto it = m_localeGenLines.begin(); it != m_localeGenLines.end(); ++it )
|
||||
// Also simplify whitespace.
|
||||
auto unredundant = []( QString& s ) {
|
||||
if ( s.endsWith( " UTF-8" ) )
|
||||
{
|
||||
if ( it->endsWith( " UTF-8" ) )
|
||||
{
|
||||
it->chop( 6 );
|
||||
s.chop( 6 );
|
||||
}
|
||||
*it = it->simplified();
|
||||
}
|
||||
updateGlobalStorage();
|
||||
updateLocaleLabels();
|
||||
s = s.simplified();
|
||||
};
|
||||
std::for_each( localeGenLines.begin(), localeGenLines.end(), unredundant );
|
||||
|
||||
return localeGenLines;
|
||||
}
|
||||
|
||||
void
|
||||
Config::updateGlobalLocale()
|
||||
static inline const CalamaresUtils::Locale::CStringPairList&
|
||||
timezoneData()
|
||||
{
|
||||
auto* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
const QString bcp47 = m_selectedLocaleConfiguration.toBcp47();
|
||||
gs->insert( "locale", bcp47 );
|
||||
return CalamaresUtils::Locale::TZRegion::fromZoneTab();
|
||||
}
|
||||
|
||||
void
|
||||
Config::updateGlobalStorage()
|
||||
|
||||
Config::Config( QObject* parent )
|
||||
: QObject( parent )
|
||||
, m_regionModel( std::make_unique< CalamaresUtils::Locale::CStringListModel >( ::timezoneData() ) )
|
||||
, m_zonesModel( std::make_unique< CalamaresUtils::Locale::CStringListModel >() )
|
||||
{
|
||||
// Slightly unusual: connect to our *own* signals. Wherever the language
|
||||
// or the location is changed, these signals are emitted, so hook up to
|
||||
// them to update global storage accordingly. This simplifies code:
|
||||
// we don't need to call an update-GS method, or introduce an intermediate
|
||||
// update-thing-and-GS method. And everywhere where we **do** change
|
||||
// language or location, we already emit the signal.
|
||||
connect( this, &Config::currentLanguageCodeChanged, [&]() {
|
||||
auto* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
gs->insert( "locale", m_selectedLocaleConfiguration.toBcp47() );
|
||||
} );
|
||||
|
||||
connect( this, &Config::currentLCCodeChanged, [&]() {
|
||||
auto* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
// Update GS localeConf (the LC_ variables)
|
||||
auto map = localeConfiguration().toMap();
|
||||
QVariantMap vm;
|
||||
for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
|
||||
{
|
||||
vm.insert( it.key(), it.value() );
|
||||
}
|
||||
gs->insert( "localeConf", vm );
|
||||
} );
|
||||
|
||||
connect( this, &Config::currentLocationChanged, [&]() {
|
||||
auto* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
|
||||
// Update the GS region and zone (and possibly the live timezone)
|
||||
const auto* location = currentLocation();
|
||||
bool locationChanged = ( location->region() != gs->value( "locationRegion" ) )
|
||||
|| ( location->zone() != gs->value( "locationZone" ) );
|
||||
#ifdef DEBUG_TIMEZONES
|
||||
if ( locationChanged )
|
||||
{
|
||||
cDebug() << "Location changed" << gs->value( "locationRegion" ) << ',' << gs->value( "locationZone" ) << "to"
|
||||
<< location->region() << ',' << location->zone();
|
||||
}
|
||||
#endif
|
||||
|
||||
gs->insert( "locationRegion", location->region() );
|
||||
gs->insert( "locationZone", location->zone() );
|
||||
|
||||
updateGlobalLocale();
|
||||
|
||||
// If we're in chroot mode (normal install mode), then we immediately set the
|
||||
// timezone on the live system. When debugging timezones, don't bother.
|
||||
#ifndef DEBUG_TIMEZONES
|
||||
if ( locationChanged && Calamares::Settings::instance()->doChroot() )
|
||||
if ( locationChanged && m_adjustLiveTimezone )
|
||||
{
|
||||
QProcess::execute( "timedatectl", // depends on systemd
|
||||
{ "set-timezone", location->region() + '/' + location->zone() } );
|
||||
}
|
||||
#endif
|
||||
} );
|
||||
|
||||
// Preserve those settings that have been made explicit.
|
||||
auto newLocale = guessLocaleConfiguration();
|
||||
if ( !m_selectedLocaleConfiguration.isEmpty() && m_selectedLocaleConfiguration.explicit_lang )
|
||||
{
|
||||
newLocale.setLanguage( m_selectedLocaleConfiguration.language() );
|
||||
}
|
||||
if ( !m_selectedLocaleConfiguration.isEmpty() && m_selectedLocaleConfiguration.explicit_lc )
|
||||
{
|
||||
newLocale.lc_numeric = m_selectedLocaleConfiguration.lc_numeric;
|
||||
newLocale.lc_time = m_selectedLocaleConfiguration.lc_time;
|
||||
newLocale.lc_monetary = m_selectedLocaleConfiguration.lc_monetary;
|
||||
newLocale.lc_paper = m_selectedLocaleConfiguration.lc_paper;
|
||||
newLocale.lc_name = m_selectedLocaleConfiguration.lc_name;
|
||||
newLocale.lc_address = m_selectedLocaleConfiguration.lc_address;
|
||||
newLocale.lc_telephone = m_selectedLocaleConfiguration.lc_telephone;
|
||||
newLocale.lc_measurement = m_selectedLocaleConfiguration.lc_measurement;
|
||||
newLocale.lc_identification = m_selectedLocaleConfiguration.lc_identification;
|
||||
}
|
||||
newLocale.explicit_lang = m_selectedLocaleConfiguration.explicit_lang;
|
||||
newLocale.explicit_lc = m_selectedLocaleConfiguration.explicit_lc;
|
||||
auto prettyStatusNotify = [&]() { emit prettyStatusChanged( prettyStatus() ); };
|
||||
connect( this, &Config::currentLanguageStatusChanged, prettyStatusNotify );
|
||||
connect( this, &Config::currentLCStatusChanged, prettyStatusNotify );
|
||||
connect( this, &Config::currentLocationStatusChanged, prettyStatusNotify );
|
||||
}
|
||||
|
||||
m_selectedLocaleConfiguration = newLocale;
|
||||
updateLocaleLabels();
|
||||
Config::~Config() {}
|
||||
|
||||
const CalamaresUtils::Locale::CStringPairList&
|
||||
Config::timezoneData() const
|
||||
{
|
||||
return ::timezoneData();
|
||||
}
|
||||
|
||||
void
|
||||
Config::updateLocaleLabels()
|
||||
Config::setCurrentLocation()
|
||||
{
|
||||
LocaleConfiguration lc
|
||||
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration;
|
||||
auto labels = prettyLocaleStatus( lc );
|
||||
emit prettyStatusChanged();
|
||||
if ( !m_currentLocation && m_startingTimezone.isValid() )
|
||||
{
|
||||
setCurrentLocation( m_startingTimezone.first, m_startingTimezone.second );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::pair< QString, QString >
|
||||
Config::prettyLocaleStatus( const LocaleConfiguration& lc ) const
|
||||
void Config::setCurrentLocation(const QString& regionzone)
|
||||
{
|
||||
using CalamaresUtils::Locale::Label;
|
||||
|
||||
Label lang( lc.language(), Label::LabelFormat::AlwaysWithCountry );
|
||||
Label num( lc.lc_numeric, Label::LabelFormat::AlwaysWithCountry );
|
||||
|
||||
return std::make_pair< QString, QString >(
|
||||
tr( "The system language will be set to %1." ).arg( lang.label() ),
|
||||
tr( "The numbers and dates locale will be set to %1." ).arg( num.label() ) );
|
||||
auto r = CalamaresUtils::GeoIP::splitTZString( regionzone );
|
||||
if ( r.isValid() )
|
||||
{
|
||||
setCurrentLocation( r.first, r.second );
|
||||
}
|
||||
}
|
||||
|
||||
Calamares::JobList
|
||||
Config::createJobs()
|
||||
void
|
||||
Config::setCurrentLocation( const QString& regionName, const QString& zoneName )
|
||||
{
|
||||
QList< Calamares::job_ptr > list;
|
||||
const CalamaresUtils::Locale::TZZone* location = currentLocation();
|
||||
using namespace CalamaresUtils::Locale;
|
||||
auto* region = timezoneData().find< TZRegion >( regionName );
|
||||
auto* zone = region ? region->zones().find< TZZone >( zoneName ) : nullptr;
|
||||
if ( zone )
|
||||
{
|
||||
setCurrentLocation( zone );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recursive, but America/New_York always exists.
|
||||
setCurrentLocation( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
|
||||
}
|
||||
}
|
||||
|
||||
Calamares::Job* j = new SetTimezoneJob( location->region(), location->zone() );
|
||||
list.append( Calamares::job_ptr( j ) );
|
||||
void
|
||||
Config::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
|
||||
{
|
||||
if ( location != m_currentLocation )
|
||||
{
|
||||
m_currentLocation = location;
|
||||
// Overwrite those settings that have not been made explicit.
|
||||
auto newLocale = automaticLocaleConfiguration();
|
||||
if ( !m_selectedLocaleConfiguration.explicit_lang )
|
||||
{
|
||||
m_selectedLocaleConfiguration.setLanguage( newLocale.language() );
|
||||
emit currentLanguageStatusChanged( currentLanguageStatus() );
|
||||
}
|
||||
if ( !m_selectedLocaleConfiguration.explicit_lc )
|
||||
{
|
||||
m_selectedLocaleConfiguration.lc_numeric = newLocale.lc_numeric;
|
||||
m_selectedLocaleConfiguration.lc_time = newLocale.lc_time;
|
||||
m_selectedLocaleConfiguration.lc_monetary = newLocale.lc_monetary;
|
||||
m_selectedLocaleConfiguration.lc_paper = newLocale.lc_paper;
|
||||
m_selectedLocaleConfiguration.lc_name = newLocale.lc_name;
|
||||
m_selectedLocaleConfiguration.lc_address = newLocale.lc_address;
|
||||
m_selectedLocaleConfiguration.lc_telephone = newLocale.lc_telephone;
|
||||
m_selectedLocaleConfiguration.lc_measurement = newLocale.lc_measurement;
|
||||
m_selectedLocaleConfiguration.lc_identification = newLocale.lc_identification;
|
||||
|
||||
return list;
|
||||
emit currentLCStatusChanged( currentLCStatus() );
|
||||
}
|
||||
emit currentLocationChanged( m_currentLocation );
|
||||
}
|
||||
}
|
||||
|
||||
LocaleConfiguration
|
||||
Config::guessLocaleConfiguration() const
|
||||
Config::automaticLocaleConfiguration() const
|
||||
{
|
||||
// Special case: no location has been set at **all**
|
||||
if ( !currentLocation() )
|
||||
{
|
||||
return LocaleConfiguration();
|
||||
}
|
||||
return LocaleConfiguration::fromLanguageAndLocation(
|
||||
QLocale().name(), m_localeGenLines, currentLocation() ? currentLocation()->country() : "" );
|
||||
QLocale().name(), supportedLocales(), currentLocation()->country() );
|
||||
}
|
||||
|
||||
QMap< QString, QString >
|
||||
Config::localesMap()
|
||||
LocaleConfiguration
|
||||
Config::localeConfiguration() const
|
||||
{
|
||||
return m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().toMap()
|
||||
: m_selectedLocaleConfiguration.toMap();
|
||||
return m_selectedLocaleConfiguration.isEmpty() ? automaticLocaleConfiguration() : m_selectedLocaleConfiguration;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setLanguageExplicitly( const QString& language )
|
||||
{
|
||||
m_selectedLocaleConfiguration.setLanguage( language );
|
||||
m_selectedLocaleConfiguration.explicit_lang = true;
|
||||
|
||||
emit currentLanguageStatusChanged( currentLanguageStatus() );
|
||||
emit currentLanguageCodeChanged( currentLanguageCode() );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setLCLocaleExplicitly( const QString& locale )
|
||||
{
|
||||
// TODO: improve the granularity of this setting.
|
||||
m_selectedLocaleConfiguration.lc_numeric = locale;
|
||||
m_selectedLocaleConfiguration.lc_time = locale;
|
||||
m_selectedLocaleConfiguration.lc_monetary = locale;
|
||||
m_selectedLocaleConfiguration.lc_paper = locale;
|
||||
m_selectedLocaleConfiguration.lc_name = locale;
|
||||
m_selectedLocaleConfiguration.lc_address = locale;
|
||||
m_selectedLocaleConfiguration.lc_telephone = locale;
|
||||
m_selectedLocaleConfiguration.lc_measurement = locale;
|
||||
m_selectedLocaleConfiguration.lc_identification = locale;
|
||||
m_selectedLocaleConfiguration.explicit_lc = true;
|
||||
|
||||
emit currentLCStatusChanged( currentLCStatus() );
|
||||
emit currentLCCodeChanged( currentLCCode() );
|
||||
}
|
||||
|
||||
QString
|
||||
Config::currentLocationStatus() const
|
||||
{
|
||||
return tr( "Set timezone to %1/%2." ).arg( m_currentLocation->region(), m_currentLocation->zone() );
|
||||
}
|
||||
|
||||
static inline QString
|
||||
localeLabel( const QString& s )
|
||||
{
|
||||
using CalamaresUtils::Locale::Label;
|
||||
|
||||
Label lang( s, Label::LabelFormat::AlwaysWithCountry );
|
||||
return lang.label();
|
||||
}
|
||||
|
||||
QString
|
||||
Config::currentLanguageStatus() const
|
||||
{
|
||||
return tr( "The system language will be set to %1." )
|
||||
.arg( localeLabel( m_selectedLocaleConfiguration.language() ) );
|
||||
}
|
||||
|
||||
QString
|
||||
Config::currentLCStatus() const
|
||||
{
|
||||
return tr( "The numbers and dates locale will be set to %1." )
|
||||
.arg( localeLabel( m_selectedLocaleConfiguration.lc_numeric ) );
|
||||
}
|
||||
|
||||
QString
|
||||
Config::prettyStatus() const
|
||||
{
|
||||
QString status;
|
||||
status += tr( "Set timezone to %1/%2.<br/>" )
|
||||
.arg( m_regionModel->item( m_regionModel->currentIndex() )->tr() )
|
||||
.arg( m_zonesModel->item( m_zonesModel->currentIndex() )->tr() );
|
||||
|
||||
LocaleConfiguration lc
|
||||
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration;
|
||||
auto labels = prettyLocaleStatus( lc );
|
||||
status += labels.first + "<br/>";
|
||||
status += labels.second + "<br/>";
|
||||
|
||||
return status;
|
||||
QStringList l { currentLocationStatus(), currentLanguageStatus(), currentLCStatus() };
|
||||
return l.join( QStringLiteral( "<br/>" ) );
|
||||
}
|
||||
|
||||
|
||||
const CalamaresUtils::Locale::TZZone*
|
||||
Config::currentLocation() const
|
||||
static inline void
|
||||
getLocaleGenLines( const QVariantMap& configurationMap, QStringList& localeGenLines )
|
||||
{
|
||||
return static_cast< const CalamaresUtils::Locale::TZZone* >( m_zonesModel->item( m_zonesModel->currentIndex() ) );
|
||||
QString localeGenPath = CalamaresUtils::getString( configurationMap, "localeGenPath" );
|
||||
if ( localeGenPath.isEmpty() )
|
||||
{
|
||||
localeGenPath = QStringLiteral( "/etc/locale.gen" );
|
||||
}
|
||||
localeGenLines = loadLocales( localeGenPath );
|
||||
}
|
||||
|
||||
static inline void
|
||||
getAdjustLiveTimezone( const QVariantMap& configurationMap, bool& adjustLiveTimezone )
|
||||
{
|
||||
adjustLiveTimezone = CalamaresUtils::getBool(
|
||||
configurationMap, "adjustLiveTimezone", Calamares::Settings::instance()->doChroot() );
|
||||
#ifdef DEBUG_TIMEZONES
|
||||
if ( m_adjustLiveTimezone )
|
||||
{
|
||||
cWarning() << "Turning off live-timezone adjustments because debugging is on.";
|
||||
adjustLiveTimezone = false;
|
||||
}
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
if ( adjustLiveTimezone )
|
||||
{
|
||||
cWarning() << "Turning off live-timezone adjustments on FreeBSD.";
|
||||
adjustLiveTimezone = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
getStartingTimezone( const QVariantMap& configurationMap, CalamaresUtils::GeoIP::RegionZonePair& startingTimezone )
|
||||
{
|
||||
QString region = CalamaresUtils::getString( configurationMap, "region" );
|
||||
QString zone = CalamaresUtils::getString( configurationMap, "zone" );
|
||||
if ( !region.isEmpty() && !zone.isEmpty() )
|
||||
{
|
||||
startingTimezone = CalamaresUtils::GeoIP::RegionZonePair( region, zone );
|
||||
}
|
||||
else
|
||||
{
|
||||
startingTimezone
|
||||
= CalamaresUtils::GeoIP::RegionZonePair( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
|
||||
}
|
||||
|
||||
if ( CalamaresUtils::getBool( configurationMap, "useSystemTimezone", false ) )
|
||||
{
|
||||
auto systemtz = CalamaresUtils::GeoIP::splitTZString( QTimeZone::systemTimeZoneId() );
|
||||
if ( systemtz.isValid() )
|
||||
{
|
||||
cDebug() << "Overriding configured timezone" << startingTimezone << "with system timezone" << systemtz;
|
||||
startingTimezone = systemtz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
getGeoIP( const QVariantMap& configurationMap, std::unique_ptr< CalamaresUtils::GeoIP::Handler >& geoip )
|
||||
{
|
||||
bool ok = false;
|
||||
QVariantMap map = CalamaresUtils::getSubMap( configurationMap, "geoip", ok );
|
||||
if ( ok )
|
||||
{
|
||||
QString url = CalamaresUtils::getString( map, "url" );
|
||||
QString style = CalamaresUtils::getString( map, "style" );
|
||||
QString selector = CalamaresUtils::getString( map, "selector" );
|
||||
|
||||
geoip = std::make_unique< CalamaresUtils::GeoIP::Handler >( style, url, selector );
|
||||
if ( !geoip->isValid() )
|
||||
{
|
||||
cWarning() << "GeoIP Style" << style << "is not recognized.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
getLocaleGenLines( configurationMap, m_localeGenLines );
|
||||
getAdjustLiveTimezone( configurationMap, m_adjustLiveTimezone );
|
||||
getStartingTimezone( configurationMap, m_startingTimezone );
|
||||
getGeoIP( configurationMap, m_geoip );
|
||||
|
||||
if ( m_geoip && m_geoip->isValid() )
|
||||
{
|
||||
connect(
|
||||
Calamares::ModuleManager::instance(), &Calamares::ModuleManager::modulesLoaded, this, &Config::startGeoIP );
|
||||
}
|
||||
}
|
||||
|
||||
Calamares::JobList
|
||||
Config::createJobs()
|
||||
{
|
||||
Calamares::JobList list;
|
||||
const CalamaresUtils::Locale::TZZone* location = currentLocation();
|
||||
|
||||
if ( location )
|
||||
{
|
||||
Calamares::Job* j = new SetTimezoneJob( location->region(), location->zone() );
|
||||
list.append( Calamares::job_ptr( j ) );
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void
|
||||
Config::startGeoIP()
|
||||
{
|
||||
if ( m_geoip && m_geoip->isValid() )
|
||||
{
|
||||
auto& network = CalamaresUtils::Network::Manager::instance();
|
||||
if ( network.hasInternet() || network.synchronousPing( m_geoip->url() ) )
|
||||
{
|
||||
using Watcher = QFutureWatcher< CalamaresUtils::GeoIP::RegionZonePair >;
|
||||
m_geoipWatcher = std::make_unique< Watcher >();
|
||||
m_geoipWatcher->setFuture( m_geoip->query() );
|
||||
connect( m_geoipWatcher.get(), &Watcher::finished, this, &Config::completeGeoIP );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::completeGeoIP()
|
||||
{
|
||||
if ( !currentLocation() )
|
||||
{
|
||||
auto r = m_geoipWatcher->result();
|
||||
if ( r.isValid() )
|
||||
{
|
||||
m_startingTimezone = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
cWarning() << "GeoIP returned invalid result.";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cWarning() << "GeoIP result ignored because a location is already set.";
|
||||
}
|
||||
m_geoipWatcher.reset();
|
||||
m_geoip.reset();
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright 2019-2020, Adriaan de Groot <groot@kde.org>
|
||||
* Copyright 2020, Camilo Higuita <milo.h@aol.com>
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -23,9 +24,11 @@
|
||||
#include "LocaleConfiguration.h"
|
||||
|
||||
#include "Job.h"
|
||||
#include "geoip/Handler.h"
|
||||
#include "geoip/Interface.h"
|
||||
#include "locale/TimeZone.h"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QFutureWatcher>
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
@ -33,53 +36,150 @@
|
||||
class Config : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY( const QStringList& supportedLocales READ supportedLocales CONSTANT FINAL )
|
||||
Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* zonesModel READ zonesModel CONSTANT FINAL )
|
||||
Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* regionModel READ regionModel CONSTANT FINAL )
|
||||
|
||||
Q_PROPERTY( const CalamaresUtils::Locale::TZZone* currentLocation READ currentLocation WRITE setCurrentLocation
|
||||
NOTIFY currentLocationChanged )
|
||||
|
||||
// Status are complete, human-readable, messages
|
||||
Q_PROPERTY( QString currentLocationStatus READ currentLocationStatus NOTIFY currentLanguageStatusChanged )
|
||||
Q_PROPERTY( QString currentLanguageStatus READ currentLanguageStatus NOTIFY currentLanguageStatusChanged )
|
||||
Q_PROPERTY( QString currentLCStatus READ currentLCStatus NOTIFY currentLCStatusChanged )
|
||||
// Code are internal identifiers, like "en_US.UTF-8"
|
||||
Q_PROPERTY( QString currentLanguageCode READ currentLanguageCode WRITE setLanguageExplicitly NOTIFY currentLanguageCodeChanged )
|
||||
Q_PROPERTY( QString currentLCCode READ currentLCCode WRITE setLCLocaleExplicitly NOTIFY currentLCCodeChanged )
|
||||
|
||||
// This is a long human-readable string with all three statuses
|
||||
Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL )
|
||||
|
||||
public:
|
||||
Config( QObject* parent = nullptr );
|
||||
~Config();
|
||||
CalamaresUtils::Locale::CStringListModel* regionModel() const;
|
||||
CalamaresUtils::Locale::CStringListModel* zonesModel() const;
|
||||
|
||||
void setLocaleInfo( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath );
|
||||
|
||||
void setConfigurationMap( const QVariantMap& );
|
||||
Calamares::JobList createJobs();
|
||||
QMap< QString, QString > localesMap();
|
||||
|
||||
// Underlying data for the models
|
||||
const CalamaresUtils::Locale::CStringPairList& timezoneData() const;
|
||||
|
||||
/** @brief The currently selected location (timezone)
|
||||
*
|
||||
* The location is a pointer into the date that timezoneData() returns.
|
||||
*/
|
||||
const CalamaresUtils::Locale::TZZone* currentLocation() const { return m_currentLocation; }
|
||||
|
||||
/// locale configuration (LC_* and LANG) based solely on the current location.
|
||||
LocaleConfiguration automaticLocaleConfiguration() const;
|
||||
/// locale configuration that takes explicit settings into account
|
||||
LocaleConfiguration localeConfiguration() const;
|
||||
|
||||
/// The human-readable description of what timezone is used
|
||||
QString currentLocationStatus() const;
|
||||
/// The human-readable description of what language is used
|
||||
QString currentLanguageStatus() const;
|
||||
/// The human-readable description of what locale (LC_*) is used
|
||||
QString currentLCStatus() const;
|
||||
|
||||
/// The human-readable summary of what the module will do
|
||||
QString prettyStatus() const;
|
||||
|
||||
private:
|
||||
CalamaresUtils::Locale::CStringPairList m_regionList;
|
||||
CalamaresUtils::Locale::CStringListModel* m_regionModel;
|
||||
CalamaresUtils::Locale::CStringListModel* m_zonesModel;
|
||||
const QStringList& supportedLocales() const { return m_localeGenLines; }
|
||||
CalamaresUtils::Locale::CStringListModel* regionModel() const { return m_regionModel.get(); }
|
||||
CalamaresUtils::Locale::CStringListModel* zonesModel() const { return m_zonesModel.get(); }
|
||||
|
||||
LocaleConfiguration m_selectedLocaleConfiguration;
|
||||
/// Special case, set location from starting timezone if not already set
|
||||
void setCurrentLocation();
|
||||
|
||||
QStringList m_localeGenLines;
|
||||
int m_currentRegion = -1;
|
||||
public Q_SLOTS:
|
||||
/// Set a language by user-choice, overriding future location changes
|
||||
void setLanguageExplicitly( const QString& language );
|
||||
/// Set LC (formats) by user-choice, overriding future location changes
|
||||
void setLCLocaleExplicitly( const QString& locale );
|
||||
|
||||
bool m_blockTzWidgetSet;
|
||||
|
||||
LocaleConfiguration guessLocaleConfiguration() const;
|
||||
|
||||
// For the given locale config, return two strings describing
|
||||
// the settings for language and numbers.
|
||||
std::pair< QString, QString > prettyLocaleStatus( const LocaleConfiguration& ) const;
|
||||
|
||||
/** @brief Update the GS *locale* key with the selected system language.
|
||||
/** @brief Sets a location by full name
|
||||
*
|
||||
* This uses whatever is set in m_selectedLocaleConfiguration as the language,
|
||||
* and writes it to GS *locale* key (as a string, in BCP47 format).
|
||||
* @p regionzone should be an identifier from zone.tab, e.g. "Africa/Abidjan",
|
||||
* which is split into regon and zone. Invalid names will **not**
|
||||
* change the actual location.
|
||||
*/
|
||||
void updateGlobalLocale();
|
||||
void updateGlobalStorage();
|
||||
void updateLocaleLabels();
|
||||
void setCurrentLocation( const QString& regionzone );
|
||||
/** @brief Sets a location by split name
|
||||
*
|
||||
* @p region should be "America" or the like, while @p zone
|
||||
* names a zone within that region.
|
||||
*/
|
||||
void setCurrentLocation( const QString& region, const QString& zone );
|
||||
/** @brief Sets a location by pointer
|
||||
*
|
||||
* Pointer should be within the same model as the widget uses.
|
||||
* This can update the locale configuration -- the automatic one
|
||||
* follows the current location, and otherwise only explicitly-set
|
||||
* values will ignore changes to the location.
|
||||
*/
|
||||
void setCurrentLocation( const CalamaresUtils::Locale::TZZone* location );
|
||||
|
||||
const CalamaresUtils::Locale::TZZone* currentLocation() const;
|
||||
QString currentLanguageCode() const { return localeConfiguration().language(); }
|
||||
QString currentLCCode() const { return localeConfiguration().lc_numeric; }
|
||||
|
||||
signals:
|
||||
void prettyStatusChanged();
|
||||
void currentLocationChanged( const CalamaresUtils::Locale::TZZone* location ) const;
|
||||
void currentLocationStatusChanged( const QString& ) const;
|
||||
void currentLanguageStatusChanged( const QString& ) const;
|
||||
void currentLCStatusChanged( const QString& ) const;
|
||||
void prettyStatusChanged( const QString& ) const;
|
||||
void currentLanguageCodeChanged( const QString& ) const;
|
||||
void currentLCCodeChanged( const QString& ) const;
|
||||
|
||||
private:
|
||||
/// A list of supported locale identifiers (e.g. "en_US.UTF-8")
|
||||
QStringList m_localeGenLines;
|
||||
|
||||
/// The regions (America, Asia, Europe ..)
|
||||
std::unique_ptr< CalamaresUtils::Locale::CStringListModel > m_regionModel;
|
||||
/// The zones for the current region (e.g. America/New_York)
|
||||
std::unique_ptr< CalamaresUtils::Locale::CStringListModel > m_zonesModel;
|
||||
|
||||
/// The location, points into the timezone data
|
||||
const CalamaresUtils::Locale::TZZone* m_currentLocation = nullptr;
|
||||
|
||||
/** @brief Specific locale configurations
|
||||
*
|
||||
* "Automatic" locale configuration based on the location (e.g.
|
||||
* Europe/Amsterdam means Dutch language and Dutch locale) leave
|
||||
* this empty; if the user explicitly sets something, then
|
||||
* this configuration is non-empty and takes precedence over the
|
||||
* automatic location settings (so a user in Amsterdam can still
|
||||
* pick Ukranian settings, for instance).
|
||||
*/
|
||||
LocaleConfiguration m_selectedLocaleConfiguration;
|
||||
|
||||
/** @brief Should we adjust the "live" timezone when the location changes?
|
||||
*
|
||||
* In the Widgets UI, clicking around on the world map adjusts the
|
||||
* timezone, and the live system can be made to follow that.
|
||||
*/
|
||||
bool m_adjustLiveTimezone;
|
||||
|
||||
/** @brief The initial timezone (region, zone) specified in the config.
|
||||
*
|
||||
* This may be overridden by setting *useSystemTimezone* or by
|
||||
* GeoIP settings.
|
||||
*/
|
||||
CalamaresUtils::GeoIP::RegionZonePair m_startingTimezone;
|
||||
|
||||
/** @brief Handler for GeoIP lookup (if configured)
|
||||
*
|
||||
* The GeoIP lookup needs to be started at some suitable time,
|
||||
* by explicitly calling *TODO*
|
||||
*/
|
||||
std::unique_ptr< CalamaresUtils::GeoIP::Handler > m_geoip;
|
||||
|
||||
// Implementation details for doing GeoIP lookup
|
||||
void startGeoIP();
|
||||
void completeGeoIP();
|
||||
std::unique_ptr< QFutureWatcher< CalamaresUtils::GeoIP::RegionZonePair > > m_geoipWatcher;
|
||||
};
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ LocaleConfiguration::LocaleConfiguration( const QString& localeName, const QStri
|
||||
lc_numeric = lc_time = lc_monetary = lc_paper = lc_name = lc_address = lc_telephone = lc_measurement
|
||||
= lc_identification = formatsName;
|
||||
|
||||
(void)setLanguage( localeName );
|
||||
setLanguage( localeName );
|
||||
}
|
||||
|
||||
|
||||
@ -83,7 +83,7 @@ LocaleConfiguration::fromLanguageAndLocation( const QString& languageLocale,
|
||||
if ( language == "pt" || language == "zh" )
|
||||
{
|
||||
QString proposedLocale = QString( "%1_%2" ).arg( language ).arg( countryCode );
|
||||
foreach ( QString line, linesForLanguage )
|
||||
for ( const QString& line : linesForLanguage )
|
||||
{
|
||||
if ( line.contains( proposedLocale ) )
|
||||
{
|
||||
|
@ -26,36 +26,53 @@
|
||||
|
||||
class LocaleConfiguration
|
||||
{
|
||||
public:
|
||||
/// @brief Create an empty locale, with nothing set
|
||||
explicit LocaleConfiguration();
|
||||
/// @brief Create a locale with everything set to the given @p localeName
|
||||
public: // TODO: private (but need to be public for tests)
|
||||
/** @brief Create a locale with everything set to the given @p localeName
|
||||
*
|
||||
* Consumers should use fromLanguageAndLocation() instead.
|
||||
*/
|
||||
explicit LocaleConfiguration( const QString& localeName /* "en_US.UTF-8" */ )
|
||||
: LocaleConfiguration( localeName, localeName )
|
||||
{
|
||||
}
|
||||
/// @brief Create a locale with language and formats separate
|
||||
/** @brief Create a locale with language and formats separate
|
||||
*
|
||||
* Consumers should use fromLanguageAndLocation() instead.
|
||||
*/
|
||||
explicit LocaleConfiguration( const QString& localeName, const QString& formatsName );
|
||||
|
||||
/// @brief Create an empty locale, with nothing set
|
||||
explicit LocaleConfiguration();
|
||||
|
||||
/** @brief Create a "sensible" locale configuration for @p language and @p countryCode
|
||||
*
|
||||
* This method applies some heuristics to pick a good locale (from the list
|
||||
* @p availableLocales), along with a good language (for instance, in
|
||||
* large countries with many languages, picking a generally used one).
|
||||
*/
|
||||
static LocaleConfiguration
|
||||
fromLanguageAndLocation( const QString& language, const QStringList& availableLocales, const QString& countryCode );
|
||||
|
||||
/// Is this an empty (default-constructed and not modified) configuration?
|
||||
bool isEmpty() const;
|
||||
|
||||
/** @brief sets lang and the BCP47 representation
|
||||
/** @brief sets language to @p localeName
|
||||
*
|
||||
* Note that the documentation how this works is in packages.conf
|
||||
* The language may be regionalized, e.g. "nl_BE". Both the language
|
||||
* (with region) and BCP47 representation (without region, lowercase)
|
||||
* are updated. The BCP47 representation is used by the packages module.
|
||||
* See also `packages.conf` for a discussion of how this is used.
|
||||
*/
|
||||
void setLanguage( const QString& localeName );
|
||||
/// Current language (including region)
|
||||
QString language() const { return m_lang; }
|
||||
|
||||
// Note that the documentation how this works is in packages.conf
|
||||
/// Current language (lowercase, BCP47 format, no region)
|
||||
QString toBcp47() const { return m_languageLocaleBcp47; }
|
||||
|
||||
QMap< QString, QString > toMap() const;
|
||||
|
||||
// These become all uppercase in locale.conf, but we keep them lowercase here to
|
||||
// avoid confusion with locale.h.
|
||||
// avoid confusion with <locale.h>, which defines (e.g.) LC_NUMERIC macro.
|
||||
QString lc_numeric, lc_time, lc_monetary, lc_paper, lc_name, lc_address, lc_telephone, lc_measurement,
|
||||
lc_identification;
|
||||
|
||||
|
@ -19,37 +19,30 @@
|
||||
|
||||
#include "LocalePage.h"
|
||||
|
||||
#include "SetTimezoneJob.h"
|
||||
#include "Config.h"
|
||||
#include "LCLocaleDialog.h"
|
||||
#include "timezonewidget/timezonewidget.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "LCLocaleDialog.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include "locale/Label.h"
|
||||
#include "locale/TimeZone.h"
|
||||
#include "utils/CalamaresUtilsGui.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/RAII.h"
|
||||
#include "utils/Retranslator.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <QFile>
|
||||
#include <QLabel>
|
||||
#include <QProcess>
|
||||
#include <QPointer>
|
||||
#include <QPushButton>
|
||||
|
||||
LocalePage::LocalePage( QWidget* parent )
|
||||
LocalePage::LocalePage( Config* config, QWidget* parent )
|
||||
: QWidget( parent )
|
||||
, m_regionList( CalamaresUtils::Locale::TZRegion::fromZoneTab() )
|
||||
, m_regionModel( std::make_unique< CalamaresUtils::Locale::CStringListModel >( m_regionList ) )
|
||||
, m_config( config )
|
||||
, m_blockTzWidgetSet( false )
|
||||
{
|
||||
QBoxLayout* mainLayout = new QVBoxLayout;
|
||||
|
||||
QBoxLayout* tzwLayout = new QHBoxLayout;
|
||||
m_tzWidget = new TimeZoneWidget( this );
|
||||
m_tzWidget = new TimeZoneWidget( config->timezoneData(), this );
|
||||
tzwLayout->addStretch();
|
||||
tzwLayout->addWidget( m_tzWidget );
|
||||
tzwLayout->addStretch();
|
||||
@ -105,9 +98,24 @@ LocalePage::LocalePage( QWidget* parent )
|
||||
setMinimumWidth( m_tzWidget->width() );
|
||||
setLayout( mainLayout );
|
||||
|
||||
// Set up the location before connecting signals, to avoid a signal
|
||||
// storm as various parts interact.
|
||||
m_regionCombo->setModel( m_config->regionModel() );
|
||||
locationChanged( m_config->currentLocation() ); // doesn't inform TZ widget
|
||||
m_tzWidget->setCurrentLocation( m_config->currentLocation() );
|
||||
|
||||
connect( config, &Config::currentLCStatusChanged, m_formatsLabel, &QLabel::setText );
|
||||
connect( config, &Config::currentLanguageStatusChanged, m_localeLabel, &QLabel::setText );
|
||||
connect( config, &Config::currentLocationChanged, m_tzWidget, &TimeZoneWidget::setCurrentLocation );
|
||||
connect( config, &Config::currentLocationChanged, this, &LocalePage::locationChanged );
|
||||
connect( m_tzWidget,
|
||||
&TimeZoneWidget::locationChanged,
|
||||
config,
|
||||
QOverload< const CalamaresUtils::Locale::TZZone* >::of( &Config::setCurrentLocation ) );
|
||||
|
||||
connect( m_regionCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::regionChanged );
|
||||
connect( m_zoneCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::zoneChanged );
|
||||
connect( m_tzWidget, &TimeZoneWidget::locationChanged, this, &LocalePage::locationChanged );
|
||||
|
||||
connect( m_localeChangeButton, &QPushButton::clicked, this, &LocalePage::changeLocale );
|
||||
connect( m_formatsChangeButton, &QPushButton::clicked, this, &LocalePage::changeFormats );
|
||||
|
||||
@ -115,10 +123,7 @@ LocalePage::LocalePage( QWidget* parent )
|
||||
}
|
||||
|
||||
|
||||
LocalePage::~LocalePage()
|
||||
{
|
||||
qDeleteAll( m_regionList );
|
||||
}
|
||||
LocalePage::~LocalePage() {}
|
||||
|
||||
|
||||
void
|
||||
@ -128,175 +133,8 @@ LocalePage::updateLocaleLabels()
|
||||
m_zoneLabel->setText( tr( "Zone:" ) );
|
||||
m_localeChangeButton->setText( tr( "&Change..." ) );
|
||||
m_formatsChangeButton->setText( tr( "&Change..." ) );
|
||||
|
||||
LocaleConfiguration lc
|
||||
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration;
|
||||
auto labels = prettyLocaleStatus( lc );
|
||||
m_localeLabel->setText( labels.first );
|
||||
m_formatsLabel->setText( labels.second );
|
||||
}
|
||||
|
||||
void
|
||||
LocalePage::init( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath )
|
||||
{
|
||||
using namespace CalamaresUtils::Locale;
|
||||
|
||||
m_regionCombo->setModel( m_regionModel.get() );
|
||||
m_regionCombo->currentIndexChanged( m_regionCombo->currentIndex() );
|
||||
|
||||
auto* region = m_regionList.find< TZRegion >( initialRegion );
|
||||
if ( region && region->zones().find< TZZone >( initialZone ) )
|
||||
{
|
||||
m_tzWidget->setCurrentLocation( initialRegion, initialZone );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tzWidget->setCurrentLocation( "America", "New_York" );
|
||||
}
|
||||
|
||||
// Some distros come with a meaningfully commented and easy to parse locale.gen,
|
||||
// and others ship a separate file /usr/share/i18n/SUPPORTED with a clean list of
|
||||
// supported locales. We first try that one, and if it doesn't exist, we fall back
|
||||
// to parsing the lines from locale.gen
|
||||
m_localeGenLines.clear();
|
||||
QFile supported( "/usr/share/i18n/SUPPORTED" );
|
||||
QByteArray ba;
|
||||
|
||||
if ( supported.exists() && supported.open( QIODevice::ReadOnly | QIODevice::Text ) )
|
||||
{
|
||||
ba = supported.readAll();
|
||||
supported.close();
|
||||
|
||||
const auto lines = ba.split( '\n' );
|
||||
for ( const QByteArray& line : lines )
|
||||
{
|
||||
m_localeGenLines.append( QString::fromLatin1( line.simplified() ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QFile localeGen( localeGenPath );
|
||||
if ( localeGen.open( QIODevice::ReadOnly | QIODevice::Text ) )
|
||||
{
|
||||
ba = localeGen.readAll();
|
||||
localeGen.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
cWarning() << "Cannot open file" << localeGenPath
|
||||
<< ". Assuming the supported languages are already built into "
|
||||
"the locale archive.";
|
||||
QProcess localeA;
|
||||
localeA.start( "locale", QStringList() << "-a" );
|
||||
localeA.waitForFinished();
|
||||
ba = localeA.readAllStandardOutput();
|
||||
}
|
||||
const auto lines = ba.split( '\n' );
|
||||
for ( const QByteArray& line : lines )
|
||||
{
|
||||
if ( line.startsWith( "## " ) || line.startsWith( "# " ) || line.simplified() == "#" )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QString lineString = QString::fromLatin1( line.simplified() );
|
||||
if ( lineString.startsWith( "#" ) )
|
||||
{
|
||||
lineString.remove( '#' );
|
||||
}
|
||||
lineString = lineString.simplified();
|
||||
|
||||
if ( lineString.isEmpty() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_localeGenLines.append( lineString );
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_localeGenLines.isEmpty() )
|
||||
{
|
||||
cWarning() << "cannot acquire a list of available locales."
|
||||
<< "The locale and localecfg modules will be broken as long as this "
|
||||
"system does not provide"
|
||||
<< "\n\t "
|
||||
<< "* a well-formed" << supported.fileName() << "\n\tOR"
|
||||
<< "* a well-formed"
|
||||
<< ( localeGenPath.isEmpty() ? QLatin1String( "/etc/locale.gen" ) : localeGenPath ) << "\n\tOR"
|
||||
<< "* a complete pre-compiled locale-gen database which allows complete locale -a output.";
|
||||
return; // something went wrong and there's nothing we can do about it.
|
||||
}
|
||||
|
||||
// Assuming we have a list of supported locales, we usually only want UTF-8 ones
|
||||
// because it's not 1995.
|
||||
auto notUtf8 = []( const QString& s ) {
|
||||
return !s.contains( "UTF-8", Qt::CaseInsensitive ) && !s.contains( "utf8", Qt::CaseInsensitive );
|
||||
};
|
||||
auto it = std::remove_if( m_localeGenLines.begin(), m_localeGenLines.end(), notUtf8 );
|
||||
m_localeGenLines.erase( it, m_localeGenLines.end() );
|
||||
|
||||
// We strip " UTF-8" from "en_US.UTF-8 UTF-8" because it's redundant redundant.
|
||||
// Also simplify whitespace.
|
||||
auto unredundant = []( QString& s ) {
|
||||
if ( s.endsWith( " UTF-8" ) )
|
||||
{
|
||||
s.chop( 6 );
|
||||
}
|
||||
s = s.simplified();
|
||||
};
|
||||
std::for_each( m_localeGenLines.begin(), m_localeGenLines.end(), unredundant );
|
||||
|
||||
updateGlobalStorage();
|
||||
}
|
||||
|
||||
std::pair< QString, QString >
|
||||
LocalePage::prettyLocaleStatus( const LocaleConfiguration& lc ) const
|
||||
{
|
||||
using CalamaresUtils::Locale::Label;
|
||||
|
||||
Label lang( lc.language(), Label::LabelFormat::AlwaysWithCountry );
|
||||
Label num( lc.lc_numeric, Label::LabelFormat::AlwaysWithCountry );
|
||||
|
||||
return std::make_pair< QString, QString >(
|
||||
tr( "The system language will be set to %1." ).arg( lang.label() ),
|
||||
tr( "The numbers and dates locale will be set to %1." ).arg( num.label() ) );
|
||||
}
|
||||
|
||||
QString
|
||||
LocalePage::prettyStatus() const
|
||||
{
|
||||
QString status;
|
||||
status += tr( "Set timezone to %1/%2.<br/>" ).arg( m_regionCombo->currentText() ).arg( m_zoneCombo->currentText() );
|
||||
|
||||
LocaleConfiguration lc
|
||||
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration;
|
||||
auto labels = prettyLocaleStatus( lc );
|
||||
status += labels.first + "<br/>";
|
||||
status += labels.second + "<br/>";
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
Calamares::JobList
|
||||
LocalePage::createJobs()
|
||||
{
|
||||
QList< Calamares::job_ptr > list;
|
||||
const CalamaresUtils::Locale::TZZone* location = m_tzWidget->currentLocation();
|
||||
|
||||
Calamares::Job* j = new SetTimezoneJob( location->region(), location->zone() );
|
||||
list.append( Calamares::job_ptr( j ) );
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
QMap< QString, QString >
|
||||
LocalePage::localesMap()
|
||||
{
|
||||
return m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().toMap()
|
||||
: m_selectedLocaleConfiguration.toMap();
|
||||
m_localeLabel->setText( m_config->currentLanguageStatus() );
|
||||
m_formatsLabel->setText( m_config->currentLCStatus() );
|
||||
}
|
||||
|
||||
|
||||
@ -304,82 +142,10 @@ void
|
||||
LocalePage::onActivate()
|
||||
{
|
||||
m_regionCombo->setFocus();
|
||||
if ( m_selectedLocaleConfiguration.isEmpty() || !m_selectedLocaleConfiguration.explicit_lang )
|
||||
{
|
||||
auto newLocale = guessLocaleConfiguration();
|
||||
m_selectedLocaleConfiguration.setLanguage( newLocale.language() );
|
||||
updateGlobalLocale();
|
||||
updateLocaleLabels();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LocaleConfiguration
|
||||
LocalePage::guessLocaleConfiguration() const
|
||||
{
|
||||
return LocaleConfiguration::fromLanguageAndLocation(
|
||||
QLocale().name(), m_localeGenLines, m_tzWidget->currentLocation()->country() );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalePage::updateGlobalLocale()
|
||||
{
|
||||
auto* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
const QString bcp47 = m_selectedLocaleConfiguration.toBcp47();
|
||||
gs->insert( "locale", bcp47 );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalePage::updateGlobalStorage()
|
||||
{
|
||||
auto* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
|
||||
const auto* location = m_tzWidget->currentLocation();
|
||||
bool locationChanged = ( location->region() != gs->value( "locationRegion" ) )
|
||||
|| ( location->zone() != gs->value( "locationZone" ) );
|
||||
|
||||
gs->insert( "locationRegion", location->region() );
|
||||
gs->insert( "locationZone", location->zone() );
|
||||
|
||||
updateGlobalLocale();
|
||||
|
||||
// If we're in chroot mode (normal install mode), then we immediately set the
|
||||
// timezone on the live system. When debugging timezones, don't bother.
|
||||
#ifndef DEBUG_TIMEZONES
|
||||
if ( locationChanged && Calamares::Settings::instance()->doChroot() )
|
||||
{
|
||||
QProcess::execute( "timedatectl", // depends on systemd
|
||||
{ "set-timezone", location->region() + '/' + location->zone() } );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Preserve those settings that have been made explicit.
|
||||
auto newLocale = guessLocaleConfiguration();
|
||||
if ( !m_selectedLocaleConfiguration.isEmpty() && m_selectedLocaleConfiguration.explicit_lang )
|
||||
{
|
||||
newLocale.setLanguage( m_selectedLocaleConfiguration.language() );
|
||||
}
|
||||
if ( !m_selectedLocaleConfiguration.isEmpty() && m_selectedLocaleConfiguration.explicit_lc )
|
||||
{
|
||||
newLocale.lc_numeric = m_selectedLocaleConfiguration.lc_numeric;
|
||||
newLocale.lc_time = m_selectedLocaleConfiguration.lc_time;
|
||||
newLocale.lc_monetary = m_selectedLocaleConfiguration.lc_monetary;
|
||||
newLocale.lc_paper = m_selectedLocaleConfiguration.lc_paper;
|
||||
newLocale.lc_name = m_selectedLocaleConfiguration.lc_name;
|
||||
newLocale.lc_address = m_selectedLocaleConfiguration.lc_address;
|
||||
newLocale.lc_telephone = m_selectedLocaleConfiguration.lc_telephone;
|
||||
newLocale.lc_measurement = m_selectedLocaleConfiguration.lc_measurement;
|
||||
newLocale.lc_identification = m_selectedLocaleConfiguration.lc_identification;
|
||||
}
|
||||
newLocale.explicit_lang = m_selectedLocaleConfiguration.explicit_lang;
|
||||
newLocale.explicit_lc = m_selectedLocaleConfiguration.explicit_lc;
|
||||
|
||||
m_selectedLocaleConfiguration = newLocale;
|
||||
updateLocaleLabels();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalePage::regionChanged( int currentIndex )
|
||||
{
|
||||
@ -388,15 +154,17 @@ LocalePage::regionChanged( int currentIndex )
|
||||
Q_UNUSED( currentIndex )
|
||||
QString selectedRegion = m_regionCombo->currentData().toString();
|
||||
|
||||
TZRegion* region = m_regionList.find< TZRegion >( selectedRegion );
|
||||
TZRegion* region = m_config->timezoneData().find< TZRegion >( selectedRegion );
|
||||
if ( !region )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_zoneCombo->blockSignals( true );
|
||||
{
|
||||
cSignalBlocker b( m_zoneCombo );
|
||||
m_zoneCombo->setModel( new CStringListModel( region->zones() ) );
|
||||
m_zoneCombo->blockSignals( false );
|
||||
}
|
||||
|
||||
m_zoneCombo->currentIndexChanged( m_zoneCombo->currentIndex() );
|
||||
}
|
||||
|
||||
@ -405,16 +173,19 @@ LocalePage::zoneChanged( int currentIndex )
|
||||
{
|
||||
Q_UNUSED( currentIndex )
|
||||
if ( !m_blockTzWidgetSet )
|
||||
m_tzWidget->setCurrentLocation( m_regionCombo->currentData().toString(),
|
||||
m_zoneCombo->currentData().toString() );
|
||||
|
||||
updateGlobalStorage();
|
||||
{
|
||||
m_config->setCurrentLocation( m_regionCombo->currentData().toString(), m_zoneCombo->currentData().toString() );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LocalePage::locationChanged( const CalamaresUtils::Locale::TZZone* location )
|
||||
{
|
||||
m_blockTzWidgetSet = true;
|
||||
if ( !location )
|
||||
{
|
||||
return;
|
||||
}
|
||||
cBoolSetter< true > b( m_blockTzWidgetSet );
|
||||
|
||||
// Set region index
|
||||
int index = m_regionCombo->findData( location->region() );
|
||||
@ -433,58 +204,35 @@ LocalePage::locationChanged( const CalamaresUtils::Locale::TZZone* location )
|
||||
}
|
||||
|
||||
m_zoneCombo->setCurrentIndex( index );
|
||||
|
||||
m_blockTzWidgetSet = false;
|
||||
|
||||
updateGlobalStorage();
|
||||
}
|
||||
|
||||
void
|
||||
LocalePage::changeLocale()
|
||||
{
|
||||
LCLocaleDialog* dlg
|
||||
= new LCLocaleDialog( m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().language()
|
||||
: m_selectedLocaleConfiguration.language(),
|
||||
m_localeGenLines,
|
||||
this );
|
||||
QPointer< LCLocaleDialog > dlg(
|
||||
new LCLocaleDialog( m_config->localeConfiguration().language(), m_config->supportedLocales(), this ) );
|
||||
dlg->exec();
|
||||
if ( dlg->result() == QDialog::Accepted && !dlg->selectedLCLocale().isEmpty() )
|
||||
if ( dlg && dlg->result() == QDialog::Accepted && !dlg->selectedLCLocale().isEmpty() )
|
||||
{
|
||||
m_selectedLocaleConfiguration.setLanguage( dlg->selectedLCLocale() );
|
||||
m_selectedLocaleConfiguration.explicit_lang = true;
|
||||
this->updateGlobalLocale();
|
||||
this->updateLocaleLabels();
|
||||
m_config->setLanguageExplicitly( dlg->selectedLCLocale() );
|
||||
updateLocaleLabels();
|
||||
}
|
||||
|
||||
dlg->deleteLater();
|
||||
delete dlg;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalePage::changeFormats()
|
||||
{
|
||||
LCLocaleDialog* dlg
|
||||
= new LCLocaleDialog( m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().lc_numeric
|
||||
: m_selectedLocaleConfiguration.lc_numeric,
|
||||
m_localeGenLines,
|
||||
this );
|
||||
QPointer< LCLocaleDialog > dlg(
|
||||
new LCLocaleDialog( m_config->localeConfiguration().lc_numeric, m_config->supportedLocales(), this ) );
|
||||
dlg->exec();
|
||||
if ( dlg->result() == QDialog::Accepted && !dlg->selectedLCLocale().isEmpty() )
|
||||
if ( dlg && dlg->result() == QDialog::Accepted && !dlg->selectedLCLocale().isEmpty() )
|
||||
{
|
||||
// TODO: improve the granularity of this setting.
|
||||
m_selectedLocaleConfiguration.lc_numeric = dlg->selectedLCLocale();
|
||||
m_selectedLocaleConfiguration.lc_time = dlg->selectedLCLocale();
|
||||
m_selectedLocaleConfiguration.lc_monetary = dlg->selectedLCLocale();
|
||||
m_selectedLocaleConfiguration.lc_paper = dlg->selectedLCLocale();
|
||||
m_selectedLocaleConfiguration.lc_name = dlg->selectedLCLocale();
|
||||
m_selectedLocaleConfiguration.lc_address = dlg->selectedLCLocale();
|
||||
m_selectedLocaleConfiguration.lc_telephone = dlg->selectedLCLocale();
|
||||
m_selectedLocaleConfiguration.lc_measurement = dlg->selectedLCLocale();
|
||||
m_selectedLocaleConfiguration.lc_identification = dlg->selectedLCLocale();
|
||||
m_selectedLocaleConfiguration.explicit_lc = true;
|
||||
|
||||
this->updateLocaleLabels();
|
||||
m_config->setLCLocaleExplicitly( dlg->selectedLCLocale() );
|
||||
updateLocaleLabels();
|
||||
}
|
||||
|
||||
dlg->deleteLater();
|
||||
delete dlg;
|
||||
}
|
||||
|
@ -32,39 +32,23 @@
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
class Config;
|
||||
class TimeZoneWidget;
|
||||
|
||||
class LocalePage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LocalePage( QWidget* parent = nullptr );
|
||||
explicit LocalePage( class Config* config, QWidget* parent = nullptr );
|
||||
virtual ~LocalePage();
|
||||
|
||||
void init( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath );
|
||||
|
||||
QString prettyStatus() const;
|
||||
|
||||
Calamares::JobList createJobs();
|
||||
|
||||
QMap< QString, QString > localesMap();
|
||||
|
||||
void onActivate();
|
||||
|
||||
private:
|
||||
LocaleConfiguration guessLocaleConfiguration() const;
|
||||
/// @brief Non-owning pointer to the ViewStep's config
|
||||
Config* m_config;
|
||||
|
||||
// For the given locale config, return two strings describing
|
||||
// the settings for language and numbers.
|
||||
std::pair< QString, QString > prettyLocaleStatus( const LocaleConfiguration& ) const;
|
||||
|
||||
/** @brief Update the GS *locale* key with the selected system language.
|
||||
*
|
||||
* This uses whatever is set in m_selectedLocaleConfiguration as the language,
|
||||
* and writes it to GS *locale* key (as a string, in BCP47 format).
|
||||
*/
|
||||
void updateGlobalLocale();
|
||||
void updateGlobalStorage();
|
||||
void updateLocaleLabels();
|
||||
|
||||
void regionChanged( int currentIndex );
|
||||
@ -73,10 +57,6 @@ private:
|
||||
void changeLocale();
|
||||
void changeFormats();
|
||||
|
||||
// Dynamically, QList< TZRegion* >
|
||||
CalamaresUtils::Locale::CStringPairList m_regionList;
|
||||
std::unique_ptr< CalamaresUtils::Locale::CStringListModel > m_regionModel;
|
||||
|
||||
TimeZoneWidget* m_tzWidget;
|
||||
QComboBox* m_regionCombo;
|
||||
QComboBox* m_zoneCombo;
|
||||
@ -88,9 +68,6 @@ private:
|
||||
QLabel* m_formatsLabel;
|
||||
QPushButton* m_formatsChangeButton;
|
||||
|
||||
LocaleConfiguration m_selectedLocaleConfiguration;
|
||||
|
||||
QStringList m_localeGenLines;
|
||||
|
||||
bool m_blockTzWidgetSet;
|
||||
};
|
||||
|
@ -34,7 +34,6 @@
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleViewStepFactory, registerPlugin< LocaleViewStep >(); )
|
||||
@ -44,7 +43,7 @@ LocaleViewStep::LocaleViewStep( QObject* parent )
|
||||
, m_widget( new QWidget() )
|
||||
, m_actualWidget( nullptr )
|
||||
, m_nextEnabled( false )
|
||||
, m_geoip( nullptr )
|
||||
, m_config( std::make_unique< Config >() )
|
||||
{
|
||||
QBoxLayout* mainLayout = new QHBoxLayout;
|
||||
m_widget->setLayout( mainLayout );
|
||||
@ -66,11 +65,11 @@ LocaleViewStep::~LocaleViewStep()
|
||||
void
|
||||
LocaleViewStep::setUpPage()
|
||||
{
|
||||
m_config->setCurrentLocation();
|
||||
if ( !m_actualWidget )
|
||||
{
|
||||
m_actualWidget = new LocalePage();
|
||||
m_actualWidget = new LocalePage( m_config.get() );
|
||||
}
|
||||
m_actualWidget->init( m_startingTimezone.first, m_startingTimezone.second, m_localeGenPath );
|
||||
m_widget->layout()->addWidget( m_actualWidget );
|
||||
|
||||
ensureSize( m_actualWidget->sizeHint() );
|
||||
@ -80,20 +79,6 @@ LocaleViewStep::setUpPage()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocaleViewStep::fetchGeoIpTimezone()
|
||||
{
|
||||
if ( m_geoip && m_geoip->isValid() )
|
||||
{
|
||||
m_startingTimezone = m_geoip->get();
|
||||
if ( !m_startingTimezone.isValid() )
|
||||
{
|
||||
cWarning() << "GeoIP lookup at" << m_geoip->url() << "failed.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
LocaleViewStep::prettyName() const
|
||||
{
|
||||
@ -104,7 +89,7 @@ LocaleViewStep::prettyName() const
|
||||
QString
|
||||
LocaleViewStep::prettyStatus() const
|
||||
{
|
||||
return m_prettyStatus;
|
||||
return m_config->prettyStatus();
|
||||
}
|
||||
|
||||
|
||||
@ -146,7 +131,7 @@ LocaleViewStep::isAtEnd() const
|
||||
Calamares::JobList
|
||||
LocaleViewStep::jobs() const
|
||||
{
|
||||
return m_jobs;
|
||||
return m_config->createJobs();
|
||||
}
|
||||
|
||||
|
||||
@ -164,83 +149,11 @@ LocaleViewStep::onActivate()
|
||||
void
|
||||
LocaleViewStep::onLeave()
|
||||
{
|
||||
if ( m_actualWidget )
|
||||
{
|
||||
m_jobs = m_actualWidget->createJobs();
|
||||
m_prettyStatus = m_actualWidget->prettyStatus();
|
||||
|
||||
auto map = m_actualWidget->localesMap();
|
||||
QVariantMap vm;
|
||||
for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
|
||||
{
|
||||
vm.insert( it.key(), it.value() );
|
||||
}
|
||||
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert( "localeConf", vm );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_jobs.clear();
|
||||
Calamares::JobQueue::instance()->globalStorage()->remove( "localeConf" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocaleViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
QString region = CalamaresUtils::getString( configurationMap, "region" );
|
||||
QString zone = CalamaresUtils::getString( configurationMap, "zone" );
|
||||
if ( !region.isEmpty() && !zone.isEmpty() )
|
||||
{
|
||||
m_startingTimezone = CalamaresUtils::GeoIP::RegionZonePair( region, zone );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_startingTimezone
|
||||
= CalamaresUtils::GeoIP::RegionZonePair( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
|
||||
}
|
||||
|
||||
m_localeGenPath = CalamaresUtils::getString( configurationMap, "localeGenPath" );
|
||||
if ( m_localeGenPath.isEmpty() )
|
||||
{
|
||||
m_localeGenPath = QStringLiteral( "/etc/locale.gen" );
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
QVariantMap geoip = CalamaresUtils::getSubMap( configurationMap, "geoip", ok );
|
||||
if ( ok )
|
||||
{
|
||||
QString url = CalamaresUtils::getString( geoip, "url" );
|
||||
QString style = CalamaresUtils::getString( geoip, "style" );
|
||||
QString selector = CalamaresUtils::getString( geoip, "selector" );
|
||||
|
||||
m_geoip = std::make_unique< CalamaresUtils::GeoIP::Handler >( style, url, selector );
|
||||
if ( !m_geoip->isValid() )
|
||||
{
|
||||
cWarning() << "GeoIP Style" << style << "is not recognized.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Calamares::RequirementsList
|
||||
LocaleViewStep::checkRequirements()
|
||||
{
|
||||
if ( m_geoip && m_geoip->isValid() )
|
||||
{
|
||||
auto& network = CalamaresUtils::Network::Manager::instance();
|
||||
if ( network.hasInternet() )
|
||||
{
|
||||
fetchGeoIpTimezone();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( network.synchronousPing( m_geoip->url() ) )
|
||||
{
|
||||
fetchGeoIpTimezone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Calamares::RequirementsList();
|
||||
m_config->setConfigurationMap( configurationMap );
|
||||
}
|
||||
|
@ -20,15 +20,11 @@
|
||||
#ifndef LOCALEVIEWSTEP_H
|
||||
#define LOCALEVIEWSTEP_H
|
||||
|
||||
#include "geoip/Handler.h"
|
||||
#include "geoip/Interface.h"
|
||||
#include "utils/PluginFactory.h"
|
||||
#include "viewpages/ViewStep.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "DllMacro.h"
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QObject>
|
||||
#include "utils/PluginFactory.h"
|
||||
#include "viewpages/ViewStep.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -60,25 +56,16 @@ public:
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
|
||||
/// @brief Do setup (returns empty list) asynchronously
|
||||
virtual Calamares::RequirementsList checkRequirements() override;
|
||||
|
||||
private slots:
|
||||
void setUpPage();
|
||||
|
||||
private:
|
||||
void fetchGeoIpTimezone();
|
||||
QWidget* m_widget;
|
||||
|
||||
LocalePage* m_actualWidget;
|
||||
bool m_nextEnabled;
|
||||
QString m_prettyStatus;
|
||||
|
||||
CalamaresUtils::GeoIP::RegionZonePair m_startingTimezone;
|
||||
QString m_localeGenPath;
|
||||
|
||||
Calamares::JobList m_jobs;
|
||||
std::unique_ptr< CalamaresUtils::GeoIP::Handler > m_geoip;
|
||||
std::unique_ptr< Config > m_config;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleViewStepFactory )
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
# This settings are used to set your default system time zone.
|
||||
# These settings are used to set your default system time zone.
|
||||
# Time zones are usually located under /usr/share/zoneinfo and
|
||||
# provided by the 'tzdata' package of your Distribution.
|
||||
#
|
||||
@ -11,20 +11,41 @@
|
||||
# the locale page can be set through keys *region* and *zone*.
|
||||
# If either is not set, defaults to America/New_York.
|
||||
#
|
||||
# Note that useSystemTimezone and GeoIP settings can change the
|
||||
# starting time zone.
|
||||
#
|
||||
region: "America"
|
||||
zone: "New_York"
|
||||
|
||||
# Instead of using *region* and *zone* specified above,
|
||||
# you can use the system's notion of the timezone, instead.
|
||||
# This can help if your system is automatically configured with
|
||||
# a sensible TZ rather than chasing a fixed default.
|
||||
#
|
||||
# The default is false.
|
||||
#
|
||||
# useSystemTimezone: true
|
||||
|
||||
# Should changing the system location (e.g. clicking around on the timezone
|
||||
# map) immediately reflect the changed timezone in the live system?
|
||||
# By default, installers (with a target system) do, and setup (e.g. OEM
|
||||
# configuration) does not, but you can switch it on here (or off, if
|
||||
# you think it's annoying in the installer).
|
||||
#
|
||||
# Note that not all systems support live adjustment.
|
||||
#
|
||||
# adjustLiveTimezone: true
|
||||
|
||||
# System locales are detected in the following order:
|
||||
#
|
||||
# - /usr/share/i18n/SUPPORTED
|
||||
# - localeGenPath (defaults to /etc/locale.gen if not set)
|
||||
# - 'locale -a' output
|
||||
# - `locale -a` output
|
||||
#
|
||||
# Enable only when your Distribution is using an
|
||||
# Enable only when your Distribution is using a
|
||||
# custom path for locale.gen
|
||||
#
|
||||
#localeGenPath: "PATH_TO/locale.gen"
|
||||
#localeGenPath: "/etc/locale.gen"
|
||||
|
||||
# GeoIP based Language settings: Leave commented out to disable GeoIP.
|
||||
#
|
||||
|
@ -4,7 +4,35 @@ $id: https://calamares.io/schemas/locale
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
"region": { type: str }
|
||||
"zone": { type: str }
|
||||
"localeGenPath": { type: string, required: true }
|
||||
"geoipUrl": { type: str }
|
||||
region: { type: string,
|
||||
enum: [
|
||||
Africa,
|
||||
America,
|
||||
Antarctica,
|
||||
Arctic,
|
||||
Asia,
|
||||
Atlantic,
|
||||
Australia,
|
||||
Europe,
|
||||
Indian,
|
||||
Pacific
|
||||
]
|
||||
}
|
||||
zone: { type: string }
|
||||
useSystemTimezone: { type: boolean, default: false }
|
||||
|
||||
adjustLiveTimezone: { type: boolean, default: true }
|
||||
|
||||
localeGenPath: { type: string }
|
||||
|
||||
# TODO: refactor, this is reused in welcome
|
||||
geoip:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
style: { type: string, enum: [ none, fixed, xml, json ] }
|
||||
url: { type: string }
|
||||
selector: { type: string }
|
||||
required: [ style, url, selector ]
|
||||
|
||||
required: [ region, zone ]
|
||||
|
@ -26,8 +26,8 @@
|
||||
|
||||
static const char* zoneNames[]
|
||||
= { "0.0", "1.0", "2.0", "3.0", "3.5", "4.0", "4.5", "5.0", "5.5", "5.75", "6.0", "6.5", "7.0",
|
||||
"8.0", "9.0", "9.5", "10.0", "10.5", "11.0", "12.0", "12.75", "13.0", "-1.0", "-2.0", "-3.0",
|
||||
"-3.5", "-4.0", "-4.5", "-5.0", "-5.5", "-6.0", "-7.0", "-8.0", "-9.0", "-9.5", "-10.0", "-11.0" };
|
||||
"8.0", "9.0", "9.5", "10.0", "10.5", "11.0", "12.0", "12.75", "13.0", "-1.0", "-2.0", "-3.0", "-3.5",
|
||||
"-4.0", "-4.5", "-5.0", "-5.5", "-6.0", "-7.0", "-8.0", "-9.0", "-9.5", "-10.0", "-11.0" };
|
||||
static_assert( TimeZoneImageList::zoneCount == ( sizeof( zoneNames ) / sizeof( zoneNames[ 0 ] ) ),
|
||||
"Incorrect number of zones" );
|
||||
|
||||
|
@ -34,9 +34,17 @@
|
||||
#define ZONE_NAME QStringLiteral( "zone" )
|
||||
#endif
|
||||
|
||||
TimeZoneWidget::TimeZoneWidget( QWidget* parent )
|
||||
static QPoint
|
||||
getLocationPosition( const CalamaresUtils::Locale::TZZone* l )
|
||||
{
|
||||
return TimeZoneImageList::getLocationPosition( l->longitude(), l->latitude() );
|
||||
}
|
||||
|
||||
|
||||
TimeZoneWidget::TimeZoneWidget( const CalamaresUtils::Locale::CStringPairList& zones, QWidget* parent )
|
||||
: QWidget( parent )
|
||||
, timeZoneImages( TimeZoneImageList::fromQRC() )
|
||||
, m_zonesData( zones )
|
||||
{
|
||||
setMouseTracking( false );
|
||||
setCursor( Qt::PointingHandCursor );
|
||||
@ -57,27 +65,13 @@ TimeZoneWidget::TimeZoneWidget( QWidget* parent )
|
||||
|
||||
|
||||
void
|
||||
TimeZoneWidget::setCurrentLocation( QString regionName, QString zoneName )
|
||||
TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
|
||||
{
|
||||
using namespace CalamaresUtils::Locale;
|
||||
const auto& regions = TZRegion::fromZoneTab();
|
||||
auto* region = regions.find< TZRegion >( regionName );
|
||||
if ( !region )
|
||||
if ( location == m_currentLocation )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto* zone = region->zones().find< TZZone >( zoneName );
|
||||
if ( zone )
|
||||
{
|
||||
setCurrentLocation( zone );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
|
||||
{
|
||||
m_currentLocation = location;
|
||||
|
||||
// Set zone
|
||||
@ -93,7 +87,6 @@ TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* locati
|
||||
|
||||
// Repaint widget
|
||||
repaint();
|
||||
emit locationChanged( m_currentLocation );
|
||||
}
|
||||
|
||||
|
||||
@ -101,11 +94,18 @@ TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* locati
|
||||
//### Private
|
||||
//###
|
||||
|
||||
struct PainterEnder
|
||||
{
|
||||
QPainter& p;
|
||||
~PainterEnder() { p.end(); }
|
||||
};
|
||||
|
||||
void
|
||||
TimeZoneWidget::paintEvent( QPaintEvent* )
|
||||
{
|
||||
QFontMetrics fontMetrics( font );
|
||||
QPainter painter( this );
|
||||
PainterEnder painter_end { painter };
|
||||
|
||||
painter.setRenderHint( QPainter::Antialiasing );
|
||||
painter.setFont( font );
|
||||
@ -116,6 +116,11 @@ TimeZoneWidget::paintEvent( QPaintEvent* )
|
||||
// Draw zone image
|
||||
painter.drawImage( 0, 0, currentZoneImage );
|
||||
|
||||
if ( !m_currentLocation )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TIMEZONES
|
||||
QPoint point = getLocationPosition( m_currentLocation );
|
||||
// Draw latitude lines
|
||||
@ -175,8 +180,6 @@ TimeZoneWidget::paintEvent( QPaintEvent* )
|
||||
painter.setPen( Qt::white );
|
||||
painter.drawText( rect.x() + 5, rect.bottom() - 4, m_currentLocation ? m_currentLocation->tr() : QString() );
|
||||
#endif
|
||||
|
||||
painter.end();
|
||||
}
|
||||
|
||||
|
||||
@ -194,7 +197,7 @@ TimeZoneWidget::mousePressEvent( QMouseEvent* event )
|
||||
|
||||
using namespace CalamaresUtils::Locale;
|
||||
const TZZone* closest = nullptr;
|
||||
for ( const auto* region_p : TZRegion::fromZoneTab() )
|
||||
for ( const auto* region_p : m_zonesData )
|
||||
{
|
||||
const auto* region = dynamic_cast< const TZRegion* >( region_p );
|
||||
if ( region )
|
||||
@ -222,6 +225,6 @@ TimeZoneWidget::mousePressEvent( QMouseEvent* event )
|
||||
// Set zone image and repaint widget
|
||||
setCurrentLocation( closest );
|
||||
// Emit signal
|
||||
emit locationChanged( m_currentLocation );
|
||||
emit locationChanged( closest );
|
||||
}
|
||||
}
|
||||
|
@ -31,32 +31,48 @@
|
||||
#include <QFont>
|
||||
#include <QWidget>
|
||||
|
||||
/** @brief The TimeZoneWidget shows a map and reports where clicks happen
|
||||
*
|
||||
* This widget shows a map (unspecified whether it's a whole world map
|
||||
* or can show regionsvia some kind of internal state). Mouse clicks are
|
||||
* translated into timezone locations (e.g. the zone for America/New_York).
|
||||
*
|
||||
* The current location can be changed programmatically, by name
|
||||
* or through a pointer to a location. If a pointer is used, take care
|
||||
* that the pointer is to a zone in the same model as used by the
|
||||
* widget.
|
||||
*
|
||||
* When a location is chosen -- by mouse click or programmatically --
|
||||
* the locationChanged() signal is emitted with the new location.
|
||||
*
|
||||
* NOTE: the widget currently uses the globally cached TZRegion::fromZoneTab()
|
||||
*/
|
||||
class TimeZoneWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using TZZone = CalamaresUtils::Locale::TZZone;
|
||||
|
||||
explicit TimeZoneWidget( QWidget* parent = nullptr );
|
||||
explicit TimeZoneWidget( const CalamaresUtils::Locale::CStringPairList& zones, QWidget* parent = nullptr );
|
||||
|
||||
void setCurrentLocation( QString region, QString zone );
|
||||
public Q_SLOTS:
|
||||
/** @brief Sets a location by pointer
|
||||
*
|
||||
* Pointer should be within the same model as the widget uses.
|
||||
*/
|
||||
void setCurrentLocation( const TZZone* location );
|
||||
const TZZone* currentLocation() { return m_currentLocation; }
|
||||
|
||||
|
||||
signals:
|
||||
/** @brief The location has changed by mouse click */
|
||||
void locationChanged( const TZZone* location );
|
||||
|
||||
private:
|
||||
QFont font;
|
||||
QImage background, pin, currentZoneImage;
|
||||
TimeZoneImageList timeZoneImages;
|
||||
const TZZone* m_currentLocation = nullptr; // Not owned by me
|
||||
|
||||
QPoint getLocationPosition( const TZZone* l )
|
||||
{
|
||||
return timeZoneImages.getLocationPosition( l->longitude(), l->latitude() );
|
||||
}
|
||||
const CalamaresUtils::Locale::CStringPairList& m_zonesData;
|
||||
const TZZone* m_currentLocation = nullptr; // Not owned by me
|
||||
|
||||
void paintEvent( QPaintEvent* event );
|
||||
void mousePressEvent( QMouseEvent* event );
|
||||
|
@ -19,74 +19,20 @@
|
||||
|
||||
#include "LocaleQmlViewStep.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
|
||||
#include "geoip/Handler.h"
|
||||
#include "network/Manager.h"
|
||||
#include "utils/CalamaresUtilsGui.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
#include "utils/Yaml.h"
|
||||
|
||||
#include "Branding.h"
|
||||
#include "modulesystem/ModuleManager.h"
|
||||
#include <QQmlEngine>
|
||||
#include <QFutureWatcher>
|
||||
#include <QPixmap>
|
||||
#include <QVariant>
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleQmlViewStepFactory, registerPlugin< LocaleQmlViewStep >(); )
|
||||
|
||||
LocaleQmlViewStep::LocaleQmlViewStep( QObject* parent )
|
||||
: Calamares::QmlViewStep( parent )
|
||||
, m_config( new Config( this ) )
|
||||
, m_nextEnabled( false )
|
||||
, m_geoip( nullptr )
|
||||
: Calamares::QmlViewStep( parent )
|
||||
, m_config( std::make_unique< Config >( this ) )
|
||||
{
|
||||
emit nextStatusChanged( m_nextEnabled );
|
||||
}
|
||||
|
||||
QObject*
|
||||
LocaleQmlViewStep::getConfig()
|
||||
{
|
||||
return m_config;
|
||||
}
|
||||
|
||||
void
|
||||
LocaleQmlViewStep::fetchGeoIpTimezone()
|
||||
{
|
||||
if ( m_geoip && m_geoip->isValid() )
|
||||
{
|
||||
m_startingTimezone = m_geoip->get();
|
||||
if ( !m_startingTimezone.isValid() )
|
||||
{
|
||||
cWarning() << "GeoIP lookup at" << m_geoip->url() << "failed.";
|
||||
}
|
||||
}
|
||||
|
||||
m_config->setLocaleInfo(m_startingTimezone.first, m_startingTimezone.second, m_localeGenPath);
|
||||
}
|
||||
|
||||
Calamares::RequirementsList LocaleQmlViewStep::checkRequirements()
|
||||
{
|
||||
if ( m_geoip && m_geoip->isValid() )
|
||||
{
|
||||
auto& network = CalamaresUtils::Network::Manager::instance();
|
||||
if ( network.hasInternet() )
|
||||
{
|
||||
fetchGeoIpTimezone();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( network.synchronousPing( m_geoip->url() ) )
|
||||
{
|
||||
fetchGeoIpTimezone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Calamares::RequirementsList();
|
||||
return m_config.get();
|
||||
}
|
||||
|
||||
QString
|
||||
@ -95,17 +41,21 @@ LocaleQmlViewStep::prettyName() const
|
||||
return tr( "Location" );
|
||||
}
|
||||
|
||||
QString
|
||||
LocaleQmlViewStep::prettyStatus() const
|
||||
{
|
||||
return m_config->prettyStatus();
|
||||
}
|
||||
|
||||
bool
|
||||
LocaleQmlViewStep::isNextEnabled() const
|
||||
{
|
||||
// TODO: should return true
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
LocaleQmlViewStep::isBackEnabled() const
|
||||
{
|
||||
// TODO: should return true (it's weird that you are not allowed to have welcome *after* anything
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -113,7 +63,6 @@ LocaleQmlViewStep::isBackEnabled() const
|
||||
bool
|
||||
LocaleQmlViewStep::isAtBeginning() const
|
||||
{
|
||||
// TODO: adjust to "pages" in the QML
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -121,79 +70,18 @@ LocaleQmlViewStep::isAtBeginning() const
|
||||
bool
|
||||
LocaleQmlViewStep::isAtEnd() const
|
||||
{
|
||||
// TODO: adjust to "pages" in the QML
|
||||
return true;
|
||||
}
|
||||
|
||||
Calamares::JobList
|
||||
LocaleQmlViewStep::jobs() const
|
||||
{
|
||||
return m_jobs;
|
||||
return m_config->createJobs();
|
||||
}
|
||||
|
||||
void LocaleQmlViewStep::onActivate()
|
||||
void
|
||||
LocaleQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
// TODO no sure if it is needed at all or for the abstract class to start something
|
||||
}
|
||||
|
||||
void LocaleQmlViewStep::onLeave()
|
||||
{
|
||||
if ( true )
|
||||
{
|
||||
m_jobs = m_config->createJobs();
|
||||
// m_prettyStatus = m_actualWidget->prettyStatus();
|
||||
|
||||
auto map = m_config->localesMap();
|
||||
QVariantMap vm;
|
||||
for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
|
||||
{
|
||||
vm.insert( it.key(), it.value() );
|
||||
}
|
||||
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert( "localeConf", vm );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_jobs.clear();
|
||||
Calamares::JobQueue::instance()->globalStorage()->remove( "localeConf" );
|
||||
}
|
||||
}
|
||||
|
||||
void LocaleQmlViewStep::setConfigurationMap(const QVariantMap& configurationMap)
|
||||
{
|
||||
QString region = CalamaresUtils::getString( configurationMap, "region" );
|
||||
QString zone = CalamaresUtils::getString( configurationMap, "zone" );
|
||||
if ( !region.isEmpty() && !zone.isEmpty() )
|
||||
{
|
||||
m_startingTimezone = CalamaresUtils::GeoIP::RegionZonePair( region, zone );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_startingTimezone
|
||||
= CalamaresUtils::GeoIP::RegionZonePair( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
|
||||
}
|
||||
|
||||
m_localeGenPath = CalamaresUtils::getString( configurationMap, "localeGenPath" );
|
||||
if ( m_localeGenPath.isEmpty() )
|
||||
{
|
||||
m_localeGenPath = QStringLiteral( "/etc/locale.gen" );
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
QVariantMap geoip = CalamaresUtils::getSubMap( configurationMap, "geoip", ok );
|
||||
if ( ok )
|
||||
{
|
||||
QString url = CalamaresUtils::getString( geoip, "url" );
|
||||
QString style = CalamaresUtils::getString( geoip, "style" );
|
||||
QString selector = CalamaresUtils::getString( geoip, "selector" );
|
||||
|
||||
m_geoip = std::make_unique< CalamaresUtils::GeoIP::Handler >( style, url, selector );
|
||||
if ( !m_geoip->isValid() )
|
||||
{
|
||||
cWarning() << "GeoIP Style" << style << "is not recognized.";
|
||||
}
|
||||
}
|
||||
|
||||
checkRequirements();
|
||||
m_config->setConfigurationMap( configurationMap );
|
||||
Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last
|
||||
}
|
||||
|
@ -20,14 +20,10 @@
|
||||
#define LOCALE_QMLVIEWSTEP_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "geoip/Handler.h"
|
||||
#include "geoip/Interface.h"
|
||||
|
||||
#include "DllMacro.h"
|
||||
#include "utils/PluginFactory.h"
|
||||
#include "viewpages/QmlViewStep.h"
|
||||
#include <DllMacro.h>
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -39,6 +35,7 @@ public:
|
||||
explicit LocaleQmlViewStep( QObject* parent = nullptr );
|
||||
|
||||
QString prettyName() const override;
|
||||
QString prettyStatus() const override;
|
||||
|
||||
bool isNextEnabled() const override;
|
||||
bool isBackEnabled() const override;
|
||||
@ -47,28 +44,12 @@ public:
|
||||
bool isAtEnd() const override;
|
||||
|
||||
Calamares::JobList jobs() const override;
|
||||
void onActivate() override;
|
||||
void onLeave() override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
QObject* getConfig() override;
|
||||
|
||||
virtual Calamares::RequirementsList checkRequirements() override;
|
||||
|
||||
private:
|
||||
// TODO: a generic QML viewstep should return a config object from a method
|
||||
Config *m_config;
|
||||
|
||||
bool m_nextEnabled;
|
||||
QString m_prettyStatus;
|
||||
|
||||
CalamaresUtils::GeoIP::RegionZonePair m_startingTimezone;
|
||||
QString m_localeGenPath;
|
||||
|
||||
Calamares::JobList m_jobs;
|
||||
std::unique_ptr< CalamaresUtils::GeoIP::Handler > m_geoip;
|
||||
|
||||
void fetchGeoIpTimezone();
|
||||
std::unique_ptr< Config > m_config;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleQmlViewStepFactory )
|
||||
|
@ -74,6 +74,7 @@ Column {
|
||||
var tz2 = responseJSON.timezoneId
|
||||
|
||||
tzText.text = "Timezone: " + tz2
|
||||
config.setCurrentLocation(tz2)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,10 +32,6 @@ Item {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
//Needs to come from Locale config
|
||||
property var confLang: "en_US.UTF8"
|
||||
property var confLocale: "nl_NL.UTF8"
|
||||
|
||||
Rectangle {
|
||||
id: textArea
|
||||
x: 28
|
||||
@ -57,7 +53,7 @@ Item {
|
||||
width: 240
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("<h1>Languages</h1> </br>
|
||||
The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>.").arg(confLang)
|
||||
The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>.").arg(config.currentLanguageCode)
|
||||
font.pointSize: 10
|
||||
}
|
||||
}
|
||||
@ -76,10 +72,9 @@ Item {
|
||||
id: list1
|
||||
focus: true
|
||||
|
||||
// bogus entries, need to come from Locale config
|
||||
model: ["en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8"]
|
||||
model: config.supportedLocales
|
||||
|
||||
currentIndex: 1
|
||||
currentIndex: -1
|
||||
highlight: Rectangle {
|
||||
color: Kirigami.Theme.highlightColor
|
||||
}
|
||||
@ -95,17 +90,17 @@ Item {
|
||||
}
|
||||
onClicked: {
|
||||
list1.currentIndex = index
|
||||
confLang = list1.currentIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
onCurrentItemChanged: { config.currentLanguageCode = model[currentIndex] } /* This works because model is a stringlist */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: i18n
|
||||
id: lc_numeric
|
||||
x: 430
|
||||
y: 40
|
||||
|
||||
@ -118,7 +113,7 @@ Item {
|
||||
width: 240
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("<h1>Locales</h1> </br>
|
||||
The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>.").arg(confLocale)
|
||||
The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>.").arg(config.currentLCCode)
|
||||
font.pointSize: 10
|
||||
}
|
||||
}
|
||||
@ -138,10 +133,9 @@ Item {
|
||||
width: 180; height: 200
|
||||
focus: true
|
||||
|
||||
// bogus entries, need to come from Locale config
|
||||
model: ["en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8"]
|
||||
model: config.supportedLocales
|
||||
|
||||
currentIndex: 2
|
||||
currentIndex: -1
|
||||
highlight: Rectangle {
|
||||
color: Kirigami.Theme.highlightColor
|
||||
}
|
||||
@ -154,11 +148,10 @@ Item {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
list2.currentIndex = index
|
||||
confLocale = list1.currentIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
onCurrentItemChanged: console.debug(currentIndex)
|
||||
onCurrentItemChanged: { config.currentLCCode = model[currentIndex]; } /* This works because model is a stringlist */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,43 +29,13 @@ Page {
|
||||
width: 800
|
||||
height: 550
|
||||
|
||||
property var confLang: "American English"
|
||||
property var confLocale: "Nederland"
|
||||
//Needs to come from .conf/geoip
|
||||
property var hasInternet: true
|
||||
|
||||
function getInt(format) {
|
||||
var requestURL = "https://example.org/";
|
||||
var xhr = new XMLHttpRequest;
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
|
||||
if (xhr.status !== 200) {
|
||||
console.log("Disconnected!!");
|
||||
var connected = false
|
||||
hasInternet = connected
|
||||
return;
|
||||
}
|
||||
|
||||
else {
|
||||
console.log("Connected!!");
|
||||
}
|
||||
}
|
||||
}
|
||||
xhr.open("GET", requestURL, true);
|
||||
xhr.send();
|
||||
}
|
||||
Component.onCompleted: {
|
||||
getInt();
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: image
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
height: parent.height / 1.28
|
||||
source: (hasInternet) ? "Map.qml" : "Offline.qml"
|
||||
// Network is in io.calamares.core
|
||||
source: Network.hasInternet ? "Map.qml" : "Offline.qml"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@ -95,7 +65,7 @@ Page {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("System language set to %1").arg(confLang)
|
||||
text: config.currentLanguageStatus
|
||||
}
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
@ -103,7 +73,7 @@ Page {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Numbers and dates locale set to %1").arg(confLocale)
|
||||
text: config.currentLCStatus
|
||||
}
|
||||
}
|
||||
Button {
|
||||
|
@ -15,8 +15,6 @@ dbus: true
|
||||
# Whether /var/lib/dbus/machine-id should be a symlink to /etc/machine-id
|
||||
# (ignored if dbus is false, or if there is no /etc/machine-id to point to).
|
||||
dbus-symlink: true
|
||||
# this is a deprecated form of *dbus-symlink*
|
||||
symlink: true
|
||||
|
||||
# Whether to create an entropy file
|
||||
entropy: false
|
||||
|
@ -4,6 +4,10 @@ $id: https://calamares.io/schemas/machineid
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
"systemd": { type: boolean, default: true }
|
||||
"dbus": { type: boolean, default: true }
|
||||
"symlink": { type: boolean, default: true }
|
||||
systemd: { type: boolean, default: true }
|
||||
dbus: { type: boolean, default: true }
|
||||
"dbus-symlink": { type: boolean, default: true }
|
||||
entropy: { type: boolean, default: false }
|
||||
"entropy-copy": { type: boolean, default: false }
|
||||
# Deprecated properties
|
||||
symlink: { type: boolean, default: true }
|
||||
|
@ -9,8 +9,6 @@ calamares_add_plugin( netinstall
|
||||
PackageModel.cpp
|
||||
UI
|
||||
page_netinst.ui
|
||||
RESOURCES
|
||||
netinstall.qrc
|
||||
LINK_PRIVATE_LIBRARIES
|
||||
calamaresui
|
||||
Qt5::Network
|
||||
|
@ -332,9 +332,7 @@ ChoicePage::setupChoices()
|
||||
|
||||
CALAMARES_RETRANSLATE(
|
||||
m_somethingElseButton->setText( tr( "<strong>Manual partitioning</strong><br/>"
|
||||
"You can create or resize partitions yourself."
|
||||
" Having a GPT partition table and <strong>fat32 512Mb /boot partition "
|
||||
"is a must for UEFI installs</strong>, either use an existing without formatting or create one." ) );
|
||||
"You can create or resize partitions yourself." ) );
|
||||
updateSwapChoicesTr( m_eraseSwapChoiceComboBox );
|
||||
)
|
||||
}
|
||||
@ -1444,46 +1442,75 @@ ChoicePage::currentChoice() const
|
||||
return m_choice;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ChoicePage::updateNextEnabled()
|
||||
bool
|
||||
ChoicePage::calculateNextEnabled() const
|
||||
{
|
||||
bool enabled = false;
|
||||
|
||||
auto sm_p = m_beforePartitionBarsView ? m_beforePartitionBarsView->selectionModel() : nullptr;
|
||||
|
||||
switch ( m_choice )
|
||||
{
|
||||
case NoChoice:
|
||||
enabled = false;
|
||||
break;
|
||||
cDebug() << "No partitioning choice";
|
||||
return false;
|
||||
case Replace:
|
||||
case Alongside:
|
||||
enabled = sm_p && sm_p->currentIndex().isValid();
|
||||
if ( !( sm_p && sm_p->currentIndex().isValid() ) )
|
||||
{
|
||||
cDebug() << "No partition selected";
|
||||
return false;
|
||||
}
|
||||
enabled = true;
|
||||
break;
|
||||
case Erase:
|
||||
case Manual:
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
if ( m_isEfi &&
|
||||
( m_choice == Alongside ||
|
||||
m_choice == Replace ) )
|
||||
if (!enabled)
|
||||
{
|
||||
if ( m_core->efiSystemPartitions().count() == 0 )
|
||||
enabled = false;
|
||||
cDebug() << "No valid choice made";
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_choice != Manual &&
|
||||
m_encryptWidget->isVisible() &&
|
||||
m_encryptWidget->state() == EncryptWidget::Encryption::Unconfirmed )
|
||||
enabled = false;
|
||||
|
||||
if ( enabled == m_nextEnabled )
|
||||
return;
|
||||
if ( m_isEfi && ( m_choice == Alongside || m_choice == Replace ) )
|
||||
{
|
||||
if ( m_core->efiSystemPartitions().count() == 0 )
|
||||
{
|
||||
cDebug() << "No EFI partition for alongside or replace";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_choice != Manual && m_encryptWidget->isVisible() )
|
||||
{
|
||||
switch ( m_encryptWidget->state() )
|
||||
{
|
||||
case EncryptWidget::Encryption::Unconfirmed:
|
||||
cDebug() << "No passphrase provided";
|
||||
return false;
|
||||
case EncryptWidget::Encryption::Disabled:
|
||||
case EncryptWidget::Encryption::Confirmed:
|
||||
// Checkbox not checked, **or** passphrases match
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ChoicePage::updateNextEnabled()
|
||||
{
|
||||
bool enabled = calculateNextEnabled();
|
||||
|
||||
if ( enabled != m_nextEnabled )
|
||||
{
|
||||
m_nextEnabled = enabled;
|
||||
emit nextStatusChanged( enabled );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -126,6 +126,7 @@ private slots:
|
||||
void onEraseSwapChoiceChanged();
|
||||
|
||||
private:
|
||||
bool calculateNextEnabled() const;
|
||||
void updateNextEnabled();
|
||||
void setupChoices();
|
||||
QComboBox* createBootloaderComboBox( QWidget* parentButton );
|
||||
|
@ -91,9 +91,39 @@ EncryptWidget::retranslate()
|
||||
}
|
||||
|
||||
|
||||
///@brief Give @p label the @p pixmap from the standard-pixmaps
|
||||
static void
|
||||
applyPixmap( QLabel* label, CalamaresUtils::ImageType pixmap )
|
||||
{
|
||||
label->setFixedWidth( label->height() );
|
||||
label->setPixmap( CalamaresUtils::defaultPixmap( pixmap, CalamaresUtils::Original, label->size() ) );
|
||||
}
|
||||
|
||||
void
|
||||
EncryptWidget::updateState()
|
||||
{
|
||||
if ( m_ui->m_passphraseLineEdit->isVisible() )
|
||||
{
|
||||
QString p1 = m_ui->m_passphraseLineEdit->text();
|
||||
QString p2 = m_ui->m_confirmLineEdit->text();
|
||||
|
||||
if ( p1.isEmpty() && p2.isEmpty() )
|
||||
{
|
||||
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusWarning );
|
||||
m_ui->m_iconLabel->setToolTip( tr( "Please enter the same passphrase in both boxes." ) );
|
||||
}
|
||||
else if ( p1 == p2 )
|
||||
{
|
||||
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusOk );
|
||||
m_ui->m_iconLabel->setToolTip( QString() );
|
||||
}
|
||||
else
|
||||
{
|
||||
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusError );
|
||||
m_ui->m_iconLabel->setToolTip( tr( "Please enter the same passphrase in both boxes." ) );
|
||||
}
|
||||
}
|
||||
|
||||
Encryption newState;
|
||||
if ( m_ui->m_encryptCheckBox->isChecked() )
|
||||
{
|
||||
@ -119,14 +149,6 @@ EncryptWidget::updateState()
|
||||
}
|
||||
}
|
||||
|
||||
///@brief Give @p label the @p pixmap from the standard-pixmaps
|
||||
static void
|
||||
applyPixmap( QLabel* label, CalamaresUtils::ImageType pixmap )
|
||||
{
|
||||
label->setFixedWidth( label->height() );
|
||||
label->setPixmap( CalamaresUtils::defaultPixmap( pixmap, CalamaresUtils::Original, label->size() ) );
|
||||
}
|
||||
|
||||
void
|
||||
EncryptWidget::onPassphraseEdited()
|
||||
{
|
||||
@ -135,24 +157,6 @@ EncryptWidget::onPassphraseEdited()
|
||||
m_ui->m_iconLabel->show();
|
||||
}
|
||||
|
||||
QString p1 = m_ui->m_passphraseLineEdit->text();
|
||||
QString p2 = m_ui->m_confirmLineEdit->text();
|
||||
|
||||
m_ui->m_iconLabel->setToolTip( QString() );
|
||||
if ( p1.isEmpty() && p2.isEmpty() )
|
||||
{
|
||||
m_ui->m_iconLabel->clear();
|
||||
}
|
||||
else if ( p1 == p2 )
|
||||
{
|
||||
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::Yes );
|
||||
}
|
||||
else
|
||||
{
|
||||
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::No );
|
||||
m_ui->m_iconLabel->setToolTip( tr( "Please enter the same passphrase in both boxes." ) );
|
||||
}
|
||||
|
||||
updateState();
|
||||
}
|
||||
|
||||
|
@ -107,8 +107,8 @@ PartitionViewStep::continueLoading()
|
||||
m_waitingWidget->deleteLater();
|
||||
m_waitingWidget = nullptr;
|
||||
|
||||
connect( m_core, &PartitionCoreModule::hasRootMountPointChanged, this, &PartitionViewStep::nextStatusChanged );
|
||||
connect( m_choicePage, &ChoicePage::nextStatusChanged, this, &PartitionViewStep::nextStatusChanged );
|
||||
connect( m_core, &PartitionCoreModule::hasRootMountPointChanged, this, &PartitionViewStep::nextPossiblyChanged );
|
||||
connect( m_choicePage, &ChoicePage::nextStatusChanged, this, &PartitionViewStep::nextPossiblyChanged );
|
||||
}
|
||||
|
||||
|
||||
@ -348,6 +348,11 @@ PartitionViewStep::isNextEnabled() const
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
PartitionViewStep::nextPossiblyChanged(bool)
|
||||
{
|
||||
emit nextStatusChanged( isNextEnabled() );
|
||||
}
|
||||
|
||||
bool
|
||||
PartitionViewStep::isBackEnabled() const
|
||||
|
@ -78,6 +78,9 @@ private:
|
||||
void initPartitionCoreModule();
|
||||
void continueLoading();
|
||||
|
||||
/// "slot" for changes to next-status from the KPMCore and ChoicePage
|
||||
void nextPossiblyChanged( bool );
|
||||
|
||||
PartitionCoreModule* m_core;
|
||||
QStackedWidget* m_widget;
|
||||
ChoicePage* m_choicePage;
|
||||
|
@ -4,7 +4,6 @@ calamares_add_plugin( preservefiles
|
||||
TYPE job
|
||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||
SOURCES
|
||||
permissions.cpp
|
||||
PreserveFiles.cpp
|
||||
LINK_PRIVATE_LIBRARIES
|
||||
calamares
|
||||
|
@ -1,39 +1,28 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright 2018, Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
* 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 "PreserveFiles.h"
|
||||
|
||||
#include "permissions.h"
|
||||
|
||||
#include "CalamaresVersion.h"
|
||||
#include "JobQueue.h"
|
||||
#include "GlobalStorage.h"
|
||||
|
||||
#include "JobQueue.h"
|
||||
#include "utils/CalamaresUtilsSystem.h"
|
||||
#include "utils/CommandList.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Permissions.h"
|
||||
#include "utils/Units.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
using CalamaresUtils::operator""_MiB;
|
||||
|
||||
QString targetPrefix()
|
||||
QString
|
||||
targetPrefix()
|
||||
{
|
||||
if ( CalamaresUtils::System::instance()->doChroot() )
|
||||
{
|
||||
@ -42,10 +31,14 @@ QString targetPrefix()
|
||||
{
|
||||
QString r = gs->value( "rootMountPoint" ).toString();
|
||||
if ( !r.isEmpty() )
|
||||
{
|
||||
return r;
|
||||
}
|
||||
else
|
||||
{
|
||||
cDebug() << "RootMountPoint is empty";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cDebug() << "No rootMountPoint defined, preserving files to '/'";
|
||||
@ -55,16 +48,21 @@ QString targetPrefix()
|
||||
return QLatin1String( "/" );
|
||||
}
|
||||
|
||||
QString atReplacements( QString s )
|
||||
QString
|
||||
atReplacements( QString s )
|
||||
{
|
||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
QString root( "/" );
|
||||
QString user;
|
||||
|
||||
if ( gs && gs->contains( "rootMountPoint" ) )
|
||||
{
|
||||
root = gs->value( "rootMountPoint" ).toString();
|
||||
}
|
||||
if ( gs && gs->contains( "username" ) )
|
||||
{
|
||||
user = gs->value( "username" ).toString();
|
||||
}
|
||||
|
||||
return s.replace( "@@ROOT@@", root ).replace( "@@USER@@", user );
|
||||
}
|
||||
@ -74,9 +72,7 @@ PreserveFiles::PreserveFiles( QObject* parent )
|
||||
{
|
||||
}
|
||||
|
||||
PreserveFiles::~PreserveFiles()
|
||||
{
|
||||
}
|
||||
PreserveFiles::~PreserveFiles() {}
|
||||
|
||||
QString
|
||||
PreserveFiles::prettyName() const
|
||||
@ -107,8 +103,7 @@ copy_file( const QString& source, const QString& dest )
|
||||
{
|
||||
b = sourcef.read( 1_MiB );
|
||||
destf.write( b );
|
||||
}
|
||||
while ( b.count() > 0 );
|
||||
} while ( b.count() > 0 );
|
||||
|
||||
sourcef.close();
|
||||
destf.close();
|
||||
@ -116,14 +111,19 @@ copy_file( const QString& source, const QString& dest )
|
||||
return true;
|
||||
}
|
||||
|
||||
Calamares::JobResult PreserveFiles::exec()
|
||||
Calamares::JobResult
|
||||
PreserveFiles::exec()
|
||||
{
|
||||
if ( m_items.isEmpty() )
|
||||
{
|
||||
return Calamares::JobResult::error( tr( "No files configured to save for later." ) );
|
||||
}
|
||||
|
||||
QString prefix = targetPrefix();
|
||||
if ( !prefix.endsWith( '/' ) )
|
||||
{
|
||||
prefix.append( '/' );
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for ( const auto& it : m_items )
|
||||
@ -133,37 +133,34 @@ Calamares::JobResult PreserveFiles::exec()
|
||||
QString dest = prefix + bare_dest;
|
||||
|
||||
if ( it.type == ItemType::Log )
|
||||
{
|
||||
source = Logger::logFile();
|
||||
}
|
||||
if ( it.type == ItemType::Config )
|
||||
{
|
||||
if ( Calamares::JobQueue::instance()->globalStorage()->save( dest ) )
|
||||
{
|
||||
cWarning() << "Could not write config for" << dest;
|
||||
}
|
||||
else
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
else if ( source.isEmpty() )
|
||||
{
|
||||
cWarning() << "Skipping unnamed source file for" << dest;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( copy_file( source, dest ) )
|
||||
{
|
||||
if ( it.perm.isValid() )
|
||||
{
|
||||
auto s_p = CalamaresUtils::System::instance();
|
||||
|
||||
int r;
|
||||
|
||||
r = s_p->targetEnvCall( QStringList{ "chown", it.perm.username(), bare_dest } );
|
||||
if ( r )
|
||||
cWarning() << "Could not chown target" << bare_dest;
|
||||
|
||||
r = s_p->targetEnvCall( QStringList{ "chgrp", it.perm.group(), bare_dest } );
|
||||
if ( r )
|
||||
cWarning() << "Could not chgrp target" << bare_dest;
|
||||
|
||||
r = s_p->targetEnvCall( QStringList{ "chmod", it.perm.octal(), bare_dest } );
|
||||
if ( r )
|
||||
cWarning() << "Could not chmod target" << bare_dest;
|
||||
if ( !it.perm.apply( CalamaresUtils::System::instance()->targetPath( bare_dest ) ) )
|
||||
{
|
||||
cWarning() << "Could not set attributes of" << bare_dest;
|
||||
}
|
||||
}
|
||||
|
||||
++count;
|
||||
@ -171,12 +168,13 @@ Calamares::JobResult PreserveFiles::exec()
|
||||
}
|
||||
}
|
||||
|
||||
return count == m_items.count() ?
|
||||
Calamares::JobResult::ok() :
|
||||
Calamares::JobResult::error( tr( "Not all of the configured files could be preserved." ) );
|
||||
return count == m_items.count()
|
||||
? Calamares::JobResult::ok()
|
||||
: Calamares::JobResult::error( tr( "Not all of the configured files could be preserved." ) );
|
||||
}
|
||||
|
||||
void PreserveFiles::setConfigurationMap(const QVariantMap& configurationMap)
|
||||
void
|
||||
PreserveFiles::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
auto files = configurationMap[ "files" ];
|
||||
if ( !files.isValid() )
|
||||
@ -193,7 +191,9 @@ void PreserveFiles::setConfigurationMap(const QVariantMap& configurationMap)
|
||||
|
||||
QString defaultPermissions = configurationMap[ "perm" ].toString();
|
||||
if ( defaultPermissions.isEmpty() )
|
||||
{
|
||||
defaultPermissions = QStringLiteral( "root:root:0400" );
|
||||
}
|
||||
|
||||
QVariantList l = files.toList();
|
||||
unsigned int c = 0;
|
||||
@ -203,22 +203,23 @@ void PreserveFiles::setConfigurationMap(const QVariantMap& configurationMap)
|
||||
{
|
||||
QString filename = li.toString();
|
||||
if ( !filename.isEmpty() )
|
||||
m_items.append( Item{ filename, filename, Permissions( defaultPermissions ), ItemType::Path } );
|
||||
m_items.append( Item { filename, filename, CalamaresUtils::Permissions( defaultPermissions ), ItemType::Path } );
|
||||
else
|
||||
{
|
||||
cDebug() << "Empty filename for preservefiles, item" << c;
|
||||
}
|
||||
}
|
||||
else if ( li.type() == QVariant::Map )
|
||||
{
|
||||
const auto map = li.toMap();
|
||||
QString dest = map[ "dest" ].toString();
|
||||
QString from = map[ "from" ].toString();
|
||||
ItemType t =
|
||||
( from == "log" ) ? ItemType::Log :
|
||||
( from == "config" ) ? ItemType::Config :
|
||||
ItemType::None;
|
||||
ItemType t = ( from == "log" ) ? ItemType::Log : ( from == "config" ) ? ItemType::Config : ItemType::None;
|
||||
QString perm = map[ "perm" ].toString();
|
||||
if ( perm.isEmpty() )
|
||||
{
|
||||
perm = defaultPermissions;
|
||||
}
|
||||
|
||||
if ( dest.isEmpty() )
|
||||
{
|
||||
@ -230,15 +231,16 @@ void PreserveFiles::setConfigurationMap(const QVariantMap& configurationMap)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_items.append( Item{ QString(), dest, Permissions( perm ), t } );
|
||||
m_items.append( Item { QString(), dest, CalamaresUtils::Permissions( perm ), t } );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cDebug() << "Invalid type for preservefiles, item" << c;
|
||||
}
|
||||
|
||||
++c;
|
||||
}
|
||||
}
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( PreserveFilesFactory, registerPlugin<PreserveFiles>(); )
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( PreserveFilesFactory, registerPlugin< PreserveFiles >(); )
|
||||
|
@ -1,35 +1,23 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright 2018, Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
* 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 PRESERVEFILES_H
|
||||
#define PRESERVEFILES_H
|
||||
|
||||
#include "CppJob.h"
|
||||
#include "DllMacro.h"
|
||||
#include "utils/Permissions.h"
|
||||
#include "utils/PluginFactory.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include "CppJob.h"
|
||||
#include "DllMacro.h"
|
||||
|
||||
#include "utils/PluginFactory.h"
|
||||
|
||||
#include "permissions.h"
|
||||
|
||||
class PLUGINDLLEXPORT PreserveFiles : public Calamares::CppJob
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -40,15 +28,15 @@ class PLUGINDLLEXPORT PreserveFiles : public Calamares::CppJob
|
||||
Path,
|
||||
Log,
|
||||
Config
|
||||
} ;
|
||||
};
|
||||
|
||||
struct Item
|
||||
{
|
||||
QString source;
|
||||
QString dest;
|
||||
Permissions perm;
|
||||
CalamaresUtils::Permissions perm;
|
||||
ItemType type;
|
||||
} ;
|
||||
};
|
||||
|
||||
using ItemList = QList< Item >;
|
||||
|
||||
|
@ -1,75 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright (C) 2018 Scott Harvey <scott@spharvey.me>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include "permissions.h"
|
||||
|
||||
Permissions::Permissions() :
|
||||
m_username(),
|
||||
m_group(),
|
||||
m_valid(false),
|
||||
m_value(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Permissions::Permissions(QString p) : Permissions()
|
||||
{
|
||||
parsePermissions(p);
|
||||
}
|
||||
|
||||
void Permissions::parsePermissions(const QString& p) {
|
||||
|
||||
QStringList segments = p.split(":");
|
||||
|
||||
if (segments.length() != 3) {
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (segments[0].isEmpty() || segments[1].isEmpty()) {
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
int octal = segments[2].toInt(&ok, 8);
|
||||
if (!ok || octal == 0) {
|
||||
m_valid = false;
|
||||
return;
|
||||
} else {
|
||||
m_value = octal;
|
||||
}
|
||||
|
||||
// We have exactly three segments and the third is valid octal,
|
||||
// so we can declare the string valid and set the user and group names
|
||||
m_valid = true;
|
||||
m_username = segments[0];
|
||||
m_group = segments[1];
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,62 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright (C) 2018 Scott Harvey <scott@spharvey.me>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PERMISSIONS_H
|
||||
#define PERMISSIONS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
* @brief The Permissions class takes a QString @p in the form of
|
||||
* <user>:<group>:<permissions>, checks it for validity, and makes the three
|
||||
* components available indivdually.
|
||||
*/
|
||||
class Permissions
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/** @brief Constructor
|
||||
*
|
||||
* Splits the string @p at the colon (":") into separate elements for
|
||||
* <user>, <group>, and <value> (permissions), where <value> is returned as
|
||||
* an **octal** integer.
|
||||
*/
|
||||
Permissions(QString p);
|
||||
|
||||
/** @brief Default constructor of an invalid Permissions. */
|
||||
Permissions();
|
||||
|
||||
bool isValid() const { return m_valid; }
|
||||
QString username() const { return m_username; }
|
||||
QString group() const { return m_group; }
|
||||
int value() const { return m_value; }
|
||||
QString octal() const { return QString::number( m_value, 8 ); }
|
||||
|
||||
private:
|
||||
void parsePermissions(QString const &p);
|
||||
|
||||
QString m_username;
|
||||
QString m_group;
|
||||
bool m_valid;
|
||||
int m_value;
|
||||
|
||||
};
|
||||
|
||||
#endif // PERMISSIONS_H
|
@ -28,6 +28,7 @@ calamares_add_plugin( users
|
||||
UsersPage.cpp
|
||||
SetHostNameJob.cpp
|
||||
CheckPWQuality.cpp
|
||||
Config.cpp
|
||||
UI
|
||||
page_usersetup.ui
|
||||
RESOURCES
|
||||
@ -43,17 +44,31 @@ calamares_add_plugin( users
|
||||
calamares_add_test(
|
||||
userspasswordtest
|
||||
SOURCES
|
||||
PasswordTests.cpp
|
||||
TestPasswordJob.cpp
|
||||
SetPasswordJob.cpp
|
||||
LIBRARIES
|
||||
${CRYPT_LIBRARIES}
|
||||
)
|
||||
|
||||
calamares_add_test(
|
||||
userstest
|
||||
userscreatetest
|
||||
SOURCES
|
||||
Tests.cpp
|
||||
TestCreateUserJob.cpp
|
||||
CreateUserJob.cpp
|
||||
)
|
||||
|
||||
calamares_add_test(
|
||||
usershostnametest
|
||||
SOURCES
|
||||
TestSetHostNameJob.cpp
|
||||
SetHostNameJob.cpp
|
||||
LIBRARIES
|
||||
Qt5::DBus
|
||||
)
|
||||
|
||||
calamares_add_test(
|
||||
userstest
|
||||
SOURCES
|
||||
Tests.cpp
|
||||
Config.cpp
|
||||
)
|
||||
|
@ -55,7 +55,7 @@ DEFINE_CHECK_FUNC( minLength )
|
||||
{
|
||||
cDebug() << Logger::SubEntry << "minLength set to" << minLength;
|
||||
checks.push_back( PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is too short" ); },
|
||||
[ minLength ]( const QString& s ) { return s.length() >= minLength; },
|
||||
[minLength]( const QString& s ) { return s.length() >= minLength; },
|
||||
PasswordCheck::Weight( 10 ) ) );
|
||||
}
|
||||
}
|
||||
@ -71,7 +71,7 @@ DEFINE_CHECK_FUNC( maxLength )
|
||||
{
|
||||
cDebug() << Logger::SubEntry << "maxLength set to" << maxLength;
|
||||
checks.push_back( PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is too long" ); },
|
||||
[ maxLength ]( const QString& s ) { return s.length() <= maxLength; },
|
||||
[maxLength]( const QString& s ) { return s.length() <= maxLength; },
|
||||
PasswordCheck::Weight( 10 ) ) );
|
||||
}
|
||||
}
|
||||
@ -349,8 +349,8 @@ DEFINE_CHECK_FUNC( libpwquality )
|
||||
/* Something actually added? */
|
||||
if ( requirement_count )
|
||||
{
|
||||
checks.push_back( PasswordCheck( [ settings ]() { return settings->explanation(); },
|
||||
[ settings ]( const QString& s ) {
|
||||
checks.push_back( PasswordCheck( [settings]() { return settings->explanation(); },
|
||||
[settings]( const QString& s ) {
|
||||
int r = settings->check( s );
|
||||
if ( r < 0 )
|
||||
{
|
||||
|
380
src/modules/users/Config.cpp
Normal file
380
src/modules/users/Config.cpp
Normal file
@ -0,0 +1,380 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
* 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 "Config.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/String.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QRegExp>
|
||||
|
||||
static const QRegExp USERNAME_RX( "^[a-z_][a-z0-9_-]*[$]?$" );
|
||||
static constexpr const int USERNAME_MAX_LENGTH = 31;
|
||||
|
||||
static const QRegExp HOSTNAME_RX( "^[a-zA-Z0-9][-a-zA-Z0-9_]*$" );
|
||||
static constexpr const int HOSTNAME_MIN_LENGTH = 2;
|
||||
static constexpr const int HOSTNAME_MAX_LENGTH = 63;
|
||||
|
||||
Config::Config( QObject* parent )
|
||||
: QObject( parent )
|
||||
{
|
||||
}
|
||||
|
||||
Config::~Config() {}
|
||||
|
||||
void
|
||||
Config::setUserShell( const QString& shell )
|
||||
{
|
||||
if ( !shell.isEmpty() && !shell.startsWith( '/' ) )
|
||||
{
|
||||
cWarning() << "User shell" << shell << "is not an absolute path.";
|
||||
return;
|
||||
}
|
||||
// The shell is put into GS because the CreateUser job expects it there
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert( "userShell", shell );
|
||||
}
|
||||
|
||||
static inline void
|
||||
insertInGlobalStorage( const QString& key, const QString& group )
|
||||
{
|
||||
auto* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
if ( !gs || group.isEmpty() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
gs->insert( key, group );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setAutologinGroup( const QString& group )
|
||||
{
|
||||
insertInGlobalStorage( QStringLiteral( "autologinGroup" ), group );
|
||||
emit autologinGroupChanged( group );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setSudoersGroup( const QString& group )
|
||||
{
|
||||
insertInGlobalStorage( QStringLiteral( "sudoersGroup" ), group );
|
||||
emit sudoersGroupChanged( group );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Config::setLoginName( const QString& login )
|
||||
{
|
||||
if ( login != m_loginName )
|
||||
{
|
||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
if ( login.isEmpty() )
|
||||
{
|
||||
gs->remove( "username" );
|
||||
}
|
||||
else
|
||||
{
|
||||
gs->insert( "username", login );
|
||||
}
|
||||
|
||||
m_customLoginName = !login.isEmpty();
|
||||
m_loginName = login;
|
||||
emit loginNameChanged( login );
|
||||
emit loginNameStatusChanged( loginNameStatus() );
|
||||
}
|
||||
}
|
||||
|
||||
const QStringList&
|
||||
Config::forbiddenLoginNames()
|
||||
{
|
||||
static QStringList forbidden { "root" };
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
QString
|
||||
Config::loginNameStatus() const
|
||||
{
|
||||
// An empty login is "ok", even if it isn't really
|
||||
if ( m_loginName.isEmpty() )
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
if ( m_loginName.length() > USERNAME_MAX_LENGTH )
|
||||
{
|
||||
return tr( "Your username is too long." );
|
||||
}
|
||||
for ( const QString& badName : forbiddenLoginNames() )
|
||||
{
|
||||
if ( 0 == QString::compare( badName, m_loginName, Qt::CaseSensitive ) )
|
||||
{
|
||||
return tr( "'%1' is not allowed as username." ).arg( badName );
|
||||
}
|
||||
}
|
||||
|
||||
QRegExp validateFirstLetter( "^[a-z_]" );
|
||||
if ( validateFirstLetter.indexIn( m_loginName ) != 0 )
|
||||
{
|
||||
return tr( "Your username must start with a lowercase letter or underscore." );
|
||||
}
|
||||
if ( !USERNAME_RX.exactMatch( m_loginName ) )
|
||||
{
|
||||
return tr( "Only lowercase letters, numbers, underscore and hyphen are allowed." );
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void
|
||||
Config::setHostName( const QString& host )
|
||||
{
|
||||
if ( host != m_hostName )
|
||||
{
|
||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
if ( host.isEmpty() )
|
||||
{
|
||||
gs->remove( "hostname" );
|
||||
}
|
||||
else
|
||||
{
|
||||
gs->insert( "hostname", host );
|
||||
}
|
||||
|
||||
m_customHostName = !host.isEmpty();
|
||||
m_hostName = host;
|
||||
emit hostNameChanged( host );
|
||||
emit hostNameStatusChanged( hostNameStatus() );
|
||||
}
|
||||
}
|
||||
|
||||
const QStringList&
|
||||
Config::forbiddenHostNames()
|
||||
{
|
||||
static QStringList forbidden { "localhost" };
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
QString
|
||||
Config::hostNameStatus() const
|
||||
{
|
||||
// An empty hostname is "ok", even if it isn't really
|
||||
if ( m_hostName.isEmpty() )
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
if ( m_hostName.length() < HOSTNAME_MIN_LENGTH )
|
||||
{
|
||||
return tr( "Your hostname is too short." );
|
||||
}
|
||||
if ( m_hostName.length() > HOSTNAME_MAX_LENGTH )
|
||||
{
|
||||
return tr( "Your hostname is too long." );
|
||||
}
|
||||
for ( const QString& badName : forbiddenHostNames() )
|
||||
{
|
||||
if ( 0 == QString::compare( badName, m_hostName, Qt::CaseSensitive ) )
|
||||
{
|
||||
return tr( "'%1' is not allowed as hostname." ).arg( badName );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !HOSTNAME_RX.exactMatch( m_hostName ) )
|
||||
{
|
||||
return tr( "Only letters, numbers, underscore and hyphen are allowed." );
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
/** @brief Guess the machine's name
|
||||
*
|
||||
* If there is DMI data, use that; otherwise, just call the machine "-pc".
|
||||
* Reads the DMI data just once.
|
||||
*/
|
||||
static QString
|
||||
guessProductName()
|
||||
{
|
||||
static bool tried = false;
|
||||
static QString dmiProduct;
|
||||
|
||||
if ( !tried )
|
||||
{
|
||||
// yes validateHostnameText() but these files can be a mess
|
||||
QRegExp dmirx( "[^a-zA-Z0-9]", Qt::CaseInsensitive );
|
||||
QFile dmiFile( QStringLiteral( "/sys/devices/virtual/dmi/id/product_name" ) );
|
||||
|
||||
if ( dmiFile.exists() && dmiFile.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
dmiProduct = QString::fromLocal8Bit( dmiFile.readAll().simplified().data() )
|
||||
.toLower()
|
||||
.replace( dmirx, " " )
|
||||
.remove( ' ' );
|
||||
}
|
||||
if ( dmiProduct.isEmpty() )
|
||||
{
|
||||
dmiProduct = QStringLiteral( "pc" );
|
||||
}
|
||||
tried = true;
|
||||
}
|
||||
return dmiProduct;
|
||||
}
|
||||
|
||||
static QString
|
||||
makeLoginNameSuggestion( const QStringList& parts )
|
||||
{
|
||||
if ( parts.isEmpty() || parts.first().isEmpty() )
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString usernameSuggestion = parts.first();
|
||||
for ( int i = 1; i < parts.length(); ++i )
|
||||
{
|
||||
if ( !parts.value( i ).isEmpty() )
|
||||
{
|
||||
usernameSuggestion.append( parts.value( i ).at( 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
return USERNAME_RX.indexIn( usernameSuggestion ) != -1 ? usernameSuggestion : QString();
|
||||
}
|
||||
|
||||
static QString
|
||||
makeHostnameSuggestion( const QStringList& parts )
|
||||
{
|
||||
static const QRegExp HOSTNAME_RX( "^[a-zA-Z0-9][-a-zA-Z0-9_]*$" );
|
||||
if ( parts.isEmpty() || parts.first().isEmpty() )
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString productName = guessProductName();
|
||||
QString hostnameSuggestion = QStringLiteral( "%1-%2" ).arg( parts.first() ).arg( productName );
|
||||
return HOSTNAME_RX.indexIn( hostnameSuggestion ) != -1 ? hostnameSuggestion : QString();
|
||||
}
|
||||
|
||||
void
|
||||
Config::setFullName( const QString& name )
|
||||
{
|
||||
if ( name.isEmpty() && !m_fullName.isEmpty() )
|
||||
{
|
||||
if ( !m_customHostName )
|
||||
{
|
||||
setHostName( name );
|
||||
}
|
||||
if ( !m_customLoginName )
|
||||
{
|
||||
setLoginName( name );
|
||||
}
|
||||
m_fullName = name;
|
||||
emit fullNameChanged( name );
|
||||
}
|
||||
|
||||
if ( name != m_fullName )
|
||||
{
|
||||
m_fullName = name;
|
||||
emit fullNameChanged( name );
|
||||
|
||||
// Build login and hostname, if needed
|
||||
QRegExp rx( "[^a-zA-Z0-9 ]", Qt::CaseInsensitive );
|
||||
QString cleanName = CalamaresUtils::removeDiacritics( name ).toLower().replace( rx, " " ).simplified();
|
||||
QStringList cleanParts = cleanName.split( ' ' );
|
||||
|
||||
if ( !m_customLoginName )
|
||||
{
|
||||
QString login = makeLoginNameSuggestion( cleanParts );
|
||||
if ( !login.isEmpty() && login != m_loginName )
|
||||
{
|
||||
m_loginName = login;
|
||||
emit loginNameChanged( login );
|
||||
emit loginNameStatusChanged( loginNameStatus() );
|
||||
}
|
||||
}
|
||||
if ( !m_customHostName )
|
||||
{
|
||||
QString hostname = makeHostnameSuggestion( cleanParts );
|
||||
if ( !hostname.isEmpty() && hostname != m_hostName )
|
||||
{
|
||||
m_hostName = hostname;
|
||||
emit hostNameChanged( hostname );
|
||||
emit hostNameStatusChanged( hostNameStatus() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::setAutoLogin( bool b )
|
||||
{
|
||||
if ( b != m_doAutoLogin )
|
||||
{
|
||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
if ( b )
|
||||
{
|
||||
gs->insert( "autologinUser", loginName() );
|
||||
}
|
||||
else
|
||||
{
|
||||
gs->remove( "autologinUser" );
|
||||
}
|
||||
m_doAutoLogin = b;
|
||||
emit autoLoginChanged( b );
|
||||
}
|
||||
}
|
||||
|
||||
STATICTEST inline void
|
||||
setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups )
|
||||
{
|
||||
// '#' is not a valid group name; use that to distinguish an empty-list
|
||||
// in the configuration (which is a legitimate, if unusual, choice)
|
||||
// from a bad or missing configuration value.
|
||||
defaultGroups = CalamaresUtils::getStringList( map, QStringLiteral( "defaultGroups" ), QStringList { "#" } );
|
||||
if ( defaultGroups.contains( QStringLiteral( "#" ) ) )
|
||||
{
|
||||
cWarning() << "Using fallback groups. Please check *defaultGroups* in users.conf";
|
||||
defaultGroups = QStringList { "lp", "video", "network", "storage", "wheel", "audio" };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Config::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
QString shell( QLatin1String( "/bin/bash" ) ); // as if it's not set at all
|
||||
if ( configurationMap.contains( "userShell" ) )
|
||||
{
|
||||
shell = CalamaresUtils::getString( configurationMap, "userShell" );
|
||||
}
|
||||
// Now it might be explicitly set to empty, which is ok
|
||||
setUserShell( shell );
|
||||
|
||||
setAutologinGroup( CalamaresUtils::getString( configurationMap, "autologinGroup" ) );
|
||||
setSudoersGroup( CalamaresUtils::getString( configurationMap, "sudoersGroup" ) );
|
||||
|
||||
setConfigurationDefaultGroups( configurationMap, m_defaultGroups );
|
||||
m_doAutoLogin = CalamaresUtils::getBool( configurationMap, "doAutologin", false );
|
||||
|
||||
m_writeRootPassword = CalamaresUtils::getBool( configurationMap, "setRootPassword", true );
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert( "setRootPassword", m_writeRootPassword );
|
||||
}
|
138
src/modules/users/Config.h
Normal file
138
src/modules/users/Config.h
Normal file
@ -0,0 +1,138 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
* 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 USERS_CONFIG_H
|
||||
#define USERS_CONFIG_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
class Config : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY( QString userShell READ userShell WRITE setUserShell NOTIFY userShellChanged )
|
||||
|
||||
Q_PROPERTY( QString autologinGroup READ autologinGroup WRITE setAutologinGroup NOTIFY autologinGroupChanged )
|
||||
Q_PROPERTY( QString sudoersGroup READ sudoersGroup WRITE setSudoersGroup NOTIFY sudoersGroupChanged )
|
||||
|
||||
Q_PROPERTY( bool doAutoLogin READ doAutoLogin WRITE setAutoLogin NOTIFY autoLoginChanged )
|
||||
|
||||
Q_PROPERTY( QString fullName READ fullName WRITE setFullName NOTIFY fullNameChanged )
|
||||
Q_PROPERTY( QString loginName READ loginName WRITE setLoginName NOTIFY loginNameChanged )
|
||||
Q_PROPERTY( QString loginNameStatus READ loginNameStatus NOTIFY loginNameStatusChanged )
|
||||
|
||||
Q_PROPERTY( QString hostName READ hostName WRITE setHostName NOTIFY hostNameChanged )
|
||||
Q_PROPERTY( QString hostNameStatus READ hostNameStatus NOTIFY hostNameStatusChanged )
|
||||
|
||||
public:
|
||||
Config( QObject* parent = nullptr );
|
||||
~Config();
|
||||
|
||||
void setConfigurationMap( const QVariantMap& );
|
||||
|
||||
/** @brief Full path to the user's shell executable
|
||||
*
|
||||
* Typically this will be /bin/bash, but it can be set from
|
||||
* the config file with the *userShell* setting.
|
||||
*/
|
||||
QString userShell() const { return m_userShell; }
|
||||
|
||||
/// The group of which auto-login users must be a member
|
||||
QString autologinGroup() const { return m_autologinGroup; }
|
||||
/// The group of which users who can "sudo" must be a member
|
||||
QString sudoersGroup() const { return m_sudoersGroup; }
|
||||
|
||||
/// The full (GECOS) name of the user
|
||||
QString fullName() const { return m_fullName; }
|
||||
/// The login name of the user
|
||||
QString loginName() const { return m_loginName; }
|
||||
/// Status message about login -- empty for "ok"
|
||||
QString loginNameStatus() const;
|
||||
|
||||
/// The host name (name for the system)
|
||||
QString hostName() const { return m_hostName; }
|
||||
/// Status message about hostname -- empty for "ok"
|
||||
QString hostNameStatus() const;
|
||||
|
||||
/// Should the user be automatically logged-in?
|
||||
bool doAutoLogin() const { return m_doAutoLogin; }
|
||||
/// Should the root password be written (if false, no password is set and the root account is disabled for login)
|
||||
bool writeRootPassword() const { return m_writeRootPassword; }
|
||||
|
||||
const QStringList& defaultGroups() const { return m_defaultGroups; }
|
||||
|
||||
static const QStringList& forbiddenLoginNames();
|
||||
static const QStringList& forbiddenHostNames();
|
||||
|
||||
public Q_SLOTS:
|
||||
/** @brief Sets the user's shell if possible
|
||||
*
|
||||
* If the path is empty, that's ok: no shell will be explicitly set,
|
||||
* so the user will get whatever shell is set to default in the target.
|
||||
*
|
||||
* The given non-empty @p path must be an absolute path (for use inside
|
||||
* the target system!); if it is not, the shell is not changed.
|
||||
*/
|
||||
void setUserShell( const QString& path );
|
||||
|
||||
/// Sets the autologin group; empty is ignored
|
||||
void setAutologinGroup( const QString& group );
|
||||
/// Sets the sudoer group; empty is ignored
|
||||
void setSudoersGroup( const QString& group );
|
||||
|
||||
/// Sets the full name, may guess a loginName
|
||||
void setFullName( const QString& name );
|
||||
/// Sets the login name (flags it as "custom")
|
||||
void setLoginName( const QString& login );
|
||||
|
||||
/// Sets the host name (flags it as "custom")
|
||||
void setHostName( const QString& host );
|
||||
|
||||
/// Sets the autologin flag
|
||||
void setAutoLogin( bool b );
|
||||
|
||||
signals:
|
||||
void userShellChanged( const QString& );
|
||||
void autologinGroupChanged( const QString& );
|
||||
void sudoersGroupChanged( const QString& );
|
||||
void fullNameChanged( const QString& );
|
||||
void loginNameChanged( const QString& );
|
||||
void loginNameStatusChanged( const QString& );
|
||||
void hostNameChanged( const QString& );
|
||||
void hostNameStatusChanged( const QString& );
|
||||
void autoLoginChanged( bool );
|
||||
|
||||
private:
|
||||
QStringList m_defaultGroups;
|
||||
QString m_userShell;
|
||||
QString m_autologinGroup;
|
||||
QString m_sudoersGroup;
|
||||
QString m_fullName;
|
||||
QString m_loginName;
|
||||
QString m_hostName;
|
||||
bool m_doAutoLogin = false;
|
||||
bool m_writeRootPassword = true;
|
||||
|
||||
bool m_customLoginName = false;
|
||||
bool m_customHostName = false;
|
||||
};
|
||||
|
||||
#endif
|
@ -12,6 +12,7 @@
|
||||
#include "JobQueue.h"
|
||||
#include "utils/CalamaresUtilsSystem.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Permissions.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
@ -54,6 +55,109 @@ CreateUserJob::prettyStatusMessage() const
|
||||
return tr( "Creating user %1." ).arg( m_userName );
|
||||
}
|
||||
|
||||
STATICTEST QStringList
|
||||
groupsInTargetSystem( const QDir& targetRoot )
|
||||
{
|
||||
QFileInfo groupsFi( targetRoot.absoluteFilePath( "etc/group" ) );
|
||||
QFile groupsFile( groupsFi.absoluteFilePath() );
|
||||
if ( !groupsFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
|
||||
{
|
||||
return QStringList();
|
||||
}
|
||||
QString groupsData = QString::fromLocal8Bit( groupsFile.readAll() );
|
||||
QStringList groupsLines = groupsData.split( '\n' );
|
||||
QStringList::iterator it = groupsLines.begin();
|
||||
while ( it != groupsLines.end() )
|
||||
{
|
||||
if ( it->startsWith( '#' ) )
|
||||
{
|
||||
it = groupsLines.erase( it );
|
||||
continue;
|
||||
}
|
||||
int indexOfFirstToDrop = it->indexOf( ':' );
|
||||
if ( indexOfFirstToDrop < 1 )
|
||||
{
|
||||
it = groupsLines.erase( it );
|
||||
continue;
|
||||
}
|
||||
it->truncate( indexOfFirstToDrop );
|
||||
++it;
|
||||
}
|
||||
return groupsLines;
|
||||
}
|
||||
|
||||
static void
|
||||
ensureGroupsExistInTarget( const QStringList& wantedGroups, const QStringList& availableGroups )
|
||||
{
|
||||
for ( const QString& group : wantedGroups )
|
||||
{
|
||||
if ( !availableGroups.contains( group ) )
|
||||
{
|
||||
#ifdef __FreeBSD__
|
||||
CalamaresUtils::System::instance()->targetEnvCall( { "pw", "groupadd", "-n", group } );
|
||||
#else
|
||||
CalamaresUtils::System::instance()->targetEnvCall( { "groupadd", group } );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Calamares::JobResult
|
||||
createUser( const QString& loginName, const QString& fullName, const QString& shell )
|
||||
{
|
||||
QStringList useraddCommand;
|
||||
#ifdef __FreeBSD__
|
||||
useraddCommand << "pw"
|
||||
<< "useradd"
|
||||
<< "-n" << loginName << "-m"
|
||||
<< "-c" << fullName;
|
||||
if ( !shell.isEmpty() )
|
||||
{
|
||||
useraddCommand << "-s" << shell;
|
||||
}
|
||||
#else
|
||||
useraddCommand << "useradd"
|
||||
<< "-m"
|
||||
<< "-U";
|
||||
if ( !shell.isEmpty() )
|
||||
{
|
||||
useraddCommand << "-s" << shell;
|
||||
}
|
||||
useraddCommand << "-c" << fullName;
|
||||
useraddCommand << loginName;
|
||||
#endif
|
||||
|
||||
auto commandResult = CalamaresUtils::System::instance()->targetEnvCommand( useraddCommand );
|
||||
if ( commandResult.getExitCode() )
|
||||
{
|
||||
cError() << "useradd failed" << commandResult.getExitCode();
|
||||
return commandResult.explainProcess( useraddCommand, std::chrono::seconds( 10 ) /* bogus timeout */ );
|
||||
}
|
||||
return Calamares::JobResult::ok();
|
||||
}
|
||||
|
||||
static Calamares::JobResult
|
||||
setUserGroups( const QString& loginName, const QStringList& groups )
|
||||
{
|
||||
QStringList setgroupsCommand;
|
||||
#ifdef __FreeBSD__
|
||||
setgroupsCommand << "pw"
|
||||
<< "usermod"
|
||||
<< "-n" << loginName << "-G" << groups.join( ',' );
|
||||
#else
|
||||
setgroupsCommand << "usermod"
|
||||
<< "-aG" << groups.join( ',' ) << loginName;
|
||||
#endif
|
||||
|
||||
auto commandResult = CalamaresUtils::System::instance()->targetEnvCommand( setgroupsCommand );
|
||||
if ( commandResult.getExitCode() )
|
||||
{
|
||||
cError() << "usermod failed" << commandResult.getExitCode();
|
||||
return commandResult.explainProcess( setgroupsCommand, std::chrono::seconds( 10 ) /* bogus timeout */ );
|
||||
}
|
||||
return Calamares::JobResult::ok();
|
||||
}
|
||||
|
||||
|
||||
Calamares::JobResult
|
||||
CreateUserJob::exec()
|
||||
@ -65,65 +169,37 @@ CreateUserJob::exec()
|
||||
{
|
||||
cDebug() << "[CREATEUSER]: preparing sudoers";
|
||||
|
||||
QFileInfo sudoersFi( destDir.absoluteFilePath( "etc/sudoers.d/10-installer" ) );
|
||||
QString sudoersLine = QString( "%%1 ALL=(ALL) ALL\n" ).arg( gs->value( "sudoersGroup" ).toString() );
|
||||
auto fileResult
|
||||
= CalamaresUtils::System::instance()->createTargetFile( QStringLiteral( "/etc/sudoers.d/10-installer" ),
|
||||
sudoersLine.toUtf8().constData(),
|
||||
CalamaresUtils::System::WriteMode::Overwrite );
|
||||
|
||||
if ( !sudoersFi.absoluteDir().exists() )
|
||||
if ( fileResult )
|
||||
{
|
||||
return Calamares::JobResult::error( tr( "Sudoers dir is not writable." ) );
|
||||
if ( CalamaresUtils::Permissions::apply( fileResult.path(), 0440 ) )
|
||||
{
|
||||
return Calamares::JobResult::error( tr( "Cannot chmod sudoers file." ) );
|
||||
}
|
||||
|
||||
QFile sudoersFile( sudoersFi.absoluteFilePath() );
|
||||
if ( !sudoersFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
|
||||
}
|
||||
else
|
||||
{
|
||||
return Calamares::JobResult::error( tr( "Cannot create sudoers file for writing." ) );
|
||||
}
|
||||
|
||||
QString sudoersGroup = gs->value( "sudoersGroup" ).toString();
|
||||
|
||||
QTextStream sudoersOut( &sudoersFile );
|
||||
sudoersOut << QString( "%%1 ALL=(ALL) ALL\n" ).arg( sudoersGroup );
|
||||
|
||||
if ( QProcess::execute( "chmod", { "440", sudoersFi.absoluteFilePath() } ) )
|
||||
return Calamares::JobResult::error( tr( "Cannot chmod sudoers file." ) );
|
||||
}
|
||||
|
||||
cDebug() << "[CREATEUSER]: preparing groups";
|
||||
|
||||
QFileInfo groupsFi( destDir.absoluteFilePath( "etc/group" ) );
|
||||
QFile groupsFile( groupsFi.absoluteFilePath() );
|
||||
if ( !groupsFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
|
||||
QStringList availableGroups = groupsInTargetSystem( destDir );
|
||||
QStringList groupsForThisUser = m_defaultGroups;
|
||||
if ( m_autologin && gs->contains( "autologinGroup" ) && !gs->value( "autologinGroup" ).toString().isEmpty() )
|
||||
{
|
||||
return Calamares::JobResult::error( tr( "Cannot open groups file for reading." ) );
|
||||
}
|
||||
QString groupsData = QString::fromLocal8Bit( groupsFile.readAll() );
|
||||
QStringList groupsLines = groupsData.split( '\n' );
|
||||
for ( QStringList::iterator it = groupsLines.begin(); it != groupsLines.end(); ++it )
|
||||
{
|
||||
int indexOfFirstToDrop = it->indexOf( ':' );
|
||||
it->truncate( indexOfFirstToDrop );
|
||||
groupsForThisUser << gs->value( "autologinGroup" ).toString();
|
||||
}
|
||||
ensureGroupsExistInTarget( m_defaultGroups, availableGroups );
|
||||
|
||||
for ( const QString& group : m_defaultGroups )
|
||||
{
|
||||
if ( !groupsLines.contains( group ) )
|
||||
{
|
||||
CalamaresUtils::System::instance()->targetEnvCall( { "groupadd", group } );
|
||||
}
|
||||
}
|
||||
|
||||
QString defaultGroups = m_defaultGroups.join( ',' );
|
||||
if ( m_autologin )
|
||||
{
|
||||
QString autologinGroup;
|
||||
if ( gs->contains( "autologinGroup" ) && !gs->value( "autologinGroup" ).toString().isEmpty() )
|
||||
{
|
||||
autologinGroup = gs->value( "autologinGroup" ).toString();
|
||||
CalamaresUtils::System::instance()->targetEnvCall( { "groupadd", autologinGroup } );
|
||||
defaultGroups.append( QString( ",%1" ).arg( autologinGroup ) );
|
||||
}
|
||||
}
|
||||
|
||||
// If we're looking to reuse the contents of an existing /home
|
||||
// If we're looking to reuse the contents of an existing /home.
|
||||
// This GS setting comes from the **partitioning** module.
|
||||
if ( gs->value( "reuseHome" ).toBool() )
|
||||
{
|
||||
QString shellFriendlyHome = "/home/" + m_userName;
|
||||
@ -133,6 +209,7 @@ CreateUserJob::exec()
|
||||
QString backupDirName = "dotfiles_backup_" + QDateTime::currentDateTime().toString( "yyyy-MM-dd_HH-mm-ss" );
|
||||
existingHome.mkdir( backupDirName );
|
||||
|
||||
// We need the extra `sh -c` here to ensure that we can expand the shell globs
|
||||
CalamaresUtils::System::instance()->targetEnvCall(
|
||||
{ "sh", "-c", "mv -f " + shellFriendlyHome + "/.* " + shellFriendlyHome + "/" + backupDirName } );
|
||||
}
|
||||
@ -140,33 +217,21 @@ CreateUserJob::exec()
|
||||
|
||||
cDebug() << "[CREATEUSER]: creating user";
|
||||
|
||||
QStringList useradd { "useradd", "-m", "-U" };
|
||||
QString shell = gs->value( "userShell" ).toString();
|
||||
if ( !shell.isEmpty() )
|
||||
auto useraddResult = createUser( m_userName, m_fullName, gs->value( "userShell" ).toString() );
|
||||
if ( !useraddResult )
|
||||
{
|
||||
useradd << "-s" << shell;
|
||||
}
|
||||
useradd << "-c" << m_fullName;
|
||||
useradd << m_userName;
|
||||
|
||||
auto commandResult = CalamaresUtils::System::instance()->targetEnvCommand( useradd );
|
||||
if ( commandResult.getExitCode() )
|
||||
{
|
||||
cError() << "useradd failed" << commandResult.getExitCode();
|
||||
return commandResult.explainProcess( useradd, std::chrono::seconds( 10 ) /* bogus timeout */ );
|
||||
return useraddResult;
|
||||
}
|
||||
|
||||
commandResult
|
||||
= CalamaresUtils::System::instance()->targetEnvCommand( { "usermod", "-aG", defaultGroups, m_userName } );
|
||||
if ( commandResult.getExitCode() )
|
||||
auto usergroupsResult = setUserGroups( m_userName, groupsForThisUser );
|
||||
if ( !usergroupsResult )
|
||||
{
|
||||
cError() << "usermod failed" << commandResult.getExitCode();
|
||||
return commandResult.explainProcess( "usermod", std::chrono::seconds( 10 ) /* bogus timeout */ );
|
||||
return usergroupsResult;
|
||||
}
|
||||
|
||||
QString userGroup = QString( "%1:%2" ).arg( m_userName ).arg( m_userName );
|
||||
QString homeDir = QString( "/home/%1" ).arg( m_userName );
|
||||
commandResult = CalamaresUtils::System::instance()->targetEnvCommand( { "chown", "-R", userGroup, homeDir } );
|
||||
auto commandResult = CalamaresUtils::System::instance()->targetEnvCommand( { "chown", "-R", userGroup, homeDir } );
|
||||
if ( commandResult.getExitCode() )
|
||||
{
|
||||
cError() << "chown failed" << commandResult.getExitCode();
|
||||
|
@ -1,36 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright 2017, 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 PASSWORDTESTS_H
|
||||
#define PASSWORDTESTS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class PasswordTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PasswordTests();
|
||||
~PasswordTests() override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testSalt();
|
||||
};
|
||||
|
||||
#endif
|
79
src/modules/users/TestCreateUserJob.cpp
Normal file
79
src/modules/users/TestCreateUserJob.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* 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 "CreateUserJob.h"
|
||||
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
// Implementation details
|
||||
extern QStringList groupsInTargetSystem( const QDir& targetRoot ); // CreateUserJob
|
||||
|
||||
class CreateUserTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CreateUserTests();
|
||||
virtual ~CreateUserTests() {}
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void testReadGroup();
|
||||
};
|
||||
|
||||
CreateUserTests::CreateUserTests() {}
|
||||
|
||||
void
|
||||
CreateUserTests::initTestCase()
|
||||
{
|
||||
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||
cDebug() << "Users test started.";
|
||||
}
|
||||
|
||||
void
|
||||
CreateUserTests::testReadGroup()
|
||||
{
|
||||
QDir root( "/" );
|
||||
QVERIFY( root.exists() );
|
||||
|
||||
// Get the groups in the host system
|
||||
QStringList groups = groupsInTargetSystem( root );
|
||||
QVERIFY( groups.count() > 2 );
|
||||
#ifdef __FreeBSD__
|
||||
QVERIFY( groups.contains( QStringLiteral( "wheel" ) ) );
|
||||
#else
|
||||
QVERIFY( groups.contains( QStringLiteral( "root" ) ) );
|
||||
#endif
|
||||
QVERIFY( groups.contains( QStringLiteral( "sys" ) ) );
|
||||
|
||||
for ( const QString& s : groups )
|
||||
{
|
||||
QVERIFY( !s.isEmpty() );
|
||||
QVERIFY( !s.contains( '#' ) );
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN( CreateUserTests )
|
||||
|
||||
#include "utils/moc-warnings.h"
|
||||
|
||||
#include "TestCreateUserJob.moc"
|
@ -1,6 +1,7 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright 2017, Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -18,15 +19,23 @@
|
||||
|
||||
#include "SetPasswordJob.h"
|
||||
|
||||
#include "PasswordTests.h"
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
QTEST_GUILESS_MAIN( PasswordTests )
|
||||
class PasswordTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PasswordTests();
|
||||
~PasswordTests() override;
|
||||
|
||||
PasswordTests::PasswordTests() { }
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testSalt();
|
||||
};
|
||||
|
||||
PasswordTests::~PasswordTests() { }
|
||||
PasswordTests::PasswordTests() {}
|
||||
|
||||
PasswordTests::~PasswordTests() {}
|
||||
|
||||
void
|
||||
PasswordTests::initTestCase()
|
||||
@ -48,3 +57,9 @@ PasswordTests::testSalt()
|
||||
QVERIFY( s.endsWith( '$' ) );
|
||||
qDebug() << "Obtained salt" << s;
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN( PasswordTests )
|
||||
|
||||
#include "utils/moc-warnings.h"
|
||||
|
||||
#include "TestPasswordJob.moc"
|
146
src/modules/users/TestSetHostNameJob.cpp
Normal file
146
src/modules/users/TestSetHostNameJob.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* 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 "SetHostNameJob.h"
|
||||
|
||||
// Implementation details
|
||||
extern bool setFileHostname( const QString& );
|
||||
extern bool writeFileEtcHosts( const QString& );
|
||||
extern bool setSystemdHostname( const QString& );
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "utils/CalamaresUtilsSystem.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Yaml.h"
|
||||
|
||||
#include <QTemporaryDir>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class UsersTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
UsersTests();
|
||||
virtual ~UsersTests() {}
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void testEtcHostname();
|
||||
void testEtcHosts();
|
||||
void testHostnamed();
|
||||
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
QTemporaryDir m_dir;
|
||||
};
|
||||
|
||||
UsersTests::UsersTests()
|
||||
: m_dir( QStringLiteral( "/tmp/calamares-usertest" ) )
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
UsersTests::initTestCase()
|
||||
{
|
||||
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||
cDebug() << "Users test started.";
|
||||
cDebug() << "Test dir" << m_dir.path();
|
||||
|
||||
// Ensure we have a system object, expect it to be a "bogus" one
|
||||
CalamaresUtils::System* system = CalamaresUtils::System::instance();
|
||||
QVERIFY( system );
|
||||
QVERIFY( system->doChroot() );
|
||||
|
||||
// Ensure we have a system-wide GlobalStorage with /tmp as root
|
||||
if ( !Calamares::JobQueue::instance() )
|
||||
{
|
||||
cDebug() << "Creating new JobQueue";
|
||||
(void)new Calamares::JobQueue();
|
||||
}
|
||||
Calamares::GlobalStorage* gs
|
||||
= Calamares::JobQueue::instance() ? Calamares::JobQueue::instance()->globalStorage() : nullptr;
|
||||
QVERIFY( gs );
|
||||
gs->insert( "rootMountPoint", m_dir.path() );
|
||||
}
|
||||
|
||||
void
|
||||
UsersTests::testEtcHostname()
|
||||
{
|
||||
cDebug() << "Test dir" << m_dir.path();
|
||||
|
||||
QVERIFY( QFile::exists( m_dir.path() ) );
|
||||
QVERIFY( !QFile::exists( m_dir.filePath( "etc" ) ) );
|
||||
|
||||
// Doesn't create intermediate directories
|
||||
QVERIFY( !setFileHostname( QStringLiteral( "tubophone.calamares.io" ) ) );
|
||||
|
||||
QVERIFY( CalamaresUtils::System::instance()->createTargetDirs( "/etc" ) );
|
||||
QVERIFY( QFile::exists( m_dir.filePath( "etc" ) ) );
|
||||
|
||||
// Does write the file
|
||||
QVERIFY( setFileHostname( QStringLiteral( "tubophone.calamares.io" ) ) );
|
||||
QVERIFY( QFile::exists( m_dir.filePath( "etc/hostname" ) ) );
|
||||
|
||||
// 22 for the test string, above, and 1 for the newline
|
||||
QCOMPARE( QFileInfo( m_dir.filePath( "etc/hostname" ) ).size(), 22 + 1 );
|
||||
}
|
||||
|
||||
void
|
||||
UsersTests::testEtcHosts()
|
||||
{
|
||||
// Assume previous tests did their work
|
||||
QVERIFY( QFile::exists( m_dir.path() ) );
|
||||
QVERIFY( QFile::exists( m_dir.filePath( "etc" ) ) );
|
||||
|
||||
QVERIFY( writeFileEtcHosts( QStringLiteral( "tubophone.calamares.io" ) ) );
|
||||
QVERIFY( QFile::exists( m_dir.filePath( "etc/hosts" ) ) );
|
||||
// The skeleton contains %1 which has the hostname substituted in, so we lose two,
|
||||
// and the rest of the blabla is 150 (according to Python)
|
||||
QCOMPARE( QFileInfo( m_dir.filePath( "etc/hosts" ) ).size(), 150 + 22 - 2 );
|
||||
}
|
||||
|
||||
void
|
||||
UsersTests::testHostnamed()
|
||||
{
|
||||
// Since the service might not be running (e.g. non-systemd systems,
|
||||
// FreeBSD, docker, ..) we're not going to fail a test here.
|
||||
// There's also the permissions problem to think of.
|
||||
QEXPECT_FAIL( "", "Hostname changes are access-controlled", Continue );
|
||||
QVERIFY( setSystemdHostname( "tubophone.calamares.io" ) );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UsersTests::cleanup()
|
||||
{
|
||||
if ( QTest::currentTestFailed() )
|
||||
{
|
||||
m_dir.setAutoRemove( false );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QTEST_GUILESS_MAIN( UsersTests )
|
||||
|
||||
#include "utils/moc-warnings.h"
|
||||
|
||||
#include "TestSetHostNameJob.moc"
|
@ -1,6 +1,7 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* Copyright 2020, Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -16,129 +17,96 @@
|
||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SetHostNameJob.h"
|
||||
#include "Config.h"
|
||||
|
||||
// Implementation details
|
||||
extern bool setFileHostname( const QString& );
|
||||
extern bool writeFileEtcHosts( const QString& );
|
||||
extern bool setSystemdHostname( const QString& );
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "utils/CalamaresUtilsSystem.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Yaml.h"
|
||||
|
||||
#include <QTemporaryDir>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class UsersTests : public QObject
|
||||
// Implementation details
|
||||
extern void setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups );
|
||||
|
||||
/** @brief Test Config object methods and internals
|
||||
*
|
||||
*/
|
||||
class UserTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
UsersTests();
|
||||
virtual ~UsersTests() { }
|
||||
UserTests();
|
||||
virtual ~UserTests() {}
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void testEtcHostname();
|
||||
void testEtcHosts();
|
||||
void testHostnamed();
|
||||
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
QTemporaryDir m_dir;
|
||||
void testDefaultGroups();
|
||||
};
|
||||
|
||||
UsersTests::UsersTests()
|
||||
: m_dir( QStringLiteral( "/tmp/calamares-usertest" ) )
|
||||
{
|
||||
}
|
||||
UserTests::UserTests() {}
|
||||
|
||||
void
|
||||
UsersTests::initTestCase()
|
||||
UserTests::initTestCase()
|
||||
{
|
||||
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||
cDebug() << "Users test started.";
|
||||
cDebug() << "Test dir" << m_dir.path();
|
||||
}
|
||||
|
||||
// Ensure we have a system object, expect it to be a "bogus" one
|
||||
CalamaresUtils::System* system = CalamaresUtils::System::instance();
|
||||
QVERIFY( system );
|
||||
QVERIFY( system->doChroot() );
|
||||
|
||||
// Ensure we have a system-wide GlobalStorage with /tmp as root
|
||||
if ( !Calamares::JobQueue::instance() )
|
||||
void
|
||||
UserTests::testDefaultGroups()
|
||||
{
|
||||
{
|
||||
cDebug() << "Creating new JobQueue";
|
||||
(void)new Calamares::JobQueue();
|
||||
QStringList groups;
|
||||
QVariantMap hweelGroup;
|
||||
QVERIFY( groups.isEmpty() );
|
||||
hweelGroup.insert( "defaultGroups", QStringList { "hweel" } );
|
||||
setConfigurationDefaultGroups( hweelGroup, groups );
|
||||
QCOMPARE( groups.count(), 1 );
|
||||
QVERIFY( groups.contains( "hweel" ) );
|
||||
}
|
||||
Calamares::GlobalStorage* gs
|
||||
= Calamares::JobQueue::instance() ? Calamares::JobQueue::instance()->globalStorage() : nullptr;
|
||||
QVERIFY( gs );
|
||||
gs->insert( "rootMountPoint", m_dir.path() );
|
||||
}
|
||||
|
||||
void
|
||||
UsersTests::testEtcHostname()
|
||||
{
|
||||
cDebug() << "Test dir" << m_dir.path();
|
||||
|
||||
QVERIFY( QFile::exists( m_dir.path() ) );
|
||||
QVERIFY( !QFile::exists( m_dir.filePath( "etc" ) ) );
|
||||
|
||||
// Doesn't create intermediate directories
|
||||
QVERIFY( !setFileHostname( QStringLiteral( "tubophone.calamares.io" ) ) );
|
||||
|
||||
QVERIFY( CalamaresUtils::System::instance()->createTargetDirs( "/etc" ) );
|
||||
QVERIFY( QFile::exists( m_dir.filePath( "etc" ) ) );
|
||||
|
||||
// Does write the file
|
||||
QVERIFY( setFileHostname( QStringLiteral( "tubophone.calamares.io" ) ) );
|
||||
QVERIFY( QFile::exists( m_dir.filePath( "etc/hostname" ) ) );
|
||||
|
||||
// 22 for the test string, above, and 1 for the newline
|
||||
QCOMPARE( QFileInfo( m_dir.filePath( "etc/hostname" ) ).size(), 22 + 1 );
|
||||
}
|
||||
|
||||
void
|
||||
UsersTests::testEtcHosts()
|
||||
{
|
||||
// Assume previous tests did their work
|
||||
QVERIFY( QFile::exists( m_dir.path() ) );
|
||||
QVERIFY( QFile::exists( m_dir.filePath( "etc" ) ) );
|
||||
|
||||
QVERIFY( writeFileEtcHosts( QStringLiteral( "tubophone.calamares.io" ) ) );
|
||||
QVERIFY( QFile::exists( m_dir.filePath( "etc/hosts" ) ) );
|
||||
// The skeleton contains %1 which has the hostname substituted in, so we lose two,
|
||||
// and the rest of the blabla is 150 (according to Python)
|
||||
QCOMPARE( QFileInfo( m_dir.filePath( "etc/hosts" ) ).size(), 150 + 22 - 2 );
|
||||
}
|
||||
|
||||
void
|
||||
UsersTests::testHostnamed()
|
||||
{
|
||||
// Since the service might not be running (e.g. non-systemd systems,
|
||||
// FreeBSD, docker, ..) we're not going to fail a test here.
|
||||
// There's also the permissions problem to think of.
|
||||
QEXPECT_FAIL( "", "Hostname changes are access-controlled", Continue );
|
||||
QVERIFY( setSystemdHostname( "tubophone.calamares.io" ) );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UsersTests::cleanup()
|
||||
{
|
||||
if ( QTest::currentTestFailed() )
|
||||
{
|
||||
m_dir.setAutoRemove( false );
|
||||
QStringList desired { "wheel", "root", "operator" };
|
||||
QStringList groups;
|
||||
QVariantMap threeGroup;
|
||||
QVERIFY( groups.isEmpty() );
|
||||
threeGroup.insert( "defaultGroups", desired );
|
||||
setConfigurationDefaultGroups( threeGroup, groups );
|
||||
QCOMPARE( groups.count(), 3 );
|
||||
QVERIFY( !groups.contains( "hweel" ) );
|
||||
QCOMPARE( groups, desired );
|
||||
}
|
||||
|
||||
{
|
||||
QStringList groups;
|
||||
QVariantMap explicitEmpty;
|
||||
QVERIFY( groups.isEmpty() );
|
||||
explicitEmpty.insert( "defaultGroups", QStringList() );
|
||||
setConfigurationDefaultGroups( explicitEmpty, groups );
|
||||
QCOMPARE( groups.count(), 0 );
|
||||
}
|
||||
|
||||
{
|
||||
QStringList groups;
|
||||
QVariantMap missing;
|
||||
QVERIFY( groups.isEmpty() );
|
||||
setConfigurationDefaultGroups( missing, groups );
|
||||
QCOMPARE( groups.count(), 6 ); // because of fallback!
|
||||
QVERIFY( groups.contains( "lp" ) );
|
||||
}
|
||||
|
||||
{
|
||||
QStringList groups;
|
||||
QVariantMap typeMismatch;
|
||||
QVERIFY( groups.isEmpty() );
|
||||
typeMismatch.insert( "defaultGroups", 1 );
|
||||
setConfigurationDefaultGroups( typeMismatch, groups );
|
||||
QCOMPARE( groups.count(), 6 ); // because of fallback!
|
||||
QVERIFY( groups.contains( "lp" ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QTEST_GUILESS_MAIN( UsersTests )
|
||||
QTEST_GUILESS_MAIN( UserTests )
|
||||
|
||||
#include "utils/moc-warnings.h"
|
||||
|
||||
|
@ -25,16 +25,12 @@
|
||||
|
||||
#include "UsersPage.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "ui_page_usersetup.h"
|
||||
|
||||
#include "CreateUserJob.h"
|
||||
#include "SetHostNameJob.h"
|
||||
#include "SetPasswordJob.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include "utils/CalamaresUtilsGui.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Retranslator.h"
|
||||
@ -44,14 +40,6 @@
|
||||
#include <QFile>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QRegExp>
|
||||
#include <QRegExpValidator>
|
||||
|
||||
static const QRegExp USERNAME_RX( "^[a-z_][a-z0-9_-]*[$]?$" );
|
||||
static const QRegExp HOSTNAME_RX( "^[a-zA-Z0-9][-a-zA-Z0-9_]*$" );
|
||||
static constexpr const int USERNAME_MAX_LENGTH = 31;
|
||||
static constexpr const int HOSTNAME_MIN_LENGTH = 2;
|
||||
static constexpr const int HOSTNAME_MAX_LENGTH = 63;
|
||||
|
||||
/** @brief How bad is the error for labelError() ? */
|
||||
enum class Badness
|
||||
@ -79,54 +67,87 @@ labelOk( QLabel* pix, QLabel* label )
|
||||
pix->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes, CalamaresUtils::Original, label->size() ) );
|
||||
}
|
||||
|
||||
UsersPage::UsersPage( QWidget* parent )
|
||||
/** Indicate error, update @p ok based on @p status */
|
||||
static inline void
|
||||
labelStatus( QLabel* pix, QLabel* label, const QString& value, const QString& status, bool& ok )
|
||||
{
|
||||
if ( status.isEmpty() )
|
||||
{
|
||||
if ( value.isEmpty() )
|
||||
{
|
||||
// This is different from labelOK() because no checkmark is shown
|
||||
label->clear();
|
||||
pix->clear();
|
||||
ok = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
labelOk( pix, label );
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
labelError( pix, label, status );
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
UsersPage::UsersPage( Config* config, QWidget* parent )
|
||||
: QWidget( parent )
|
||||
, ui( new Ui::Page_UserSetup )
|
||||
, m_config( config )
|
||||
, m_readyFullName( false )
|
||||
, m_readyUsername( false )
|
||||
, m_readyHostname( false )
|
||||
, m_readyPassword( false )
|
||||
, m_readyRootPassword( false )
|
||||
, m_writeRootPassword( true )
|
||||
{
|
||||
ui->setupUi( this );
|
||||
|
||||
// Connect signals and slots
|
||||
connect( ui->textBoxFullName, &QLineEdit::textEdited, this, &UsersPage::onFullNameTextEdited );
|
||||
connect( ui->textBoxUsername, &QLineEdit::textEdited, this, &UsersPage::onUsernameTextEdited );
|
||||
connect( ui->textBoxHostname, &QLineEdit::textEdited, this, &UsersPage::onHostnameTextEdited );
|
||||
connect( ui->textBoxUserPassword, &QLineEdit::textChanged, this, &UsersPage::onPasswordTextChanged );
|
||||
connect( ui->textBoxUserVerifiedPassword, &QLineEdit::textChanged, this, &UsersPage::onPasswordTextChanged );
|
||||
connect( ui->textBoxRootPassword, &QLineEdit::textChanged, this, &UsersPage::onRootPasswordTextChanged );
|
||||
connect( ui->textBoxVerifiedRootPassword, &QLineEdit::textChanged, this, &UsersPage::onRootPasswordTextChanged );
|
||||
connect( ui->checkBoxValidatePassword, &QCheckBox::stateChanged, this, [ this ]( int ) {
|
||||
connect( ui->checkBoxValidatePassword, &QCheckBox::stateChanged, this, [this]( int ) {
|
||||
onPasswordTextChanged( ui->textBoxUserPassword->text() );
|
||||
onRootPasswordTextChanged( ui->textBoxRootPassword->text() );
|
||||
checkReady( isReady() );
|
||||
} );
|
||||
connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged, this, [ this ]( int checked ) {
|
||||
connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged, this, [this]( const int checked ) {
|
||||
/* When "reuse" is checked, hide the fields for explicitly
|
||||
* entering the root password. However, if we're going to
|
||||
* disable the root password anyway, hide them all regardless of
|
||||
* the checkbox -- so when writeRoot is false, checked needs
|
||||
* to be true, to hide them all.
|
||||
*/
|
||||
if ( !m_writeRootPassword )
|
||||
{
|
||||
checked = true;
|
||||
}
|
||||
ui->labelChooseRootPassword->setVisible( !checked );
|
||||
ui->labelRootPassword->setVisible( !checked );
|
||||
ui->labelRootPasswordError->setVisible( !checked );
|
||||
ui->textBoxRootPassword->setVisible( !checked );
|
||||
ui->textBoxVerifiedRootPassword->setVisible( !checked );
|
||||
const bool visible = m_config->writeRootPassword() ? !checked : false;
|
||||
ui->labelChooseRootPassword->setVisible( visible );
|
||||
ui->labelRootPassword->setVisible( visible );
|
||||
ui->labelRootPasswordError->setVisible( visible );
|
||||
ui->textBoxRootPassword->setVisible( visible );
|
||||
ui->textBoxVerifiedRootPassword->setVisible( visible );
|
||||
checkReady( isReady() );
|
||||
} );
|
||||
|
||||
m_customUsername = false;
|
||||
m_customHostname = false;
|
||||
connect( ui->textBoxFullName, &QLineEdit::textEdited, config, &Config::setFullName );
|
||||
connect( config, &Config::fullNameChanged, this, &UsersPage::onFullNameTextEdited );
|
||||
|
||||
setWriteRootPassword( true );
|
||||
connect( ui->textBoxHostName, &QLineEdit::textEdited, config, &Config::setHostName );
|
||||
connect( config, &Config::hostNameChanged, ui->textBoxHostName, &QLineEdit::setText );
|
||||
connect( config, &Config::hostNameStatusChanged, this, &UsersPage::reportHostNameStatus );
|
||||
|
||||
connect( ui->textBoxLoginName, &QLineEdit::textEdited, config, &Config::setLoginName );
|
||||
connect( config, &Config::loginNameChanged, ui->textBoxLoginName, &QLineEdit::setText );
|
||||
connect( config, &Config::loginNameStatusChanged, this, &UsersPage::reportLoginNameStatus );
|
||||
|
||||
connect( ui->checkBoxDoAutoLogin, &QCheckBox::stateChanged, this, [this]( int checked ) {
|
||||
m_config->setAutoLogin( checked != Qt::Unchecked );
|
||||
} );
|
||||
connect( config, &Config::autoLoginChanged, ui->checkBoxDoAutoLogin, &QCheckBox::setChecked );
|
||||
|
||||
ui->checkBoxReusePassword->setVisible( m_config->writeRootPassword() );
|
||||
ui->checkBoxReusePassword->setChecked( true );
|
||||
ui->checkBoxValidatePassword->setChecked( true );
|
||||
|
||||
@ -146,13 +167,13 @@ UsersPage::retranslate()
|
||||
ui->retranslateUi( this );
|
||||
if ( Calamares::Settings::instance()->isSetupMode() )
|
||||
{
|
||||
ui->textBoxUsername->setToolTip( tr( "<small>If more than one person will "
|
||||
ui->textBoxLoginName->setToolTip( tr( "<small>If more than one person will "
|
||||
"use this computer, you can create multiple "
|
||||
"accounts after setup.</small>" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->textBoxUsername->setToolTip( tr( "<small>If more than one person will "
|
||||
ui->textBoxLoginName->setToolTip( tr( "<small>If more than one person will "
|
||||
"use this computer, you can create multiple "
|
||||
"accounts after installation.</small>" ) );
|
||||
}
|
||||
@ -165,27 +186,19 @@ UsersPage::retranslate()
|
||||
|
||||
|
||||
bool
|
||||
UsersPage::isReady()
|
||||
UsersPage::isReady() const
|
||||
{
|
||||
bool readyFields = m_readyFullName && m_readyHostname && m_readyPassword && m_readyUsername;
|
||||
if ( !m_writeRootPassword || ui->checkBoxReusePassword->isChecked() )
|
||||
{
|
||||
// If we're going to write a root password, we need a valid one (or reuse the user's password)
|
||||
readyFields
|
||||
&= m_config->writeRootPassword() ? ( m_readyRootPassword || ui->checkBoxReusePassword->isChecked() ) : true;
|
||||
return readyFields;
|
||||
}
|
||||
|
||||
return readyFields && m_readyRootPassword;
|
||||
}
|
||||
|
||||
QString
|
||||
UsersPage::getHostname() const
|
||||
{
|
||||
return ui->textBoxHostname->text();
|
||||
}
|
||||
|
||||
QString
|
||||
UsersPage::getRootPassword() const
|
||||
{
|
||||
if ( m_writeRootPassword )
|
||||
if ( m_config->writeRootPassword() )
|
||||
{
|
||||
if ( ui->checkBoxReusePassword->isChecked() )
|
||||
{
|
||||
@ -205,42 +218,24 @@ UsersPage::getRootPassword() const
|
||||
QPair< QString, QString >
|
||||
UsersPage::getUserPassword() const
|
||||
{
|
||||
return QPair< QString, QString >( ui->textBoxUsername->text(), ui->textBoxUserPassword->text() );
|
||||
return QPair< QString, QString >( m_config->loginName(), ui->textBoxUserPassword->text() );
|
||||
}
|
||||
|
||||
QList< Calamares::job_ptr >
|
||||
UsersPage::createJobs( const QStringList& defaultGroupsList )
|
||||
void
|
||||
UsersPage::fillGlobalStorage() const
|
||||
{
|
||||
QList< Calamares::job_ptr > list;
|
||||
if ( !isReady() )
|
||||
{
|
||||
return list;
|
||||
return;
|
||||
}
|
||||
|
||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
|
||||
Calamares::Job* j;
|
||||
j = new CreateUserJob( ui->textBoxUsername->text(),
|
||||
ui->textBoxFullName->text().isEmpty() ? ui->textBoxUsername->text()
|
||||
: ui->textBoxFullName->text(),
|
||||
ui->checkBoxAutoLogin->isChecked(),
|
||||
defaultGroupsList );
|
||||
list.append( Calamares::job_ptr( j ) );
|
||||
|
||||
if ( m_writeRootPassword )
|
||||
if ( m_config->writeRootPassword() )
|
||||
{
|
||||
gs->insert( "reuseRootPassword", ui->checkBoxReusePassword->isChecked() );
|
||||
}
|
||||
gs->insert( "hostname", ui->textBoxHostname->text() );
|
||||
if ( ui->checkBoxAutoLogin->isChecked() )
|
||||
{
|
||||
gs->insert( "autologinUser", ui->textBoxUsername->text() );
|
||||
}
|
||||
|
||||
gs->insert( "username", ui->textBoxUsername->text() );
|
||||
gs->insert( "password", CalamaresUtils::obscure( ui->textBoxUserPassword->text() ) );
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@ -254,215 +249,23 @@ UsersPage::onActivate()
|
||||
|
||||
|
||||
void
|
||||
UsersPage::setWriteRootPassword( bool write )
|
||||
UsersPage::onFullNameTextEdited( const QString& fullName )
|
||||
{
|
||||
m_writeRootPassword = write;
|
||||
ui->checkBoxReusePassword->setVisible( write );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UsersPage::onFullNameTextEdited( const QString& textRef )
|
||||
{
|
||||
if ( textRef.isEmpty() )
|
||||
{
|
||||
ui->labelFullNameError->clear();
|
||||
ui->labelFullName->clear();
|
||||
if ( !m_customUsername )
|
||||
{
|
||||
ui->textBoxUsername->clear();
|
||||
}
|
||||
if ( !m_customHostname )
|
||||
{
|
||||
ui->textBoxHostname->clear();
|
||||
}
|
||||
m_readyFullName = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->labelFullName->setPixmap(
|
||||
CalamaresUtils::defaultPixmap( CalamaresUtils::Yes, CalamaresUtils::Original, ui->labelFullName->size() ) );
|
||||
m_readyFullName = true;
|
||||
fillSuggestions();
|
||||
}
|
||||
labelStatus( ui->labelFullName, ui->labelFullNameError, fullName, QString(), m_readyFullName );
|
||||
checkReady( isReady() );
|
||||
}
|
||||
|
||||
/** @brief Guess the machine's name
|
||||
*
|
||||
* If there is DMI data, use that; otherwise, just call the machine "-pc".
|
||||
* Reads the DMI data just once.
|
||||
*/
|
||||
static QString
|
||||
guessProductName()
|
||||
{
|
||||
static bool tried = false;
|
||||
static QString dmiProduct;
|
||||
|
||||
if ( !tried )
|
||||
{
|
||||
// yes validateHostnameText() but these files can be a mess
|
||||
QRegExp dmirx( "[^a-zA-Z0-9]", Qt::CaseInsensitive );
|
||||
QFile dmiFile( QStringLiteral( "/sys/devices/virtual/dmi/id/product_name" ) );
|
||||
|
||||
if ( dmiFile.exists() && dmiFile.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
dmiProduct = QString::fromLocal8Bit( dmiFile.readAll().simplified().data() )
|
||||
.toLower()
|
||||
.replace( dmirx, " " )
|
||||
.remove( ' ' );
|
||||
}
|
||||
if ( dmiProduct.isEmpty() )
|
||||
{
|
||||
dmiProduct = QStringLiteral( "-pc" );
|
||||
}
|
||||
tried = true;
|
||||
}
|
||||
return dmiProduct;
|
||||
}
|
||||
|
||||
void
|
||||
UsersPage::fillSuggestions()
|
||||
UsersPage::reportLoginNameStatus( const QString& status )
|
||||
{
|
||||
QString fullName = ui->textBoxFullName->text();
|
||||
QRegExp rx( "[^a-zA-Z0-9 ]", Qt::CaseInsensitive );
|
||||
QString cleanName = CalamaresUtils::removeDiacritics( fullName ).toLower().replace( rx, " " ).simplified();
|
||||
QStringList cleanParts = cleanName.split( ' ' );
|
||||
|
||||
if ( !m_customUsername )
|
||||
{
|
||||
if ( !cleanParts.isEmpty() && !cleanParts.first().isEmpty() )
|
||||
{
|
||||
QString usernameSuggestion = cleanParts.first();
|
||||
for ( int i = 1; i < cleanParts.length(); ++i )
|
||||
{
|
||||
if ( !cleanParts.value( i ).isEmpty() )
|
||||
{
|
||||
usernameSuggestion.append( cleanParts.value( i ).at( 0 ) );
|
||||
}
|
||||
}
|
||||
if ( USERNAME_RX.indexIn( usernameSuggestion ) != -1 )
|
||||
{
|
||||
ui->textBoxUsername->setText( usernameSuggestion );
|
||||
validateUsernameText( usernameSuggestion );
|
||||
m_customUsername = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !m_customHostname )
|
||||
{
|
||||
if ( !cleanParts.isEmpty() && !cleanParts.first().isEmpty() )
|
||||
{
|
||||
QString hostnameSuggestion;
|
||||
QString productName = guessProductName();
|
||||
hostnameSuggestion = QString( "%1-%2" ).arg( cleanParts.first() ).arg( productName );
|
||||
if ( HOSTNAME_RX.indexIn( hostnameSuggestion ) != -1 )
|
||||
{
|
||||
ui->textBoxHostname->setText( hostnameSuggestion );
|
||||
validateHostnameText( hostnameSuggestion );
|
||||
m_customHostname = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UsersPage::onUsernameTextEdited( const QString& textRef )
|
||||
{
|
||||
m_customUsername = true;
|
||||
validateUsernameText( textRef );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UsersPage::validateUsernameText( const QString& textRef )
|
||||
{
|
||||
QString text( textRef );
|
||||
QRegExpValidator val_whole( USERNAME_RX );
|
||||
QRegExpValidator val_start( QRegExp( "[a-z_].*" ) ); // anchors are implicit in QRegExpValidator
|
||||
int pos = -1;
|
||||
|
||||
if ( text.isEmpty() )
|
||||
{
|
||||
ui->labelUsernameError->clear();
|
||||
ui->labelUsername->clear();
|
||||
m_readyUsername = false;
|
||||
}
|
||||
else if ( text.length() > USERNAME_MAX_LENGTH )
|
||||
{
|
||||
labelError( ui->labelUsername, ui->labelUsernameError, tr( "Your username is too long." ) );
|
||||
m_readyUsername = false;
|
||||
}
|
||||
else if ( val_start.validate( text, pos ) == QValidator::Invalid )
|
||||
{
|
||||
labelError( ui->labelUsername,
|
||||
ui->labelUsernameError,
|
||||
tr( "Your username must start with a lowercase letter or underscore." ) );
|
||||
m_readyUsername = false;
|
||||
}
|
||||
else if ( val_whole.validate( text, pos ) == QValidator::Invalid )
|
||||
{
|
||||
labelError( ui->labelUsername,
|
||||
ui->labelUsernameError,
|
||||
tr( "Only lowercase letters, numbers, underscore and hyphen are allowed." ) );
|
||||
m_readyUsername = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
labelOk( ui->labelUsername, ui->labelUsernameError );
|
||||
m_readyUsername = true;
|
||||
}
|
||||
|
||||
labelStatus( ui->labelUsername, ui->labelUsernameError, m_config->loginName(), status, m_readyUsername );
|
||||
emit checkReady( isReady() );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UsersPage::onHostnameTextEdited( const QString& textRef )
|
||||
UsersPage::reportHostNameStatus( const QString& status )
|
||||
{
|
||||
m_customHostname = true;
|
||||
validateHostnameText( textRef );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UsersPage::validateHostnameText( const QString& textRef )
|
||||
{
|
||||
QString text = textRef;
|
||||
QRegExpValidator val( HOSTNAME_RX );
|
||||
int pos = -1;
|
||||
|
||||
if ( text.isEmpty() )
|
||||
{
|
||||
ui->labelHostnameError->clear();
|
||||
ui->labelHostname->clear();
|
||||
m_readyHostname = false;
|
||||
}
|
||||
else if ( text.length() < HOSTNAME_MIN_LENGTH )
|
||||
{
|
||||
labelError( ui->labelHostname, ui->labelHostnameError, tr( "Your hostname is too short." ) );
|
||||
m_readyHostname = false;
|
||||
}
|
||||
else if ( text.length() > HOSTNAME_MAX_LENGTH )
|
||||
{
|
||||
labelError( ui->labelHostname, ui->labelHostnameError, tr( "Your hostname is too long." ) );
|
||||
m_readyHostname = false;
|
||||
}
|
||||
else if ( val.validate( text, pos ) == QValidator::Invalid )
|
||||
{
|
||||
labelError( ui->labelHostname,
|
||||
ui->labelHostnameError,
|
||||
tr( "Only letters, numbers, underscore and hyphen are allowed." ) );
|
||||
m_readyHostname = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
labelOk( ui->labelHostname, ui->labelHostnameError );
|
||||
m_readyHostname = true;
|
||||
}
|
||||
|
||||
labelStatus( ui->labelHostname, ui->labelHostnameError, m_config->hostName(), status, m_readyHostname );
|
||||
emit checkReady( isReady() );
|
||||
}
|
||||
|
||||
@ -546,13 +349,6 @@ UsersPage::setValidatePasswordDefault( bool checked )
|
||||
emit checkReady( isReady() );
|
||||
}
|
||||
|
||||
void
|
||||
UsersPage::setAutologinDefault( bool checked )
|
||||
{
|
||||
ui->checkBoxAutoLogin->setChecked( checked );
|
||||
emit checkReady( isReady() );
|
||||
}
|
||||
|
||||
void
|
||||
UsersPage::setReusePasswordDefault( bool checked )
|
||||
{
|
||||
|
@ -25,10 +25,11 @@
|
||||
#define USERSPAGE_H
|
||||
|
||||
#include "CheckPWQuality.h"
|
||||
#include "Job.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class Config;
|
||||
|
||||
class QLabel;
|
||||
|
||||
namespace Ui
|
||||
@ -40,19 +41,17 @@ class UsersPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UsersPage( QWidget* parent = nullptr );
|
||||
explicit UsersPage( Config* config, QWidget* parent = nullptr );
|
||||
virtual ~UsersPage();
|
||||
|
||||
bool isReady();
|
||||
bool isReady() const;
|
||||
|
||||
Calamares::JobList createJobs( const QStringList& defaultGroupsList );
|
||||
void fillGlobalStorage() const;
|
||||
|
||||
void onActivate();
|
||||
|
||||
void setWriteRootPassword( bool show );
|
||||
void setPasswordCheckboxVisible( bool visible );
|
||||
void setValidatePasswordDefault( bool checked );
|
||||
void setAutologinDefault( bool checked );
|
||||
void setReusePasswordDefault( bool checked );
|
||||
|
||||
/** @brief Process entries in the passwordRequirements config entry
|
||||
@ -63,8 +62,6 @@ public:
|
||||
*/
|
||||
void addPasswordCheck( const QString& key, const QVariant& value );
|
||||
|
||||
///@brief Hostname as entered / auto-filled
|
||||
QString getHostname() const;
|
||||
///@brief Root password, depends on settings, may be empty
|
||||
QString getRootPassword() const;
|
||||
///@brief User name and password
|
||||
@ -72,11 +69,8 @@ public:
|
||||
|
||||
protected slots:
|
||||
void onFullNameTextEdited( const QString& );
|
||||
void fillSuggestions();
|
||||
void onUsernameTextEdited( const QString& );
|
||||
void validateUsernameText( const QString& );
|
||||
void onHostnameTextEdited( const QString& );
|
||||
void validateHostnameText( const QString& );
|
||||
void reportLoginNameStatus( const QString& );
|
||||
void reportHostNameStatus( const QString& );
|
||||
void onPasswordTextChanged( const QString& );
|
||||
void onRootPasswordTextChanged( const QString& );
|
||||
|
||||
@ -95,19 +89,16 @@ private:
|
||||
void retranslate();
|
||||
|
||||
Ui::Page_UserSetup* ui;
|
||||
Config* m_config;
|
||||
|
||||
PasswordCheckList m_passwordChecks;
|
||||
bool m_passwordChecksChanged = false;
|
||||
|
||||
bool m_readyFullName;
|
||||
bool m_readyUsername;
|
||||
bool m_customUsername;
|
||||
bool m_readyHostname;
|
||||
bool m_customHostname;
|
||||
bool m_readyPassword;
|
||||
bool m_readyRootPassword;
|
||||
|
||||
bool m_writeRootPassword;
|
||||
};
|
||||
|
||||
#endif // USERSPAGE_H
|
||||
|
@ -20,17 +20,18 @@
|
||||
|
||||
#include "UsersViewStep.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "CreateUserJob.h"
|
||||
#include "SetHostNameJob.h"
|
||||
#include "SetPasswordJob.h"
|
||||
#include "UsersPage.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/NamedEnum.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( UsersViewStepFactory, registerPlugin< UsersViewStep >(); )
|
||||
|
||||
static const NamedEnumTable< SetHostNameJob::Action >&
|
||||
@ -53,11 +54,11 @@ hostnameActions()
|
||||
|
||||
UsersViewStep::UsersViewStep( QObject* parent )
|
||||
: Calamares::ViewStep( parent )
|
||||
, m_widget( new UsersPage() )
|
||||
, m_widget( nullptr )
|
||||
, m_actions( SetHostNameJob::Action::None )
|
||||
, m_config( new Config( this ) )
|
||||
{
|
||||
emit nextStatusChanged( true );
|
||||
connect( m_widget, &UsersPage::checkReady, this, &UsersViewStep::nextStatusChanged );
|
||||
}
|
||||
|
||||
|
||||
@ -80,6 +81,11 @@ UsersViewStep::prettyName() const
|
||||
QWidget*
|
||||
UsersViewStep::widget()
|
||||
{
|
||||
if ( !m_widget )
|
||||
{
|
||||
m_widget = new UsersPage( m_config );
|
||||
connect( m_widget, &UsersPage::checkReady, this, &UsersViewStep::nextStatusChanged );
|
||||
}
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
@ -87,7 +93,7 @@ UsersViewStep::widget()
|
||||
bool
|
||||
UsersViewStep::isNextEnabled() const
|
||||
{
|
||||
return m_widget->isReady();
|
||||
return m_widget ? m_widget->isReady() : true;
|
||||
}
|
||||
|
||||
|
||||
@ -122,7 +128,10 @@ UsersViewStep::jobs() const
|
||||
void
|
||||
UsersViewStep::onActivate()
|
||||
{
|
||||
if ( m_widget )
|
||||
{
|
||||
m_widget->onActivate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -130,9 +139,17 @@ void
|
||||
UsersViewStep::onLeave()
|
||||
{
|
||||
m_jobs.clear();
|
||||
m_jobs.append( m_widget->createJobs( m_defaultGroups ) );
|
||||
if ( !m_widget || !m_widget->isReady() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Calamares::Job* j;
|
||||
// TODO: Config object should create jobs, like this one, that depend only on config values
|
||||
j = new CreateUserJob( m_config->loginName(),
|
||||
m_config->fullName().isEmpty() ? m_config->loginName() : m_config->fullName(),
|
||||
m_config->doAutoLogin(),
|
||||
m_config->defaultGroups() );
|
||||
|
||||
auto userPW = m_widget->getUserPassword();
|
||||
j = new SetPasswordJob( userPW.first, userPW.second );
|
||||
@ -141,46 +158,20 @@ UsersViewStep::onLeave()
|
||||
j = new SetPasswordJob( "root", m_widget->getRootPassword() );
|
||||
m_jobs.append( Calamares::job_ptr( j ) );
|
||||
|
||||
j = new SetHostNameJob( m_widget->getHostname(), m_actions );
|
||||
j = new SetHostNameJob( m_config->hostName(), m_actions );
|
||||
m_jobs.append( Calamares::job_ptr( j ) );
|
||||
|
||||
m_widget->fillGlobalStorage();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
// Create the widget, after all .. as long as writing configuration to the UI is needed
|
||||
(void)this->widget();
|
||||
using CalamaresUtils::getBool;
|
||||
|
||||
if ( configurationMap.contains( "defaultGroups" )
|
||||
&& configurationMap.value( "defaultGroups" ).type() == QVariant::List )
|
||||
{
|
||||
m_defaultGroups = configurationMap.value( "defaultGroups" ).toStringList();
|
||||
}
|
||||
else
|
||||
{
|
||||
cWarning() << "Using fallback groups. Please check defaultGroups in users.conf";
|
||||
m_defaultGroups = QStringList { "lp", "video", "network", "storage", "wheel", "audio" };
|
||||
}
|
||||
|
||||
if ( configurationMap.contains( "autologinGroup" )
|
||||
&& configurationMap.value( "autologinGroup" ).type() == QVariant::String )
|
||||
{
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert(
|
||||
"autologinGroup", configurationMap.value( "autologinGroup" ).toString() );
|
||||
}
|
||||
|
||||
if ( configurationMap.contains( "sudoersGroup" )
|
||||
&& configurationMap.value( "sudoersGroup" ).type() == QVariant::String )
|
||||
{
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert( "sudoersGroup",
|
||||
configurationMap.value( "sudoersGroup" ).toString() );
|
||||
}
|
||||
|
||||
bool setRootPassword = getBool( configurationMap, "setRootPassword", true );
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert( "setRootPassword", setRootPassword );
|
||||
|
||||
m_widget->setWriteRootPassword( setRootPassword );
|
||||
m_widget->setAutologinDefault( getBool( configurationMap, "doAutologin", false ) );
|
||||
m_widget->setReusePasswordDefault( getBool( configurationMap, "doReusePassword", false ) );
|
||||
|
||||
if ( configurationMap.contains( "passwordRequirements" )
|
||||
@ -197,15 +188,6 @@ UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
m_widget->setPasswordCheckboxVisible( getBool( configurationMap, "allowWeakPasswords", false ) );
|
||||
m_widget->setValidatePasswordDefault( !getBool( configurationMap, "allowWeakPasswordsDefault", false ) );
|
||||
|
||||
QString shell( QLatin1String( "/bin/bash" ) ); // as if it's not set at all
|
||||
if ( configurationMap.contains( "userShell" ) )
|
||||
{
|
||||
shell = CalamaresUtils::getString( configurationMap, "userShell" );
|
||||
}
|
||||
// Now it might be explicitly set to empty, which is ok
|
||||
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert( "userShell", shell );
|
||||
|
||||
using Action = SetHostNameJob::Action;
|
||||
|
||||
QString hostnameActionString = CalamaresUtils::getString( configurationMap, "setHostname" );
|
||||
@ -222,4 +204,6 @@ UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
|
||||
Action hostsfileAction = getBool( configurationMap, "writeHostsFile", true ) ? Action::WriteEtcHosts : Action::None;
|
||||
m_actions = hostsfileAction | hostnameAction;
|
||||
|
||||
m_config->setConfigurationMap( configurationMap );
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
class Config;
|
||||
class UsersPage;
|
||||
|
||||
class PLUGINDLLEXPORT UsersViewStep : public Calamares::ViewStep
|
||||
@ -60,8 +61,9 @@ private:
|
||||
UsersPage* m_widget;
|
||||
QList< Calamares::job_ptr > m_jobs;
|
||||
|
||||
QStringList m_defaultGroups;
|
||||
SetHostNameJob::Actions m_actions;
|
||||
|
||||
Config* m_config;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( UsersViewStepFactory )
|
||||
|
@ -127,7 +127,7 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="textBoxUsername">
|
||||
<widget class="QLineEdit" name="textBoxLoginName">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@ -226,7 +226,7 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="textBoxHostname">
|
||||
<widget class="QLineEdit" name="textBoxHostName">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@ -456,7 +456,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxAutoLogin">
|
||||
<widget class="QCheckBox" name="checkBoxDoAutoLogin">
|
||||
<property name="text">
|
||||
<string>Log in automatically without asking for the password.</string>
|
||||
</property>
|
||||
|
Loading…
Reference in New Issue
Block a user