summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Sivak <msivak@redhat.com>2013-02-01 16:54:51 +0100
committerMartin Sivak <msivak@redhat.com>2013-03-01 11:36:47 +0100
commit83dca356f03677782d9508477e92041428a6813c (patch)
tree0e24599a240dafbb02de06815b1ed77bb3a30c54
parentcf030941ccb6ff7c8d6788d512227c00a261b6e9 (diff)
downloadanaconda-83dca356f03677782d9508477e92041428a6813c.tar.gz
anaconda-83dca356f03677782d9508477e92041428a6813c.tar.xz
anaconda-83dca356f03677782d9508477e92041428a6813c.zip
Add the User creation spoke including the Advanced dialog
-rw-r--r--po/POTFILES.in3
-rw-r--r--pyanaconda/ui/gui/spokes/advanced_user.glade437
-rw-r--r--pyanaconda/ui/gui/spokes/password.py2
-rw-r--r--pyanaconda/ui/gui/spokes/user.glade377
-rw-r--r--pyanaconda/ui/gui/spokes/user.py420
5 files changed, 1238 insertions, 1 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in
index aaa1231b3..c2ad78d3c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -66,6 +66,7 @@ pyanaconda/ui/gui/spokes/password.py
pyanaconda/ui/gui/spokes/software.py
pyanaconda/ui/gui/spokes/source.py
pyanaconda/ui/gui/spokes/storage.py
+pyanaconda/ui/gui/spokes/user.py
pyanaconda/ui/gui/spokes/welcome.py
pyanaconda/ui/gui/spokes/lib/accordion.py
pyanaconda/ui/gui/spokes/lib/cart.py
@@ -73,6 +74,7 @@ pyanaconda/ui/gui/spokes/lib/passphrase.py
pyanaconda/ui/gui/spokes/lib/resize.py
# Interface files.
+pyanaconda/ui/gui/spokes/advanced_user.glade
pyanaconda/ui/gui/spokes/datetime_spoke.glade
pyanaconda/ui/gui/spokes/network.glade
pyanaconda/ui/gui/spokes/software.glade
@@ -81,6 +83,7 @@ pyanaconda/ui/gui/spokes/keyboard.glade
pyanaconda/ui/gui/spokes/password.glade
pyanaconda/ui/gui/spokes/source.glade
pyanaconda/ui/gui/spokes/welcome.glade
+pyanaconda/ui/gui/spokes/user.glade
pyanaconda/ui/gui/spokes/custom.glade
pyanaconda/ui/gui/spokes/lib/cart.glade
pyanaconda/ui/gui/spokes/lib/detailederror.glade
diff --git a/pyanaconda/ui/gui/spokes/advanced_user.glade b/pyanaconda/ui/gui/spokes/advanced_user.glade
new file mode 100644
index 000000000..6159f5304
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/advanced_user.glade
@@ -0,0 +1,437 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkDialog" id="advancedUserDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="type_hint">dialog</property>
+ <property name="decorated">False</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label" translatable="yes">Save Changes</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">16</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">ADVANCED USER CONFIGURATION</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="100"/>
+ <attribute name="size" value="125"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">8</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Home Directory</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">16</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkCheckButton" id="c_home">
+ <property name="label" translatable="yes">Create a _home directory for this user.</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">4</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="_apply_checkboxes" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">42</property>
+ <property name="margin_top">3</property>
+ <child>
+ <object class="GtkLabel" id="l_home">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Home _directory:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">t_home</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="t_home">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="margin_left">13</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">8</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">User and Group IDs</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">16</property>
+ <property name="margin_top">3</property>
+ <child>
+ <object class="GtkCheckButton" id="c_uid">
+ <property name="label" translatable="yes">Specify a _user ID manually:</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="_apply_checkboxes" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="c_gid">
+ <property name="label" translatable="yes">Specify a _group ID manually:</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="_apply_checkboxes" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spin_uid">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="margin_left">8</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="progress_pulse_step">0.0099999997764825821</property>
+ <property name="adjustment">uid</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spin_gid">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="margin_left">8</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="adjustment">gid</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">8</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Group Membership</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">16</property>
+ <property name="margin_top">3</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Add user to the following groups:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">t_groups</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="t_groups">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="margin_top">3</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">3</property>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">5</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">wheel, my-team (1245), project-x (29935)</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">5</property>
+ <property name="margin_top">3</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">You may input a comma-separated list of group names and group IDs here.
+Groups that do not already exist will be created; specify their GID in parentheses. </property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Example:</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">3</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">Tip:</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="2">button2</action-widget>
+ <action-widget response="1">button1</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkAdjustment" id="gid">
+ <property name="lower">500</property>
+ <property name="upper">32535</property>
+ <property name="value">1000</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="uid">
+ <property name="lower">500</property>
+ <property name="upper">32535</property>
+ <property name="value">1000</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+</interface>
diff --git a/pyanaconda/ui/gui/spokes/password.py b/pyanaconda/ui/gui/spokes/password.py
index 6950472e6..441050e57 100644
--- a/pyanaconda/ui/gui/spokes/password.py
+++ b/pyanaconda/ui/gui/spokes/password.py
@@ -85,7 +85,7 @@ class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke):
@property
def mandatory(self):
- return False
+ return not self.data.user.userList
def apply(self):
self.data.rootpw.password = cryptPassword(self._password)
diff --git a/pyanaconda/ui/gui/spokes/user.glade b/pyanaconda/ui/gui/spokes/user.glade
new file mode 100644
index 000000000..7dbd705e0
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/user.glade
@@ -0,0 +1,377 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <!-- interface-requires AnacondaWidgets 1.0 -->
+ <object class="AnacondaSpokeWindow" id="userCreationWindow">
+ <property name="startup_id">filler</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="startup_id">filler</property>
+ <property name="window_name" translatable="yes">CREATE USER</property>
+ <signal name="button-clicked" handler="on_back_clicked" swapped="no"/>
+ <child internal-child="main_box">
+ <object class="GtkBox" id="AnacondaSpokeWindow-main_box1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child internal-child="nav_box">
+ <object class="GtkEventBox" id="AnacondaSpokeWindow-nav_box1">
+ <property name="app_paintable">True</property>
+ <property name="can_focus">False</property>
+ <child internal-child="nav_area">
+ <object class="GtkGrid" id="AnacondaSpokeWindow-nav_area1">
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child internal-child="alignment">
+ <object class="GtkAlignment" id="AnacondaSpokeWindow-alignment1">
+ <property name="can_focus">False</property>
+ <property name="margin_top">12</property>
+ <property name="yalign">0</property>
+ <property name="xscale">0.75</property>
+ <property name="yscale">0.75</property>
+ <property name="bottom_padding">48</property>
+ <property name="left_padding">24</property>
+ <property name="right_padding">24</property>
+ <child internal-child="action_area">
+ <object class="GtkBox" id="AnacondaSpokeWindow-action_area1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">8</property>
+ <property name="column_spacing">9</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="xpad">10</property>
+ <property name="label" translatable="yes">_Full name</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">t_fullname</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="xpad">10</property>
+ <property name="label" translatable="yes">_Username</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">t_username</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="t_fullname">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="caps_lock_warning">False</property>
+ <signal name="changed" handler="_guessNames" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="t_username">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <signal name="changed" handler="_guessNameDisabler" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="xpad">10</property>
+ <property name="label" translatable="yes">_Password</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">t_password</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">5</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="xpad">10</property>
+ <property name="label" translatable="yes">_Confirm password</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">t_verifypassword</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">7</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="t_password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">●</property>
+ <signal name="changed" handler="_checkPassword" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">5</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="t_verifypassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">●</property>
+ <signal name="changed" handler="_checkPassword" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">7</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Tip:&lt;/b&gt; lorem ipsum dolor sit amet...</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="c_usepassword">
+ <property name="label" translatable="yes">Require a password to use this account</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="_passwordDisabler" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLevelBar" id="password_bar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <property name="mode">GTK_LEVEL_BAR_MODE_DISCRETE</property>
+ <property name="min-value">0</property>
+ <property name="max-value">4</property>
+ <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+ <property name="value">2</property>
+ <property name="halign">fill</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="password_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">empty password</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">6</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="c_admin">
+ <property name="label" translatable="yes">Make this user administrator</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="b_advanced">
+ <property name="label" translatable="yes">_Advanced...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <signal name="clicked" handler="on_advanced_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="b_network_login">
+ <property name="label" translatable="yes">Use network login...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">6</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">8</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/pyanaconda/ui/gui/spokes/user.py b/pyanaconda/ui/gui/spokes/user.py
new file mode 100644
index 000000000..011d71d4f
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/user.py
@@ -0,0 +1,420 @@
+# User creation spoke
+#
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties 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, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Martin Sivak <msivak@redhat.com>
+#
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+N_ = lambda x: x
+
+from gi.repository import Gtk
+
+from pyanaconda.users import cryptPassword, validatePassword
+from pwquality import PWQError
+
+from pyanaconda.ui.gui.spokes import NormalSpoke
+from pyanaconda.ui.gui import GUIObject
+from pyanaconda.ui.gui.categories.user_settings import UserSettingsCategory
+from pyanaconda.ui.common import FirstbootSpokeMixIn
+from pyanaconda.ui.gui.utils import enlightbox
+
+import unicodedata
+import pwquality
+
+__all__ = ["UserSpoke", "AdvancedUserDialog"]
+
+def strip_accents(s):
+ """This function takes arbitrary unicode string
+ and returns it with all the diacritics removed.
+
+ :param s: arbitrary string
+ :type s: unicode
+
+ :return: s with diacritics removed
+ :rtype: unicode
+
+ """
+ return ''.join((c for c in unicodedata.normalize('NFD', s)
+ if unicodedata.category(c) != 'Mn'))
+
+class AdvancedUserDialog(GUIObject):
+ builderObjects = ["advancedUserDialog", "uid", "gid"]
+ mainWidgetName = "advancedUserDialog"
+ uiFile = "advanced_user.glade"
+
+ def __init__(self, user, groupDict, data):
+ GUIObject.__init__(self, data)
+ self._user = user
+ self._groupDict = groupDict
+
+ def initialize(self):
+ GUIObject.initialize(self)
+
+ def _apply_checkboxes(self, _editable, data = None):
+ """Update the state of this screen according to the
+ checkbox states on the screen. It is called from
+ the toggled Gtk event.
+ """
+ c_home = self.builder.get_object("c_home").get_active()
+ c_uid = self.builder.get_object("c_uid").get_active()
+ c_gid = self.builder.get_object("c_gid").get_active()
+
+ self.builder.get_object("t_home").set_sensitive(c_home)
+ self.builder.get_object("l_home").set_sensitive(c_home)
+ self.builder.get_object("spin_uid").set_sensitive(c_uid)
+ self.builder.get_object("spin_gid").set_sensitive(c_gid)
+
+ def refresh(self):
+ t_home = self.builder.get_object("t_home")
+ if self._user.homedir:
+ t_home.set_text(self._user.homedir)
+ elif self._user.name:
+ t_home.set_text("/home/%s" % self._user.name)
+
+ groups = []
+ for group_name in self._user.groups:
+ group = self._groupDict[group_name]
+
+ if group.name and group.gid is not None:
+ groups.append("%s (%d)" % (group.name, group.gid))
+ elif group.name:
+ groups.append(group.name)
+ elif group.gid is not None:
+ groups.append("(%d)" % (group.gid,))
+
+ self.builder.get_object("t_groups").set_text(", ".join(groups))
+
+ def run(self):
+ self.window.show()
+ rc = self.window.run()
+ self.window.hide()
+
+ #OK clicked
+ if rc == 1:
+ if self.builder.get_object("c_home").get_active():
+ self._user.homedir = self.builder.get_object("t_home").get_text()
+ else:
+ self._user.homedir = None
+
+ if self.builder.get_object("c_uid").get_active():
+ self._user.uid = int(self.builder.get_object("uid").get_value())
+ else:
+ self._user.uid = None
+
+ if self.builder.get_object("c_gid").get_active():
+ pass
+ #self._user.gid = int(self.builder.get_widget("gid").get_value())
+ else:
+ #self._user.gid = None
+ pass
+
+ groups = self.builder.get_object("t_groups").get_text().split(",")
+ self._user.groups = []
+ for group in groups:
+ group = group.strip()
+ if group not in self._groupDict:
+ self._groupDict[group] = self.data.GroupData(name = group)
+ self._user.groups.append(group)
+
+ #Cancel clicked, window destroyed...
+ else:
+ pass
+
+ return rc
+
+
+
+class UserSpoke(FirstbootSpokeMixIn, NormalSpoke):
+ builderObjects = ["userCreationWindow"]
+
+ mainWidgetName = "userCreationWindow"
+ uiFile = "spokes/user.glade"
+
+ category = UserSettingsCategory
+
+ icon = "avatar-default-symbolic"
+ title = N_("_USER CREATION")
+
+ def __init__(self, *args):
+ NormalSpoke.__init__(self, *args)
+ self._oldweak = None
+ self._error = False
+
+ def initialize(self):
+ NormalSpoke.initialize(self)
+
+ if self.data.user.userList:
+ self._user = self.data.user.userList[0]
+ else:
+ self._user = self.data.UserData()
+ self._wheel = self.data.GroupData(name = "wheel")
+ self._groupDict = {"wheel": self._wheel}
+
+ # placeholders for the text boxes
+ self.fullname = self.builder.get_object("t_fullname")
+ self.username = self.builder.get_object("t_username")
+ self.pw = self.builder.get_object("t_password")
+ self.confirm = self.builder.get_object("t_verifypassword")
+ self.admin = self.builder.get_object("c_admin")
+ self.usepassword = self.builder.get_object("c_usepassword")
+
+ self.guesser = {
+ self.username: True
+ }
+
+ # set up passphrase quality checker
+ self._pwq = pwquality.PWQSettings()
+ self._pwq.read_config()
+
+ self.pw_bar = self.builder.get_object("password_bar")
+ self.pw_label = self.builder.get_object("password_label")
+
+ self._advanced = AdvancedUserDialog(self._user, self._groupDict,
+ self.data)
+ self._advanced.initialize()
+
+ def refresh(self):
+ self.username.set_text(self._user.name)
+ self.fullname.set_text(self._user.gecos)
+ self.admin.set_active(self._wheel.name in self._user.groups)
+
+ if self.usepassword.get_active():
+ self._checkPassword()
+
+ if self.username.get_text() and self.usepassword.get_active():
+ self.pw.grab_focus()
+ elif self.fullname.get_text():
+ self.username.grab_focus()
+ else:
+ self.fullname.grab_focus()
+
+ @property
+ def status(self):
+ if self._error:
+ return _("Error creating user account: %s") % self._error
+ elif len(self.data.user.userList) == 0:
+ return _("No user will be created")
+ elif self._wheel.name in self.data.user.userList[0].groups:
+ return _("Administrator %s will be created") % self.data.user.userList[0].name
+ else:
+ return _("User %s will be created") % self.data.user.userList[0].name
+
+ @property
+ def mandatory(self):
+ # mandatory only if root account is disabled
+ return (not self.data.rootpw.password) or self.data.rootpw.lock
+
+ def apply(self):
+ if self.username.get_text():
+ self._user.name = self.username.get_text()
+ self._user.gecos = self.fullname.get_text()
+ self._user.password = cryptPassword(self.pw.get_text())
+ self._user.isCrypted = True
+
+ if self.admin.get_active() and \
+ self._wheel.name not in self._user.groups:
+ self._user.groups.append(self._wheel.name)
+ elif not self.admin.get_active() and \
+ self._wheel.name in self._user.groups:
+ self._user.groups.remove(self._wheel.name)
+
+ self.data.group.groupList += [self._groupDict[g] for g in self._user.groups
+ if g != self._wheel.name]
+
+ if self._user not in self.data.user.userList:
+ self.data.user.userList.append(self._user)
+
+ elif self._user in self.data.user.userList:
+ self.data.user.userList.remove(self._user)
+
+ @property
+ def completed(self):
+ return self._user in self.data.user.userList
+
+ def _passwordDisabler(self, editable = None, data = None):
+ """Called by Gtk callback when the "Use password" check
+ button is toggled. It will make password entries in/sensitive."""
+
+ self.pw.set_sensitive(self.usepassword.get_active())
+ self.confirm.set_sensitive(self.usepassword.get_active())
+ if not self.usepassword.get_active():
+ self.clear_info()
+ else:
+ self._checkPassword()
+
+ def _guessNameDisabler(self, editable = None, data = None):
+ """Called by Gtk callback when the username or hostname
+ entry changes. It disables the guess algorithm if the
+ user added his own text there and reenable it when the
+ user deletes the whole text."""
+
+ if editable.get_text() == "":
+ self.guesser[editable] = True
+ self._guessNames()
+ else:
+ self.guesser[editable] = False
+
+ def _guessNames(self, editable = None, data = None):
+ """Called by Gtk callback when the full name field changes.
+ It guesses the username and hostname, strips diacritics
+ and make those lowercase.
+ """
+
+ fullname = self.fullname.get_text().split()
+ username = fullname[-1].decode("utf-8").lower()
+ if len(fullname) > 1:
+ username = fullname[0][0].decode("utf-8").lower() + username
+ username = strip_accents(username).encode("utf-8")
+
+ # after the text is updated in guesser, the guess has to be reenabled
+ if self.guesser[self.username]:
+ self.username.set_text(username)
+ self.guesser[self.username] = True
+
+ def _checkPassword(self, editable = None, data = None):
+ """This method updates the password indicators according
+ to the passwords entered by the user. It is called by
+ the changed Gtk event handler.
+ """
+ if self.pw.get_text() == "":
+ strength = -2
+ elif self.pw.get_text() != self.confirm.get_text():
+ strength = -1
+ else:
+ try:
+ strength = self._pwq.check(self.pw.get_text(), None, None)
+ _pwq_error = None
+ except pwquality.PWQError as (e, msg):
+ _pwq_error = msg
+ strength = 0
+
+ if strength == -1:
+ val = 0
+ text = _("Mismatch!")
+ self._error = _("The passwords do not match!")
+ elif strength == -2:
+ val = 0
+ text = _("Empty!")
+ self._error = _("The password is empty!")
+ elif strength < 50:
+ val = 1
+ text = _("Weak")
+ self._error = _("The password you have provided is weak")
+ if _pwq_error:
+ self._error += ": %s. " % _pwq_error
+ else:
+ self._error += ". "
+ self._error += _("You will have to press Done twice to confirm it.")
+ elif strength < 75:
+ val = 2
+ text = _("Fair")
+ self._error = False
+ elif strength < 90:
+ val = 3
+ text = _("Good")
+ self._error = False
+ else:
+ val = 4
+ text = _("Strong")
+ self._error = False
+
+ self.pw_bar.set_value(val)
+ self.pw_label.set_text(text)
+
+ self.clear_info()
+ if self._error:
+ self.set_warning(self._error)
+ self.window.show_all()
+
+ def _validatePassword(self):
+ """This method checks the password weakness and
+ implements the Press Done twice logic. It is used from
+ the on_back_clicked handler.
+
+ It also sets the self._error of the password is not
+ sufficient or does not pass the pwquality checks.
+
+ :return: True if the password should be accepted, False otherwise
+ :rtype: bool
+
+ """
+
+ # Do various steps to validate the password
+ # sets self._error to an error string
+ # Return True if valid, False otherwise
+ self._error = False
+ pw = self.pw.get_text()
+ confirm = self.confirm.get_text()
+
+ if not pw and not confirm:
+ self._error = _("You must provide and confirm a password.")
+ return False
+
+ try:
+ self._error = validatePassword(pw, confirm)
+ except PWQError as (_e, msg):
+ if pw == self._oldweak:
+ # We got a second attempt with the same weak password
+ pass
+ else:
+ self._error = _("You have provided a weak password: %s. "
+ " Press Done again to use anyway.") % msg
+ self._oldweak = pw
+ return False
+
+ if self._error:
+ return False
+
+ # if no errors, clear the info for next time we go into the spoke
+ self._password = pw
+ self.clear_info()
+ self._error = False
+ return True
+
+ def on_advanced_clicked(self, _button):
+ """Handler for the Advanced.. button. It starts the Advanced dialog
+ for setting homedit, uid, gid and groups.
+ """
+
+ self._user.name = self.username.get_text()
+
+ if self.admin.get_active() and \
+ self._wheel.name not in self._user.groups:
+ self._user.groups.append(self._wheel.name)
+ elif not self.admin.get_active() and \
+ self._wheel.name in self._user.groups:
+ self._user.groups.remove(self._wheel.name)
+
+ self._advanced.refresh()
+ with enlightbox(self.window, self._advanced.window):
+ response = self._advanced.run()
+
+ self.admin.set_active(self._wheel.name in self._user.groups)
+
+ def on_back_clicked(self, button):
+ if not self.usepassword.get_active() or self._validatePassword():
+ self._error = False
+ self.clear_info()
+ NormalSpoke.on_back_clicked(self, button)
+ else:
+ self.clear_info()
+ self.set_warning(self._error)
+ self.pw.grab_focus()
+ self.window.show_all()
+