diff --git a/recipe.json b/recipe.json
index 3ab7941..53df8c0 100644
--- a/recipe.json
+++ b/recipe.json
@@ -50,6 +50,9 @@
"theme": {
"template": "theme"
},
+ "user": {
+ "template": "user"
+ },
"packages": {
"template": "preferences",
"is-advanced": true,
diff --git a/vanilla_first_setup/defaults/applications.py b/vanilla_first_setup/defaults/applications.py
index 3572ebd..ff71f98 100644
--- a/vanilla_first_setup/defaults/applications.py
+++ b/vanilla_first_setup/defaults/applications.py
@@ -39,7 +39,7 @@ class VanillaLayoutApplications(Adw.Bin):
# signals
self.btn_next.connect("clicked", self.__next_step)
self.__window.connect("page-changed", self.__on_page_changed)
-
+
@property
def step_id(self):
return self.__key
@@ -92,7 +92,7 @@ class VanillaLayoutApplications(Adw.Bin):
def close_customize(widget, dialog):
dialog.hide()
-
+
def apply_preferences(widget, dialog, apps_list, item):
for app in item["applications"]:
app["active"] = app["switch"].get_active()
@@ -148,12 +148,12 @@ class VanillaLayoutApplications(Adw.Bin):
_customize.connect("clicked", present_customize, selection_dialogs[-1], _apps_list, item)
_cancel_button.connect("clicked", close_customize, selection_dialogs[-1])
_apply_button.connect("clicked", apply_preferences, selection_dialogs[-1], _apps_list, item)
-
+
self.bundles_list.add(_action_row)
self.__register_widgets.append((item["id"], _switcher, _index))
_index += 1
-
+
def __on_page_changed(self, widget, page):
if page == self.__key:
if True not in [
@@ -163,7 +163,7 @@ class VanillaLayoutApplications(Adw.Bin):
self.bundles_list.set_sensitive(False)
else:
self.bundles_list.set_sensitive(True)
-
+
def __next_step(self, *args):
self.__window.next()
@@ -192,4 +192,4 @@ class VanillaLayoutApplications(Adw.Bin):
for app in self.__step["bundles"][index]["applications"]:
finals["vars"][app["name"]] = False
- return finals
\ No newline at end of file
+ return finals
diff --git a/vanilla_first_setup/defaults/meson.build b/vanilla_first_setup/defaults/meson.build
index 32cf3c9..01a406f 100644
--- a/vanilla_first_setup/defaults/meson.build
+++ b/vanilla_first_setup/defaults/meson.build
@@ -5,6 +5,7 @@ sources = [
'__init__.py',
'welcome.py',
'theme.py',
+ 'user.py',
'applications.py',
'conn_check.py'
]
diff --git a/vanilla_first_setup/defaults/user.py b/vanilla_first_setup/defaults/user.py
new file mode 100644
index 0000000..4a6cd45
--- /dev/null
+++ b/vanilla_first_setup/defaults/user.py
@@ -0,0 +1,146 @@
+# user.py
+#
+# Copyright 2022 mirkobrombin
+# Copyright 2022 muqtadir
+#
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundationat version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import sys
+import time
+import re, subprocess, shutil
+import crypt
+from gi.repository import Gtk, Gio, GLib, Adw
+
+
+@Gtk.Template(resource_path='/io/github/vanilla-os/FirstSetup/gtk/default-user.ui')
+class VanillaDefaultUser(Adw.Bin):
+ __gtype_name__ = 'VanillaDefaultUser'
+
+ btn_next = Gtk.Template.Child()
+ fullname_entry = Gtk.Template.Child()
+ username_entry = Gtk.Template.Child()
+ password_entry = Gtk.Template.Child()
+ password_confirmation = Gtk.Template.Child()
+
+ fullname = ""
+ fullname_filled = False
+ username = ""
+ username_filled = False
+ password_filled = False
+
+ def __init__(self, window, distro_info, key, step, **kwargs):
+ super().__init__(**kwargs)
+ self.__window = window
+ self.__distro_info = distro_info
+ self.__key = key
+ self.__step = step
+
+ # signals
+ self.btn_next.connect("clicked", self.__window.next)
+ self.fullname_entry.connect('changed', self.__on_fullname_entry_changed)
+ self.username_entry.connect('changed', self.__on_username_entry_changed)
+ self.password_entry.connect('changed', self.__on_password_changed)
+ self.password_confirmation.connect('changed', self.__on_password_changed)
+
+ @property
+ def step_id(self):
+ return self.__key
+
+ def get_finals(self):
+ return {
+ "vars": {
+ "create": True
+ },
+ "funcs": [
+ {
+ "if": "create",
+ "type": "command",
+ "commands": [
+ f"useradd -m {self.username} -c \"{self.fullname}\" -G sudo,adm,lpadmin -p {crypt.crypt(self.password_entry.get_text())}",
+ ]
+ }
+ ]
+ }
+
+ def __on_fullname_entry_changed(self, *args):
+ _fullname = self.fullname_entry.get_text()
+
+ if len(_fullname) > 32:
+ self.fullname_entry.set_text(_fullname[:32])
+ self.fullname_entry.set_position(-1)
+ _fullname = self.fullname_entry.get_text()
+
+ self.fullname_filled = True
+ self.__verify_continue()
+ self.fullname = _fullname
+
+ def __on_username_entry_changed(self, *args):
+ _input = self.username_entry.get_text()
+ _status = True
+
+ # cannot be longer than 32 characters
+ if len(_input) > 32:
+ self.username_entry.set_text(_input[:32])
+ self.username_entry.set_position(-1)
+ _input = self.username_entry.get_text()
+
+ # cannot contain special characters
+ if re.search(r'[^a-z0-9]', _input):
+ _status = False
+ self.__window.toast("Username cannot contain special characters or uppercase letters. Please choose another username.")
+
+ # cannot be empty
+ elif not _input:
+ _status = False
+ self.__window.toast("Username cannot be empty. Please type a username.")
+
+ # cannot be root
+ elif _input == "root":
+ _status = False
+ self.__window.toast("root user is reserved. Please choose another username.")
+
+ if not _status:
+ self.username_entry.add_css_class('error')
+ self.username_filled = False
+ self.__verify_continue()
+ else:
+ self.username_entry.remove_css_class('error')
+ self.username_filled = True
+ self.__verify_continue()
+ self.username = _input
+
+
+ def __on_password_changed(self, *args):
+ password = self.password_entry.get_text()
+ if password == self.password_confirmation.get_text() \
+ and password.strip():
+ self.password_filled = True;
+ self.password_confirmation.remove_css_class('error')
+ self.password = self.__encrypt_password(password)
+ else:
+ self.password_filled = False;
+ self.password_confirmation.add_css_class('error')
+
+ self.__verify_continue();
+
+ def __verify_continue(self):
+ self.btn_next.set_sensitive(self.fullname_filled and self.password_filled and self.username_filled)
+
+ def __encrypt_password(self, password):
+ command = subprocess.run(
+ [shutil.which("openssl"), "passwd", "-crypt", password],
+ capture_output=True
+ )
+ password_encrypted = command.stdout.decode('utf-8').strip('\n')
+ return password_encrypted
diff --git a/vanilla_first_setup/gtk/default-user.ui b/vanilla_first_setup/gtk/default-user.ui
new file mode 100644
index 0000000..6315266
--- /dev/null
+++ b/vanilla_first_setup/gtk/default-user.ui
@@ -0,0 +1,70 @@
+
+
+
+
+ fill
+ center
+ true
+
+
+
+
+
diff --git a/vanilla_first_setup/gtk/window.ui b/vanilla_first_setup/gtk/window.ui
index 9fb70ff..18124b3 100644
--- a/vanilla_first_setup/gtk/window.ui
+++ b/vanilla_first_setup/gtk/window.ui
@@ -3,8 +3,8 @@
- 750
- 640
+ 800
+ 750
First Setup
@@ -45,4 +45,4 @@
-
\ No newline at end of file
+
diff --git a/vanilla_first_setup/meson.build b/vanilla_first_setup/meson.build
index 59b0ee4..bd02177 100644
--- a/vanilla_first_setup/meson.build
+++ b/vanilla_first_setup/meson.build
@@ -20,6 +20,7 @@ conf.set('pkgdatadir', pkgdatadir)
configure_file(
input: 'vanilla-first-setup.in',
output: 'vanilla-first-setup',
+ install_mode: 'rwxr-xr-x',
configuration: conf,
install: true,
install_dir: get_option('bindir')
diff --git a/vanilla_first_setup/utils/builder.py b/vanilla_first_setup/utils/builder.py
index 894e673..e02d932 100644
--- a/vanilla_first_setup/utils/builder.py
+++ b/vanilla_first_setup/utils/builder.py
@@ -24,6 +24,7 @@ from vanilla_first_setup.utils.recipe import RecipeLoader
from vanilla_first_setup.defaults.conn_check import VanillaDefaultConnCheck
from vanilla_first_setup.defaults.welcome import VanillaDefaultWelcome
from vanilla_first_setup.defaults.theme import VanillaDefaultTheme
+from vanilla_first_setup.defaults.user import VanillaDefaultUser
from vanilla_first_setup.layouts.preferences import VanillaLayoutPreferences
from vanilla_first_setup.layouts.yes_no import VanillaLayoutYesNo
@@ -37,6 +38,7 @@ templates = {
"conn-check": VanillaDefaultConnCheck,
"welcome": VanillaDefaultWelcome,
"theme": VanillaDefaultTheme,
+ "user": VanillaDefaultUser,
"preferences": VanillaLayoutPreferences,
"yes-no": VanillaLayoutYesNo,
"applications": VanillaLayoutApplications
@@ -102,7 +104,7 @@ class Builder:
if step["template"] in templates:
_widget = templates[step["template"]](self.__window, self.distro_info, key, step)
self.__register_widgets.append((_widget, _status, _protected))
-
+
def get_temp_finals(self, step_id: str):
for widget, _, _ in self.__register_widgets:
if widget.step_id == step_id:
@@ -125,7 +127,7 @@ class Builder:
@property
def recipe(self):
return self.__recipe.raw
-
+
@property
def distro_info(self):
return {
diff --git a/vanilla_first_setup/utils/parser.py b/vanilla_first_setup/utils/parser.py
index 7855b32..b01473d 100644
--- a/vanilla_first_setup/utils/parser.py
+++ b/vanilla_first_setup/utils/parser.py
@@ -74,7 +74,7 @@ class Parser:
# check if the condition is met
if _condition == _vars[_func["if"]]:
commands += _func["commands"]
-
+
# set-up warps if any
for warp in warps:
_vars = warp["vars"]
diff --git a/vanilla_first_setup/utils/processor.py b/vanilla_first_setup/utils/processor.py
index ce7b134..c02b5e2 100644
--- a/vanilla_first_setup/utils/processor.py
+++ b/vanilla_first_setup/utils/processor.py
@@ -113,7 +113,7 @@ class Processor:
f.write("if [ $? -eq 0 ]; then")
f.write(f"{out_run}\n")
f.write("fi")
-
+
# create the done file
f.write("if [ $? -eq 0 ]; then\n")
f.write(f"touch {done_file}\n")
diff --git a/vanilla_first_setup/vanilla-first-setup.gresource.xml b/vanilla_first_setup/vanilla-first-setup.gresource.xml
index ff7b364..7cbad4b 100644
--- a/vanilla_first_setup/vanilla-first-setup.gresource.xml
+++ b/vanilla_first_setup/vanilla-first-setup.gresource.xml
@@ -11,6 +11,7 @@
gtk/default-conn-check.ui
gtk/default-theme.ui
gtk/default-welcome.ui
+ gtk/default-user.ui
gtk/layout-preferences.ui
gtk/layout-yes-no.ui
diff --git a/vanilla_first_setup/window.py b/vanilla_first_setup/window.py
index d69f15c..a7de457 100644
--- a/vanilla_first_setup/window.py
+++ b/vanilla_first_setup/window.py
@@ -172,8 +172,8 @@ class VanillaWindow(Adw.ApplicationWindow):
commands
)
- self.__view_progress.start(res, Processor.hide_first_setup, self.__user)
-
+ self.__view_progress.start(res, Processor.hide_first_setup)
+
def set_installation_result(self, result, terminal):
self.__view_done.set_result(result, terminal)
self.next()