[users] enhance dialog

- multiple users management.
- ability to set different shells based on configuration.
- ability to select avatars from files and then copy them to a target location in the user's home directory. No specific location suggested due to differences between DEs on where to read avatars from.
This commit is contained in:
Philip 2016-11-01 05:42:56 +01:00
parent 0b4f015d3a
commit 09f6392173
23 changed files with 1883 additions and 1155 deletions

View File

@ -7,13 +7,16 @@ calamares_add_plugin( users
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
CreateUserJob.cpp
SetPasswordJob.cpp
jobs/CreateUserJob.cpp
jobs/SetPasswordJob.cpp
UsersViewStep.cpp
UsersPage.cpp
SetHostNameJob.cpp
gui/UsersPage.cpp
jobs/SetHostNameJob.cpp
jobs/SetAvatarJob.cpp
gui/AddUserDialog.cpp
UI
page_usersetup.ui
gui/page_usersetup.ui
gui/adduserdialog.ui
RESOURCES
users.qrc
LINK_LIBRARIES

View File

@ -0,0 +1,16 @@
# Users module
The users module take care of managing the users that will be created on the installed system.
## Configuration of the module
The following settings are available in **users.conf**:
- ```defaultGroups```: list of groups every user will be added to.
- ```autologinGroup```: group to add the user with autologin to, if any.
- ```doAutologin```: allow for users with autologin (at most one per system). Defaults to false.
- ```sudoersGroup```: group for sudoers usage.
- ```setRootPassword```: allow to set the root password in the installed system. Defaults to false.
- ```availableShells```: comma-separated list of available shells for new users. If not present, new users will not have any explicit shell in /etc/passwd, therefore the system default (usually, /bin/bash) will be used.
- ```avatarFilePath```: path where to copy user avatars to; ~ can be used to represent the user's home directory. If not present or empty, we won't allow users to set avatars.

View File

@ -1,440 +0,0 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
*
* Portions from the Manjaro Installation Framework
* by Roland Singer <roland@manjaro.org>
* Copyright (C) 2007 Free Software Foundation, Inc.
*
* 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 "UsersPage.h"
#include "ui_page_usersetup.h"
#include "CreateUserJob.h"
#include "SetPasswordJob.h"
#include "SetHostNameJob.h"
#include "JobQueue.h"
#include "GlobalStorage.h"
#include "utils/Logger.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Retranslator.h"
#include <QBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QRegExp>
#include <QRegExpValidator>
UsersPage::UsersPage( QWidget* parent )
: QWidget( parent )
, ui( new Ui::Page_UserSetup )
, m_readyFullName( false )
, m_readyUsername( false )
, m_readyHostname( false )
, m_readyPassword( false )
, m_readyRootPassword( false )
, m_writeRootPassword( true )
{
ui->setupUi( this );
// Connect signals and slots
connect( ui->textBoxFullName, &QLineEdit::textEdited,
this, &UsersPage::onFullNameTextEdited );
connect( ui->textBoxUsername, &QLineEdit::textEdited,
this, &UsersPage::onUsernameTextEdited );
connect( ui->textBoxHostname, &QLineEdit::textEdited,
this, &UsersPage::onHostnameTextEdited );
connect( ui->textBoxUserPassword, &QLineEdit::textChanged,
this, &UsersPage::onPasswordTextChanged );
connect( ui->textBoxUserVerifiedPassword, &QLineEdit::textChanged,
this, &UsersPage::onPasswordTextChanged );
connect( ui->textBoxRootPassword, &QLineEdit::textChanged,
this, &UsersPage::onRootPasswordTextChanged );
connect( ui->textBoxVerifiedRootPassword, &QLineEdit::textChanged,
this, &UsersPage::onRootPasswordTextChanged );
connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged,
this, [this]( int checked )
{
ui->labelChooseRootPassword->setVisible( !checked );
ui->labelExtraRootPassword->setVisible( !checked );
ui->labelRootPassword->setVisible( !checked );
ui->labelRootPasswordError->setVisible( !checked );
ui->textBoxRootPassword->setVisible( !checked );
ui->textBoxVerifiedRootPassword->setVisible( !checked );
checkReady( isReady() );
} );
m_customUsername = false;
m_customHostname = false;
setWriteRootPassword( true );
ui->checkBoxReusePassword->setChecked( true );
CALAMARES_RETRANSLATE( ui->retranslateUi( this ); )
}
UsersPage::~UsersPage()
{
delete ui;
}
bool
UsersPage::isReady()
{
bool readyFields = m_readyFullName &&
m_readyHostname &&
m_readyPassword &&
m_readyUsername;
if ( !m_writeRootPassword || ui->checkBoxReusePassword->isChecked() )
return readyFields;
return readyFields && m_readyRootPassword;
}
QList< Calamares::job_ptr >
UsersPage::createJobs( const QStringList& defaultGroupsList )
{
QList< Calamares::job_ptr > list;
if ( !isReady() )
return list;
Calamares::Job* j;
j = new CreateUserJob( ui->textBoxUsername->text(),
ui->textBoxFullName->text().isEmpty() ?
ui->textBoxUsername->text() :
ui->textBoxFullName->text(),
ui->checkBoxAutoLogin->isChecked(),
defaultGroupsList );
list.append( Calamares::job_ptr( j ) );
j = new SetPasswordJob( ui->textBoxUsername->text(),
ui->textBoxUserPassword->text() );
list.append( Calamares::job_ptr( j ) );
if ( m_writeRootPassword )
{
if ( ui->checkBoxReusePassword->isChecked() )
j = new SetPasswordJob( "root",
ui->textBoxUserPassword->text() );
else
j = new SetPasswordJob( "root",
ui->textBoxRootPassword->text() );
list.append( Calamares::job_ptr( j ) );
}
j = new SetHostNameJob( ui->textBoxHostname->text() );
list.append( Calamares::job_ptr( j ) );
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
gs->insert( "hostname", ui->textBoxHostname->text() );
if ( ui->checkBoxAutoLogin->isChecked() )
gs->insert( "autologinUser", ui->textBoxUsername->text() );
gs->insert( "username", ui->textBoxUsername->text() );
gs->insert( "password", CalamaresUtils::obscure( ui->textBoxUserPassword->text() ) );
return list;
}
void
UsersPage::onActivate()
{
ui->textBoxFullName->setFocus();
}
void
UsersPage::setWriteRootPassword( bool write )
{
ui->checkBoxReusePassword->setVisible( write );
m_writeRootPassword = 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() );
}
void
UsersPage::fillSuggestions()
{
QString fullName = ui->textBoxFullName->text();
QRegExp rx( "[^a-zA-Z0-9 ]", Qt::CaseInsensitive );
QString cleanName = CalamaresUtils::removeDiacritics( fullName )
.toLower().replace( rx, " " ).simplified();
QStringList cleanParts = cleanName.split( ' ' );
if ( !m_customUsername )
{
if ( !cleanParts.isEmpty() && !cleanParts.first().isEmpty() )
{
QString usernameSuggestion = cleanParts.first();
for ( int i = 1; i < cleanParts.length(); ++i )
{
if ( !cleanParts.value( i ).isEmpty() )
usernameSuggestion.append( cleanParts.value( i ).at( 0 ) );
}
if ( USERNAME_RX.indexIn( usernameSuggestion ) != -1 )
{
ui->textBoxUsername->setText( usernameSuggestion );
validateUsernameText( usernameSuggestion );
m_customUsername = false;
}
}
}
if ( !m_customHostname )
{
if ( !cleanParts.isEmpty() && !cleanParts.first().isEmpty() )
{
QString hostnameSuggestion = QString( "%1-pc" ).arg( cleanParts.first() );
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 );
QRegExp rx( USERNAME_RX );
QRegExpValidator val( rx );
int pos = -1;
if ( text.isEmpty() )
{
ui->labelUsernameError->clear();
ui->labelUsername->clear();
m_readyUsername = false;
}
else if ( text.length() > USERNAME_MAX_LENGTH )
{
ui->labelUsername->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelUsername->size() ) );
ui->labelUsernameError->setText(
tr( "Your username is too long." ) );
m_readyUsername = false;
}
else if ( val.validate( text, pos ) == QValidator::Invalid )
{
ui->labelUsername->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelUsername->size() ) );
ui->labelUsernameError->setText(
tr( "Your username contains invalid characters. Only lowercase letters and numbers are allowed." ) );
m_readyUsername = false;
}
else {
ui->labelUsername->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes,
CalamaresUtils::Original,
ui->labelUsername->size() ) );
ui->labelUsernameError->clear();
m_readyUsername = true;
}
emit checkReady( isReady() );
}
void
UsersPage::onHostnameTextEdited( const QString& textRef )
{
m_customHostname = true;
validateHostnameText( textRef );
}
void
UsersPage::validateHostnameText( const QString& textRef )
{
QString text = textRef;
QRegExp rx( HOSTNAME_RX );
QRegExpValidator val( rx );
int pos = -1;
if ( text.isEmpty() )
{
ui->labelHostnameError->clear();
ui->labelHostname->clear();
m_readyHostname= false;
}
else if ( text.length() < HOSTNAME_MIN_LENGTH )
{
ui->labelHostname->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelHostname->size() ) );
ui->labelHostnameError->setText(
tr( "Your hostname is too short." ) );
m_readyHostname = false;
}
else if ( text.length() > HOSTNAME_MAX_LENGTH )
{
ui->labelHostname->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelHostname->size() ) );
ui->labelHostnameError->setText(
tr( "Your hostname is too long." ) );
m_readyHostname = false;
}
else if ( val.validate( text, pos ) == QValidator::Invalid )
{
ui->labelHostname->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelHostname->size() ) );
ui->labelHostnameError->setText(
tr( "Your hostname contains invalid characters. Only letters, numbers and dashes are allowed." ) );
m_readyHostname = false;
}
else
{
ui->labelHostname->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes,
CalamaresUtils::Original,
ui->labelHostname->size() ) );
ui->labelHostnameError->clear();
m_readyHostname = true;
}
emit checkReady( isReady() );
}
void
UsersPage::onPasswordTextChanged( const QString& )
{
QString pw1 = ui->textBoxUserPassword->text();
QString pw2 = ui->textBoxUserVerifiedPassword->text();
if ( pw1.isEmpty() && pw2.isEmpty() )
{
ui->labelUserPasswordError->clear();
ui->labelUserPassword->clear();
m_readyPassword = false;
}
else if ( pw1 != pw2 )
{
ui->labelUserPasswordError->setText( tr( "Your passwords do not match!" ) );
ui->labelUserPassword->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelUserPassword->size() ) );
m_readyPassword = false;
}
else
{
ui->labelUserPasswordError->clear();
ui->labelUserPassword->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes,
CalamaresUtils::Original,
ui->labelUserPassword->size() ) );
m_readyPassword = true;
}
emit checkReady( isReady() );
}
void
UsersPage::onRootPasswordTextChanged( const QString& )
{
QString pw1 = ui->textBoxRootPassword->text();
QString pw2 = ui->textBoxVerifiedRootPassword->text();
if ( pw1.isEmpty() && pw2.isEmpty() )
{
ui->labelRootPasswordError->clear();
ui->labelRootPassword->clear();
m_readyRootPassword = false;
}
else if ( pw1 != pw2 )
{
ui->labelRootPasswordError->setText( tr( "Your passwords do not match!" ) );
ui->labelRootPassword->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelRootPassword->size() ) );
m_readyRootPassword = false;
}
else
{
ui->labelRootPasswordError->clear();
ui->labelRootPassword->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes,
CalamaresUtils::Original,
ui->labelRootPassword->size() ) );
m_readyRootPassword = true;
}
emit checkReady( isReady() );
}
void
UsersPage::setAutologinDefault( bool checked )
{
ui->checkBoxAutoLogin->setChecked( checked );
emit checkReady( isReady() );
}
void
UsersPage::setReusePasswordDefault( bool checked )
{
ui->checkBoxReusePassword->setChecked( checked );
emit checkReady( isReady() );
}

View File

@ -1,84 +0,0 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
*
* Portions from the Manjaro Installation Framework
* by Roland Singer <roland@manjaro.org>
* Copyright (C) 2007 Free Software Foundation, Inc.
*
* 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 USERSPAGE_H
#define USERSPAGE_H
#include "Typedefs.h"
#include <QWidget>
namespace Ui {
class Page_UserSetup;
}
class UsersPage : public QWidget
{
Q_OBJECT
public:
explicit UsersPage( QWidget* parent = nullptr );
virtual ~UsersPage();
bool isReady();
QList< Calamares::job_ptr > createJobs( const QStringList& defaultGroupsList );
void onActivate();
void setWriteRootPassword( bool show );
void setAutologinDefault( bool checked );
void setReusePasswordDefault( bool checked );
protected slots:
void onFullNameTextEdited( const QString& );
void fillSuggestions();
void onUsernameTextEdited( const QString& );
void validateUsernameText( const QString& );
void onHostnameTextEdited( const QString& );
void validateHostnameText( const QString& );
void onPasswordTextChanged( const QString& );
void onRootPasswordTextChanged( const QString& );
signals:
void checkReady( bool );
private:
Ui::Page_UserSetup* ui;
const QRegExp USERNAME_RX = QRegExp( "^[a-z_][a-z0-9_-]*[$]?$" );
const QRegExp HOSTNAME_RX = QRegExp( "^[a-zA-Z0-9][-a-zA-Z0-9_]*$" );
const int USERNAME_MAX_LENGTH = 31;
const int HOSTNAME_MIN_LENGTH = 2;
const int HOSTNAME_MAX_LENGTH = 24;
bool m_readyFullName;
bool m_readyUsername;
bool m_customUsername;
bool m_readyHostname;
bool m_customHostname;
bool m_readyPassword;
bool m_readyRootPassword;
bool m_writeRootPassword;
};
#endif // USERSPAGE_H

View File

@ -18,7 +18,7 @@
#include "UsersViewStep.h"
#include "UsersPage.h"
#include "gui/UsersPage.h"
#include "JobQueue.h"
#include "GlobalStorage.h"
@ -139,6 +139,12 @@ UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap )
configurationMap.value( "autologinGroup" ).toString() );
}
if ( configurationMap.contains( "doAutologin" ) &&
configurationMap.value( "doAutologin" ).type() == QVariant::Bool )
{
m_widget->setAutologin( configurationMap.value( "doAutologin" ).toBool() );
}
if ( configurationMap.contains( "sudoersGroup" ) &&
configurationMap.value( "sudoersGroup" ).type() == QVariant::String )
{
@ -151,19 +157,18 @@ UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
Calamares::JobQueue::instance()->globalStorage()->insert( "setRootPassword",
configurationMap.value( "setRootPassword" ).toBool() );
m_widget->setWriteRootPassword( configurationMap.value( "setRootPassword" ).toBool() );
m_widget->setHaveRootPassword( configurationMap.value( "setRootPassword" ).toBool() );
}
if ( configurationMap.contains( "doAutologin" ) &&
configurationMap.value( "doAutologin" ).type() == QVariant::Bool )
if ( configurationMap.contains( "availableShells" ) &&
configurationMap.value( "availableShells" ).type() == QVariant::List )
{
m_widget->setAutologinDefault( configurationMap.value( "doAutologin" ).toBool() );
m_widget->setAvailableShells( configurationMap.value( "availableShells" ).toStringList() );
}
if ( configurationMap.contains( "doReusePassword" ) &&
configurationMap.value( "doReusePassword" ).type() == QVariant::Bool )
if ( configurationMap.contains( "avatarFilePath" ) &&
configurationMap.value( "avatarFilePath").type() == QVariant::String )
{
m_widget->setReusePasswordDefault( configurationMap.value( "doReusePassword" ).toBool() );
m_widget->setAvatarFilePath( configurationMap.value( "avatarFilePath" ).toString() );
}
}

View File

@ -0,0 +1,217 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Lisa Vitolo <shainer@chakralinux.org>
*
* Portions from Tribe (Chakra Linux)
* by Dario Freddi, Drake Justice and Manuel Tortosa
* Copyright (C) 2013 Free Software Foundation, Inc.
*
* 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 <functional>
#include <QDialogButtonBox>
#include <QDebug>
#include <QDesktopWidget>
#include <QFileDialog>
#include <QRegExpValidator>
#include "AddUserDialog.h"
#include "utils/CalamaresUtilsGui.h"
AddUserDialog::AddUserDialog(const QStringList& existingUsers, bool avatar, bool autologin, const QStringList& shells, bool haveRootPassword, QWidget* parent)
: QDialog(parent),
m_existingUsers(existingUsers)
{
m_badUsernames
<< "root"
<< "bin"
<< "daemon"
<< "mail"
<< "ftp"
<< "http"
<< "nobody"
<< "dbus"
<< "avahi"
<< "usbmux"
<< "postgres"
<< "quassel"
<< "rtkit"
<< "git"
<< "polkitd"
<< "nm-openconnect"
<< "kdm"
<< "uuidd"
<< "ntp"
<< "mysql"
<< "clamav"
<< "postfix"
<< "lightdm";
ui.setupUi(this);
ui.passLine->setEchoMode(QLineEdit::Password);
ui.confirmPassLine->setEchoMode(QLineEdit::Password);
ui.labelUsernameError->setFont( QFont("Arial", 10) );
ui.loginShellSelection->setAutoCompletion(true);
ui.loginShellSelection->addItems(shells);
connect(ui.userNameLine, &QLineEdit::textEdited, this,
&AddUserDialog::validateUsername);
m_passwordsMatch = m_passwordsEmpty = true;
connect(ui.passLine, &QLineEdit::textChanged, this, &AddUserDialog::passwordChanged);
connect(ui.confirmPassLine, &QLineEdit::textChanged, this, &AddUserDialog::passwordChanged);
if (!haveRootPassword) {
ui.rootUsesUserPwCheckBox->setEnabled(false);
}
if (!autologin) {
ui.autoLoginCheckBox->setEnabled(false);
}
if (!avatar) {
ui.avatarFileLine->setEnabled(false);
ui.selectImageButton->setEnabled(false);
}
connect(ui.selectImageButton, &QPushButton::clicked, this, &AddUserDialog::avatarClicked);
// Do not enable until valid information have been entered.
ui.dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
connect(ui.dialogButtonBox, &QDialogButtonBox::accepted, this, &AddUserDialog::accept);
connect(ui.dialogButtonBox, &QDialogButtonBox::rejected, this, &AddUserDialog::reject);
}
AddUserDialog::~AddUserDialog() {}
void AddUserDialog::accept() {
// Store all the information from the dialog in member variables.
login = ui.userNameLine->text();
password = ui.passLine->text();
shell = ui.loginShellSelection->currentText();
fullName = ui.nameLine->text();
autoLogin = ui.autoLoginCheckBox->isEnabled();
useUserPw = ui.rootUsesUserPwCheckBox->isChecked();
QDialog::accept();
}
void AddUserDialog::validateUsername(const QString& username) {
QRegExp rx( USERNAME_RX );
QRegExpValidator val( rx );
int pos = -1;
if ( username.isEmpty() )
{
ui.labelUsernameError->clear();
ui.iconUsername->clear();
m_validUsername = false;
}
else if ( username.length() > USERNAME_MAX_LENGTH )
{
ui.iconUsername->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui.iconUsername->size() ) );
ui.labelUsernameError->setText(
tr( "Your username is too long." ) );
m_validUsername = false;
}
else if ( val.validate( (QString &)username, pos ) == QValidator::Invalid )
{
ui.iconUsername->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui.iconUsername->size() ) );
ui.labelUsernameError->setText(
tr( "Only lowercase letters and numbers are allowed." ) );
m_validUsername = false;
}
else if ( m_existingUsers.contains(username) ) {
ui.iconUsername->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui.iconUsername->size() ) );
ui.labelUsernameError->setText(
tr( "This username was already created." ) );
m_validUsername = false;
}
else if ( m_badUsernames.contains(username) ) {
ui.iconUsername->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui.iconUsername->size() ) );
ui.labelUsernameError->setText(
tr( "Username is already in use in the system." ) );
m_validUsername = false;
}
else {
ui.iconUsername->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes,
CalamaresUtils::Original,
ui.iconUsername->size() ) );
ui.labelUsernameError->setText("Username valid.");
m_validUsername = true;
}
updateValidityUi();
}
void AddUserDialog::passwordChanged() {
m_passwordsMatch = (ui.passLine->text() == ui.confirmPassLine->text());
m_passwordsEmpty = (ui.passLine->text().length() == 0);
if (m_passwordsMatch && !m_passwordsEmpty) {
ui.confirmPwCheck->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes,
CalamaresUtils::Original,
ui.confirmPwCheck->size()) );
} else {
ui.confirmPwCheck->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui.confirmPwCheck->size()) );
}
updateValidityUi();
}
void AddUserDialog::updateValidityUi()
{
if (m_validUsername && !m_passwordsEmpty && m_passwordsMatch) {
ui.dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
} else {
ui.dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
}
}
// TODO: support getting the avatar from a URL too.
void AddUserDialog::avatarClicked()
{
// Open a file dialog restricted to images.
QFileDialog dlg( this );
dlg.setNameFilter(tr("Images (*.png *.xpm *.jpg)"));
dlg.setViewMode(QFileDialog::Detail);
// TODO: read this from configuration as we could have a
// preferred directory to store avatars in the live installation.
dlg.setDirectory("/home");
if ( dlg.exec() == QDialog::Accepted ) {
avatarFile = dlg.selectedFiles().at(0);
ui.avatarFileLine->setText(avatarFile);
}
}

View File

@ -0,0 +1,82 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Lisa Vitolo <shainer@chakralinux.org>
*
* Portions from Tribe (Chakra Linux)
* by Dario Freddi, Drake Justice and Manuel Tortosa
* Copyright (C) 2013 Free Software Foundation, Inc.
*
* 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 ADDUSERDIALOG_H
#define ADDUSERDIALOG_H
#include "ui_adduserdialog.h"
#include <QLineEdit>
#include <QValidator>
#include <QRegExp>
class AddUserDialog : public QDialog
{
Q_OBJECT
public:
/*
* - existingUsers: username of the users that were already created.
* This avoids us creating a second one with the same name.
* - avatar: whether we should allow to choose an avatar.
* - autologin: whether we should allow the autologin option.
* - shells: the available login shells for users.
*/
AddUserDialog(const QStringList &existingUsers, bool avatar, bool autologin, const QStringList &shells, bool haveRootPassword, QWidget *parent = 0);
virtual ~AddUserDialog();
QString login;
QString shell;
QString password;
QString avatarFile;
QString fullName;
bool autoLogin;
bool useUserPw;
void validateUsername(const QString&);
public slots:
void accept() override;
signals:
void addUserClicked();
private slots:
void avatarClicked();
void passwordChanged();
void updateValidityUi();
private:
Ui::AddUserDialog ui;
QStringList m_existingUsers;
QStringList m_badUsernames;
bool m_validUsername;
bool m_passwordsMatch;
bool m_passwordsEmpty;
const QRegExp USERNAME_RX = QRegExp( "^[a-z_][a-z0-9_-]*[$]?$" );
const int USERNAME_MAX_LENGTH = 31;
};
#endif /* ADDUSERDIALOG_H */

View File

@ -0,0 +1,428 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
*
* Portions from the Manjaro Installation Framework
* by Roland Singer <roland@manjaro.org>
* Copyright (C) 2007 Free Software Foundation, Inc.
*
* 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 "UsersPage.h"
#include "ui_page_usersetup.h"
#include "AddUserDialog.h"
#include <jobs/CreateUserJob.h>
#include <jobs/SetAvatarJob.h>
#include <jobs/SetPasswordJob.h>
#include <jobs/SetHostNameJob.h>
#include "JobQueue.h"
#include "GlobalStorage.h"
#include "utils/Logger.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Retranslator.h"
#include <QBoxLayout>
#include <QMessageBox>
#include <QLabel>
#include <QLineEdit>
#include <QItemSelectionModel>
#include <QPointer>
#include <QRegExp>
#include <QRegExpValidator>
UsersListModel::~UsersListModel() {
qDeleteAll(m_currentUsers);
}
int UsersListModel::rowCount(const QModelIndex &parent) const {
return m_currentUsers.size();
}
int UsersListModel::columnCount(const QModelIndex &parent) const {
return 4;
}
QVariant UsersListModel::headerData(int section, Qt::Orientation orientation, int role) const {
QVariant header;
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
switch (section) {
case 0:
header = QVariant(tr("User name"));
break;
case 1:
header = QVariant(tr("Full name"));
break;
case 2:
header = QVariant(tr("Shell"));
break;
case 3:
header = QVariant(tr("Autologin?"));
break;
}
}
return header;
}
QVariant UsersListModel::data(const QModelIndex &index, int role) const {
if (role == Qt::DisplayRole) {
User* user = m_currentUsers.at( index.row() );
QVariant data;
switch (index.column()) {
case 0:
data = QVariant( user->toString() );
break;
case 1:
data = QVariant( user->fullname );
break;
case 2:
data = QVariant( user->shell );
break;
case 3:
data = (user->autologin ? "yes" : "no");
}
return data;
}
if (role == Qt::TextAlignmentRole) {
return Qt::AlignCenter;
}
return QVariant();
}
Qt::ItemFlags UsersListModel::flags(const QModelIndex & /*index*/) const {
return Qt::ItemIsSelectable | Qt::ItemIsEnabled ;
}
void UsersListModel::addUser(User* user) {
beginInsertRows( QModelIndex(), 0, 0);
m_currentUsers.append(user);
endInsertRows();
}
void UsersListModel::deleteUser(int index) {
if (index < 0 || index >= m_currentUsers.size()) {
return;
}
beginRemoveRows(QModelIndex(), 0, 0);
m_currentUsers.removeAt(index);
endRemoveRows();
}
QList<User *> UsersListModel::getUsers() const {
return m_currentUsers;
}
UsersPage::UsersPage( QWidget* parent )
: QWidget( parent )
, ui( new Ui::UserCreation )
, m_readyHostname( false )
, m_readyRootPassword( false )
, m_haveRootPassword( true )
{
ui->setupUi( this );
connect(ui->addUser, &QPushButton::clicked, this, &UsersPage::addUserClicked);
connect(ui->deleteUser, &QPushButton::clicked, this, &UsersPage::deleteUserClicked);
connect(ui->hostname, &QLineEdit::textChanged, this, &UsersPage::onHostnameTextEdited);
// The columns will occupy all the space available in the view.
ui->usersView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// You can only select entire rows, not single cells, i.e. you
// always select the user as a whole.
ui->usersView->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->usersView->setModel(&m_userModel);
ui->usersView->show();
CALAMARES_RETRANSLATE( ui->retranslateUi( this ); );
}
UsersPage::~UsersPage()
{
delete ui;
}
void
UsersPage::addUserClicked() {
QStringList existingUsers;
bool userHasAutologin = false;
for (const User* user : m_userModel.getUsers()) {
existingUsers.append( user->username );
if (user->autologin) {
userHasAutologin = true;
}
}
// Only allow autologin if the module configuration allows it,
// and there exists no other users with autologin enabled.
bool allowAutologin = m_autologin && !userHasAutologin;
QPointer<AddUserDialog> dlg = new AddUserDialog(
existingUsers, !m_avatarFilePath.isEmpty(), allowAutologin, m_availableShells, m_haveRootPassword, this );
if ( dlg->exec() == QDialog::Accepted ) {
addUser(dlg->login, dlg->fullName, dlg->password, dlg->shell, dlg->avatarFile, dlg->autoLogin);
if (dlg->useUserPw && m_haveRootPassword) {
ui->rootPw->setText(dlg->password);
ui->confirmRootPw->setText(dlg->password);
}
}
delete dlg;
}
void
UsersPage::deleteUserClicked() {
QItemSelectionModel* selectionModel = ui->usersView->selectionModel();
if (!selectionModel->hasSelection()) {
return;
}
QModelIndex selectionIndex = selectionModel->currentIndex();
if (!selectionIndex.isValid()) {
return;
}
m_userModel.deleteUser(selectionIndex.row());
}
void
UsersPage::addUser(const QString &login, const QString &fullName, const QString &password, const QString &shell, const QString& avatarFile, bool autologin) {
User* newUser = new User(login, fullName, password, shell, avatarFile, autologin);
m_userModel.addUser(newUser);
ui->hostname->setText( login + "-pc" );
emit checkReady( isReady() );
}
bool
UsersPage::isReady()
{
return (m_readyHostname &&
// At least one user should have been created.
m_userModel.getUsers().size() > 0 &&
(!m_haveRootPassword || m_readyRootPassword));
}
QList< Calamares::job_ptr >
UsersPage::createJobs( const QStringList& defaultGroupsList )
{
QList< Calamares::job_ptr > list;
if ( !isReady() )
return list;
for (const User* user : m_userModel.getUsers()) {
Calamares::Job* j;
j = new CreateUserJob( user->username,
(!user->fullname.isEmpty()) ? user->fullname : user->username,
user->shell,
user->autologin,
defaultGroupsList );
list.append( Calamares::job_ptr( j ) );
j = new SetPasswordJob( user->username, user->password );
list.append( Calamares::job_ptr( j ) );
j = new SetHostNameJob( ui->hostname->text() );
list.append( Calamares::job_ptr( j ) );
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
gs->insert( "hostname", ui->hostname->text() );
// Given the validation we do, we assume this is always true
// for at most one user in the page.
if ( user->autologin )
gs->insert( "autologinUser", user->username );
if ( !user->avatarFile.isEmpty() ) {
if (m_avatarFilePath.contains("~")) {
QString home( "/home/" + user->username);
m_avatarFilePath.replace("~", home);
}
j = new SetAvatarJob( user->avatarFile, m_avatarFilePath );
list.append( Calamares::job_ptr(j) );
}
}
if ( m_haveRootPassword )
{
Calamares::Job* j = new SetPasswordJob( "root", ui->rootPw->text() );
list.append( Calamares::job_ptr( j ) );
}
return list;
}
void
UsersPage::onActivate()
{
emit checkReady( isReady() );
// If there is no user yet, open the dialog as soon as the
// page is enabled. This makes it easy for people who want
// to create one user.
if (m_userModel.rowCount() == 0) {
addUserClicked();
}
}
void
UsersPage::onHostnameTextEdited( const QString& textRef )
{
validateHostnameText( textRef );
}
void
UsersPage::validateHostnameText( const QString& textRef )
{
QString text = textRef;
QRegExp rx( HOSTNAME_RX );
QRegExpValidator val( rx );
int pos = -1;
if ( text.isEmpty() )
{
ui->labelHostnameError->clear();
ui->labelHostname->clear();
m_readyHostname= false;
}
else if ( text.length() < HOSTNAME_MIN_LENGTH )
{
ui->labelHostname->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelHostname->size() ) );
ui->labelHostnameError->setText(
tr( "Your hostname is too short." ) );
m_readyHostname = false;
}
else if ( text.length() > HOSTNAME_MAX_LENGTH )
{
ui->labelHostname->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelHostname->size() ) );
ui->labelHostnameError->setText(
tr( "Your hostname is too long." ) );
m_readyHostname = false;
}
else if ( val.validate( text, pos ) == QValidator::Invalid )
{
ui->labelHostname->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelHostname->size() ) );
ui->labelHostnameError->setText(
tr( "Your hostname contains invalid characters. Only letters, numbers and dashes are allowed." ) );
m_readyHostname = false;
}
else
{
ui->labelHostname->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes,
CalamaresUtils::Original,
ui->labelHostname->size() ) );
ui->labelHostnameError->clear();
m_readyHostname = true;
}
emit checkReady( isReady() );
}
void
UsersPage::onRootPasswordTextChanged( const QString& textRef )
{
QString pw1 = ui->rootPw->text();
QString pw2 = ui->confirmRootPw->text();
if ( pw1.isEmpty() && pw2.isEmpty() )
{
ui->labelRootPwError->clear();
ui->labelRootPwIcon->clear();
m_readyRootPassword = false;
}
else if ( pw1 != pw2 )
{
ui->labelRootPwError->setText( tr( "Your passwords do not match!" ) );
ui->labelRootPwIcon->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No,
CalamaresUtils::Original,
ui->labelRootPwIcon->size() ) );
m_readyRootPassword = false;
}
else
{
ui->labelRootPwError->clear();
ui->labelRootPwIcon->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes,
CalamaresUtils::Original,
ui->labelRootPwIcon->size() ) );
m_readyRootPassword = true;
}
emit checkReady( isReady() );
}
void UsersPage::setAutologin(bool autologin) {
m_autologin = autologin;
}
void UsersPage::setHaveRootPassword(bool haveRootPassword) {
m_haveRootPassword = haveRootPassword;
if (m_haveRootPassword) {
ui->rootPw->show();
ui->confirmRootPw->show();
ui->labelRootPw->show();
ui->labelConfirmRootPw->show();
connect(ui->rootPw, &QLineEdit::textChanged, this, &UsersPage::onRootPasswordTextChanged);
connect(ui->confirmRootPw, &QLineEdit::textChanged, this, &UsersPage::onRootPasswordTextChanged);
} else {
ui->rootPw->hide();
ui->confirmRootPw->hide();
ui->labelRootPw->hide();
ui->labelConfirmRootPw->hide();
}
}
void UsersPage::setAvatarFilePath(const QString &path) {
m_avatarFilePath = path;
}
void UsersPage::setAvailableShells(const QStringList &shells) {
m_availableShells = shells;
}

View File

@ -0,0 +1,139 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
*
* Portions from the Manjaro Installation Framework
* by Roland Singer <roland@manjaro.org>
* Copyright (C) 2007 Free Software Foundation, Inc.
*
* 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 USERSPAGE_H
#define USERSPAGE_H
#include "Typedefs.h"
#include <QAbstractListModel>
#include <QtGlobal>
#include <QItemSelection>
#include <QVariant>
#include <QWidget>
namespace Ui {
class UserCreation;
}
/*
* Represents the properties of a user in the system.
*
* The fullname and avatar file can be empty and they will be ignored.
* The password can technically be empty too, but that is of course
* not recommended in general.
*/
struct User {
User(const QString& username, const QString& fullname, const QString& password, const QString& shell, const QString& avatarFile, bool autologin)
: username(username), fullname(fullname), password(password), shell(shell), avatarFile(avatarFile), autologin(autologin) {}
QString toString() const {
return username;
}
QString username;
QString fullname;
QString password;
QString shell;
QString avatarFile;
bool autologin;
};
/*
* Model to contain the users created during the installation
* process. This is associated to a QTableView in the main UI.
*
* Each row is a user and each column a property of the user.
*/
class UsersListModel : public QAbstractTableModel {
Q_OBJECT
public:
virtual ~UsersListModel();
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent) const Q_DECL_OVERRIDE;
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE;
Qt::ItemFlags flags(const QModelIndex & /*index*/) const Q_DECL_OVERRIDE;
void addUser(User *user);
void deleteUser(int index);
QList<User *> getUsers() const;
private:
QList<User *> m_currentUsers;
};
class UsersPage : public QWidget
{
Q_OBJECT
public:
explicit UsersPage( QWidget* parent = nullptr );
virtual ~UsersPage();
bool isReady();
QList< Calamares::job_ptr > createJobs( const QStringList& defaultGroupsList );
void onActivate();
// Setters for properties of this page, usually from configuration.
void setAutologin( bool autologin );
void setAvatarFilePath( const QString& path );
void setAvailableShells( const QStringList& shells);
void setHaveRootPassword(bool);
protected slots:
void onHostnameTextEdited( const QString& );
void validateHostnameText( const QString& );
void onRootPasswordTextChanged( const QString& );
void addUserClicked();
void deleteUserClicked();
signals:
void checkReady( bool );
private:
void addUser(const QString& login, const QString& fullName, const QString& password, const QString& shell, const QString& avatarFile, bool autologin);
Ui::UserCreation* ui;
UsersListModel m_userModel;
// Hostname validation.
const QRegExp HOSTNAME_RX = QRegExp( "^[a-zA-Z0-9][-a-zA-Z0-9_]*$" );
const int HOSTNAME_MIN_LENGTH = 2;
const int HOSTNAME_MAX_LENGTH = 24;
// Are the following fields valid with their current value?
bool m_readyHostname;
bool m_readyRootPassword;
bool m_autologin;
bool m_haveRootPassword;
QString m_avatarFilePath;
QStringList m_availableShells;
};
#endif // USERSPAGE_H

View File

@ -0,0 +1,382 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddUserDialog</class>
<widget class="QDialog" name="AddUserDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>712</width>
<height>383</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2" rowspan="3">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Password: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="3" rowspan="4">
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>13</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Confirm Password:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="4">
<layout class="QVBoxLayout" name="userNameLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>15</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>45</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>User name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="userNameLine">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The user name may consist of a lowercase character or &amp;quot;_&amp;quot;, followed by up to 31 lowercase characters, digits or &amp;quot;_&amp;quot; and &amp;quot;-&amp;quot;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="iconUsername">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelUsernameError">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="3" column="4" colspan="2">
<widget class="QLineEdit" name="confirmPassLine">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="0" column="4" rowspan="3" colspan="2">
<widget class="QLineEdit" name="passLine">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="0" column="6" rowspan="4" colspan="2">
<widget class="QLabel" name="confirmPwCheck">
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="1" rowspan="4">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="extWidget" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="1">
<widget class="QCheckBox" name="rootUsesUserPwCheckBox">
<property name="text">
<string>Select to use the above password
as the Administrator password</string>
</property>
</widget>
</item>
<item row="1" column="4" colspan="3">
<widget class="QComboBox" name="loginShellSelection"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Advanced settings</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string> Full name: </string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="autoLoginCheckBox">
<property name="text">
<string>Login Automatically</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="7">
<widget class="QWidget" name="avatarSelectionWidget" native="true">
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Avatar selection</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="avatarFileLine"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QPushButton" name="selectImageButton">
<property name="text">
<string>Image file...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Login shell: </string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="nameLine">
<property name="maximumSize">
<size>
<width>180</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="dialogButtonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>userNameLine</tabstop>
<tabstop>passLine</tabstop>
<tabstop>confirmPassLine</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,277 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UserCreation</class>
<widget class="QWidget" name="UserCreation">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>694</width>
<height>474</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Enter at least one login name and password.</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="addUser">
<property name="minimumSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>48</height>
</size>
</property>
<property name="toolTip">
<string>Add another user account.</string>
</property>
<property name="text">
<string>Create user</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteUser">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>80</width>
<height>48</height>
</size>
</property>
<property name="text">
<string>Delete user</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QTableView" name="usersView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>678</width>
<height>248</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="_23">
<property name="verticalSpacing">
<number>11</number>
</property>
<item row="2" column="4">
<widget class="QLabel" name="labelConfirmRootPw">
<property name="text">
<string>Confirm: </string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="hostname">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>700</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QLineEdit" name="confirmRootPw">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="labelRootPw">
<property name="text">
<string>Root password: </string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_3">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Hostname:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLineEdit" name="rootPw">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QLabel" name="labelHostnameError">
<property name="minimumSize">
<size>
<width>230</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>230</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="labelHostname">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="labelRootPwIcon">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="labelRootPwError">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -16,7 +16,7 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include <CreateUserJob.h>
#include <jobs/CreateUserJob.h>
#include "JobQueue.h"
#include "GlobalStorage.h"
@ -33,11 +33,13 @@
CreateUserJob::CreateUserJob( const QString& userName,
const QString& fullName,
const QString& shell,
bool autologin,
const QStringList& defaultGroups )
: Calamares::Job()
, m_userName( userName )
, m_fullName( fullName )
, m_shell( shell )
, m_autologin( autologin )
, m_defaultGroups( defaultGroups )
{
@ -151,7 +153,7 @@ CreateUserJob::exec()
targetEnvCall( { "useradd",
"-m",
"-s",
"/bin/bash",
m_shell,
"-U",
"-c",
m_fullName,

View File

@ -0,0 +1,182 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2014-2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include <jobs/CreateUserJob.h>
#include "JobQueue.h"
#include "GlobalStorage.h"
#include "utils/Logger.h"
#include "utils/CalamaresUtilsSystem.h"
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
#include <QTextStream>
CreateUserJob::CreateUserJob( const QString& userName,
const QString& fullName,
const QString& shell,
bool autologin,
const QStringList& defaultGroups )
: Calamares::Job()
, m_userName( userName )
, m_fullName( fullName )
, m_shell( shell )
, m_autologin( autologin )
, m_defaultGroups( defaultGroups )
{
}
QString
CreateUserJob::prettyName() const
{
return tr( "Create user %1" ).arg( m_userName );
}
QString
CreateUserJob::prettyDescription() const
{
return tr( "Create user <strong>%1</strong>." ).arg( m_userName );
}
QString
CreateUserJob::prettyStatusMessage() const
{
return tr( "Creating user %1." ).arg( m_userName );
}
Calamares::JobResult
CreateUserJob::exec()
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
QDir destDir( gs->value( "rootMountPoint" ).toString() );
if ( gs->contains( "sudoersGroup" ) &&
!gs->value( "sudoersGroup" ).toString().isEmpty() )
{
QFileInfo sudoersFi( destDir.absoluteFilePath( "etc/sudoers.d/10-installer" ) );
if ( !sudoersFi.absoluteDir().exists() )
return Calamares::JobResult::error( tr( "Sudoers dir is not writable." ) );
QFile sudoersFile( sudoersFi.absoluteFilePath() );
if (!sudoersFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
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." ) );
}
QFileInfo groupsFi( destDir.absoluteFilePath( "etc/group" ) );
QFile groupsFile( groupsFi.absoluteFilePath() );
if ( !groupsFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
return Calamares::JobResult::error( tr( "Cannot open groups file for reading." ) );
QString groupsData = QString::fromLocal8Bit( groupsFile.readAll() );
QStringList groupsLines = groupsData.split( '\n' );
for ( QStringList::iterator it = groupsLines.begin();
it != groupsLines.end(); ++it )
{
int indexOfFirstToDrop = it->indexOf( ':' );
it->truncate( indexOfFirstToDrop );
}
foreach ( const QString& group, m_defaultGroups )
if ( !groupsLines.contains( group ) )
CalamaresUtils::System::instance()->
targetEnvCall( { "groupadd", group } );
QString defaultGroups = m_defaultGroups.join( ',' );
if ( m_autologin )
{
QString autologinGroup;
if ( gs->contains( "autologinGroup" ) &&
!gs->value( "autologinGroup" ).toString().isEmpty() )
autologinGroup = gs->value( "autologinGroup" ).toString();
else
autologinGroup = QStringLiteral( "autologin" );
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() )
{
QString shellFriendlyHome = "/home/" + m_userName;
QDir existingHome( destDir.absolutePath() + shellFriendlyHome );
if ( existingHome.exists() )
{
QString backupDirName = "dotfiles_backup_" +
QDateTime::currentDateTime()
.toString( "yyyy-MM-dd_HH-mm-ss" );
existingHome.mkdir( backupDirName );
CalamaresUtils::System::instance()->
targetEnvCall( { "sh",
"-c",
"mv -f " +
shellFriendlyHome + "/.* " +
shellFriendlyHome + "/" +
backupDirName
} );
}
}
int ec = CalamaresUtils::System::instance()->
targetEnvCall( { "useradd",
"-m",
"-s",
m_shell,
"-U",
"-G",
defaultGroups,
"-c",
m_fullName,
m_userName } );
if ( ec )
return Calamares::JobResult::error( tr( "Cannot create user %1." )
.arg( m_userName ),
tr( "useradd terminated with error code %1." )
.arg( ec ) );
ec = CalamaresUtils::System::instance()->
targetEnvCall( { "chown",
"-R",
QString( "%1:%2" ).arg( m_userName )
.arg( m_userName ),
QString( "/home/%1" ).arg( m_userName ) } );
if ( ec )
return Calamares::JobResult::error( tr( "Cannot set home directory ownership for user %1." )
.arg( m_userName ),
tr( "chown terminated with error code %1." )
.arg( ec ) );
return Calamares::JobResult::ok();
}

View File

@ -29,6 +29,7 @@ class CreateUserJob : public Calamares::Job
public:
CreateUserJob( const QString& userName,
const QString& fullName,
const QString& shell,
bool autologin,
const QStringList& defaultGroups );
QString prettyName() const override;
@ -39,6 +40,7 @@ public:
private:
QString m_userName;
QString m_fullName;
QString m_shell;
bool m_autologin;
QStringList m_defaultGroups;
};

View File

@ -0,0 +1,85 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Lisa Vitolo <shainer@chakralinux.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include <jobs/SetAvatarJob.h>
#include "GlobalStorage.h"
#include "utils/Logger.h"
#include "JobQueue.h"
#include <QFile>
#include <QDir>
SetAvatarJob::SetAvatarJob(const QString &avatarFile, const QString &destPath)
: Calamares::Job()
, m_avatarFile(avatarFile)
, m_destPath(destPath)
{
}
QString SetAvatarJob::prettyName() const
{
return tr( "Set avatar %1" ).arg( m_avatarFile );
}
QString
SetAvatarJob::prettyDescription() const
{
return tr( "Set avatar from <strong>%1</strong>." ).arg( m_avatarFile );
}
QString
SetAvatarJob::prettyStatusMessage() const
{
return tr( "Setting avatar from %1." ).arg( m_avatarFile );
}
Calamares::JobResult SetAvatarJob::exec()
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( !gs || !gs->contains( "rootMountPoint" ) )
{
cLog() << "No rootMountPoint in global storage";
return Calamares::JobResult::error( tr( "Internal Error" ) );
}
QString destDir = gs->value( "rootMountPoint" ).toString();
if ( !QDir( destDir ).exists() )
{
cLog() << "rootMountPoint points to a dir which does not exist";
return Calamares::JobResult::error( tr( "Internal Error" ) );
}
QString destination( destDir + m_destPath );
QFile avatarFile( m_avatarFile );
if (!avatarFile.exists()) {
cLog() << "Avatar file does not exist";
return Calamares::JobResult::error( tr("Avatar file does not exist") );
}
if (!avatarFile.copy(destination)) {
cLog() << "Error copying:" << avatarFile.errorString();
return Calamares::JobResult::error( tr("Error copying") );
}
return Calamares::JobResult::ok();
}

View File

@ -0,0 +1,39 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Lisa Vitolo <shainer@chakralinux.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SETAVATARJOB_CPP_H
#define SETAVATARJOB_CPP_H
#include <Job.h>
class SetAvatarJob : public Calamares::Job
{
Q_OBJECT
public:
SetAvatarJob( const QString& avatarFile, const QString& destPath );
QString prettyName() const override;
QString prettyDescription() const override;
QString prettyStatusMessage() const override;
Calamares::JobResult exec() override;
private:
const QString m_avatarFile;
const QString m_destPath;
};
#endif // SETAVATARJOB_CPP_H

View File

@ -17,7 +17,7 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SetHostNameJob.h"
#include <jobs/SetHostNameJob.h>
#include "GlobalStorage.h"
#include "utils/Logger.h"

View File

@ -16,7 +16,7 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include <SetPasswordJob.h>
#include <jobs/SetPasswordJob.h>
#include "JobQueue.h"
#include "GlobalStorage.h"

View File

@ -1,609 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Page_UserSetup</class>
<widget class="QWidget" name="Page_UserSetup">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>862</width>
<height>683</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labelWhatIsYourName">
<property name="text">
<string>What is your name?</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="textBoxFullName">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelFullName">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelFullNameError">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="username_label_2">
<property name="text">
<string>What name do you want to use to log in?</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="textBoxUsername">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelUsername">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelUsernameError">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="username_extra_label_2">
<property name="styleSheet">
<string>font-weight: normal</string>
</property>
<property name="text">
<string>&lt;small&gt;If more than one person will use this computer, you can set up multiple accounts after installation.&lt;/small&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="hostname_label_2">
<property name="text">
<string>What is the name of this computer?</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="textBoxHostname">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelHostname">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelHostnameError">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="hostname_extra_label_2">
<property name="styleSheet">
<string>font-weight: normal</string>
</property>
<property name="text">
<string>&lt;small&gt;This name will be used if you make the computer visible to others on a network.&lt;/small&gt;</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="password_label_2">
<property name="text">
<string>Choose a password to keep your account safe.</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="textBoxUserPassword">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="textBoxUserVerifiedPassword">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelUserPassword">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelUserPasswordError">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="password_extra_label_3">
<property name="styleSheet">
<string>font-weight: normal</string>
</property>
<property name="text">
<string>&lt;small&gt;Enter the same password twice, so that it can be checked for typing errors. A good password will contain a mixture of letters, numbers and punctuation, should be at least eight characters long, and should be changed at regular intervals.&lt;/small&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxAutoLogin">
<property name="text">
<string>Log in automatically without asking for the password.</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxReusePassword">
<property name="text">
<string>Use the same password for the administrator account.</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labelChooseRootPassword">
<property name="text">
<string>Choose a password for the administrator account.</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="textBoxRootPassword">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="textBoxVerifiedRootPassword">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelRootPassword">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelRootPasswordError">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="labelExtraRootPassword">
<property name="styleSheet">
<string>font-weight: normal</string>
</property>
<property name="text">
<string>&lt;small&gt;Enter the same password twice, so that it can be checked for typing errors.&lt;/small&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -8,7 +8,7 @@ defaultGroups:
- wheel
- audio
autologinGroup: autologin
doAutologin: true
doAutologin: false
sudoersGroup: wheel
setRootPassword: true
doReusePassword: true
setRootPassword: false
availableShells: /bin/bash, /bin/zsh

View File

@ -2,5 +2,7 @@
<qresource prefix="/">
<file>images/invalid.png</file>
<file>images/valid.png</file>
<file>images/arrow-down.png</file>
<file>images/arrow-up.png</file>
</qresource>
</RCC>