: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) #
|
# 3.2.28 (unreleased) #
|
||||||
|
|
||||||
This release contains contributions from (alphabetically by first name):
|
This release contains contributions from (alphabetically by first name):
|
||||||
- No external contributors yet
|
- Anke Boersma
|
||||||
|
- apt-ghetto
|
||||||
|
|
||||||
## Core ##
|
## 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 ##
|
## 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) #
|
# 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
|
# copy these four lines to four backup lines, add "p", and then update
|
||||||
# the original four lines with the current translations).
|
# the original four lines with the current translations).
|
||||||
#
|
#
|
||||||
# Total 66 languages
|
# Total 68 languages
|
||||||
set( _tx_complete az az_AZ ca he hi hr ja sq tr_TR uk zh_TW )
|
set( _tx_complete az az_AZ ca da fi_FI he hi hr ja pt_BR sq tr_TR
|
||||||
set( _tx_good as ast be cs_CZ da de es fi_FI fr hu it_IT ko lt ml
|
uk zh_TW )
|
||||||
nl pt_BR pt_PT ru sk sv zh_CN )
|
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
|
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 )
|
ro sl sr sr@latin tg th )
|
||||||
set( _tx_incomplete bn ca@valencia eo fr_CH gu kk kn lo lv mk ne_NP
|
set( _tx_incomplete bn ca@valencia eo fr_CH gu ie kk kn lo lv mk
|
||||||
ur uz )
|
ne_NP ur uz )
|
||||||
|
|
||||||
### Required versions
|
### Required versions
|
||||||
#
|
#
|
||||||
@ -355,23 +356,33 @@ set_package_properties(
|
|||||||
URL "https://python.org"
|
URL "https://python.org"
|
||||||
PURPOSE "Python 3 interpreter for certain tests."
|
PURPOSE "Python 3 interpreter for certain tests."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set( _schema_explanation "" )
|
||||||
if ( PYTHONINTERP_FOUND )
|
if ( PYTHONINTERP_FOUND )
|
||||||
message(STATUS "Found Python 3 interpreter ${PYTHON_EXECUTABLE}")
|
|
||||||
if ( BUILD_SCHEMA_TESTING )
|
if ( BUILD_SCHEMA_TESTING )
|
||||||
# The configuration validator script has some dependencies,
|
# The configuration validator script has some dependencies,
|
||||||
# and if they are not installed, don't run. If errors out
|
# and if they are not installed, don't run. If errors out
|
||||||
# with exit(1) on missing dependencies.
|
# with exit(1) on missing dependencies.
|
||||||
exec_program( ${PYTHON_EXECUTABLE} ARGS "${CMAKE_SOURCE_DIR}/ci/configvalidator.py" -x RETURN_VALUE _validator_deps )
|
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
|
# It should never succeed, but only returns 1 when the imports fail
|
||||||
if ( _validator_deps EQUAL 1 )
|
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 )
|
set( BUILD_SCHEMA_TESTING OFF )
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
# Can't run schema tests without Python3.
|
# Can't run schema tests without Python3.
|
||||||
|
set( _schema_explanation " Missing Python3." )
|
||||||
set( BUILD_SCHEMA_TESTING OFF )
|
set( BUILD_SCHEMA_TESTING OFF )
|
||||||
endif()
|
endif()
|
||||||
|
add_feature_info( yaml-schema BUILD_SCHEMA_TESTING "Validate YAML (config files) with schema.${_schema_explanation}" )
|
||||||
|
|
||||||
find_package( PythonLibs ${PYTHONLIBS_VERSION} )
|
find_package( PythonLibs ${PYTHONLIBS_VERSION} )
|
||||||
set_package_properties(
|
set_package_properties(
|
||||||
PythonLibs PROPERTIES
|
PythonLibs PROPERTIES
|
||||||
@ -536,7 +547,11 @@ if( CALAMARES_VERSION_RC )
|
|||||||
set( CALAMARES_VERSION ${CALAMARES_VERSION}rc${CALAMARES_VERSION_RC} )
|
set( CALAMARES_VERSION ${CALAMARES_VERSION}rc${CALAMARES_VERSION_RC} )
|
||||||
endif()
|
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/" )
|
if( NOT BUILD_RELEASE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git/" )
|
||||||
include( CMakeDateStamp )
|
include( CMakeDateStamp )
|
||||||
set( CALAMARES_VERSION_DATE "${CMAKE_DATESTAMP_YEAR}${CMAKE_DATESTAMP_MONTH}${CMAKE_DATESTAMP_DAY}" )
|
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})
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
# add resources from current dir
|
# add resources from current dir
|
||||||
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${LIBRARY_RESOURCES}")
|
if(LIBRARY_RESOURCES)
|
||||||
qt5_add_resources(LIBRARY_RC_SOURCES "${LIBRARY_RESOURCES}")
|
list(APPEND LIBRARY_SOURCES ${LIBRARY_RESOURCES})
|
||||||
list(APPEND LIBRARY_SOURCES ${LIBRARY_RC_SOURCES})
|
|
||||||
unset(LIBRARY_RC_SOURCES)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# add target
|
# add target
|
||||||
@ -81,6 +79,9 @@ function(calamares_add_library)
|
|||||||
if(LIBRARY_UI)
|
if(LIBRARY_UI)
|
||||||
calamares_autouic(${target} ${LIBRARY_UI})
|
calamares_autouic(${target} ${LIBRARY_UI})
|
||||||
endif()
|
endif()
|
||||||
|
if(LIBRARY_RESOURCES)
|
||||||
|
calamares_autorcc(${target} ${LIBRARY_RESOURCES})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(LIBRARY_EXPORT_MACRO)
|
if(LIBRARY_EXPORT_MACRO)
|
||||||
set_target_properties(${target} PROPERTIES COMPILE_DEFINITIONS ${LIBRARY_EXPORT_MACRO})
|
set_target_properties(${target} PROPERTIES COMPILE_DEFINITIONS ${LIBRARY_EXPORT_MACRO})
|
||||||
|
@ -22,50 +22,15 @@
|
|||||||
|
|
||||||
include( CMakeParseArguments )
|
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
|
# Internal macro for adding the C++ / Qt translations to the
|
||||||
# build and install tree. Should be called only once, from
|
# build and install tree. Should be called only once, from
|
||||||
# src/calamares/CMakeLists.txt.
|
# src/calamares/CMakeLists.txt.
|
||||||
macro(add_calamares_translations language)
|
macro(add_calamares_translations language)
|
||||||
list( APPEND CALAMARES_LANGUAGES ${ARGV} )
|
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
|
# calamares and qt language files
|
||||||
set( calamares_i18n_qrc_content "${calamares_i18n_qrc_content}<qresource prefix=\"/lang\">\n" )
|
|
||||||
foreach( lang ${CALAMARES_LANGUAGES} )
|
foreach( lang ${CALAMARES_LANGUAGES} )
|
||||||
foreach( tlsource "calamares_${lang}" "tz_${lang}" )
|
foreach( tlsource "calamares_${lang}" "tz_${lang}" )
|
||||||
if( EXISTS "${CMAKE_SOURCE_DIR}/lang/${tlsource}.ts" )
|
if( EXISTS "${CMAKE_SOURCE_DIR}/lang/${tlsource}.ts" )
|
||||||
@ -75,31 +40,19 @@ macro(add_calamares_translations language)
|
|||||||
endforeach()
|
endforeach()
|
||||||
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_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_infile ${CMAKE_CURRENT_BINARY_DIR}/${trans_file}.qrc )
|
||||||
set( trans_outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${trans_file}.cxx )
|
set( trans_outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${trans_file}.cxx )
|
||||||
|
|
||||||
# Copy the QRC file to the output directory
|
configure_file( ${CMAKE_SOURCE_DIR}/lang/calamares_i18n.qrc.in ${trans_infile} @ONLY )
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${trans_infile}
|
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy ${trans_srcfile} ${trans_infile}
|
|
||||||
MAIN_DEPENDENCY ${trans_srcfile}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run the resource compiler (rcc_options should already be set)
|
# Run the resource compiler (rcc_options should already be set)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${trans_outfile}
|
OUTPUT ${trans_outfile}
|
||||||
COMMAND "${Qt5Core_RCC_EXECUTABLE}"
|
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}
|
MAIN_DEPENDENCY ${trans_infile}
|
||||||
DEPENDS ${QM_FILES}
|
DEPENDS ${QM_FILES}
|
||||||
)
|
)
|
||||||
|
@ -18,17 +18,28 @@
|
|||||||
#
|
#
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
# 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.
|
||||||
#
|
#
|
||||||
# Sets AUTOMOC TRUE for a target.
|
# calamares_automoc(target)
|
||||||
|
# Sets AUTOMOC TRUE for a target.
|
||||||
#
|
#
|
||||||
# If the global variable CALAMARES_AUTOMOC_OPTIONS is set, uses that
|
# If the global variable CALAMARES_AUTOMOC_OPTIONS is set, uses that
|
||||||
# as well to set options passed to MOC. This can be used to add
|
# as well to set options passed to MOC. This can be used to add
|
||||||
# libcalamares/utils/moc-warnings.h file to the moc, which in turn
|
# libcalamares/utils/moc-warnings.h file to the moc, which in turn
|
||||||
# reduces compiler warnings in generated MOC code.
|
# reduces compiler warnings in generated MOC code.
|
||||||
#
|
#
|
||||||
# If the global variable CALAMARES_AUTOUIC_OPTIONS is set, adds that
|
# calamares_autouic(target [uifile ..])
|
||||||
# to the options passed to uic.
|
# Sets AUTOUIC TRUE for a target.
|
||||||
|
#
|
||||||
|
# If the global variable CALAMARES_AUTOUIC_OPTIONS is set, adds that
|
||||||
|
# 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)
|
function(calamares_automoc TARGET)
|
||||||
set_target_properties( ${TARGET} PROPERTIES AUTOMOC TRUE )
|
set_target_properties( ${TARGET} PROPERTIES AUTOMOC TRUE )
|
||||||
@ -45,3 +56,12 @@ function(calamares_autouic TARGET)
|
|||||||
endforeach()
|
endforeach()
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
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
|
Icon[hr]=calamares
|
||||||
GenericName[hr]=Instalacija sustava
|
GenericName[hr]=Instalacija sustava
|
||||||
Comment[hr]=Calamares — 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
|
Name[hu]=Rendszer telepítése
|
||||||
Icon[hu]=calamares
|
Icon[hu]=calamares
|
||||||
GenericName[hu]=Rendszertelepítő
|
GenericName[hu]=Rendszertelepítő
|
||||||
@ -184,6 +188,10 @@ Name[sv]=Installera system
|
|||||||
Icon[sv]=calamares
|
Icon[sv]=calamares
|
||||||
GenericName[sv]=Systeminstallerare
|
GenericName[sv]=Systeminstallerare
|
||||||
Comment[sv]=Calamares — Systeminstallerare
|
Comment[sv]=Calamares — Systeminstallerare
|
||||||
|
Name[tg]=Насбкунии низом
|
||||||
|
Icon[tg]=calamares
|
||||||
|
GenericName[tg]=Насбкунандаи низом
|
||||||
|
Comment[tg]=Calamares — Насбкунандаи низом
|
||||||
Name[th]=ติดตั้งระบบ
|
Name[th]=ติดตั้งระบบ
|
||||||
Name[uk]=Встановити Систему
|
Name[uk]=Встановити Систему
|
||||||
Icon[uk]=calamares
|
Icon[uk]=calamares
|
||||||
|
112
ci/RELEASE.md
112
ci/RELEASE.md
@ -1,21 +1,13 @@
|
|||||||
# Calamares Release Process
|
# Calamares Release Process
|
||||||
|
|
||||||
> Calamares releases are now rolling when-they-are-ready releases.
|
> 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.
|
> LTS releases resume, these steps may be edited again.
|
||||||
>
|
>
|
||||||
> Most things are automated through the release script [RELEASE.sh](RELEASE.sh)
|
> 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 sure all tests pass.
|
||||||
```
|
```
|
||||||
make
|
make
|
||||||
@ -25,16 +17,6 @@
|
|||||||
an additional environment variable to be set for some tests, which will
|
an additional environment variable to be set for some tests, which will
|
||||||
destroy an attached disk. This is not always desirable. There are some
|
destroy an attached disk. This is not always desirable. There are some
|
||||||
sample config-files that are empty and which fail the config-tests.
|
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
|
* 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
|
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
|
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
|
fairly complete translations, or use `ci/txstats.py` for an automated
|
||||||
suggestion. If there are changes, commit them.
|
suggestion. If there are changes, commit them.
|
||||||
* Push the changes.
|
* 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.
|
* 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.
|
* Edit `CHANGES` and set the date of the release.
|
||||||
* Commit both. This is usually done with commit-message
|
* Commit both. This is usually done with commit-message
|
||||||
*Changes: pre-release housekeeping*.
|
*Changes: pre-release housekeeping*.
|
||||||
@ -73,44 +50,16 @@
|
|||||||
On success, it prints out a suitable signature- and SHA256 blurb
|
On success, it prints out a suitable signature- and SHA256 blurb
|
||||||
for use in the release announcement.
|
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
|
Follow the instructions printed by the release script.
|
||||||
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).
|
|
||||||
|
|
||||||
### (2.2) Tag
|
* Push the tags.
|
||||||
|
* Create a new release on GitHub.
|
||||||
* `git tag -s v1.1.0` Make sure the signing key is known in GitHub, so that the
|
* Upload tarball and signature.
|
||||||
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.
|
|
||||||
* Publish release article on `calamares.io`.
|
* Publish release article on `calamares.io`.
|
||||||
* Publicize on social networks.
|
* Close associated milestone on GitHub if it's entirely done.
|
||||||
* Close associated milestone on GitHub if this is the actual release.
|
* Update topic on #calamares IRC channel.
|
||||||
* Publish blog post.
|
|
||||||
|
|
||||||
## (4) Post-Release
|
## (4) Post-Release
|
||||||
|
|
||||||
@ -133,3 +82,44 @@ This release contains contributions from (alphabetically by first name):
|
|||||||
## Modules ##
|
## Modules ##
|
||||||
- No module changes yet
|
- 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>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="245"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="245"/>
|
||||||
<source><h1>Welcome to the Calamares setup program for %1</h1></source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="246"/>
|
<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>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="252"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="252"/>
|
||||||
<source><h1>Welcome to the %1 installer</h1></source>
|
<source><h1>Welcome to the %1 installer</h1></source>
|
||||||
<translation type="unfinished"/>
|
<translation><h1>Vítejte v instalátoru %1.</h1></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -3424,7 +3424,7 @@ Výstup:
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="202"/>
|
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="202"/>
|
||||||
<source>KDE user feedback</source>
|
<source>KDE user feedback</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Zpětná vazba uživatele KDE</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="214"/>
|
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="214"/>
|
||||||
@ -3445,7 +3445,7 @@ Výstup:
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="243"/>
|
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="243"/>
|
||||||
<source>Could not configure KDE user feedback correctly, Calamares error %1.</source>
|
<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>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -792,7 +792,7 @@ Dies wird das Installationsprogramm beenden und alle Änderungen gehen verloren.
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="252"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="252"/>
|
||||||
<source><h1>Welcome to the %1 installer</h1></source>
|
<source><h1>Welcome to the %1 installer</h1></source>
|
||||||
<translation type="unfinished"/>
|
<translation><h1>Willkommen zum %1 Installationsprogramm</h1></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<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>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="245"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="245"/>
|
||||||
<source><h1>Welcome to the Calamares setup program for %1</h1></source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="246"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="246"/>
|
||||||
<source><h1>Welcome to %1 setup</h1></source>
|
<source><h1>Welcome to %1 setup</h1></source>
|
||||||
<translation type="unfinished"/>
|
<translation>Benvenuto nell'installazione di %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="251"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="251"/>
|
||||||
<source><h1>Welcome to the Calamares installer for %1</h1></source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="252"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="252"/>
|
||||||
<source><h1>Welcome to the %1 installer</h1></source>
|
<source><h1>Welcome to the %1 installer</h1></source>
|
||||||
<translation type="unfinished"/>
|
<translation>Benvenuto nel programma di installazione di %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<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
|
<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
|
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>
|
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>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1926,12 +1926,12 @@ Il programma d'installazione sarà terminato e tutte le modifiche andranno perse
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/localeq/Offline.qml" line="62"/>
|
<location filename="../src/modules/localeq/Offline.qml" line="62"/>
|
||||||
<source>Timezone: %1</source>
|
<source>Timezone: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Fuso orario: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/localeq/Offline.qml" line="77"/>
|
<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>
|
<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>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -2836,7 +2836,7 @@ Output:
|
|||||||
<location filename="../src/modules/welcomeq/Recommended.qml" line="49"/>
|
<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/>
|
<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>
|
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>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -2947,13 +2947,13 @@ Output:
|
|||||||
<location filename="../src/modules/welcomeq/Requirements.qml" line="47"/>
|
<location filename="../src/modules/welcomeq/Requirements.qml" line="47"/>
|
||||||
<source><p>This computer does not satisfy the minimum requirements for installing %1.<br/>
|
<source><p>This computer does not satisfy the minimum requirements for installing %1.<br/>
|
||||||
Installation cannot continue.</p></source>
|
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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcomeq/Requirements.qml" line="49"/>
|
<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/>
|
<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>
|
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>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -3419,12 +3419,12 @@ Output:
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="202"/>
|
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="202"/>
|
||||||
<source>KDE user feedback</source>
|
<source>KDE user feedback</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Riscontro dell'utente di KDE </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="214"/>
|
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="214"/>
|
||||||
<source>Configuring KDE user feedback.</source>
|
<source>Configuring KDE user feedback.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Sto configurando il riscontro dell'utente di KDE</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="236"/>
|
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="236"/>
|
||||||
@ -3865,7 +3865,7 @@ Output:
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/localeq/localeq.qml" line="98"/>
|
<location filename="../src/modules/localeq/localeq.qml" line="98"/>
|
||||||
<source>System language set to %1</source>
|
<source>System language set to %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Lingua di sistema impostata su %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/localeq/localeq.qml" line="106"/>
|
<location filename="../src/modules/localeq/localeq.qml" line="106"/>
|
||||||
|
@ -245,7 +245,7 @@
|
|||||||
<source>(%n second(s))</source>
|
<source>(%n second(s))</source>
|
||||||
<translation>
|
<translation>
|
||||||
<numerusform>(%n seconde)</numerusform>
|
<numerusform>(%n seconde)</numerusform>
|
||||||
<numerusform>(%n seconden)</numerusform>
|
<numerusform>(%n seconde(n))</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -528,7 +528,7 @@ Het installatieprogramma zal afsluiten en alle wijzigingen zullen verloren gaan.
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="334"/>
|
<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>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="833"/>
|
<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>
|
<message>
|
||||||
<location filename="../src/modules/welcome/checker/GeneralRequirements.cpp" line="214"/>
|
<location filename="../src/modules/welcome/checker/GeneralRequirements.cpp" line="214"/>
|
||||||
<source>The screen is too small to display the installer.</source>
|
<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>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1655,12 +1655,12 @@ Het installatieprogramma zal afsluiten en alle wijzigingen zullen verloren gaan.
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/license/LicenseWidget.cpp" line="194"/>
|
<location filename="../src/modules/license/LicenseWidget.cpp" line="194"/>
|
||||||
<source>Hide license text</source>
|
<source>Hide license text</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Verberg licentietekst</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/license/LicenseWidget.cpp" line="194"/>
|
<location filename="../src/modules/license/LicenseWidget.cpp" line="194"/>
|
||||||
<source>Show the license text</source>
|
<source>Show the license text</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Toon licentietekst</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/license/LicenseWidget.cpp" line="198"/>
|
<location filename="../src/modules/license/LicenseWidget.cpp" line="198"/>
|
||||||
@ -2268,7 +2268,7 @@ Het installatieprogramma zal afsluiten en alle wijzigingen zullen verloren gaan.
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/users/page_usersetup.ui" line="51"/>
|
<location filename="../src/modules/users/page_usersetup.ui" line="51"/>
|
||||||
<source>Your Full Name</source>
|
<source>Your Full Name</source>
|
||||||
<translation>Volledige Naam</translation>
|
<translation>Volledige naam</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/users/page_usersetup.ui" line="120"/>
|
<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>
|
<message>
|
||||||
<location filename="../src/modules/users/page_usersetup.ui" line="144"/>
|
<location filename="../src/modules/users/page_usersetup.ui" line="144"/>
|
||||||
<source>login</source>
|
<source>login</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Gebruikersnaam</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/users/page_usersetup.ui" line="219"/>
|
<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="351"/>
|
||||||
<location filename="../src/modules/users/page_usersetup.ui" line="521"/>
|
<location filename="../src/modules/users/page_usersetup.ui" line="521"/>
|
||||||
<source>Password</source>
|
<source>Password</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Wachtwoord</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/users/page_usersetup.ui" line="376"/>
|
<location filename="../src/modules/users/page_usersetup.ui" line="376"/>
|
||||||
<location filename="../src/modules/users/page_usersetup.ui" line="546"/>
|
<location filename="../src/modules/users/page_usersetup.ui" line="546"/>
|
||||||
<source>Repeat Password</source>
|
<source>Repeat Password</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Herhaal wachtwoord</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/users/page_usersetup.ui" line="451"/>
|
<location filename="../src/modules/users/page_usersetup.ui" line="451"/>
|
||||||
|
@ -3549,7 +3549,7 @@ Saída:
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/users/UsersPage.cpp" line="402"/>
|
<location filename="../src/modules/users/UsersPage.cpp" line="402"/>
|
||||||
<source>Your username must start with a lowercase letter or underscore.</source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/users/UsersPage.cpp" line="409"/>
|
<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
|
# Translations
|
||||||
include( CalamaresAddTranslations )
|
include( CalamaresAddTranslations )
|
||||||
add_calamares_translations( ${CALAMARES_TRANSLATION_LANGUAGES} )
|
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} )
|
target_include_directories( calamares_bin PRIVATE ${CMAKE_SOURCE_DIR} )
|
||||||
set_target_properties(calamares_bin
|
set_target_properties(calamares_bin
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
@ -45,6 +44,7 @@ set_target_properties(calamares_bin
|
|||||||
)
|
)
|
||||||
calamares_automoc( calamares_bin )
|
calamares_automoc( calamares_bin )
|
||||||
calamares_autouic( calamares_bin )
|
calamares_autouic( calamares_bin )
|
||||||
|
calamares_autorcc( calamares_bin )
|
||||||
|
|
||||||
target_link_libraries( calamares_bin
|
target_link_libraries( calamares_bin
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
@ -27,8 +27,10 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} )
|
|||||||
|
|
||||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/CalamaresConfig.h.in
|
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/CalamaresConfig.h.in
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/CalamaresConfig.h )
|
${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 )
|
${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_PRIVATE_LIBRARIES "" )
|
||||||
set( OPTIONAL_PUBLIC_LIBRARIES "" )
|
set( OPTIONAL_PUBLIC_LIBRARIES "" )
|
||||||
@ -76,6 +78,7 @@ set( libSources
|
|||||||
utils/Dirs.cpp
|
utils/Dirs.cpp
|
||||||
utils/Entropy.cpp
|
utils/Entropy.cpp
|
||||||
utils/Logger.cpp
|
utils/Logger.cpp
|
||||||
|
utils/Permissions.cpp
|
||||||
utils/PluginFactory.cpp
|
utils/PluginFactory.cpp
|
||||||
utils/Retranslator.cpp
|
utils/Retranslator.cpp
|
||||||
utils/String.cpp
|
utils/String.cpp
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#cmakedefine CALAMARES_ORGANIZATION_NAME "${CALAMARES_ORGANIZATION_NAME}"
|
#cmakedefine CALAMARES_ORGANIZATION_NAME "${CALAMARES_ORGANIZATION_NAME}"
|
||||||
#cmakedefine CALAMARES_ORGANIZATION_DOMAIN "${CALAMARES_ORGANIZATION_DOMAIN}"
|
#cmakedefine CALAMARES_ORGANIZATION_DOMAIN "${CALAMARES_ORGANIZATION_DOMAIN}"
|
||||||
#cmakedefine CALAMARES_APPLICATION_NAME "${CALAMARES_APPLICATION_NAME}"
|
#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_SHORT "${CALAMARES_VERSION_SHORT}"
|
||||||
|
|
||||||
#cmakedefine CALAMARES_VERSION_MAJOR "${CALAMARES_VERSION_MAJOR}"
|
#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
|
@ -1,5 +1,5 @@
|
|||||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
|
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
|
||||||
*
|
*
|
||||||
@ -36,8 +36,8 @@ using CalamaresUtils::operator""_MiB;
|
|||||||
namespace Calamares
|
namespace Calamares
|
||||||
{
|
{
|
||||||
|
|
||||||
GlobalStorage::GlobalStorage()
|
GlobalStorage::GlobalStorage( QObject* parent )
|
||||||
: QObject( nullptr )
|
: QObject( parent )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
|
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
|
||||||
*
|
*
|
||||||
@ -39,7 +39,7 @@ class GlobalStorage : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit GlobalStorage();
|
explicit GlobalStorage( QObject* parent = nullptr );
|
||||||
|
|
||||||
//NOTE: thread safety is guaranteed by JobQueue, which executes jobs one by one.
|
//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.
|
// If at any time jobs become concurrent, this class must be made thread-safe.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
|
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
|
||||||
*
|
*
|
||||||
@ -170,7 +170,7 @@ JobQueue::globalStorage() const
|
|||||||
JobQueue::JobQueue( QObject* parent )
|
JobQueue::JobQueue( QObject* parent )
|
||||||
: QObject( parent )
|
: QObject( parent )
|
||||||
, m_thread( new JobThread( this ) )
|
, m_thread( new JobThread( this ) )
|
||||||
, m_storage( new GlobalStorage() )
|
, m_storage( new GlobalStorage( this ) )
|
||||||
{
|
{
|
||||||
Q_ASSERT( !s_instance );
|
Q_ASSERT( !s_instance );
|
||||||
s_instance = this;
|
s_instance = this;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
* 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
|
* Calamares is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -15,8 +16,6 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
* 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
|
class TZRegion : public CStringPair
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -120,7 +125,12 @@ private:
|
|||||||
CStringPairList m_zones;
|
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
|
class TZZone : public CStringPair
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||||
*
|
*
|
||||||
* Calamares is free software: you can redistribute it and/or modify
|
* Calamares is free software: you can redistribute it and/or modify
|
||||||
@ -168,7 +168,11 @@ Manager::checkHasInternet()
|
|||||||
{
|
{
|
||||||
hasInternet = synchronousPing( d->m_hasInternetUrl );
|
hasInternet = synchronousPing( d->m_hasInternetUrl );
|
||||||
}
|
}
|
||||||
d->m_hasInternet = hasInternet;
|
if ( hasInternet != d->m_hasInternet )
|
||||||
|
{
|
||||||
|
d->m_hasInternet = hasInternet;
|
||||||
|
emit hasInternetChanged( hasInternet );
|
||||||
|
}
|
||||||
return hasInternet;
|
return hasInternet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||||
*
|
*
|
||||||
* Calamares is free software: you can redistribute it and/or modify
|
* Calamares is free software: you can redistribute it and/or modify
|
||||||
@ -98,9 +98,10 @@ struct RequestStatus
|
|||||||
|
|
||||||
QDebug& operator<<( QDebug& s, const RequestStatus& e );
|
QDebug& operator<<( QDebug& s, const RequestStatus& e );
|
||||||
|
|
||||||
class DLLEXPORT Manager : QObject
|
class DLLEXPORT Manager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_PROPERTY( bool hasInternet READ hasInternet NOTIFY hasInternetChanged FINAL )
|
||||||
|
|
||||||
Manager();
|
Manager();
|
||||||
|
|
||||||
@ -133,6 +134,16 @@ public:
|
|||||||
|
|
||||||
/// @brief Set the URL which is used for the general "is there internet" check.
|
/// @brief Set the URL which is used for the general "is there internet" check.
|
||||||
void setCheckHasInternetUrl( const QUrl& url );
|
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.
|
/** @brief Do an explicit check for internet connectivity.
|
||||||
*
|
*
|
||||||
* This **may** do a ping to the configured check URL, but can also
|
* This **may** do a ping to the configured check URL, but can also
|
||||||
@ -148,13 +159,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool hasInternet();
|
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.
|
* The value is that returned from hasInternet() -- @c true when there
|
||||||
* This may be a nullptr if an error occurs immediately.
|
* is connectivity, @c false otherwise.
|
||||||
* The caller is responsible for cleaning up the reply (eventually).
|
|
||||||
*/
|
*/
|
||||||
QNetworkReply* asynchronousGet( const QUrl& url, const RequestOptions& options = RequestOptions() );
|
void hasInternetChanged( bool );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Private;
|
class Private;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* SPDX-FileCopyrightText: 2010-2011 Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
* SPDX-FileCopyrightText: 2010-2011 Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||||
* SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
|
* SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
|
||||||
* SPDX-FileCopyrightText: 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
|
* 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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
* 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 "Logger.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include "CalamaresVersionX.h"
|
||||||
#include <iostream>
|
#include "utils/Dirs.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -35,10 +33,10 @@
|
|||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include "CalamaresVersion.h"
|
#include <fstream>
|
||||||
#include "utils/Dirs.h"
|
#include <iostream>
|
||||||
|
|
||||||
#define LOGFILE_SIZE 1024 * 256
|
static constexpr const int LOGFILE_SIZE = 1024 * 256;
|
||||||
|
|
||||||
static std::ofstream logfile;
|
static std::ofstream logfile;
|
||||||
static unsigned int s_threshold =
|
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
|
@ -1,5 +1,5 @@
|
|||||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||||
*
|
*
|
||||||
* Calamares is free software: you can redistribute it and/or modify
|
* Calamares is free software: you can redistribute it and/or modify
|
||||||
@ -24,6 +24,7 @@
|
|||||||
#define UTILS_RAII_H
|
#define UTILS_RAII_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QSignalBlocker>
|
||||||
|
|
||||||
#include <type_traits>
|
#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
|
#endif
|
||||||
|
@ -103,11 +103,37 @@ public:
|
|||||||
RequirementsModel* requirementsModel() { return m_requirementsModel; }
|
RequirementsModel* requirementsModel() { return m_requirementsModel; }
|
||||||
|
|
||||||
signals:
|
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 initDone();
|
||||||
void modulesLoaded(); /// All of the modules were loaded successfully
|
/** @brief Emitted when all the modules are loaded successfully
|
||||||
void modulesFailed( QStringList ); /// .. or not
|
*
|
||||||
// Below, see RequirementsChecker documentation
|
* Each module listed in the settings is loaded. Modules are loaded
|
||||||
void requirementsComplete( bool );
|
* 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:
|
private slots:
|
||||||
void doInit();
|
void doInit();
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "JobQueue.h"
|
#include "JobQueue.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "ViewManager.h"
|
#include "ViewManager.h"
|
||||||
|
#include "network/Manager.h"
|
||||||
#include "utils/Dirs.h"
|
#include "utils/Dirs.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
|
|
||||||
@ -242,6 +243,10 @@ registerQmlModels()
|
|||||||
"io.calamares.core", 1, 0, "Global", []( QQmlEngine*, QJSEngine* ) -> QObject* {
|
"io.calamares.core", 1, 0, "Global", []( QQmlEngine*, QJSEngine* ) -> QObject* {
|
||||||
return Calamares::JobQueue::instance()->globalStorage();
|
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
|
z: parent.z - 1
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Kirigami.Theme.backgroundColor
|
color: "#BDC3C7"
|
||||||
radius: 5
|
radius: 5
|
||||||
opacity: 0.7
|
opacity: 0.7
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@ Page {
|
|||||||
width: 800 //parent.width
|
width: 800 //parent.width
|
||||||
height: 550 //parent.height
|
height: 550 //parent.height
|
||||||
|
|
||||||
Kirigami.Theme.backgroundColor: "#fafafa"
|
Kirigami.Theme.backgroundColor: "#FAFAFA"
|
||||||
Kirigami.Theme.textColor: "#333"
|
Kirigami.Theme.textColor: "#1F1F1F"
|
||||||
|
|
||||||
property string subtitle
|
property string subtitle
|
||||||
property string message
|
property string message
|
||||||
@ -22,39 +22,6 @@ Page {
|
|||||||
default property alias content : _content.data
|
default property alias content : _content.data
|
||||||
property alias stackView: _stackView
|
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 {
|
ColumnLayout {
|
||||||
|
|
||||||
id: _content
|
id: _content
|
||||||
@ -63,7 +30,7 @@ Page {
|
|||||||
spacing: Kirigami.Units.smallSpacing * 5
|
spacing: Kirigami.Units.smallSpacing * 5
|
||||||
anchors.margins: Kirigami.Units.smallSpacing * 5
|
anchors.margins: Kirigami.Units.smallSpacing * 5
|
||||||
anchors.bottomMargin: 20
|
anchors.bottomMargin: 20
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@ -72,7 +39,7 @@ Page {
|
|||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
text: control.title
|
text: control.title
|
||||||
color: "white"
|
color: Kirigami.Theme.textColor
|
||||||
font.bold: true
|
font.bold: true
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
font.pointSize: 24
|
font.pointSize: 24
|
||||||
@ -86,7 +53,7 @@ Page {
|
|||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
text: control.subtitle
|
text: control.subtitle
|
||||||
color: "white"
|
color: Kirigami.Theme.textColor
|
||||||
font.weight: Font.Light
|
font.weight: Font.Light
|
||||||
font.pointSize: 12
|
font.pointSize: 12
|
||||||
}
|
}
|
||||||
@ -99,7 +66,7 @@ Page {
|
|||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
text: control.message
|
text: control.message
|
||||||
color: "white"
|
color: Kirigami.Theme.textColor
|
||||||
font.weight: Font.Light
|
font.weight: Font.Light
|
||||||
font.pointSize: 10
|
font.pointSize: 10
|
||||||
}
|
}
|
||||||
@ -110,7 +77,7 @@ Page {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.preferredWidth: parent.width
|
Layout.preferredWidth: parent.width
|
||||||
clip: true
|
clip: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 101 KiB |
@ -182,14 +182,15 @@ ResponsiveBase {
|
|||||||
Layout.maximumWidth: 500
|
Layout.maximumWidth: 500
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
color: control.Kirigami.Theme.textColor
|
||||||
|
|
||||||
background:Rectangle {
|
background:Rectangle {
|
||||||
|
|
||||||
z: parent.z - 1
|
z: parent.z - 1
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: control.Kirigami.Theme.backgroundColor
|
color: "#BDC3C7"
|
||||||
radius: 5
|
radius: 5
|
||||||
opacity: 0.8
|
opacity: 0.3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,5 @@
|
|||||||
<file>ListItemDelegate.qml</file>
|
<file>ListItemDelegate.qml</file>
|
||||||
<file>ListViewTemplate.qml</file>
|
<file>ListViewTemplate.qml</file>
|
||||||
<file>ResponsiveBase.qml</file>
|
<file>ResponsiveBase.qml</file>
|
||||||
<file>keyboard.jpg</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||||
*
|
*
|
||||||
* Copyright 2019-2020, Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||||
* Copyright 2020, Camilo Higuita <milo.h@aol.com>
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
* License-Filename: LICENSE
|
||||||
*
|
*
|
||||||
* Calamares is free software: you can redistribute it and/or modify
|
* Calamares is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -19,85 +20,43 @@
|
|||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
#include "LCLocaleDialog.h"
|
|
||||||
#include "SetTimezoneJob.h"
|
#include "SetTimezoneJob.h"
|
||||||
#include "timezonewidget/timezonewidget.h"
|
|
||||||
|
|
||||||
#include "GlobalStorage.h"
|
#include "GlobalStorage.h"
|
||||||
#include "JobQueue.h"
|
#include "JobQueue.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
#include "locale/Label.h"
|
#include "locale/Label.h"
|
||||||
#include "locale/TimeZone.h"
|
#include "modulesystem/ModuleManager.h"
|
||||||
#include "utils/CalamaresUtilsGui.h"
|
#include "network/Manager.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
#include "utils/Retranslator.h"
|
#include "utils/Variant.h"
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QTimeZone>
|
||||||
|
|
||||||
Config::Config( QObject* parent )
|
/** @brief Load supported locale keys
|
||||||
: QObject( parent )
|
*
|
||||||
, m_regionList( CalamaresUtils::Locale::TZRegion::fromZoneTab() )
|
* If i18n/SUPPORTED exists, read the lines from that and return those
|
||||||
, m_regionModel( new CalamaresUtils::Locale::CStringListModel( m_regionList ) )
|
* as supported locales; otherwise, try the file at @p localeGenPath
|
||||||
, m_zonesModel( new CalamaresUtils::Locale::CStringListModel() )
|
* and get lines from that. Failing both, try the output of `locale -a`.
|
||||||
, m_blockTzWidgetSet( false )
|
*
|
||||||
|
* 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, [&]() {
|
QStringList localeGenLines;
|
||||||
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" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some distros come with a meaningfully commented and easy to parse locale.gen,
|
// 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
|
// 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
|
// supported locales. We first try that one, and if it doesn't exist, we fall back
|
||||||
// to parsing the lines from locale.gen
|
// to parsing the lines from locale.gen
|
||||||
m_localeGenLines.clear();
|
localeGenLines.clear();
|
||||||
QFile supported( "/usr/share/i18n/SUPPORTED" );
|
QFile supported( "/usr/share/i18n/SUPPORTED" );
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
|
|
||||||
@ -109,7 +68,7 @@ Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone,
|
|||||||
const auto lines = ba.split( '\n' );
|
const auto lines = ba.split( '\n' );
|
||||||
for ( const QByteArray& line : lines )
|
for ( const QByteArray& line : lines )
|
||||||
{
|
{
|
||||||
m_localeGenLines.append( QString::fromLatin1( line.simplified() ) );
|
localeGenLines.append( QString::fromLatin1( line.simplified() ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -150,11 +109,11 @@ Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_localeGenLines.append( lineString );
|
localeGenLines.append( lineString );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( m_localeGenLines.isEmpty() )
|
if ( localeGenLines.isEmpty() )
|
||||||
{
|
{
|
||||||
cWarning() << "cannot acquire a list of available locales."
|
cWarning() << "cannot acquire a list of available locales."
|
||||||
<< "The locale and localecfg modules will be broken as long as this "
|
<< "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"
|
<< "* a well-formed"
|
||||||
<< ( localeGenPath.isEmpty() ? QLatin1String( "/etc/locale.gen" ) : localeGenPath ) << "\n\tOR"
|
<< ( localeGenPath.isEmpty() ? QLatin1String( "/etc/locale.gen" ) : localeGenPath ) << "\n\tOR"
|
||||||
<< "* a complete pre-compiled locale-gen database which allows complete locale -a output.";
|
<< "* 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
|
// Assuming we have a list of supported locales, we usually only want UTF-8 ones
|
||||||
// because it's not 1995.
|
// because it's not 1995.
|
||||||
for ( auto it = m_localeGenLines.begin(); it != m_localeGenLines.end(); )
|
auto notUtf8 = []( const QString& s ) {
|
||||||
{
|
return !s.contains( "UTF-8", Qt::CaseInsensitive ) && !s.contains( "utf8", Qt::CaseInsensitive );
|
||||||
if ( !it->contains( "UTF-8", Qt::CaseInsensitive ) && !it->contains( "utf8", Qt::CaseInsensitive ) )
|
};
|
||||||
{
|
auto it = std::remove_if( localeGenLines.begin(), localeGenLines.end(), notUtf8 );
|
||||||
it = m_localeGenLines.erase( it );
|
localeGenLines.erase( it, localeGenLines.end() );
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We strip " UTF-8" from "en_US.UTF-8 UTF-8" because it's redundant redundant.
|
// 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 ( it->endsWith( " UTF-8" ) )
|
if ( s.endsWith( " UTF-8" ) )
|
||||||
{
|
{
|
||||||
it->chop( 6 );
|
s.chop( 6 );
|
||||||
}
|
}
|
||||||
*it = it->simplified();
|
s = s.simplified();
|
||||||
}
|
};
|
||||||
updateGlobalStorage();
|
std::for_each( localeGenLines.begin(), localeGenLines.end(), unredundant );
|
||||||
updateLocaleLabels();
|
|
||||||
|
return localeGenLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const CalamaresUtils::Locale::CStringPairList&
|
||||||
|
timezoneData()
|
||||||
|
{
|
||||||
|
return CalamaresUtils::Locale::TZRegion::fromZoneTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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" ) );
|
||||||
|
|
||||||
|
gs->insert( "locationRegion", location->region() );
|
||||||
|
gs->insert( "locationZone", location->zone() );
|
||||||
|
if ( locationChanged && m_adjustLiveTimezone )
|
||||||
|
{
|
||||||
|
QProcess::execute( "timedatectl", // depends on systemd
|
||||||
|
{ "set-timezone", location->region() + '/' + location->zone() } );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
auto prettyStatusNotify = [&]() { emit prettyStatusChanged( prettyStatus() ); };
|
||||||
|
connect( this, &Config::currentLanguageStatusChanged, prettyStatusNotify );
|
||||||
|
connect( this, &Config::currentLCStatusChanged, prettyStatusNotify );
|
||||||
|
connect( this, &Config::currentLocationStatusChanged, prettyStatusNotify );
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::~Config() {}
|
||||||
|
|
||||||
|
const CalamaresUtils::Locale::CStringPairList&
|
||||||
|
Config::timezoneData() const
|
||||||
|
{
|
||||||
|
return ::timezoneData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Config::updateGlobalLocale()
|
Config::setCurrentLocation()
|
||||||
{
|
{
|
||||||
auto* gs = Calamares::JobQueue::instance()->globalStorage();
|
if ( !m_currentLocation && m_startingTimezone.isValid() )
|
||||||
const QString bcp47 = m_selectedLocaleConfiguration.toBcp47();
|
{
|
||||||
gs->insert( "locale", bcp47 );
|
setCurrentLocation( m_startingTimezone.first, m_startingTimezone.second );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::setCurrentLocation(const QString& regionzone)
|
||||||
|
{
|
||||||
|
auto r = CalamaresUtils::GeoIP::splitTZString( regionzone );
|
||||||
|
if ( r.isValid() )
|
||||||
|
{
|
||||||
|
setCurrentLocation( r.first, r.second );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Config::updateGlobalStorage()
|
Config::setCurrentLocation( const QString& regionName, const QString& zoneName )
|
||||||
{
|
{
|
||||||
auto* gs = Calamares::JobQueue::instance()->globalStorage();
|
using namespace CalamaresUtils::Locale;
|
||||||
|
auto* region = timezoneData().find< TZRegion >( regionName );
|
||||||
const auto* location = currentLocation();
|
auto* zone = region ? region->zones().find< TZZone >( zoneName ) : nullptr;
|
||||||
bool locationChanged = ( location->region() != gs->value( "locationRegion" ) )
|
if ( zone )
|
||||||
|| ( location->zone() != gs->value( "locationZone" ) );
|
|
||||||
#ifdef DEBUG_TIMEZONES
|
|
||||||
if ( locationChanged )
|
|
||||||
{
|
{
|
||||||
cDebug() << "Location changed" << gs->value( "locationRegion" ) << ',' << gs->value( "locationZone" ) << "to"
|
setCurrentLocation( zone );
|
||||||
<< location->region() << ',' << location->zone();
|
|
||||||
}
|
}
|
||||||
#endif
|
else
|
||||||
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
|
// Recursive, but America/New_York always exists.
|
||||||
{ "set-timezone", location->region() + '/' + location->zone() } );
|
setCurrentLocation( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
|
||||||
}
|
}
|
||||||
#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
|
void
|
||||||
Config::updateLocaleLabels()
|
Config::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
|
||||||
{
|
{
|
||||||
LocaleConfiguration lc
|
if ( location != m_currentLocation )
|
||||||
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration;
|
{
|
||||||
auto labels = prettyLocaleStatus( lc );
|
m_currentLocation = location;
|
||||||
emit prettyStatusChanged();
|
// 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;
|
||||||
|
|
||||||
|
emit currentLCStatusChanged( currentLCStatus() );
|
||||||
std::pair< QString, QString >
|
}
|
||||||
Config::prettyLocaleStatus( const LocaleConfiguration& lc ) const
|
emit currentLocationChanged( m_currentLocation );
|
||||||
{
|
}
|
||||||
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() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
Calamares::JobList
|
|
||||||
Config::createJobs()
|
|
||||||
{
|
|
||||||
QList< Calamares::job_ptr > list;
|
|
||||||
const CalamaresUtils::Locale::TZZone* location = currentLocation();
|
|
||||||
|
|
||||||
Calamares::Job* j = new SetTimezoneJob( location->region(), location->zone() );
|
|
||||||
list.append( Calamares::job_ptr( j ) );
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocaleConfiguration
|
LocaleConfiguration
|
||||||
Config::guessLocaleConfiguration() const
|
Config::automaticLocaleConfiguration() const
|
||||||
{
|
{
|
||||||
|
// Special case: no location has been set at **all**
|
||||||
|
if ( !currentLocation() )
|
||||||
|
{
|
||||||
|
return LocaleConfiguration();
|
||||||
|
}
|
||||||
return LocaleConfiguration::fromLanguageAndLocation(
|
return LocaleConfiguration::fromLanguageAndLocation(
|
||||||
QLocale().name(), m_localeGenLines, currentLocation() ? currentLocation()->country() : "" );
|
QLocale().name(), supportedLocales(), currentLocation()->country() );
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap< QString, QString >
|
LocaleConfiguration
|
||||||
Config::localesMap()
|
Config::localeConfiguration() const
|
||||||
{
|
{
|
||||||
return m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().toMap()
|
return m_selectedLocaleConfiguration.isEmpty() ? automaticLocaleConfiguration() : m_selectedLocaleConfiguration;
|
||||||
: m_selectedLocaleConfiguration.toMap();
|
}
|
||||||
|
|
||||||
|
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
|
QString
|
||||||
Config::prettyStatus() const
|
Config::prettyStatus() const
|
||||||
{
|
{
|
||||||
QString status;
|
QStringList l { currentLocationStatus(), currentLanguageStatus(), currentLCStatus() };
|
||||||
status += tr( "Set timezone to %1/%2.<br/>" )
|
return l.join( QStringLiteral( "<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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
const CalamaresUtils::Locale::TZZone*
|
getLocaleGenLines( const QVariantMap& configurationMap, QStringList& localeGenLines )
|
||||||
Config::currentLocation() const
|
|
||||||
{
|
{
|
||||||
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> ===
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||||
*
|
*
|
||||||
* Copyright 2019-2020, Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||||
* Copyright 2020, Camilo Higuita <milo.h@aol.com>
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
* License-Filename: LICENSE
|
||||||
*
|
*
|
||||||
* Calamares is free software: you can redistribute it and/or modify
|
* Calamares is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -23,9 +24,11 @@
|
|||||||
#include "LocaleConfiguration.h"
|
#include "LocaleConfiguration.h"
|
||||||
|
|
||||||
#include "Job.h"
|
#include "Job.h"
|
||||||
|
#include "geoip/Handler.h"
|
||||||
|
#include "geoip/Interface.h"
|
||||||
#include "locale/TimeZone.h"
|
#include "locale/TimeZone.h"
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QFutureWatcher>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -33,53 +36,150 @@
|
|||||||
class Config : public QObject
|
class Config : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
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* zonesModel READ zonesModel CONSTANT FINAL )
|
||||||
Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* regionModel READ regionModel 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 )
|
Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL )
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Config( QObject* parent = nullptr );
|
Config( QObject* parent = nullptr );
|
||||||
~Config();
|
~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();
|
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;
|
QString prettyStatus() const;
|
||||||
|
|
||||||
private:
|
const QStringList& supportedLocales() const { return m_localeGenLines; }
|
||||||
CalamaresUtils::Locale::CStringPairList m_regionList;
|
CalamaresUtils::Locale::CStringListModel* regionModel() const { return m_regionModel.get(); }
|
||||||
CalamaresUtils::Locale::CStringListModel* m_regionModel;
|
CalamaresUtils::Locale::CStringListModel* zonesModel() const { return m_zonesModel.get(); }
|
||||||
CalamaresUtils::Locale::CStringListModel* m_zonesModel;
|
|
||||||
|
|
||||||
LocaleConfiguration m_selectedLocaleConfiguration;
|
/// Special case, set location from starting timezone if not already set
|
||||||
|
void setCurrentLocation();
|
||||||
|
|
||||||
QStringList m_localeGenLines;
|
public Q_SLOTS:
|
||||||
int m_currentRegion = -1;
|
/// 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;
|
/** @brief Sets a location by full name
|
||||||
|
|
||||||
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.
|
|
||||||
*
|
*
|
||||||
* This uses whatever is set in m_selectedLocaleConfiguration as the language,
|
* @p regionzone should be an identifier from zone.tab, e.g. "Africa/Abidjan",
|
||||||
* and writes it to GS *locale* key (as a string, in BCP47 format).
|
* which is split into regon and zone. Invalid names will **not**
|
||||||
|
* change the actual location.
|
||||||
*/
|
*/
|
||||||
void updateGlobalLocale();
|
void setCurrentLocation( const QString& regionzone );
|
||||||
void updateGlobalStorage();
|
/** @brief Sets a location by split name
|
||||||
void updateLocaleLabels();
|
*
|
||||||
|
* @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:
|
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_numeric = lc_time = lc_monetary = lc_paper = lc_name = lc_address = lc_telephone = lc_measurement
|
||||||
= lc_identification = formatsName;
|
= lc_identification = formatsName;
|
||||||
|
|
||||||
(void)setLanguage( localeName );
|
setLanguage( localeName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ LocaleConfiguration::fromLanguageAndLocation( const QString& languageLocale,
|
|||||||
if ( language == "pt" || language == "zh" )
|
if ( language == "pt" || language == "zh" )
|
||||||
{
|
{
|
||||||
QString proposedLocale = QString( "%1_%2" ).arg( language ).arg( countryCode );
|
QString proposedLocale = QString( "%1_%2" ).arg( language ).arg( countryCode );
|
||||||
foreach ( QString line, linesForLanguage )
|
for ( const QString& line : linesForLanguage )
|
||||||
{
|
{
|
||||||
if ( line.contains( proposedLocale ) )
|
if ( line.contains( proposedLocale ) )
|
||||||
{
|
{
|
||||||
|
@ -26,36 +26,53 @@
|
|||||||
|
|
||||||
class LocaleConfiguration
|
class LocaleConfiguration
|
||||||
{
|
{
|
||||||
public:
|
public: // TODO: private (but need to be public for tests)
|
||||||
/// @brief Create an empty locale, with nothing set
|
/** @brief Create a locale with everything set to the given @p localeName
|
||||||
explicit LocaleConfiguration();
|
*
|
||||||
/// @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" */ )
|
explicit LocaleConfiguration( const QString& localeName /* "en_US.UTF-8" */ )
|
||||||
: LocaleConfiguration( localeName, localeName )
|
: 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 );
|
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
|
static LocaleConfiguration
|
||||||
fromLanguageAndLocation( const QString& language, const QStringList& availableLocales, const QString& countryCode );
|
fromLanguageAndLocation( const QString& language, const QStringList& availableLocales, const QString& countryCode );
|
||||||
|
|
||||||
|
/// Is this an empty (default-constructed and not modified) configuration?
|
||||||
bool isEmpty() const;
|
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 );
|
void setLanguage( const QString& localeName );
|
||||||
|
/// Current language (including region)
|
||||||
QString language() const { return m_lang; }
|
QString language() const { return m_lang; }
|
||||||
|
/// Current language (lowercase, BCP47 format, no region)
|
||||||
// Note that the documentation how this works is in packages.conf
|
|
||||||
QString toBcp47() const { return m_languageLocaleBcp47; }
|
QString toBcp47() const { return m_languageLocaleBcp47; }
|
||||||
|
|
||||||
QMap< QString, QString > toMap() const;
|
QMap< QString, QString > toMap() const;
|
||||||
|
|
||||||
// These become all uppercase in locale.conf, but we keep them lowercase here to
|
// 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,
|
QString lc_numeric, lc_time, lc_monetary, lc_paper, lc_name, lc_address, lc_telephone, lc_measurement,
|
||||||
lc_identification;
|
lc_identification;
|
||||||
|
|
||||||
|
@ -19,37 +19,30 @@
|
|||||||
|
|
||||||
#include "LocalePage.h"
|
#include "LocalePage.h"
|
||||||
|
|
||||||
#include "SetTimezoneJob.h"
|
#include "Config.h"
|
||||||
|
#include "LCLocaleDialog.h"
|
||||||
#include "timezonewidget/timezonewidget.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/CalamaresUtilsGui.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
|
#include "utils/RAII.h"
|
||||||
#include "utils/Retranslator.h"
|
#include "utils/Retranslator.h"
|
||||||
|
|
||||||
#include <QBoxLayout>
|
#include <QBoxLayout>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QFile>
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QProcess>
|
#include <QPointer>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
LocalePage::LocalePage( QWidget* parent )
|
LocalePage::LocalePage( Config* config, QWidget* parent )
|
||||||
: QWidget( parent )
|
: QWidget( parent )
|
||||||
, m_regionList( CalamaresUtils::Locale::TZRegion::fromZoneTab() )
|
, m_config( config )
|
||||||
, m_regionModel( std::make_unique< CalamaresUtils::Locale::CStringListModel >( m_regionList ) )
|
|
||||||
, m_blockTzWidgetSet( false )
|
, m_blockTzWidgetSet( false )
|
||||||
{
|
{
|
||||||
QBoxLayout* mainLayout = new QVBoxLayout;
|
QBoxLayout* mainLayout = new QVBoxLayout;
|
||||||
|
|
||||||
QBoxLayout* tzwLayout = new QHBoxLayout;
|
QBoxLayout* tzwLayout = new QHBoxLayout;
|
||||||
m_tzWidget = new TimeZoneWidget( this );
|
m_tzWidget = new TimeZoneWidget( config->timezoneData(), this );
|
||||||
tzwLayout->addStretch();
|
tzwLayout->addStretch();
|
||||||
tzwLayout->addWidget( m_tzWidget );
|
tzwLayout->addWidget( m_tzWidget );
|
||||||
tzwLayout->addStretch();
|
tzwLayout->addStretch();
|
||||||
@ -105,9 +98,24 @@ LocalePage::LocalePage( QWidget* parent )
|
|||||||
setMinimumWidth( m_tzWidget->width() );
|
setMinimumWidth( m_tzWidget->width() );
|
||||||
setLayout( mainLayout );
|
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_regionCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::regionChanged );
|
||||||
connect( m_zoneCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::zoneChanged );
|
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_localeChangeButton, &QPushButton::clicked, this, &LocalePage::changeLocale );
|
||||||
connect( m_formatsChangeButton, &QPushButton::clicked, this, &LocalePage::changeFormats );
|
connect( m_formatsChangeButton, &QPushButton::clicked, this, &LocalePage::changeFormats );
|
||||||
|
|
||||||
@ -115,10 +123,7 @@ LocalePage::LocalePage( QWidget* parent )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LocalePage::~LocalePage()
|
LocalePage::~LocalePage() {}
|
||||||
{
|
|
||||||
qDeleteAll( m_regionList );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -128,175 +133,8 @@ LocalePage::updateLocaleLabels()
|
|||||||
m_zoneLabel->setText( tr( "Zone:" ) );
|
m_zoneLabel->setText( tr( "Zone:" ) );
|
||||||
m_localeChangeButton->setText( tr( "&Change..." ) );
|
m_localeChangeButton->setText( tr( "&Change..." ) );
|
||||||
m_formatsChangeButton->setText( tr( "&Change..." ) );
|
m_formatsChangeButton->setText( tr( "&Change..." ) );
|
||||||
|
m_localeLabel->setText( m_config->currentLanguageStatus() );
|
||||||
LocaleConfiguration lc
|
m_formatsLabel->setText( m_config->currentLCStatus() );
|
||||||
= 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -304,82 +142,10 @@ void
|
|||||||
LocalePage::onActivate()
|
LocalePage::onActivate()
|
||||||
{
|
{
|
||||||
m_regionCombo->setFocus();
|
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();
|
updateLocaleLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
LocalePage::regionChanged( int currentIndex )
|
LocalePage::regionChanged( int currentIndex )
|
||||||
{
|
{
|
||||||
@ -388,15 +154,17 @@ LocalePage::regionChanged( int currentIndex )
|
|||||||
Q_UNUSED( currentIndex )
|
Q_UNUSED( currentIndex )
|
||||||
QString selectedRegion = m_regionCombo->currentData().toString();
|
QString selectedRegion = m_regionCombo->currentData().toString();
|
||||||
|
|
||||||
TZRegion* region = m_regionList.find< TZRegion >( selectedRegion );
|
TZRegion* region = m_config->timezoneData().find< TZRegion >( selectedRegion );
|
||||||
if ( !region )
|
if ( !region )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_zoneCombo->blockSignals( true );
|
{
|
||||||
m_zoneCombo->setModel( new CStringListModel( region->zones() ) );
|
cSignalBlocker b( m_zoneCombo );
|
||||||
m_zoneCombo->blockSignals( false );
|
m_zoneCombo->setModel( new CStringListModel( region->zones() ) );
|
||||||
|
}
|
||||||
|
|
||||||
m_zoneCombo->currentIndexChanged( m_zoneCombo->currentIndex() );
|
m_zoneCombo->currentIndexChanged( m_zoneCombo->currentIndex() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,16 +173,19 @@ LocalePage::zoneChanged( int currentIndex )
|
|||||||
{
|
{
|
||||||
Q_UNUSED( currentIndex )
|
Q_UNUSED( currentIndex )
|
||||||
if ( !m_blockTzWidgetSet )
|
if ( !m_blockTzWidgetSet )
|
||||||
m_tzWidget->setCurrentLocation( m_regionCombo->currentData().toString(),
|
{
|
||||||
m_zoneCombo->currentData().toString() );
|
m_config->setCurrentLocation( m_regionCombo->currentData().toString(), m_zoneCombo->currentData().toString() );
|
||||||
|
}
|
||||||
updateGlobalStorage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LocalePage::locationChanged( const CalamaresUtils::Locale::TZZone* location )
|
LocalePage::locationChanged( const CalamaresUtils::Locale::TZZone* location )
|
||||||
{
|
{
|
||||||
m_blockTzWidgetSet = true;
|
if ( !location )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cBoolSetter< true > b( m_blockTzWidgetSet );
|
||||||
|
|
||||||
// Set region index
|
// Set region index
|
||||||
int index = m_regionCombo->findData( location->region() );
|
int index = m_regionCombo->findData( location->region() );
|
||||||
@ -433,58 +204,35 @@ LocalePage::locationChanged( const CalamaresUtils::Locale::TZZone* location )
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_zoneCombo->setCurrentIndex( index );
|
m_zoneCombo->setCurrentIndex( index );
|
||||||
|
|
||||||
m_blockTzWidgetSet = false;
|
|
||||||
|
|
||||||
updateGlobalStorage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LocalePage::changeLocale()
|
LocalePage::changeLocale()
|
||||||
{
|
{
|
||||||
LCLocaleDialog* dlg
|
QPointer< LCLocaleDialog > dlg(
|
||||||
= new LCLocaleDialog( m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().language()
|
new LCLocaleDialog( m_config->localeConfiguration().language(), m_config->supportedLocales(), this ) );
|
||||||
: m_selectedLocaleConfiguration.language(),
|
|
||||||
m_localeGenLines,
|
|
||||||
this );
|
|
||||||
dlg->exec();
|
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_config->setLanguageExplicitly( dlg->selectedLCLocale() );
|
||||||
m_selectedLocaleConfiguration.explicit_lang = true;
|
updateLocaleLabels();
|
||||||
this->updateGlobalLocale();
|
|
||||||
this->updateLocaleLabels();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dlg->deleteLater();
|
delete dlg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
LocalePage::changeFormats()
|
LocalePage::changeFormats()
|
||||||
{
|
{
|
||||||
LCLocaleDialog* dlg
|
QPointer< LCLocaleDialog > dlg(
|
||||||
= new LCLocaleDialog( m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().lc_numeric
|
new LCLocaleDialog( m_config->localeConfiguration().lc_numeric, m_config->supportedLocales(), this ) );
|
||||||
: m_selectedLocaleConfiguration.lc_numeric,
|
|
||||||
m_localeGenLines,
|
|
||||||
this );
|
|
||||||
dlg->exec();
|
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_config->setLCLocaleExplicitly( dlg->selectedLCLocale() );
|
||||||
m_selectedLocaleConfiguration.lc_numeric = dlg->selectedLCLocale();
|
updateLocaleLabels();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dlg->deleteLater();
|
delete dlg;
|
||||||
}
|
}
|
||||||
|
@ -32,39 +32,23 @@
|
|||||||
class QComboBox;
|
class QComboBox;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
|
|
||||||
|
class Config;
|
||||||
class TimeZoneWidget;
|
class TimeZoneWidget;
|
||||||
|
|
||||||
class LocalePage : public QWidget
|
class LocalePage : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit LocalePage( QWidget* parent = nullptr );
|
explicit LocalePage( class Config* config, QWidget* parent = nullptr );
|
||||||
virtual ~LocalePage();
|
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();
|
void onActivate();
|
||||||
|
|
||||||
private:
|
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 updateLocaleLabels();
|
||||||
|
|
||||||
void regionChanged( int currentIndex );
|
void regionChanged( int currentIndex );
|
||||||
@ -73,10 +57,6 @@ private:
|
|||||||
void changeLocale();
|
void changeLocale();
|
||||||
void changeFormats();
|
void changeFormats();
|
||||||
|
|
||||||
// Dynamically, QList< TZRegion* >
|
|
||||||
CalamaresUtils::Locale::CStringPairList m_regionList;
|
|
||||||
std::unique_ptr< CalamaresUtils::Locale::CStringListModel > m_regionModel;
|
|
||||||
|
|
||||||
TimeZoneWidget* m_tzWidget;
|
TimeZoneWidget* m_tzWidget;
|
||||||
QComboBox* m_regionCombo;
|
QComboBox* m_regionCombo;
|
||||||
QComboBox* m_zoneCombo;
|
QComboBox* m_zoneCombo;
|
||||||
@ -88,9 +68,6 @@ private:
|
|||||||
QLabel* m_formatsLabel;
|
QLabel* m_formatsLabel;
|
||||||
QPushButton* m_formatsChangeButton;
|
QPushButton* m_formatsChangeButton;
|
||||||
|
|
||||||
LocaleConfiguration m_selectedLocaleConfiguration;
|
|
||||||
|
|
||||||
QStringList m_localeGenLines;
|
|
||||||
|
|
||||||
bool m_blockTzWidgetSet;
|
bool m_blockTzWidgetSet;
|
||||||
};
|
};
|
||||||
|
@ -34,7 +34,6 @@
|
|||||||
|
|
||||||
#include <QBoxLayout>
|
#include <QBoxLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QtConcurrent/QtConcurrentRun>
|
|
||||||
|
|
||||||
|
|
||||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleViewStepFactory, registerPlugin< LocaleViewStep >(); )
|
CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleViewStepFactory, registerPlugin< LocaleViewStep >(); )
|
||||||
@ -44,7 +43,7 @@ LocaleViewStep::LocaleViewStep( QObject* parent )
|
|||||||
, m_widget( new QWidget() )
|
, m_widget( new QWidget() )
|
||||||
, m_actualWidget( nullptr )
|
, m_actualWidget( nullptr )
|
||||||
, m_nextEnabled( false )
|
, m_nextEnabled( false )
|
||||||
, m_geoip( nullptr )
|
, m_config( std::make_unique< Config >() )
|
||||||
{
|
{
|
||||||
QBoxLayout* mainLayout = new QHBoxLayout;
|
QBoxLayout* mainLayout = new QHBoxLayout;
|
||||||
m_widget->setLayout( mainLayout );
|
m_widget->setLayout( mainLayout );
|
||||||
@ -66,11 +65,11 @@ LocaleViewStep::~LocaleViewStep()
|
|||||||
void
|
void
|
||||||
LocaleViewStep::setUpPage()
|
LocaleViewStep::setUpPage()
|
||||||
{
|
{
|
||||||
|
m_config->setCurrentLocation();
|
||||||
if ( !m_actualWidget )
|
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 );
|
m_widget->layout()->addWidget( m_actualWidget );
|
||||||
|
|
||||||
ensureSize( m_actualWidget->sizeHint() );
|
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
|
QString
|
||||||
LocaleViewStep::prettyName() const
|
LocaleViewStep::prettyName() const
|
||||||
{
|
{
|
||||||
@ -104,7 +89,7 @@ LocaleViewStep::prettyName() const
|
|||||||
QString
|
QString
|
||||||
LocaleViewStep::prettyStatus() const
|
LocaleViewStep::prettyStatus() const
|
||||||
{
|
{
|
||||||
return m_prettyStatus;
|
return m_config->prettyStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -146,7 +131,7 @@ LocaleViewStep::isAtEnd() const
|
|||||||
Calamares::JobList
|
Calamares::JobList
|
||||||
LocaleViewStep::jobs() const
|
LocaleViewStep::jobs() const
|
||||||
{
|
{
|
||||||
return m_jobs;
|
return m_config->createJobs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -164,83 +149,11 @@ LocaleViewStep::onActivate()
|
|||||||
void
|
void
|
||||||
LocaleViewStep::onLeave()
|
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
|
void
|
||||||
LocaleViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
LocaleViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||||
{
|
{
|
||||||
QString region = CalamaresUtils::getString( configurationMap, "region" );
|
m_config->setConfigurationMap( configurationMap );
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
@ -20,15 +20,11 @@
|
|||||||
#ifndef LOCALEVIEWSTEP_H
|
#ifndef LOCALEVIEWSTEP_H
|
||||||
#define LOCALEVIEWSTEP_H
|
#define LOCALEVIEWSTEP_H
|
||||||
|
|
||||||
#include "geoip/Handler.h"
|
#include "Config.h"
|
||||||
#include "geoip/Interface.h"
|
|
||||||
#include "utils/PluginFactory.h"
|
|
||||||
#include "viewpages/ViewStep.h"
|
|
||||||
|
|
||||||
#include "DllMacro.h"
|
#include "DllMacro.h"
|
||||||
|
#include "utils/PluginFactory.h"
|
||||||
#include <QFutureWatcher>
|
#include "viewpages/ViewStep.h"
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@ -60,25 +56,16 @@ public:
|
|||||||
|
|
||||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||||
|
|
||||||
/// @brief Do setup (returns empty list) asynchronously
|
|
||||||
virtual Calamares::RequirementsList checkRequirements() override;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void setUpPage();
|
void setUpPage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void fetchGeoIpTimezone();
|
|
||||||
QWidget* m_widget;
|
QWidget* m_widget;
|
||||||
|
|
||||||
LocalePage* m_actualWidget;
|
LocalePage* m_actualWidget;
|
||||||
bool m_nextEnabled;
|
bool m_nextEnabled;
|
||||||
QString m_prettyStatus;
|
|
||||||
|
|
||||||
CalamaresUtils::GeoIP::RegionZonePair m_startingTimezone;
|
std::unique_ptr< Config > m_config;
|
||||||
QString m_localeGenPath;
|
|
||||||
|
|
||||||
Calamares::JobList m_jobs;
|
|
||||||
std::unique_ptr< CalamaresUtils::GeoIP::Handler > m_geoip;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleViewStepFactory )
|
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
|
# Time zones are usually located under /usr/share/zoneinfo and
|
||||||
# provided by the 'tzdata' package of your Distribution.
|
# provided by the 'tzdata' package of your Distribution.
|
||||||
#
|
#
|
||||||
@ -11,20 +11,41 @@
|
|||||||
# the locale page can be set through keys *region* and *zone*.
|
# the locale page can be set through keys *region* and *zone*.
|
||||||
# If either is not set, defaults to America/New_York.
|
# If either is not set, defaults to America/New_York.
|
||||||
#
|
#
|
||||||
|
# Note that useSystemTimezone and GeoIP settings can change the
|
||||||
|
# starting time zone.
|
||||||
|
#
|
||||||
region: "America"
|
region: "America"
|
||||||
zone: "New_York"
|
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:
|
# System locales are detected in the following order:
|
||||||
#
|
#
|
||||||
# - /usr/share/i18n/SUPPORTED
|
# - /usr/share/i18n/SUPPORTED
|
||||||
# - localeGenPath (defaults to /etc/locale.gen if not set)
|
# - 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
|
# custom path for locale.gen
|
||||||
#
|
#
|
||||||
#localeGenPath: "PATH_TO/locale.gen"
|
#localeGenPath: "/etc/locale.gen"
|
||||||
|
|
||||||
# GeoIP based Language settings: Leave commented out to disable GeoIP.
|
# GeoIP based Language settings: Leave commented out to disable GeoIP.
|
||||||
#
|
#
|
||||||
|
@ -4,7 +4,35 @@ $id: https://calamares.io/schemas/locale
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
"region": { type: str }
|
region: { type: string,
|
||||||
"zone": { type: str }
|
enum: [
|
||||||
"localeGenPath": { type: string, required: true }
|
Africa,
|
||||||
"geoipUrl": { type: str }
|
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 ]
|
||||||
|
@ -25,9 +25,9 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
static const char* zoneNames[]
|
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",
|
= { "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",
|
"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",
|
||||||
"-3.5", "-4.0", "-4.5", "-5.0", "-5.5", "-6.0", "-7.0", "-8.0", "-9.0", "-9.5", "-10.0", "-11.0" };
|
"-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 ] ) ),
|
static_assert( TimeZoneImageList::zoneCount == ( sizeof( zoneNames ) / sizeof( zoneNames[ 0 ] ) ),
|
||||||
"Incorrect number of zones" );
|
"Incorrect number of zones" );
|
||||||
|
|
||||||
|
@ -34,9 +34,17 @@
|
|||||||
#define ZONE_NAME QStringLiteral( "zone" )
|
#define ZONE_NAME QStringLiteral( "zone" )
|
||||||
#endif
|
#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 )
|
: QWidget( parent )
|
||||||
, timeZoneImages( TimeZoneImageList::fromQRC() )
|
, timeZoneImages( TimeZoneImageList::fromQRC() )
|
||||||
|
, m_zonesData( zones )
|
||||||
{
|
{
|
||||||
setMouseTracking( false );
|
setMouseTracking( false );
|
||||||
setCursor( Qt::PointingHandCursor );
|
setCursor( Qt::PointingHandCursor );
|
||||||
@ -57,27 +65,13 @@ TimeZoneWidget::TimeZoneWidget( QWidget* parent )
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TimeZoneWidget::setCurrentLocation( QString regionName, QString zoneName )
|
TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
|
||||||
{
|
{
|
||||||
using namespace CalamaresUtils::Locale;
|
if ( location == m_currentLocation )
|
||||||
const auto& regions = TZRegion::fromZoneTab();
|
|
||||||
auto* region = regions.find< TZRegion >( regionName );
|
|
||||||
if ( !region )
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* zone = region->zones().find< TZZone >( zoneName );
|
|
||||||
if ( zone )
|
|
||||||
{
|
|
||||||
setCurrentLocation( zone );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
|
|
||||||
{
|
|
||||||
m_currentLocation = location;
|
m_currentLocation = location;
|
||||||
|
|
||||||
// Set zone
|
// Set zone
|
||||||
@ -93,7 +87,6 @@ TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* locati
|
|||||||
|
|
||||||
// Repaint widget
|
// Repaint widget
|
||||||
repaint();
|
repaint();
|
||||||
emit locationChanged( m_currentLocation );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -101,11 +94,18 @@ TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* locati
|
|||||||
//### Private
|
//### Private
|
||||||
//###
|
//###
|
||||||
|
|
||||||
|
struct PainterEnder
|
||||||
|
{
|
||||||
|
QPainter& p;
|
||||||
|
~PainterEnder() { p.end(); }
|
||||||
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
TimeZoneWidget::paintEvent( QPaintEvent* )
|
TimeZoneWidget::paintEvent( QPaintEvent* )
|
||||||
{
|
{
|
||||||
QFontMetrics fontMetrics( font );
|
QFontMetrics fontMetrics( font );
|
||||||
QPainter painter( this );
|
QPainter painter( this );
|
||||||
|
PainterEnder painter_end { painter };
|
||||||
|
|
||||||
painter.setRenderHint( QPainter::Antialiasing );
|
painter.setRenderHint( QPainter::Antialiasing );
|
||||||
painter.setFont( font );
|
painter.setFont( font );
|
||||||
@ -116,6 +116,11 @@ TimeZoneWidget::paintEvent( QPaintEvent* )
|
|||||||
// Draw zone image
|
// Draw zone image
|
||||||
painter.drawImage( 0, 0, currentZoneImage );
|
painter.drawImage( 0, 0, currentZoneImage );
|
||||||
|
|
||||||
|
if ( !m_currentLocation )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_TIMEZONES
|
#ifdef DEBUG_TIMEZONES
|
||||||
QPoint point = getLocationPosition( m_currentLocation );
|
QPoint point = getLocationPosition( m_currentLocation );
|
||||||
// Draw latitude lines
|
// Draw latitude lines
|
||||||
@ -175,8 +180,6 @@ TimeZoneWidget::paintEvent( QPaintEvent* )
|
|||||||
painter.setPen( Qt::white );
|
painter.setPen( Qt::white );
|
||||||
painter.drawText( rect.x() + 5, rect.bottom() - 4, m_currentLocation ? m_currentLocation->tr() : QString() );
|
painter.drawText( rect.x() + 5, rect.bottom() - 4, m_currentLocation ? m_currentLocation->tr() : QString() );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
painter.end();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -194,7 +197,7 @@ TimeZoneWidget::mousePressEvent( QMouseEvent* event )
|
|||||||
|
|
||||||
using namespace CalamaresUtils::Locale;
|
using namespace CalamaresUtils::Locale;
|
||||||
const TZZone* closest = nullptr;
|
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 );
|
const auto* region = dynamic_cast< const TZRegion* >( region_p );
|
||||||
if ( region )
|
if ( region )
|
||||||
@ -222,6 +225,6 @@ TimeZoneWidget::mousePressEvent( QMouseEvent* event )
|
|||||||
// Set zone image and repaint widget
|
// Set zone image and repaint widget
|
||||||
setCurrentLocation( closest );
|
setCurrentLocation( closest );
|
||||||
// Emit signal
|
// Emit signal
|
||||||
emit locationChanged( m_currentLocation );
|
emit locationChanged( closest );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,32 +31,48 @@
|
|||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QWidget>
|
#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
|
class TimeZoneWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
using TZZone = CalamaresUtils::Locale::TZZone;
|
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 );
|
void setCurrentLocation( const TZZone* location );
|
||||||
const TZZone* currentLocation() { return m_currentLocation; }
|
|
||||||
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
/** @brief The location has changed by mouse click */
|
||||||
void locationChanged( const TZZone* location );
|
void locationChanged( const TZZone* location );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QFont font;
|
QFont font;
|
||||||
QImage background, pin, currentZoneImage;
|
QImage background, pin, currentZoneImage;
|
||||||
TimeZoneImageList timeZoneImages;
|
TimeZoneImageList timeZoneImages;
|
||||||
const TZZone* m_currentLocation = nullptr; // Not owned by me
|
|
||||||
|
|
||||||
QPoint getLocationPosition( const TZZone* l )
|
const CalamaresUtils::Locale::CStringPairList& m_zonesData;
|
||||||
{
|
const TZZone* m_currentLocation = nullptr; // Not owned by me
|
||||||
return timeZoneImages.getLocationPosition( l->longitude(), l->latitude() );
|
|
||||||
}
|
|
||||||
|
|
||||||
void paintEvent( QPaintEvent* event );
|
void paintEvent( QPaintEvent* event );
|
||||||
void mousePressEvent( QMouseEvent* event );
|
void mousePressEvent( QMouseEvent* event );
|
||||||
|
@ -19,74 +19,20 @@
|
|||||||
|
|
||||||
#include "LocaleQmlViewStep.h"
|
#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/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 >(); )
|
CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleQmlViewStepFactory, registerPlugin< LocaleQmlViewStep >(); )
|
||||||
|
|
||||||
LocaleQmlViewStep::LocaleQmlViewStep( QObject* parent )
|
LocaleQmlViewStep::LocaleQmlViewStep( QObject* parent )
|
||||||
: Calamares::QmlViewStep( parent )
|
: Calamares::QmlViewStep( parent )
|
||||||
, m_config( new Config( this ) )
|
, m_config( std::make_unique< Config >( this ) )
|
||||||
, m_nextEnabled( false )
|
|
||||||
, m_geoip( nullptr )
|
|
||||||
{
|
{
|
||||||
emit nextStatusChanged( m_nextEnabled );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject*
|
QObject*
|
||||||
LocaleQmlViewStep::getConfig()
|
LocaleQmlViewStep::getConfig()
|
||||||
{
|
{
|
||||||
return m_config;
|
return m_config.get();
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
@ -95,17 +41,21 @@ LocaleQmlViewStep::prettyName() const
|
|||||||
return tr( "Location" );
|
return tr( "Location" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
LocaleQmlViewStep::prettyStatus() const
|
||||||
|
{
|
||||||
|
return m_config->prettyStatus();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
LocaleQmlViewStep::isNextEnabled() const
|
LocaleQmlViewStep::isNextEnabled() const
|
||||||
{
|
{
|
||||||
// TODO: should return true
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
LocaleQmlViewStep::isBackEnabled() const
|
LocaleQmlViewStep::isBackEnabled() const
|
||||||
{
|
{
|
||||||
// TODO: should return true (it's weird that you are not allowed to have welcome *after* anything
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +63,6 @@ LocaleQmlViewStep::isBackEnabled() const
|
|||||||
bool
|
bool
|
||||||
LocaleQmlViewStep::isAtBeginning() const
|
LocaleQmlViewStep::isAtBeginning() const
|
||||||
{
|
{
|
||||||
// TODO: adjust to "pages" in the QML
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,79 +70,18 @@ LocaleQmlViewStep::isAtBeginning() const
|
|||||||
bool
|
bool
|
||||||
LocaleQmlViewStep::isAtEnd() const
|
LocaleQmlViewStep::isAtEnd() const
|
||||||
{
|
{
|
||||||
// TODO: adjust to "pages" in the QML
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Calamares::JobList
|
Calamares::JobList
|
||||||
LocaleQmlViewStep::jobs() const
|
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
|
m_config->setConfigurationMap( configurationMap );
|
||||||
}
|
Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last
|
||||||
|
|
||||||
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();
|
|
||||||
Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last
|
|
||||||
}
|
}
|
||||||
|
@ -20,14 +20,10 @@
|
|||||||
#define LOCALE_QMLVIEWSTEP_H
|
#define LOCALE_QMLVIEWSTEP_H
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "geoip/Handler.h"
|
|
||||||
#include "geoip/Interface.h"
|
#include "DllMacro.h"
|
||||||
#include "utils/PluginFactory.h"
|
#include "utils/PluginFactory.h"
|
||||||
#include "viewpages/QmlViewStep.h"
|
#include "viewpages/QmlViewStep.h"
|
||||||
#include <DllMacro.h>
|
|
||||||
|
|
||||||
#include <QFutureWatcher>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@ -39,6 +35,7 @@ public:
|
|||||||
explicit LocaleQmlViewStep( QObject* parent = nullptr );
|
explicit LocaleQmlViewStep( QObject* parent = nullptr );
|
||||||
|
|
||||||
QString prettyName() const override;
|
QString prettyName() const override;
|
||||||
|
QString prettyStatus() const override;
|
||||||
|
|
||||||
bool isNextEnabled() const override;
|
bool isNextEnabled() const override;
|
||||||
bool isBackEnabled() const override;
|
bool isBackEnabled() const override;
|
||||||
@ -47,28 +44,12 @@ public:
|
|||||||
bool isAtEnd() const override;
|
bool isAtEnd() const override;
|
||||||
|
|
||||||
Calamares::JobList jobs() const override;
|
Calamares::JobList jobs() const override;
|
||||||
void onActivate() override;
|
|
||||||
void onLeave() override;
|
|
||||||
|
|
||||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||||
QObject* getConfig() override;
|
QObject* getConfig() override;
|
||||||
|
|
||||||
virtual Calamares::RequirementsList checkRequirements() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// TODO: a generic QML viewstep should return a config object from a method
|
std::unique_ptr< Config > m_config;
|
||||||
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();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleQmlViewStepFactory )
|
CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleQmlViewStepFactory )
|
||||||
|
@ -74,6 +74,7 @@ Column {
|
|||||||
var tz2 = responseJSON.timezoneId
|
var tz2 = responseJSON.timezoneId
|
||||||
|
|
||||||
tzText.text = "Timezone: " + tz2
|
tzText.text = "Timezone: " + tz2
|
||||||
|
config.setCurrentLocation(tz2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +127,7 @@ Column {
|
|||||||
anchorPoint.x: image.width/4
|
anchorPoint.x: image.width/4
|
||||||
anchorPoint.y: image.height
|
anchorPoint.y: image.height
|
||||||
coordinate: QtPositioning.coordinate(
|
coordinate: QtPositioning.coordinate(
|
||||||
map.center.latitude,
|
map.center.latitude,
|
||||||
map.center.longitude)
|
map.center.longitude)
|
||||||
//coordinate: QtPositioning.coordinate(40.730610, -73.935242) // New York
|
//coordinate: QtPositioning.coordinate(40.730610, -73.935242) // New York
|
||||||
|
|
||||||
@ -156,7 +157,7 @@ Column {
|
|||||||
map.center.longitude = coordinate.longitude
|
map.center.longitude = coordinate.longitude
|
||||||
|
|
||||||
getTz();
|
getTz();
|
||||||
|
|
||||||
console.log(coordinate.latitude, coordinate.longitude)
|
console.log(coordinate.latitude, coordinate.longitude)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,7 +200,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 100
|
height: 100
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
@ -32,10 +32,6 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
//Needs to come from Locale config
|
|
||||||
property var confLang: "en_US.UTF8"
|
|
||||||
property var confLocale: "nl_NL.UTF8"
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: textArea
|
id: textArea
|
||||||
x: 28
|
x: 28
|
||||||
@ -57,7 +53,7 @@ Item {
|
|||||||
width: 240
|
width: 240
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
text: qsTr("<h1>Languages</h1> </br>
|
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
|
font.pointSize: 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,10 +72,9 @@ Item {
|
|||||||
id: list1
|
id: list1
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
// bogus entries, need to come from Locale config
|
model: config.supportedLocales
|
||||||
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"]
|
|
||||||
|
|
||||||
currentIndex: 1
|
currentIndex: -1
|
||||||
highlight: Rectangle {
|
highlight: Rectangle {
|
||||||
color: Kirigami.Theme.highlightColor
|
color: Kirigami.Theme.highlightColor
|
||||||
}
|
}
|
||||||
@ -95,17 +90,17 @@ Item {
|
|||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
list1.currentIndex = index
|
list1.currentIndex = index
|
||||||
confLang = list1.currentIndex
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onCurrentItemChanged: { config.currentLanguageCode = model[currentIndex] } /* This works because model is a stringlist */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: i18n
|
id: lc_numeric
|
||||||
x: 430
|
x: 430
|
||||||
y: 40
|
y: 40
|
||||||
|
|
||||||
@ -118,7 +113,7 @@ Item {
|
|||||||
width: 240
|
width: 240
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
text: qsTr("<h1>Locales</h1> </br>
|
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
|
font.pointSize: 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,10 +133,9 @@ Item {
|
|||||||
width: 180; height: 200
|
width: 180; height: 200
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
// bogus entries, need to come from Locale config
|
model: config.supportedLocales
|
||||||
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"]
|
|
||||||
|
|
||||||
currentIndex: 2
|
currentIndex: -1
|
||||||
highlight: Rectangle {
|
highlight: Rectangle {
|
||||||
color: Kirigami.Theme.highlightColor
|
color: Kirigami.Theme.highlightColor
|
||||||
}
|
}
|
||||||
@ -154,11 +148,10 @@ Item {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
list2.currentIndex = index
|
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
|
width: 800
|
||||||
height: 550
|
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 {
|
Loader {
|
||||||
id: image
|
id: image
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height / 1.28
|
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 {
|
RowLayout {
|
||||||
@ -95,7 +65,7 @@ Page {
|
|||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
text: qsTr("System language set to %1").arg(confLang)
|
text: config.currentLanguageStatus
|
||||||
}
|
}
|
||||||
Kirigami.Separator {
|
Kirigami.Separator {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@ -103,7 +73,7 @@ Page {
|
|||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
text: qsTr("Numbers and dates locale set to %1").arg(confLocale)
|
text: config.currentLCStatus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
|
@ -15,8 +15,6 @@ dbus: true
|
|||||||
# Whether /var/lib/dbus/machine-id should be a symlink to /etc/machine-id
|
# 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).
|
# (ignored if dbus is false, or if there is no /etc/machine-id to point to).
|
||||||
dbus-symlink: true
|
dbus-symlink: true
|
||||||
# this is a deprecated form of *dbus-symlink*
|
|
||||||
symlink: true
|
|
||||||
|
|
||||||
# Whether to create an entropy file
|
# Whether to create an entropy file
|
||||||
entropy: false
|
entropy: false
|
||||||
|
@ -4,6 +4,10 @@ $id: https://calamares.io/schemas/machineid
|
|||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
"systemd": { type: boolean, default: true }
|
systemd: { type: boolean, default: true }
|
||||||
"dbus": { type: boolean, default: true }
|
dbus: { type: boolean, default: true }
|
||||||
"symlink": { 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
|
PackageModel.cpp
|
||||||
UI
|
UI
|
||||||
page_netinst.ui
|
page_netinst.ui
|
||||||
RESOURCES
|
|
||||||
netinstall.qrc
|
|
||||||
LINK_PRIVATE_LIBRARIES
|
LINK_PRIVATE_LIBRARIES
|
||||||
calamaresui
|
calamaresui
|
||||||
Qt5::Network
|
Qt5::Network
|
||||||
|
@ -332,9 +332,7 @@ ChoicePage::setupChoices()
|
|||||||
|
|
||||||
CALAMARES_RETRANSLATE(
|
CALAMARES_RETRANSLATE(
|
||||||
m_somethingElseButton->setText( tr( "<strong>Manual partitioning</strong><br/>"
|
m_somethingElseButton->setText( tr( "<strong>Manual partitioning</strong><br/>"
|
||||||
"You can create or resize partitions yourself."
|
"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." ) );
|
|
||||||
updateSwapChoicesTr( m_eraseSwapChoiceComboBox );
|
updateSwapChoicesTr( m_eraseSwapChoiceComboBox );
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1444,46 +1442,75 @@ ChoicePage::currentChoice() const
|
|||||||
return m_choice;
|
return m_choice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
void
|
ChoicePage::calculateNextEnabled() const
|
||||||
ChoicePage::updateNextEnabled()
|
|
||||||
{
|
{
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
|
|
||||||
auto sm_p = m_beforePartitionBarsView ? m_beforePartitionBarsView->selectionModel() : nullptr;
|
auto sm_p = m_beforePartitionBarsView ? m_beforePartitionBarsView->selectionModel() : nullptr;
|
||||||
|
|
||||||
switch ( m_choice )
|
switch ( m_choice )
|
||||||
{
|
{
|
||||||
case NoChoice:
|
case NoChoice:
|
||||||
enabled = false;
|
cDebug() << "No partitioning choice";
|
||||||
break;
|
return false;
|
||||||
case Replace:
|
case Replace:
|
||||||
case Alongside:
|
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;
|
break;
|
||||||
case Erase:
|
case Erase:
|
||||||
case Manual:
|
case Manual:
|
||||||
enabled = true;
|
enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( m_isEfi &&
|
if (!enabled)
|
||||||
( m_choice == Alongside ||
|
|
||||||
m_choice == Replace ) )
|
|
||||||
{
|
{
|
||||||
if ( m_core->efiSystemPartitions().count() == 0 )
|
cDebug() << "No valid choice made";
|
||||||
enabled = false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( m_choice != Manual &&
|
|
||||||
m_encryptWidget->isVisible() &&
|
|
||||||
m_encryptWidget->state() == EncryptWidget::Encryption::Unconfirmed )
|
|
||||||
enabled = false;
|
|
||||||
|
|
||||||
if ( enabled == m_nextEnabled )
|
if ( m_isEfi && ( m_choice == Alongside || m_choice == Replace ) )
|
||||||
return;
|
{
|
||||||
|
if ( m_core->efiSystemPartitions().count() == 0 )
|
||||||
|
{
|
||||||
|
cDebug() << "No EFI partition for alongside or replace";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_nextEnabled = enabled;
|
if ( m_choice != Manual && m_encryptWidget->isVisible() )
|
||||||
emit nextStatusChanged( enabled );
|
{
|
||||||
|
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
|
void
|
||||||
|
@ -126,6 +126,7 @@ private slots:
|
|||||||
void onEraseSwapChoiceChanged();
|
void onEraseSwapChoiceChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool calculateNextEnabled() const;
|
||||||
void updateNextEnabled();
|
void updateNextEnabled();
|
||||||
void setupChoices();
|
void setupChoices();
|
||||||
QComboBox* createBootloaderComboBox( QWidget* parentButton );
|
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
|
void
|
||||||
EncryptWidget::updateState()
|
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;
|
Encryption newState;
|
||||||
if ( m_ui->m_encryptCheckBox->isChecked() )
|
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
|
void
|
||||||
EncryptWidget::onPassphraseEdited()
|
EncryptWidget::onPassphraseEdited()
|
||||||
{
|
{
|
||||||
@ -135,24 +157,6 @@ EncryptWidget::onPassphraseEdited()
|
|||||||
m_ui->m_iconLabel->show();
|
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();
|
updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,8 +107,8 @@ PartitionViewStep::continueLoading()
|
|||||||
m_waitingWidget->deleteLater();
|
m_waitingWidget->deleteLater();
|
||||||
m_waitingWidget = nullptr;
|
m_waitingWidget = nullptr;
|
||||||
|
|
||||||
connect( m_core, &PartitionCoreModule::hasRootMountPointChanged, this, &PartitionViewStep::nextStatusChanged );
|
connect( m_core, &PartitionCoreModule::hasRootMountPointChanged, this, &PartitionViewStep::nextPossiblyChanged );
|
||||||
connect( m_choicePage, &ChoicePage::nextStatusChanged, this, &PartitionViewStep::nextStatusChanged );
|
connect( m_choicePage, &ChoicePage::nextStatusChanged, this, &PartitionViewStep::nextPossiblyChanged );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -348,6 +348,11 @@ PartitionViewStep::isNextEnabled() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PartitionViewStep::nextPossiblyChanged(bool)
|
||||||
|
{
|
||||||
|
emit nextStatusChanged( isNextEnabled() );
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PartitionViewStep::isBackEnabled() const
|
PartitionViewStep::isBackEnabled() const
|
||||||
|
@ -78,6 +78,9 @@ private:
|
|||||||
void initPartitionCoreModule();
|
void initPartitionCoreModule();
|
||||||
void continueLoading();
|
void continueLoading();
|
||||||
|
|
||||||
|
/// "slot" for changes to next-status from the KPMCore and ChoicePage
|
||||||
|
void nextPossiblyChanged( bool );
|
||||||
|
|
||||||
PartitionCoreModule* m_core;
|
PartitionCoreModule* m_core;
|
||||||
QStackedWidget* m_widget;
|
QStackedWidget* m_widget;
|
||||||
ChoicePage* m_choicePage;
|
ChoicePage* m_choicePage;
|
||||||
|
@ -4,7 +4,6 @@ calamares_add_plugin( preservefiles
|
|||||||
TYPE job
|
TYPE job
|
||||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||||
SOURCES
|
SOURCES
|
||||||
permissions.cpp
|
|
||||||
PreserveFiles.cpp
|
PreserveFiles.cpp
|
||||||
LINK_PRIVATE_LIBRARIES
|
LINK_PRIVATE_LIBRARIES
|
||||||
calamares
|
calamares
|
||||||
|
@ -1,39 +1,28 @@
|
|||||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
/* === 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 "PreserveFiles.h"
|
||||||
|
|
||||||
#include "permissions.h"
|
|
||||||
|
|
||||||
#include "CalamaresVersion.h"
|
#include "CalamaresVersion.h"
|
||||||
#include "JobQueue.h"
|
|
||||||
#include "GlobalStorage.h"
|
#include "GlobalStorage.h"
|
||||||
|
#include "JobQueue.h"
|
||||||
#include "utils/CalamaresUtilsSystem.h"
|
#include "utils/CalamaresUtilsSystem.h"
|
||||||
#include "utils/CommandList.h"
|
#include "utils/CommandList.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
|
#include "utils/Permissions.h"
|
||||||
#include "utils/Units.h"
|
#include "utils/Units.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
using CalamaresUtils::operator""_MiB;
|
using CalamaresUtils::operator""_MiB;
|
||||||
|
|
||||||
QString targetPrefix()
|
QString
|
||||||
|
targetPrefix()
|
||||||
{
|
{
|
||||||
if ( CalamaresUtils::System::instance()->doChroot() )
|
if ( CalamaresUtils::System::instance()->doChroot() )
|
||||||
{
|
{
|
||||||
@ -42,9 +31,13 @@ QString targetPrefix()
|
|||||||
{
|
{
|
||||||
QString r = gs->value( "rootMountPoint" ).toString();
|
QString r = gs->value( "rootMountPoint" ).toString();
|
||||||
if ( !r.isEmpty() )
|
if ( !r.isEmpty() )
|
||||||
|
{
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
cDebug() << "RootMountPoint is empty";
|
cDebug() << "RootMountPoint is empty";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -55,16 +48,21 @@ QString targetPrefix()
|
|||||||
return QLatin1String( "/" );
|
return QLatin1String( "/" );
|
||||||
}
|
}
|
||||||
|
|
||||||
QString atReplacements( QString s )
|
QString
|
||||||
|
atReplacements( QString s )
|
||||||
{
|
{
|
||||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||||
QString root( "/" );
|
QString root( "/" );
|
||||||
QString user;
|
QString user;
|
||||||
|
|
||||||
if ( gs && gs->contains( "rootMountPoint" ) )
|
if ( gs && gs->contains( "rootMountPoint" ) )
|
||||||
|
{
|
||||||
root = gs->value( "rootMountPoint" ).toString();
|
root = gs->value( "rootMountPoint" ).toString();
|
||||||
|
}
|
||||||
if ( gs && gs->contains( "username" ) )
|
if ( gs && gs->contains( "username" ) )
|
||||||
|
{
|
||||||
user = gs->value( "username" ).toString();
|
user = gs->value( "username" ).toString();
|
||||||
|
}
|
||||||
|
|
||||||
return s.replace( "@@ROOT@@", root ).replace( "@@USER@@", user );
|
return s.replace( "@@ROOT@@", root ).replace( "@@USER@@", user );
|
||||||
}
|
}
|
||||||
@ -74,9 +72,7 @@ PreserveFiles::PreserveFiles( QObject* parent )
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PreserveFiles::~PreserveFiles()
|
PreserveFiles::~PreserveFiles() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString
|
QString
|
||||||
PreserveFiles::prettyName() const
|
PreserveFiles::prettyName() const
|
||||||
@ -107,8 +103,7 @@ copy_file( const QString& source, const QString& dest )
|
|||||||
{
|
{
|
||||||
b = sourcef.read( 1_MiB );
|
b = sourcef.read( 1_MiB );
|
||||||
destf.write( b );
|
destf.write( b );
|
||||||
}
|
} while ( b.count() > 0 );
|
||||||
while ( b.count() > 0 );
|
|
||||||
|
|
||||||
sourcef.close();
|
sourcef.close();
|
||||||
destf.close();
|
destf.close();
|
||||||
@ -116,14 +111,19 @@ copy_file( const QString& source, const QString& dest )
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Calamares::JobResult PreserveFiles::exec()
|
Calamares::JobResult
|
||||||
|
PreserveFiles::exec()
|
||||||
{
|
{
|
||||||
if ( m_items.isEmpty() )
|
if ( m_items.isEmpty() )
|
||||||
|
{
|
||||||
return Calamares::JobResult::error( tr( "No files configured to save for later." ) );
|
return Calamares::JobResult::error( tr( "No files configured to save for later." ) );
|
||||||
|
}
|
||||||
|
|
||||||
QString prefix = targetPrefix();
|
QString prefix = targetPrefix();
|
||||||
if ( !prefix.endsWith( '/' ) )
|
if ( !prefix.endsWith( '/' ) )
|
||||||
|
{
|
||||||
prefix.append( '/' );
|
prefix.append( '/' );
|
||||||
|
}
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for ( const auto& it : m_items )
|
for ( const auto& it : m_items )
|
||||||
@ -133,37 +133,34 @@ Calamares::JobResult PreserveFiles::exec()
|
|||||||
QString dest = prefix + bare_dest;
|
QString dest = prefix + bare_dest;
|
||||||
|
|
||||||
if ( it.type == ItemType::Log )
|
if ( it.type == ItemType::Log )
|
||||||
|
{
|
||||||
source = Logger::logFile();
|
source = Logger::logFile();
|
||||||
|
}
|
||||||
if ( it.type == ItemType::Config )
|
if ( it.type == ItemType::Config )
|
||||||
{
|
{
|
||||||
if ( Calamares::JobQueue::instance()->globalStorage()->save( dest ) )
|
if ( Calamares::JobQueue::instance()->globalStorage()->save( dest ) )
|
||||||
|
{
|
||||||
cWarning() << "Could not write config for" << dest;
|
cWarning() << "Could not write config for" << dest;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
++count;
|
++count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if ( source.isEmpty() )
|
else if ( source.isEmpty() )
|
||||||
|
{
|
||||||
cWarning() << "Skipping unnamed source file for" << dest;
|
cWarning() << "Skipping unnamed source file for" << dest;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ( copy_file( source, dest ) )
|
if ( copy_file( source, dest ) )
|
||||||
{
|
{
|
||||||
if ( it.perm.isValid() )
|
if ( it.perm.isValid() )
|
||||||
{
|
{
|
||||||
auto s_p = CalamaresUtils::System::instance();
|
if ( !it.perm.apply( CalamaresUtils::System::instance()->targetPath( bare_dest ) ) )
|
||||||
|
{
|
||||||
int r;
|
cWarning() << "Could not set attributes of" << bare_dest;
|
||||||
|
}
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++count;
|
++count;
|
||||||
@ -171,12 +168,13 @@ Calamares::JobResult PreserveFiles::exec()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return count == m_items.count() ?
|
return count == m_items.count()
|
||||||
Calamares::JobResult::ok() :
|
? Calamares::JobResult::ok()
|
||||||
Calamares::JobResult::error( tr( "Not all of the configured files could be preserved." ) );
|
: 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" ];
|
auto files = configurationMap[ "files" ];
|
||||||
if ( !files.isValid() )
|
if ( !files.isValid() )
|
||||||
@ -193,7 +191,9 @@ void PreserveFiles::setConfigurationMap(const QVariantMap& configurationMap)
|
|||||||
|
|
||||||
QString defaultPermissions = configurationMap[ "perm" ].toString();
|
QString defaultPermissions = configurationMap[ "perm" ].toString();
|
||||||
if ( defaultPermissions.isEmpty() )
|
if ( defaultPermissions.isEmpty() )
|
||||||
|
{
|
||||||
defaultPermissions = QStringLiteral( "root:root:0400" );
|
defaultPermissions = QStringLiteral( "root:root:0400" );
|
||||||
|
}
|
||||||
|
|
||||||
QVariantList l = files.toList();
|
QVariantList l = files.toList();
|
||||||
unsigned int c = 0;
|
unsigned int c = 0;
|
||||||
@ -203,22 +203,23 @@ void PreserveFiles::setConfigurationMap(const QVariantMap& configurationMap)
|
|||||||
{
|
{
|
||||||
QString filename = li.toString();
|
QString filename = li.toString();
|
||||||
if ( !filename.isEmpty() )
|
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
|
else
|
||||||
|
{
|
||||||
cDebug() << "Empty filename for preservefiles, item" << c;
|
cDebug() << "Empty filename for preservefiles, item" << c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if ( li.type() == QVariant::Map )
|
else if ( li.type() == QVariant::Map )
|
||||||
{
|
{
|
||||||
const auto map = li.toMap();
|
const auto map = li.toMap();
|
||||||
QString dest = map[ "dest" ].toString();
|
QString dest = map[ "dest" ].toString();
|
||||||
QString from = map[ "from" ].toString();
|
QString from = map[ "from" ].toString();
|
||||||
ItemType t =
|
ItemType t = ( from == "log" ) ? ItemType::Log : ( from == "config" ) ? ItemType::Config : ItemType::None;
|
||||||
( from == "log" ) ? ItemType::Log :
|
|
||||||
( from == "config" ) ? ItemType::Config :
|
|
||||||
ItemType::None;
|
|
||||||
QString perm = map[ "perm" ].toString();
|
QString perm = map[ "perm" ].toString();
|
||||||
if ( perm.isEmpty() )
|
if ( perm.isEmpty() )
|
||||||
|
{
|
||||||
perm = defaultPermissions;
|
perm = defaultPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
if ( dest.isEmpty() )
|
if ( dest.isEmpty() )
|
||||||
{
|
{
|
||||||
@ -230,15 +231,16 @@ void PreserveFiles::setConfigurationMap(const QVariantMap& configurationMap)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_items.append( Item{ QString(), dest, Permissions( perm ), t } );
|
m_items.append( Item { QString(), dest, CalamaresUtils::Permissions( perm ), t } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
cDebug() << "Invalid type for preservefiles, item" << c;
|
cDebug() << "Invalid type for preservefiles, item" << c;
|
||||||
|
}
|
||||||
|
|
||||||
++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> ===
|
/* === 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
|
#ifndef PRESERVEFILES_H
|
||||||
#define PRESERVEFILES_H
|
#define PRESERVEFILES_H
|
||||||
|
|
||||||
|
#include "CppJob.h"
|
||||||
|
#include "DllMacro.h"
|
||||||
|
#include "utils/Permissions.h"
|
||||||
|
#include "utils/PluginFactory.h"
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
|
|
||||||
#include "CppJob.h"
|
|
||||||
#include "DllMacro.h"
|
|
||||||
|
|
||||||
#include "utils/PluginFactory.h"
|
|
||||||
|
|
||||||
#include "permissions.h"
|
|
||||||
|
|
||||||
class PLUGINDLLEXPORT PreserveFiles : public Calamares::CppJob
|
class PLUGINDLLEXPORT PreserveFiles : public Calamares::CppJob
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -40,15 +28,15 @@ class PLUGINDLLEXPORT PreserveFiles : public Calamares::CppJob
|
|||||||
Path,
|
Path,
|
||||||
Log,
|
Log,
|
||||||
Config
|
Config
|
||||||
} ;
|
};
|
||||||
|
|
||||||
struct Item
|
struct Item
|
||||||
{
|
{
|
||||||
QString source;
|
QString source;
|
||||||
QString dest;
|
QString dest;
|
||||||
Permissions perm;
|
CalamaresUtils::Permissions perm;
|
||||||
ItemType type;
|
ItemType type;
|
||||||
} ;
|
};
|
||||||
|
|
||||||
using ItemList = QList< Item >;
|
using ItemList = QList< Item >;
|
||||||
|
|
||||||
@ -68,4 +56,4 @@ private:
|
|||||||
|
|
||||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( PreserveFilesFactory )
|
CALAMARES_PLUGIN_FACTORY_DECLARATION( PreserveFilesFactory )
|
||||||
|
|
||||||
#endif // PRESERVEFILES_H
|
#endif // PRESERVEFILES_H
|
||||||
|
@ -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
|
UsersPage.cpp
|
||||||
SetHostNameJob.cpp
|
SetHostNameJob.cpp
|
||||||
CheckPWQuality.cpp
|
CheckPWQuality.cpp
|
||||||
|
Config.cpp
|
||||||
UI
|
UI
|
||||||
page_usersetup.ui
|
page_usersetup.ui
|
||||||
RESOURCES
|
RESOURCES
|
||||||
@ -43,17 +44,31 @@ calamares_add_plugin( users
|
|||||||
calamares_add_test(
|
calamares_add_test(
|
||||||
userspasswordtest
|
userspasswordtest
|
||||||
SOURCES
|
SOURCES
|
||||||
PasswordTests.cpp
|
TestPasswordJob.cpp
|
||||||
SetPasswordJob.cpp
|
SetPasswordJob.cpp
|
||||||
LIBRARIES
|
LIBRARIES
|
||||||
${CRYPT_LIBRARIES}
|
${CRYPT_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
calamares_add_test(
|
calamares_add_test(
|
||||||
userstest
|
userscreatetest
|
||||||
SOURCES
|
SOURCES
|
||||||
Tests.cpp
|
TestCreateUserJob.cpp
|
||||||
|
CreateUserJob.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
calamares_add_test(
|
||||||
|
usershostnametest
|
||||||
|
SOURCES
|
||||||
|
TestSetHostNameJob.cpp
|
||||||
SetHostNameJob.cpp
|
SetHostNameJob.cpp
|
||||||
LIBRARIES
|
LIBRARIES
|
||||||
Qt5::DBus
|
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;
|
cDebug() << Logger::SubEntry << "minLength set to" << minLength;
|
||||||
checks.push_back( PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is too short" ); },
|
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 ) ) );
|
PasswordCheck::Weight( 10 ) ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ DEFINE_CHECK_FUNC( maxLength )
|
|||||||
{
|
{
|
||||||
cDebug() << Logger::SubEntry << "maxLength set to" << maxLength;
|
cDebug() << Logger::SubEntry << "maxLength set to" << maxLength;
|
||||||
checks.push_back( PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is too long" ); },
|
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 ) ) );
|
PasswordCheck::Weight( 10 ) ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -349,8 +349,8 @@ DEFINE_CHECK_FUNC( libpwquality )
|
|||||||
/* Something actually added? */
|
/* Something actually added? */
|
||||||
if ( requirement_count )
|
if ( requirement_count )
|
||||||
{
|
{
|
||||||
checks.push_back( PasswordCheck( [ settings ]() { return settings->explanation(); },
|
checks.push_back( PasswordCheck( [settings]() { return settings->explanation(); },
|
||||||
[ settings ]( const QString& s ) {
|
[settings]( const QString& s ) {
|
||||||
int r = settings->check( s );
|
int r = settings->check( s );
|
||||||
if ( r < 0 )
|
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 "JobQueue.h"
|
||||||
#include "utils/CalamaresUtilsSystem.h"
|
#include "utils/CalamaresUtilsSystem.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
|
#include "utils/Permissions.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -54,6 +55,109 @@ CreateUserJob::prettyStatusMessage() const
|
|||||||
return tr( "Creating user %1." ).arg( m_userName );
|
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
|
Calamares::JobResult
|
||||||
CreateUserJob::exec()
|
CreateUserJob::exec()
|
||||||
@ -65,65 +169,37 @@ CreateUserJob::exec()
|
|||||||
{
|
{
|
||||||
cDebug() << "[CREATEUSER]: preparing sudoers";
|
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." ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
QFile sudoersFile( sudoersFi.absoluteFilePath() );
|
|
||||||
if ( !sudoersFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
|
|
||||||
{
|
{
|
||||||
return Calamares::JobResult::error( tr( "Cannot create sudoers file for writing." ) );
|
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";
|
cDebug() << "[CREATEUSER]: preparing groups";
|
||||||
|
|
||||||
QFileInfo groupsFi( destDir.absoluteFilePath( "etc/group" ) );
|
QStringList availableGroups = groupsInTargetSystem( destDir );
|
||||||
QFile groupsFile( groupsFi.absoluteFilePath() );
|
QStringList groupsForThisUser = m_defaultGroups;
|
||||||
if ( !groupsFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
|
if ( m_autologin && gs->contains( "autologinGroup" ) && !gs->value( "autologinGroup" ).toString().isEmpty() )
|
||||||
{
|
{
|
||||||
return Calamares::JobResult::error( tr( "Cannot open groups file for reading." ) );
|
groupsForThisUser << gs->value( "autologinGroup" ).toString();
|
||||||
}
|
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
ensureGroupsExistInTarget( m_defaultGroups, availableGroups );
|
||||||
|
|
||||||
for ( const QString& group : m_defaultGroups )
|
// If we're looking to reuse the contents of an existing /home.
|
||||||
{
|
// This GS setting comes from the **partitioning** module.
|
||||||
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 ( gs->value( "reuseHome" ).toBool() )
|
if ( gs->value( "reuseHome" ).toBool() )
|
||||||
{
|
{
|
||||||
QString shellFriendlyHome = "/home/" + m_userName;
|
QString shellFriendlyHome = "/home/" + m_userName;
|
||||||
@ -133,6 +209,7 @@ CreateUserJob::exec()
|
|||||||
QString backupDirName = "dotfiles_backup_" + QDateTime::currentDateTime().toString( "yyyy-MM-dd_HH-mm-ss" );
|
QString backupDirName = "dotfiles_backup_" + QDateTime::currentDateTime().toString( "yyyy-MM-dd_HH-mm-ss" );
|
||||||
existingHome.mkdir( backupDirName );
|
existingHome.mkdir( backupDirName );
|
||||||
|
|
||||||
|
// We need the extra `sh -c` here to ensure that we can expand the shell globs
|
||||||
CalamaresUtils::System::instance()->targetEnvCall(
|
CalamaresUtils::System::instance()->targetEnvCall(
|
||||||
{ "sh", "-c", "mv -f " + shellFriendlyHome + "/.* " + shellFriendlyHome + "/" + backupDirName } );
|
{ "sh", "-c", "mv -f " + shellFriendlyHome + "/.* " + shellFriendlyHome + "/" + backupDirName } );
|
||||||
}
|
}
|
||||||
@ -140,33 +217,21 @@ CreateUserJob::exec()
|
|||||||
|
|
||||||
cDebug() << "[CREATEUSER]: creating user";
|
cDebug() << "[CREATEUSER]: creating user";
|
||||||
|
|
||||||
QStringList useradd { "useradd", "-m", "-U" };
|
auto useraddResult = createUser( m_userName, m_fullName, gs->value( "userShell" ).toString() );
|
||||||
QString shell = gs->value( "userShell" ).toString();
|
if ( !useraddResult )
|
||||||
if ( !shell.isEmpty() )
|
|
||||||
{
|
{
|
||||||
useradd << "-s" << shell;
|
return useraddResult;
|
||||||
}
|
|
||||||
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 */ );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commandResult
|
auto usergroupsResult = setUserGroups( m_userName, groupsForThisUser );
|
||||||
= CalamaresUtils::System::instance()->targetEnvCommand( { "usermod", "-aG", defaultGroups, m_userName } );
|
if ( !usergroupsResult )
|
||||||
if ( commandResult.getExitCode() )
|
|
||||||
{
|
{
|
||||||
cError() << "usermod failed" << commandResult.getExitCode();
|
return usergroupsResult;
|
||||||
return commandResult.explainProcess( "usermod", std::chrono::seconds( 10 ) /* bogus timeout */ );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString userGroup = QString( "%1:%2" ).arg( m_userName ).arg( m_userName );
|
QString userGroup = QString( "%1:%2" ).arg( m_userName ).arg( m_userName );
|
||||||
QString homeDir = QString( "/home/%1" ).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() )
|
if ( commandResult.getExitCode() )
|
||||||
{
|
{
|
||||||
cError() << "chown failed" << 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> ===
|
/* === 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
|
* Calamares is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -18,15 +19,23 @@
|
|||||||
|
|
||||||
#include "SetPasswordJob.h"
|
#include "SetPasswordJob.h"
|
||||||
|
|
||||||
#include "PasswordTests.h"
|
|
||||||
|
|
||||||
#include <QtTest/QtTest>
|
#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
|
void
|
||||||
PasswordTests::initTestCase()
|
PasswordTests::initTestCase()
|
||||||
@ -48,3 +57,9 @@ PasswordTests::testSalt()
|
|||||||
QVERIFY( s.endsWith( '$' ) );
|
QVERIFY( s.endsWith( '$' ) );
|
||||||
qDebug() << "Obtained salt" << s;
|
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> ===
|
/* === 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
|
* Calamares is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -16,129 +17,96 @@
|
|||||||
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
|
* 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/Logger.h"
|
||||||
#include "utils/Yaml.h"
|
|
||||||
|
|
||||||
#include <QTemporaryDir>
|
|
||||||
#include <QtTest/QtTest>
|
#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
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
UsersTests();
|
UserTests();
|
||||||
virtual ~UsersTests() { }
|
virtual ~UserTests() {}
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
|
|
||||||
void testEtcHostname();
|
void testDefaultGroups();
|
||||||
void testEtcHosts();
|
|
||||||
void testHostnamed();
|
|
||||||
|
|
||||||
void cleanup();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QTemporaryDir m_dir;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
UsersTests::UsersTests()
|
UserTests::UserTests() {}
|
||||||
: m_dir( QStringLiteral( "/tmp/calamares-usertest" ) )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
UsersTests::initTestCase()
|
UserTests::initTestCase()
|
||||||
{
|
{
|
||||||
Logger::setupLogLevel( Logger::LOGDEBUG );
|
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||||
cDebug() << "Users test started.";
|
cDebug() << "Users test started.";
|
||||||
cDebug() << "Test dir" << m_dir.path();
|
}
|
||||||
|
|
||||||
// Ensure we have a system object, expect it to be a "bogus" one
|
void
|
||||||
CalamaresUtils::System* system = CalamaresUtils::System::instance();
|
UserTests::testDefaultGroups()
|
||||||
QVERIFY( system );
|
{
|
||||||
QVERIFY( system->doChroot() );
|
|
||||||
|
|
||||||
// Ensure we have a system-wide GlobalStorage with /tmp as root
|
|
||||||
if ( !Calamares::JobQueue::instance() )
|
|
||||||
{
|
{
|
||||||
cDebug() << "Creating new JobQueue";
|
QStringList groups;
|
||||||
(void)new Calamares::JobQueue();
|
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"
|
#include "utils/moc-warnings.h"
|
||||||
|
|
||||||
|
@ -25,16 +25,12 @@
|
|||||||
|
|
||||||
#include "UsersPage.h"
|
#include "UsersPage.h"
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
#include "ui_page_usersetup.h"
|
#include "ui_page_usersetup.h"
|
||||||
|
|
||||||
#include "CreateUserJob.h"
|
|
||||||
#include "SetHostNameJob.h"
|
|
||||||
#include "SetPasswordJob.h"
|
|
||||||
|
|
||||||
#include "GlobalStorage.h"
|
#include "GlobalStorage.h"
|
||||||
#include "JobQueue.h"
|
#include "JobQueue.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
#include "utils/CalamaresUtilsGui.h"
|
#include "utils/CalamaresUtilsGui.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
#include "utils/Retranslator.h"
|
#include "utils/Retranslator.h"
|
||||||
@ -44,14 +40,6 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#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() ? */
|
/** @brief How bad is the error for labelError() ? */
|
||||||
enum class Badness
|
enum class Badness
|
||||||
@ -79,54 +67,87 @@ labelOk( QLabel* pix, QLabel* label )
|
|||||||
pix->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes, CalamaresUtils::Original, label->size() ) );
|
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 )
|
: QWidget( parent )
|
||||||
, ui( new Ui::Page_UserSetup )
|
, ui( new Ui::Page_UserSetup )
|
||||||
|
, m_config( config )
|
||||||
, m_readyFullName( false )
|
, m_readyFullName( false )
|
||||||
, m_readyUsername( false )
|
, m_readyUsername( false )
|
||||||
, m_readyHostname( false )
|
, m_readyHostname( false )
|
||||||
, m_readyPassword( false )
|
, m_readyPassword( false )
|
||||||
, m_readyRootPassword( false )
|
, m_readyRootPassword( false )
|
||||||
, m_writeRootPassword( true )
|
|
||||||
{
|
{
|
||||||
ui->setupUi( this );
|
ui->setupUi( this );
|
||||||
|
|
||||||
// Connect signals and slots
|
// 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->textBoxUserPassword, &QLineEdit::textChanged, this, &UsersPage::onPasswordTextChanged );
|
||||||
connect( ui->textBoxUserVerifiedPassword, &QLineEdit::textChanged, this, &UsersPage::onPasswordTextChanged );
|
connect( ui->textBoxUserVerifiedPassword, &QLineEdit::textChanged, this, &UsersPage::onPasswordTextChanged );
|
||||||
connect( ui->textBoxRootPassword, &QLineEdit::textChanged, this, &UsersPage::onRootPasswordTextChanged );
|
connect( ui->textBoxRootPassword, &QLineEdit::textChanged, this, &UsersPage::onRootPasswordTextChanged );
|
||||||
connect( ui->textBoxVerifiedRootPassword, &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() );
|
onPasswordTextChanged( ui->textBoxUserPassword->text() );
|
||||||
onRootPasswordTextChanged( ui->textBoxRootPassword->text() );
|
onRootPasswordTextChanged( ui->textBoxRootPassword->text() );
|
||||||
checkReady( isReady() );
|
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
|
/* When "reuse" is checked, hide the fields for explicitly
|
||||||
* entering the root password. However, if we're going to
|
* entering the root password. However, if we're going to
|
||||||
* disable the root password anyway, hide them all regardless of
|
* disable the root password anyway, hide them all regardless of
|
||||||
* the checkbox -- so when writeRoot is false, checked needs
|
* the checkbox -- so when writeRoot is false, checked needs
|
||||||
* to be true, to hide them all.
|
* to be true, to hide them all.
|
||||||
*/
|
*/
|
||||||
if ( !m_writeRootPassword )
|
const bool visible = m_config->writeRootPassword() ? !checked : false;
|
||||||
{
|
ui->labelChooseRootPassword->setVisible( visible );
|
||||||
checked = true;
|
ui->labelRootPassword->setVisible( visible );
|
||||||
}
|
ui->labelRootPasswordError->setVisible( visible );
|
||||||
ui->labelChooseRootPassword->setVisible( !checked );
|
ui->textBoxRootPassword->setVisible( visible );
|
||||||
ui->labelRootPassword->setVisible( !checked );
|
ui->textBoxVerifiedRootPassword->setVisible( visible );
|
||||||
ui->labelRootPasswordError->setVisible( !checked );
|
|
||||||
ui->textBoxRootPassword->setVisible( !checked );
|
|
||||||
ui->textBoxVerifiedRootPassword->setVisible( !checked );
|
|
||||||
checkReady( isReady() );
|
checkReady( isReady() );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
m_customUsername = false;
|
connect( ui->textBoxFullName, &QLineEdit::textEdited, config, &Config::setFullName );
|
||||||
m_customHostname = false;
|
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->checkBoxReusePassword->setChecked( true );
|
||||||
ui->checkBoxValidatePassword->setChecked( true );
|
ui->checkBoxValidatePassword->setChecked( true );
|
||||||
|
|
||||||
@ -146,15 +167,15 @@ UsersPage::retranslate()
|
|||||||
ui->retranslateUi( this );
|
ui->retranslateUi( this );
|
||||||
if ( Calamares::Settings::instance()->isSetupMode() )
|
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 "
|
"use this computer, you can create multiple "
|
||||||
"accounts after setup.</small>" ) );
|
"accounts after setup.</small>" ) );
|
||||||
}
|
}
|
||||||
else
|
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 "
|
"use this computer, you can create multiple "
|
||||||
"accounts after installation.</small>" ) );
|
"accounts after installation.</small>" ) );
|
||||||
}
|
}
|
||||||
// Re-do password checks (with output messages) as well.
|
// Re-do password checks (with output messages) as well.
|
||||||
// .. the password-checking methods get their values from the text boxes,
|
// .. the password-checking methods get their values from the text boxes,
|
||||||
@ -165,27 +186,19 @@ UsersPage::retranslate()
|
|||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
UsersPage::isReady()
|
UsersPage::isReady() const
|
||||||
{
|
{
|
||||||
bool readyFields = m_readyFullName && m_readyHostname && m_readyPassword && m_readyUsername;
|
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
|
||||||
return 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
|
QString
|
||||||
UsersPage::getRootPassword() const
|
UsersPage::getRootPassword() const
|
||||||
{
|
{
|
||||||
if ( m_writeRootPassword )
|
if ( m_config->writeRootPassword() )
|
||||||
{
|
{
|
||||||
if ( ui->checkBoxReusePassword->isChecked() )
|
if ( ui->checkBoxReusePassword->isChecked() )
|
||||||
{
|
{
|
||||||
@ -205,42 +218,24 @@ UsersPage::getRootPassword() const
|
|||||||
QPair< QString, QString >
|
QPair< QString, QString >
|
||||||
UsersPage::getUserPassword() const
|
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 >
|
void
|
||||||
UsersPage::createJobs( const QStringList& defaultGroupsList )
|
UsersPage::fillGlobalStorage() const
|
||||||
{
|
{
|
||||||
QList< Calamares::job_ptr > list;
|
|
||||||
if ( !isReady() )
|
if ( !isReady() )
|
||||||
{
|
{
|
||||||
return list;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||||
|
|
||||||
Calamares::Job* j;
|
if ( m_config->writeRootPassword() )
|
||||||
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 )
|
|
||||||
{
|
{
|
||||||
gs->insert( "reuseRootPassword", ui->checkBoxReusePassword->isChecked() );
|
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() ) );
|
gs->insert( "password", CalamaresUtils::obscure( ui->textBoxUserPassword->text() ) );
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -254,215 +249,23 @@ UsersPage::onActivate()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
UsersPage::setWriteRootPassword( bool write )
|
UsersPage::onFullNameTextEdited( const QString& fullName )
|
||||||
{
|
{
|
||||||
m_writeRootPassword = write;
|
labelStatus( ui->labelFullName, ui->labelFullNameError, fullName, QString(), m_readyFullName );
|
||||||
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();
|
|
||||||
}
|
|
||||||
checkReady( isReady() );
|
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
|
void
|
||||||
UsersPage::fillSuggestions()
|
UsersPage::reportLoginNameStatus( const QString& status )
|
||||||
{
|
{
|
||||||
QString fullName = ui->textBoxFullName->text();
|
labelStatus( ui->labelUsername, ui->labelUsernameError, m_config->loginName(), status, m_readyUsername );
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit checkReady( isReady() );
|
emit checkReady( isReady() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
UsersPage::onHostnameTextEdited( const QString& textRef )
|
UsersPage::reportHostNameStatus( const QString& status )
|
||||||
{
|
{
|
||||||
m_customHostname = true;
|
labelStatus( ui->labelHostname, ui->labelHostnameError, m_config->hostName(), status, m_readyHostname );
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit checkReady( isReady() );
|
emit checkReady( isReady() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,13 +349,6 @@ UsersPage::setValidatePasswordDefault( bool checked )
|
|||||||
emit checkReady( isReady() );
|
emit checkReady( isReady() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
UsersPage::setAutologinDefault( bool checked )
|
|
||||||
{
|
|
||||||
ui->checkBoxAutoLogin->setChecked( checked );
|
|
||||||
emit checkReady( isReady() );
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
UsersPage::setReusePasswordDefault( bool checked )
|
UsersPage::setReusePasswordDefault( bool checked )
|
||||||
{
|
{
|
||||||
|
@ -25,10 +25,11 @@
|
|||||||
#define USERSPAGE_H
|
#define USERSPAGE_H
|
||||||
|
|
||||||
#include "CheckPWQuality.h"
|
#include "CheckPWQuality.h"
|
||||||
#include "Job.h"
|
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
class Config;
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
@ -40,19 +41,17 @@ class UsersPage : public QWidget
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit UsersPage( QWidget* parent = nullptr );
|
explicit UsersPage( Config* config, QWidget* parent = nullptr );
|
||||||
virtual ~UsersPage();
|
virtual ~UsersPage();
|
||||||
|
|
||||||
bool isReady();
|
bool isReady() const;
|
||||||
|
|
||||||
Calamares::JobList createJobs( const QStringList& defaultGroupsList );
|
void fillGlobalStorage() const;
|
||||||
|
|
||||||
void onActivate();
|
void onActivate();
|
||||||
|
|
||||||
void setWriteRootPassword( bool show );
|
|
||||||
void setPasswordCheckboxVisible( bool visible );
|
void setPasswordCheckboxVisible( bool visible );
|
||||||
void setValidatePasswordDefault( bool checked );
|
void setValidatePasswordDefault( bool checked );
|
||||||
void setAutologinDefault( bool checked );
|
|
||||||
void setReusePasswordDefault( bool checked );
|
void setReusePasswordDefault( bool checked );
|
||||||
|
|
||||||
/** @brief Process entries in the passwordRequirements config entry
|
/** @brief Process entries in the passwordRequirements config entry
|
||||||
@ -63,8 +62,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
void addPasswordCheck( const QString& key, const QVariant& value );
|
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
|
///@brief Root password, depends on settings, may be empty
|
||||||
QString getRootPassword() const;
|
QString getRootPassword() const;
|
||||||
///@brief User name and password
|
///@brief User name and password
|
||||||
@ -72,11 +69,8 @@ public:
|
|||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onFullNameTextEdited( const QString& );
|
void onFullNameTextEdited( const QString& );
|
||||||
void fillSuggestions();
|
void reportLoginNameStatus( const QString& );
|
||||||
void onUsernameTextEdited( const QString& );
|
void reportHostNameStatus( const QString& );
|
||||||
void validateUsernameText( const QString& );
|
|
||||||
void onHostnameTextEdited( const QString& );
|
|
||||||
void validateHostnameText( const QString& );
|
|
||||||
void onPasswordTextChanged( const QString& );
|
void onPasswordTextChanged( const QString& );
|
||||||
void onRootPasswordTextChanged( const QString& );
|
void onRootPasswordTextChanged( const QString& );
|
||||||
|
|
||||||
@ -95,19 +89,16 @@ private:
|
|||||||
void retranslate();
|
void retranslate();
|
||||||
|
|
||||||
Ui::Page_UserSetup* ui;
|
Ui::Page_UserSetup* ui;
|
||||||
|
Config* m_config;
|
||||||
|
|
||||||
PasswordCheckList m_passwordChecks;
|
PasswordCheckList m_passwordChecks;
|
||||||
bool m_passwordChecksChanged = false;
|
bool m_passwordChecksChanged = false;
|
||||||
|
|
||||||
bool m_readyFullName;
|
bool m_readyFullName;
|
||||||
bool m_readyUsername;
|
bool m_readyUsername;
|
||||||
bool m_customUsername;
|
|
||||||
bool m_readyHostname;
|
bool m_readyHostname;
|
||||||
bool m_customHostname;
|
|
||||||
bool m_readyPassword;
|
bool m_readyPassword;
|
||||||
bool m_readyRootPassword;
|
bool m_readyRootPassword;
|
||||||
|
|
||||||
bool m_writeRootPassword;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // USERSPAGE_H
|
#endif // USERSPAGE_H
|
||||||
|
@ -20,17 +20,18 @@
|
|||||||
|
|
||||||
#include "UsersViewStep.h"
|
#include "UsersViewStep.h"
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
#include "CreateUserJob.h"
|
||||||
#include "SetHostNameJob.h"
|
#include "SetHostNameJob.h"
|
||||||
#include "SetPasswordJob.h"
|
#include "SetPasswordJob.h"
|
||||||
#include "UsersPage.h"
|
#include "UsersPage.h"
|
||||||
|
|
||||||
|
#include "GlobalStorage.h"
|
||||||
|
#include "JobQueue.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
#include "utils/NamedEnum.h"
|
#include "utils/NamedEnum.h"
|
||||||
#include "utils/Variant.h"
|
#include "utils/Variant.h"
|
||||||
|
|
||||||
#include "GlobalStorage.h"
|
|
||||||
#include "JobQueue.h"
|
|
||||||
|
|
||||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( UsersViewStepFactory, registerPlugin< UsersViewStep >(); )
|
CALAMARES_PLUGIN_FACTORY_DEFINITION( UsersViewStepFactory, registerPlugin< UsersViewStep >(); )
|
||||||
|
|
||||||
static const NamedEnumTable< SetHostNameJob::Action >&
|
static const NamedEnumTable< SetHostNameJob::Action >&
|
||||||
@ -53,11 +54,11 @@ hostnameActions()
|
|||||||
|
|
||||||
UsersViewStep::UsersViewStep( QObject* parent )
|
UsersViewStep::UsersViewStep( QObject* parent )
|
||||||
: Calamares::ViewStep( parent )
|
: Calamares::ViewStep( parent )
|
||||||
, m_widget( new UsersPage() )
|
, m_widget( nullptr )
|
||||||
, m_actions( SetHostNameJob::Action::None )
|
, m_actions( SetHostNameJob::Action::None )
|
||||||
|
, m_config( new Config( this ) )
|
||||||
{
|
{
|
||||||
emit nextStatusChanged( true );
|
emit nextStatusChanged( true );
|
||||||
connect( m_widget, &UsersPage::checkReady, this, &UsersViewStep::nextStatusChanged );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -80,6 +81,11 @@ UsersViewStep::prettyName() const
|
|||||||
QWidget*
|
QWidget*
|
||||||
UsersViewStep::widget()
|
UsersViewStep::widget()
|
||||||
{
|
{
|
||||||
|
if ( !m_widget )
|
||||||
|
{
|
||||||
|
m_widget = new UsersPage( m_config );
|
||||||
|
connect( m_widget, &UsersPage::checkReady, this, &UsersViewStep::nextStatusChanged );
|
||||||
|
}
|
||||||
return m_widget;
|
return m_widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +93,7 @@ UsersViewStep::widget()
|
|||||||
bool
|
bool
|
||||||
UsersViewStep::isNextEnabled() const
|
UsersViewStep::isNextEnabled() const
|
||||||
{
|
{
|
||||||
return m_widget->isReady();
|
return m_widget ? m_widget->isReady() : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -122,7 +128,10 @@ UsersViewStep::jobs() const
|
|||||||
void
|
void
|
||||||
UsersViewStep::onActivate()
|
UsersViewStep::onActivate()
|
||||||
{
|
{
|
||||||
m_widget->onActivate();
|
if ( m_widget )
|
||||||
|
{
|
||||||
|
m_widget->onActivate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -130,9 +139,17 @@ void
|
|||||||
UsersViewStep::onLeave()
|
UsersViewStep::onLeave()
|
||||||
{
|
{
|
||||||
m_jobs.clear();
|
m_jobs.clear();
|
||||||
m_jobs.append( m_widget->createJobs( m_defaultGroups ) );
|
if ( !m_widget || !m_widget->isReady() )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Calamares::Job* j;
|
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();
|
auto userPW = m_widget->getUserPassword();
|
||||||
j = new SetPasswordJob( userPW.first, userPW.second );
|
j = new SetPasswordJob( userPW.first, userPW.second );
|
||||||
@ -141,46 +158,20 @@ UsersViewStep::onLeave()
|
|||||||
j = new SetPasswordJob( "root", m_widget->getRootPassword() );
|
j = new SetPasswordJob( "root", m_widget->getRootPassword() );
|
||||||
m_jobs.append( Calamares::job_ptr( j ) );
|
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_jobs.append( Calamares::job_ptr( j ) );
|
||||||
|
|
||||||
|
m_widget->fillGlobalStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
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;
|
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 ) );
|
m_widget->setReusePasswordDefault( getBool( configurationMap, "doReusePassword", false ) );
|
||||||
|
|
||||||
if ( configurationMap.contains( "passwordRequirements" )
|
if ( configurationMap.contains( "passwordRequirements" )
|
||||||
@ -197,15 +188,6 @@ UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
|||||||
m_widget->setPasswordCheckboxVisible( getBool( configurationMap, "allowWeakPasswords", false ) );
|
m_widget->setPasswordCheckboxVisible( getBool( configurationMap, "allowWeakPasswords", false ) );
|
||||||
m_widget->setValidatePasswordDefault( !getBool( configurationMap, "allowWeakPasswordsDefault", 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;
|
using Action = SetHostNameJob::Action;
|
||||||
|
|
||||||
QString hostnameActionString = CalamaresUtils::getString( configurationMap, "setHostname" );
|
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;
|
Action hostsfileAction = getBool( configurationMap, "writeHostsFile", true ) ? Action::WriteEtcHosts : Action::None;
|
||||||
m_actions = hostsfileAction | hostnameAction;
|
m_actions = hostsfileAction | hostnameAction;
|
||||||
|
|
||||||
|
m_config->setConfigurationMap( configurationMap );
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
|
class Config;
|
||||||
class UsersPage;
|
class UsersPage;
|
||||||
|
|
||||||
class PLUGINDLLEXPORT UsersViewStep : public Calamares::ViewStep
|
class PLUGINDLLEXPORT UsersViewStep : public Calamares::ViewStep
|
||||||
@ -60,8 +61,9 @@ private:
|
|||||||
UsersPage* m_widget;
|
UsersPage* m_widget;
|
||||||
QList< Calamares::job_ptr > m_jobs;
|
QList< Calamares::job_ptr > m_jobs;
|
||||||
|
|
||||||
QStringList m_defaultGroups;
|
|
||||||
SetHostNameJob::Actions m_actions;
|
SetHostNameJob::Actions m_actions;
|
||||||
|
|
||||||
|
Config* m_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( UsersViewStepFactory )
|
CALAMARES_PLUGIN_FACTORY_DECLARATION( UsersViewStepFactory )
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="textBoxUsername">
|
<widget class="QLineEdit" name="textBoxLoginName">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
@ -226,7 +226,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="textBoxHostname">
|
<widget class="QLineEdit" name="textBoxHostName">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
@ -456,7 +456,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="checkBoxAutoLogin">
|
<widget class="QCheckBox" name="checkBoxDoAutoLogin">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Log in automatically without asking for the password.</string>
|
<string>Log in automatically without asking for the password.</string>
|
||||||
</property>
|
</property>
|
||||||
|
Loading…
Reference in New Issue
Block a user