[PATCH 1/3] Add the User creation spoke including the Advanced dialog

Martin Sivak msivak at redhat.com
Mon Feb 11 13:24:06 UTC 2013


---
 pyanaconda/ui/gui/spokes/advanced_user.glade | 429 +++++++++++++++++++++++++++
 pyanaconda/ui/gui/spokes/password.py         |   2 +-
 pyanaconda/ui/gui/spokes/user.glade          | 363 +++++++++++++++++++++++
 pyanaconda/ui/gui/spokes/user.py             | 421 ++++++++++++++++++++++++++
 4 files changed, 1214 insertions(+), 1 deletion(-)
 create mode 100644 pyanaconda/ui/gui/spokes/advanced_user.glade
 create mode 100644 pyanaconda/ui/gui/spokes/user.glade
 create mode 100644 pyanaconda/ui/gui/spokes/user.py

diff --git a/pyanaconda/ui/gui/spokes/advanced_user.glade b/pyanaconda/ui/gui/spokes/advanced_user.glade
new file mode 100644
index 0000000..ee71c86
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/advanced_user.glade
@@ -0,0 +1,429 @@
+<?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>
+    <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="xalign">0</property>
+                    <property name="draw_indicator">True</property>
+                    <signal name="toggled" handler="_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>
+                      </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="xalign">0</property>
+                    <property name="draw_indicator">True</property>
+                    <signal name="toggled" handler="_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="xalign">0</property>
+                    <property name="draw_indicator">True</property>
+                    <signal name="toggled" handler="_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>
+                  </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 6950472..441050e 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 0000000..2a29231
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/user.glade
@@ -0,0 +1,363 @@
+<?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>
+                        <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>
+                        <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>
+                      </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>
+                      </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>
+                        <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>
+                        <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>
+                      </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>
+                      </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>
+                      </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>
+                            <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 0000000..6655be6
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/user.py
@@ -0,0 +1,421 @@
+# User creation spoke
+#
+# Copyright (C) 2012 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 at 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
+import string
+
+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):
+        pass
+
+    def _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 = None
+
+    def initialize(self):
+        NormalSpoke.initialize(self)
+        self._user = self.data.UserData()
+        self._wheel = self.data.GroupData(name = "wheel")
+        self._groupDict = {"wheel": self._wheel}
+
+        # place holders 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
+            }
+
+        self.pw.connect("changed", self._checkPassword)
+        self.confirm.connect("changed", self._checkPassword)
+        self.fullname.connect("changed", self._guessNames)
+        self.username.connect("changed", self._guessNameDisabler)
+        self.usepassword.connect("toggled", self._passwordDisabler)
+
+        # 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):
+        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.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()
+
+        if response == 1:
+            pass
+
+        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()
+
-- 
1.7.11.7



More information about the anaconda-patches mailing list