1/* === This file is part of Calamares - <https://calamares.io> ===
2 *
3 *   SPDX-FileCopyrightText: 2020 - 2021 Anke Boersma <demm@kaosx.us>
4 *   SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
5 *   SPDX-License-Identifier: GPL-3.0-or-later
6 *
7 *   Calamares is Free Software: see the License-Identifier above.
8 *
9 */
10
11import io.calamares.core 1.0
12import io.calamares.ui 1.0
13
14import QtQuick 2.15
15import QtQuick.Controls 2.10
16import QtQuick.Layouts 1.3
17import org.kde.kirigami 2.7 as Kirigami
18import QtGraphicalEffects 1.0
19import QtQuick.Window 2.3
20
21Kirigami.ScrollablePage {
22    // You can hard-code a color here, or bind to a Kirigami theme color,
23    // or use a color from Calamares branding, or ..
24    readonly property color unfilledFieldColor: "#FBFBFB" //Kirigami.Theme.backgroundColor
25    readonly property color positiveFieldColor: "#F0FFF0" //Kirigami.Theme.positiveBackgroundColor
26    readonly property color negativeFieldColor: "#EBCED1" //Kirigami.Theme.negativeBackgroundColor
27    readonly property color unfilledFieldOutlineColor: "#F1F1F1"
28    readonly property color positiveFieldOutlineColor: "#DCFFDC"
29    readonly property color negativeFieldOutlineColor: "#BE5F68"
30    readonly property color headerTextColor: "#1F1F1F"
31    readonly property color commentsColor: "#6D6D6D"
32
33    width: parent.width
34    height: parent.height
35
36    header: Kirigami.Heading {
37        Layout.fillWidth: true
38        height: 50
39        horizontalAlignment: Qt.AlignHCenter
40        color: headerTextColor
41        font.weight: Font.Medium
42        font.pointSize: 12
43        text: qsTr("Pick your user name and credentials to login and perform admin tasks")
44    }
45
46    ColumnLayout {
47        id: _formLayout
48        spacing: Kirigami.Units.smallSpacing
49
50        Column {
51            Layout.fillWidth: true
52            spacing: Kirigami.Units.smallSpacing
53
54            Label {
55                width: parent.width
56                text: qsTr("What is your name?")
57            }
58
59            TextField {
60                id: _userNameField
61                width: parent.width
62                enabled: config.isEditable("fullName")
63                placeholderText: qsTr("Your Full Name")
64                text: config.fullName
65                onTextChanged: config.setFullName(text)
66
67                palette.base: _userNameField.text.length
68                    ? positiveFieldColor : unfilledFieldColor
69                palette.highlight : _userNameField.text.length
70                    ? positiveFieldOutlineColor : unfilledFieldOutlineColor
71            }
72        }
73
74        Column {
75            Layout.fillWidth: true
76            spacing: Kirigami.Units.smallSpacing
77
78            Label {
79                width: parent.width
80                text: qsTr("What name do you want to use to log in?")
81            }
82
83            TextField {
84                id: _userLoginField
85                width: parent.width
86                enabled: config.isEditable("loginName")
87                placeholderText: qsTr("Login Name")
88                text: config.loginName
89                validator: RegularExpressionValidator { regularExpression: /[a-z_][a-z0-9_-]*[$]?$/ }
90
91                onTextChanged: acceptableInput
92                    ? ( _userLoginField.text === "root"
93                    ? forbiddenMessage.visible=true
94                    : ( config.setLoginName(text),
95                    userMessage.visible = false,forbiddenMessage.visible=false ) )
96                    : ( userMessage.visible = true,console.log("Invalid") )
97
98                palette.base: _userLoginField.text.length
99                    ? ( acceptableInput
100                    ? ( _userLoginField.text === "root"
101                    ? negativeFieldColor
102                    : positiveFieldColor )
103                    : negativeFieldColor )
104                    : unfilledFieldColor
105                palette.highlight : _userLoginField.text.length
106                    ? ( acceptableInput
107                    ? ( _userLoginField.text === "root"
108                    ? negativeFieldOutlineColor
109                    : positiveFieldOutlineColor )
110                    : negativeFieldOutlineColor )
111                    : unfilledFieldOutlineColor
112            }
113
114            Label {
115                width: parent.width
116                text: qsTr("If more than one person will use this computer, you can create multiple accounts after installation.")
117                font.weight: Font.Thin
118                font.pointSize: 8
119                color: commentsColor
120            }
121        }
122
123        Kirigami.InlineMessage {
124            id: userMessage
125            Layout.fillWidth: true
126            visible: false
127            type: Kirigami.MessageType.Error
128            text: qsTr("Only lowercase letters, numbers, underscore and hyphen are allowed.")
129        }
130
131        Kirigami.InlineMessage {
132            id: forbiddenMessage
133            Layout.fillWidth: true
134            visible: false
135            type: Kirigami.MessageType.Error
136            text: qsTr("root is not allowed as username.")
137        }
138
139        Column {
140            Layout.fillWidth: true
141            spacing: Kirigami.Units.smallSpacing
142
143            Label {
144                width: parent.width
145                text: qsTr("What is the name of this computer?")
146            }
147
148            TextField {
149                id: _hostName
150                width: parent.width
151                placeholderText: qsTr("Computer Name")
152                text: config.hostName
153                validator: RegularExpressionValidator { regularExpression: /[a-zA-Z0-9][-a-zA-Z0-9_]+/ }
154
155                onTextChanged: acceptableInput
156                    ? ( _hostName.text === "localhost"
157                    ? forbiddenHost.visible=true
158                    : ( config.setHostName(text),
159                    hostMessage.visible = false,forbiddenHost.visible = false ) )
160                    : hostMessage.visible = true
161
162                palette.base: _hostName.text.length
163                    ? ( acceptableInput
164                    ? ( _hostName.text === "localhost"
165                    ? negativeFieldColor : positiveFieldColor )
166                    : negativeFieldColor)
167                    : unfilledFieldColor
168                palette.highlight : _hostName.text.length
169                    ? ( acceptableInput
170                    ? ( _hostName.text === "localhost"
171                    ? negativeFieldOutlineColor : positiveFieldOutlineColor )
172                    : negativeFieldOutlineColor)
173                    : unfilledFieldOutlineColor
174            }
175
176            Label {
177                width: parent.width
178                text: qsTr("This name will be used if you make the computer visible to others on a network.")
179                font.weight: Font.Thin
180                font.pointSize: 8
181                color: commentsColor
182            }
183        }
184
185        Kirigami.InlineMessage {
186            id: hostMessage
187            Layout.fillWidth: true
188            visible: false
189            type: Kirigami.MessageType.Error
190            text: qsTr("Only letters, numbers, underscore and hyphen are allowed, minimal of two characters.")
191        }
192
193        Kirigami.InlineMessage {
194            id: forbiddenHost
195            Layout.fillWidth: true
196            visible: false
197            type: Kirigami.MessageType.Error
198            text: qsTr("localhost is not allowed as hostname.")
199        }
200
201        Column {
202            Layout.fillWidth: true
203            spacing: Kirigami.Units.smallSpacing
204
205            Label {
206                width: parent.width
207                text: qsTr("Choose a password to keep your account safe.")
208            }
209
210            Row {
211                width: parent.width
212                spacing: 20
213
214                TextField {
215                    id: _passwordField
216                    width: parent.width / 2 - 10
217                    placeholderText: qsTr("Password")
218                    text: config.userPassword
219                    onTextChanged: config.setUserPassword(text)
220
221                    palette.base: _passwordField.text.length
222                        ? positiveFieldColor : unfilledFieldColor
223                    palette.highlight : _passwordField.text.length
224                        ? positiveFieldOutlineColor : unfilledFieldOutlineColor
225
226                    echoMode: TextInput.Password
227                    passwordMaskDelay: 300
228                    inputMethodHints: Qt.ImhNoAutoUppercase
229                }
230
231                TextField {
232                    id: _verificationPasswordField
233                    width: parent.width / 2 - 10
234                    placeholderText: qsTr("Repeat Password")
235                    text: config.userPasswordSecondary
236
237                    onTextChanged: _passwordField.text === _verificationPasswordField.text
238                        ? ( config.setUserPasswordSecondary(text),
239                        passMessage.visible = false,
240                        validityMessage.visible = true )
241                        : ( passMessage.visible = true,
242                        validityMessage.visible = false )
243
244                    palette.base: _verificationPasswordField.text.length
245                        ? ( _passwordField.text === _verificationPasswordField.text
246                        ? positiveFieldColor : negativeFieldColor )
247                        : unfilledFieldColor
248                    palette.highlight : _verificationPasswordField.text.length
249                        ? ( _passwordField.text === _verificationPasswordField.text
250                        ? positiveFieldOutlineColor : negativeFieldOutlineColor )
251                        : unfilledFieldOutlineColor
252
253                    echoMode: TextInput.Password
254                    passwordMaskDelay: 300
255                    inputMethodHints: Qt.ImhNoAutoUppercase
256                }
257            }
258
259            Label {
260                width: parent.width
261                text: qsTr("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.")
262                font.weight: Font.Thin
263                font.pointSize: 8
264                wrapMode: Text.WordWrap
265                color: commentsColor
266            }
267        }
268
269        Kirigami.InlineMessage {
270            id: passMessage
271            Layout.fillWidth: true
272            showCloseButton: true
273            visible: false
274            type: Kirigami.MessageType.Error
275            text: config.userPasswordMessage
276        }
277
278        Kirigami.InlineMessage {
279            id: validityMessage
280            Layout.fillWidth: true
281            showCloseButton: true
282            visible: false
283            type:  config.userPasswordValidity
284                ? ( config.requireStrongPasswords
285                ? Kirigami.MessageType.Error : Kirigami.MessageType.Warning )
286                : Kirigami.MessageType.Positive
287            text: config.userPasswordMessage
288        }
289
290        CheckBox {
291            id: root
292            visible: config.writeRootPassword
293            text: qsTr("Reuse user password as root password")
294            checked: config.reuseUserPasswordForRoot
295            onCheckedChanged: config.setReuseUserPasswordForRoot(checked)
296        }
297
298        Label {
299            visible: root.checked
300            width: parent.width
301            text: qsTr("Use the same password for the administrator account.")
302            font.weight: Font.Thin
303            font.pointSize: 8
304            color: commentsColor
305        }
306
307        Column {
308            visible: ! root.checked
309            Layout.fillWidth: true
310            spacing: Kirigami.Units.smallSpacing
311
312            Label {
313                width: parent.width
314                text: qsTr("Choose a root password to keep your account safe.")
315            }
316
317            Row {
318                width: parent.width
319                spacing: 20
320
321                TextField {
322                    id: _rootPasswordField
323                    width: parent.width / 2 -10
324                    placeholderText: qsTr("Root Password")
325                    text: config.rootPassword
326
327                    onTextChanged: config.setRootPassword(text)
328
329                    palette.base: _rootPasswordField.text.length
330                        ? positiveFieldColor : unfilledFieldColor
331                    palette.highlight : _rootPasswordField.text.length
332                        ? positiveFieldOutlineColor : unfilledFieldOutlineColor
333
334                    echoMode: TextInput.Password
335                    passwordMaskDelay: 300
336                    inputMethodHints: Qt.ImhNoAutoUppercase
337                }
338
339                TextField {
340                    id: _verificationRootPasswordField
341                    width: parent.width / 2 -10
342                    placeholderText: qsTr("Repeat Root Password")
343                    text: config.rootPasswordSecondary
344
345                    onTextChanged: _rootPasswordField.text === _verificationRootPasswordField.text
346                        ? ( config.setRootPasswordSecondary(text),
347                        rootPassMessage.visible = false,rootValidityMessage.visible = true )
348                        : ( rootPassMessage.visible = true,rootValidityMessage.visible = false )
349
350                    palette.base: _verificationRootPasswordField.text.length
351                        ? ( _rootPasswordField.text === _verificationRootPasswordField.text
352                        ? positiveFieldColor : negativeFieldColor)
353                        : unfilledFieldColor
354                    palette.highlight : _verificationRootPasswordField.text.length
355                        ? ( _rootPasswordField.text === _verificationRootPasswordField.text
356                        ? positiveFieldOutlineColor : negativeFieldOutlineColor)
357                        : unfilledFieldOutlineColor
358
359                    echoMode: TextInput.Password
360                    passwordMaskDelay: 300
361                    inputMethodHints: Qt.ImhNoAutoUppercase
362                }
363            }
364
365            Label {
366                visible: ! root.checked
367                width: parent.width
368                text: qsTr("Enter the same password twice, so that it can be checked for typing errors.")
369                font.weight: Font.Thin
370                font.pointSize: 8
371                color: commentsColor
372            }
373        }
374
375        Kirigami.InlineMessage {
376            id: rootPassMessage
377            Layout.fillWidth: true
378            showCloseButton: true
379            visible: false
380            type: Kirigami.MessageType.Error
381            text: config.rootPasswordMessage
382        }
383
384        Kirigami.InlineMessage {
385            id: rootValidityMessage
386            Layout.fillWidth: true
387            showCloseButton: true
388            visible: false
389            type:  config.rootPasswordValidity
390                ? ( config.requireStrongPasswords
391                ? Kirigami.MessageType.Error : Kirigami.MessageType.Warning )
392                : Kirigami.MessageType.Positive
393            text: config.rootPasswordMessage
394        }
395
396        CheckBox {
397            Layout.alignment: Qt.AlignCenter
398            text: qsTr("Log in automatically without asking for the password")
399            checked: config.doAutoLogin
400            onCheckedChanged: config.setAutoLogin(checked)
401        }
402
403        CheckBox {
404            visible: config.permitWeakPasswords
405            Layout.alignment: Qt.AlignCenter
406            text: qsTr("Validate passwords quality")
407            checked: config.requireStrongPasswords
408            onCheckedChanged: config.setRequireStrongPasswords(checked),
409                rootPassMessage.visible = false
410        }
411
412        Label {
413            visible: config.permitWeakPasswords
414            width: parent.width
415            Layout.alignment: Qt.AlignCenter
416            text: qsTr("When this box is checked, password-strength checking is done and you will not be able to use a weak password.")
417            font.weight: Font.Thin
418            font.pointSize: 8
419            color: commentsColor
420        }
421    }
422}
423