From 9cc43ea4fe22070143695dd02fd364c5c4a2287f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Mon, 6 Feb 2012 10:03:11 +0100 Subject: [PATCH] User edition Added forms that allow user display names to be set and passwords to be changed. All users can modify users at this time. --- database/users-functions.sql | 26 ++++ includes/t-basics/dao_users.inc.php | 67 +++++++--- includes/t-users/package.inc.php | 3 + includes/t-users/users.inc.php | 188 +++++++++++++++++++++++----- 4 files changed, 236 insertions(+), 48 deletions(-) diff --git a/database/users-functions.sql b/database/users-functions.sql index dd3b0db..8c2535c 100644 --- a/database/users-functions.sql +++ b/database/users-functions.sql @@ -25,6 +25,32 @@ REVOKE EXECUTE ON FUNCTION users_add( TEXT , TEXT , INT , TEXT , TEXT ) FROM PUB GRANT EXECUTE ON FUNCTION users_add( TEXT , TEXT , INT , TEXT , TEXT) TO :webapp_user; +-- +-- Update an user's address and display name +-- + +CREATE OR REPLACE FUNCTION users_edit( _id INT , _email TEXT , _name TEXT ) + RETURNS INT + LANGUAGE PLPGSQL + STRICT VOLATILE SECURITY INVOKER +AS $users_edit$ +BEGIN + IF _name = '' THEN + _name := NULL; + END IF; + + UPDATE users SET user_email = _email , user_display_name = _name + WHERE user_id = _id; + RETURN ( CASE WHEN FOUND THEN 0 ELSE 2 END ); +EXCEPTION + WHEN unique_violation THEN + RETURN 1; +END; +$users_edit$; + +REVOKE EXECUTE ON FUNCTION users_edit( INT , TEXT , TEXT ) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION users_edit( INT , TEXT , TEXT ) TO :webapp_user; + -- -- View that lists users and adds the string to use when displaying diff --git a/includes/t-basics/dao_users.inc.php b/includes/t-basics/dao_users.inc.php index 9af0791..e390af1 100644 --- a/includes/t-basics/dao_users.inc.php +++ b/includes/t-basics/dao_users.inc.php @@ -15,6 +15,25 @@ class Dao_Users return $hash; } + private function hashNewPassword( $password ) + { + $iterations = rand( 130 , 160 ); + + $randSource = array( ); + for ( $i = 0 ; $i < 26 ; $i ++ ) { + array_push( $randSource , chr( $i + ord( 'a' ) ) ); + array_push( $randSource , chr( $i + ord( 'A' ) ) ); + if ( $i < 10 ) { + array_push( $randSource , chr( $i + 48 ) ); + } + } + shuffle( $randSource ); + $salt = join( '' , array_splice( $randSource , 0 , 4 ) ); + + $hash = $this->hashPassword( $password , $salt , $iterations ); + return array( $iterations , $salt , $hash ); + } + public function getUsers( ) { @@ -25,9 +44,20 @@ class Dao_Users } + public function getUserById( $uid ) + { + $query = $this->query( 'SELECT * FROM users_view WHERE user_id = $1' ); + $results = $query->execute( $uid ); + if ( empty( $results ) ) { + return null; + } + return array_shift( $results ); + } + + public function getUser( $email ) { - $query = $this->query( 'SELECT * FROM users WHERE user_email = LOWER( $1 )' ); + $query = $this->query( 'SELECT * FROM users_view WHERE user_email = LOWER( $1 )' ); $results = $query->execute( $email ); if ( empty( $results ) ) { return null; @@ -53,21 +83,7 @@ class Dao_Users public function addUser( $email , $password , $name ) { - $iterations = rand( 130 , 160 ); - - $randSource = array( ); - for ( $i = 0 ; $i < 26 ; $i ++ ) { - array_push( $randSource , chr( $i + ord( 'a' ) ) ); - array_push( $randSource , chr( $i + ord( 'A' ) ) ); - if ( $i < 10 ) { - array_push( $randSource , chr( $i + 48 ) ); - } - } - shuffle( $randSource ); - $salt = join( '' , array_splice( $randSource , 0 , 4 ) ); - - $hash = $this->hashPassword( $password , $salt , $iterations ); - + list( $iterations , $salt , $hash ) = $this->hashNewPassword( $password ); $result = $this->query( 'SELECT users_add( $1 , $2 , $3 , $4 , $5 ) AS error' ) ->execute( $email , $salt , $iterations , $hash , $name ); return $result[ 0 ]->error; @@ -79,4 +95,23 @@ class Dao_Users $result = $this->query( 'SELECT COUNT(*) AS n_users FROM users' )->execute( ); return $result[0]->n_users > 0; } + + + public function modify( $id , $email , $name ) + { + $result = $this->query( 'SELECT users_edit( $1 , $2 , $3 ) AS error' + )->execute( $id , $email , $name ); + return $result[0]->error; + } + + + public function setPassword( $id , $password ) + { + list( $iterations , $salt , $hash ) = $this->hashNewPassword( $password ); + $this->query( + 'UPDATE users ' + . 'SET user_iterations = $1 , user_salt = $2 , user_hash = $3 ' + . 'WHERE user_id = $4' + )->execute( $iterations , $salt , $hash , $id ); + } } diff --git a/includes/t-users/package.inc.php b/includes/t-users/package.inc.php index 7f732f0..3aaf6af 100644 --- a/includes/t-users/package.inc.php +++ b/includes/t-users/package.inc.php @@ -9,5 +9,8 @@ $package[ 'pages' ][] = 'tasks_users'; $package[ 'ctrls' ][] = 'users_list'; $package[ 'ctrls' ][] = 'users_add'; $package[ 'ctrls' ][] = 'users_add_form'; +$package[ 'ctrls' ][] = 'users_edit'; +$package[ 'ctrls' ][] = 'users_edit_form'; +$package[ 'ctrls' ][] = 'users_set_password'; $package[ 'views' ][] = 'users_list'; diff --git a/includes/t-users/users.inc.php b/includes/t-users/users.inc.php index d2a9ed0..37ab930 100644 --- a/includes/t-users/users.inc.php +++ b/includes/t-users/users.inc.php @@ -9,6 +9,7 @@ class Page_TasksUsers parent::__construct( array( '' => 'users_list' , 'add' => 'users_add_form' , + 'edit' => 'users_edit_form' , ) ); $this->setTitle( 'Users' ); } @@ -31,6 +32,56 @@ class Ctrl_UsersList } + +class View_UsersList + extends BaseURLAwareView +{ + private $users; + + public function __construct( $users ) + { + $this->users = $users; + } + + public function render( ) + { + $table = HTML::make( 'table' ) + ->appendElement( HTML::make( 'tr' ) + ->setAttribute( 'class' , 'header' ) + ->appendElement( HTML::make( 'th' ) + ->appendText( 'E-mail address' ) ) + ->appendElement( HTML::make( 'th' ) + ->appendText( 'Display name' ) ) ); + + foreach ( $this->users as $user ) { + $table->appendElement( $this->makeUserRow( $user ) ); + } + + return $table; + } + + private function makeUserRow( $user ) + { + $row = HTML::make( 'tr' ) + ->appendElement( HTML::make( 'td' ) + ->appendElement( $editLink = HTML::make( 'a' ) ) ); + + $editLink->setAttribute( 'href' , $this->base . '/users/edit?id=' . $user->user_id ) + ->appendText( $user->user_email ); + + $nameColumn = HTML::make( 'td' ); + if ( $user->user_display_name !== null ) { + $nameColumn->appendText( $user->user_display_name ); + } else { + $nameColumn->appendElement( HTML::make( 'em' )->appendText( 'N/A' ) ); + } + $row->appendElement( $nameColumn ); + + return $row; + } +} + + class Ctrl_UsersAddForm extends Controller { @@ -126,48 +177,121 @@ class Ctrl_UsersAdd } - -class View_UsersList - extends BaseURLAwareView +class Ctrl_UsersEditForm + extends Controller { - private $users; - public function __construct( $users ) + public function handle( Page $page ) { - $this->users = $users; - } - - public function render( ) - { - $table = HTML::make( 'table' ) - ->appendElement( HTML::make( 'tr' ) - ->setAttribute( 'class' , 'header' ) - ->appendElement( HTML::make( 'th' ) - ->appendText( 'E-mail address' ) ) - ->appendElement( HTML::make( 'th' ) - ->appendText( 'Display name' ) ) ); - - foreach ( $this->users as $user ) { - $table->appendElement( $this->makeUserRow( $user ) ); + try { + $userId = (int) $this->getParameter( 'id' ); + } catch ( ParameterException $e ) { + return 'users'; + } + $user = Loader::DAO( 'users' )->getUserById( $userId ); + if ( $user === null ) { + return 'users'; } - return $table; + $page->setTitle( 'Modify user ' . $user->user_view_name ); + + $details = Loader::Create( 'Form' , 'Modify user' , 'user-edit' , 'Account details' ) + ->addField( Loader::Create( 'Field' , 'id' , 'hidden' ) + ->setDefaultValue( $user->user_id ) ) + ->addField( Loader::Create( 'Field' , 'email' , 'text' ) + ->setDescription( 'E-mail address:' ) + ->setValidator( Loader::Create( 'Validator_Email' , 'Invalid address.' ) ) + ->setDefaultValue( $user->user_email ) ) + ->addField( Loader::Create( 'Field' , 'display-name' , 'text' ) + ->setDescription( 'Display name:' ) + ->setMandatory( false ) + ->setValidator( Loader::Create( 'Validator_StringLength' , 'This display name', + 5 , 256 , true ) ) + ->setDefaultValue( $user->user_display_name ) ) + ->addController( Loader::Ctrl( 'users_edit' ) ) + ->setURL( 'users' ); + + $password = Loader::Create( 'Form' , 'Modify password' , 'user-set-password' , 'Account password' ) + ->addField( Loader::Create( 'Field' , 'id' , 'hidden' ) + ->setDefaultValue( $user->user_id ) ) + ->addField( Loader::Create( 'Field' , 'pass' , 'password' ) + ->setDescription( 'New password:' ) + ->setValidator( Loader::Create( 'Validator_StringLength' , 'This password' , 8 ) ) ) + ->addField( Loader::Create( 'Field' , 'pass2' , 'password' ) + ->setDescription( 'Confirm new password:' ) ) + ->addController( Loader::Ctrl( 'users_set_password' ) ) + ->setSuccessURL( 'users' ); + + return array( $details->controller( ) , $password->controller( ) ); } - private function makeUserRow( $user ) +} + + +class Ctrl_UsersEdit + extends Controller + implements FormAware +{ + private $form; + + + public function setForm( Form $form ) { - $row = HTML::make( 'tr' ) - ->appendElement( HTML::make( 'td' ) - ->appendText( $user->user_email ) ); + $this->form = $form; + } - $nameColumn = HTML::make( 'td' ); - if ( $user->user_display_name !== null ) { - $nameColumn->appendText( $user->user_display_name ); - } else { - $nameColumn->appendElement( HTML::make( 'em' )->appendText( 'N/A' ) ); + + public function handle( Page $page ) + { + $id = $this->form->field( 'id' )->value( ); + $name = $this->form->field( 'display-name' )->value( ); + $emailField = $this->form->field( 'email' ); + $email = $emailField->value( ); + + $error = Loader::DAO( 'users' )->modify( $id , $email , $name ); + switch ( $error ) { + + case 0: + return true; + + case 1: + $email->putError( 'Duplicate address.' ); + break; + + default: + $email->putError( 'An unknown error occurred (' . $error . ')' ); + break; } - $row->appendElement( $nameColumn ); - return $row; + return null; + } +} + + +class Ctrl_UsersSetPassword + extends Controller + implements FormAware +{ + private $form; + + + public function setForm( Form $form ) + { + $this->form = $form; + } + + + public function handle( Page $page ) + { + $p1 = $this->form->field( 'pass' ); + $p2 = $this->form->field( 'pass2' ); + if ( $p1->value( ) != $p2->value( ) ) { + $p1->putError( 'Passwords did not match.' ); + return null; + } + + $id = $this->form->field( 'id' )->value( ); + Loader::DAO( 'users' )->setPassword( $id , $p1->value( ) ); + return true; } }