diff options
author | Martin Sivak <msivak@redhat.com> | 2013-02-01 16:54:51 +0100 |
---|---|---|
committer | Martin Sivak <msivak@redhat.com> | 2013-03-01 11:36:47 +0100 |
commit | 83dca356f03677782d9508477e92041428a6813c (patch) | |
tree | 0e24599a240dafbb02de06815b1ed77bb3a30c54 /pyanaconda | |
parent | cf030941ccb6ff7c8d6788d512227c00a261b6e9 (diff) | |
download | anaconda-83dca356f03677782d9508477e92041428a6813c.tar.gz anaconda-83dca356f03677782d9508477e92041428a6813c.tar.xz anaconda-83dca356f03677782d9508477e92041428a6813c.zip |
Add the User creation spoke including the Advanced dialog
Diffstat (limited to 'pyanaconda')
-rw-r--r-- | pyanaconda/ui/gui/spokes/advanced_user.glade | 437 | ||||
-rw-r--r-- | pyanaconda/ui/gui/spokes/password.py | 2 | ||||
-rw-r--r-- | pyanaconda/ui/gui/spokes/user.glade | 377 | ||||
-rw-r--r-- | pyanaconda/ui/gui/spokes/user.py | 420 |
4 files changed, 1235 insertions, 1 deletions
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"><b>Tip:</b> 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() + |