diff --git a/Module.php b/Module.php index 1122c17..c5a1652 100644 --- a/Module.php +++ b/Module.php @@ -9,14 +9,10 @@ namespace ZfcUserAdmin; -use Zend\Mvc\ModuleRouteListener; -use Zend\ServiceManager\ServiceLocatorInterface; -use ZfcUser\Form\RegisterFilter; -use ZfcUser\Mapper\UserHydrator; -use ZfcUser\Validator\NoRecordExists; +use Zend\ModuleManager\Feature\ServiceProviderInterface; -class Module +class Module implements ServiceProviderInterface { public function getConfig() { @@ -36,69 +32,6 @@ public function getAutoloaderConfig() public function getServiceConfig() { - return array( - 'invokables' => array( - 'ZfcUserAdmin\Form\EditUser' => 'ZfcUserAdmin\Form\EditUser', - 'zfcuseradmin_user_service' => 'ZfcUserAdmin\Service\User', - ), - 'factories' => array( - 'zfcuseradmin_module_options' => function (ServiceLocatorInterface $sm) { - $config = $sm->get('Config'); - return new Options\ModuleOptions(isset($config['zfcuseradmin']) ? $config['zfcuseradmin'] : array()); - }, - 'zfcuseradmin_edituser_form' => function (ServiceLocatorInterface $sm) { - /** @var $options \ZfcUserAdmin\Options\ModuleOptions */ - $options = $sm->get('zfcuseradmin_module_options'); - $form = new Form\EditUser(null, $options, $sm); - return $form; - }, - 'zfcuseradmin_createuser_form' => function (ServiceLocatorInterface $sm) { - /** @var $zfcUserOptions \ZfcUser\Options\UserServiceOptionsInterface */ - $zfcUserOptions = $sm->get('zfcuser_module_options'); - /** @var $zfcUserAdminOptions \ZfcUserAdmin\Options\ModuleOptions */ - $zfcUserAdminOptions = $sm->get('zfcuseradmin_module_options'); - $form = new Form\CreateUser(null, $zfcUserAdminOptions, $zfcUserOptions, $sm); - $filter = new RegisterFilter( - new NoRecordExists(array( - 'mapper' => $sm->get('zfcuser_user_mapper'), - 'key' => 'email' - )), - new NoRecordExists(array( - 'mapper' => $sm->get('zfcuser_user_mapper'), - 'key' => 'username' - )), - $zfcUserOptions - ); - if ($zfcUserAdminOptions->getCreateUserAutoPassword()) { - $filter->remove('password')->remove('passwordVerify'); - } - $form->setInputFilter($filter); - return $form; - }, - 'zfcuser_user_mapper' => function (ServiceLocatorInterface $sm) { - /** @var $config \ZfcUserAdmin\Options\ModuleOptions */ - $config = $sm->get('zfcuseradmin_module_options'); - $mapperClass = $config->getUserMapper(); - if (stripos($mapperClass, 'doctrine') !== false) { - $mapper = new $mapperClass( - $sm->get('zfcuser_doctrine_em'), - $sm->get('zfcuser_module_options') - ); - } else { - /** @var $zfcUserOptions \ZfcUser\Options\UserServiceOptionsInterface */ - $zfcUserOptions = $sm->get('zfcuser_module_options'); - - /** @var $mapper \ZfcUserAdmin\Mapper\UserZendDb */ - $mapper = new $mapperClass(); - $mapper->setDbAdapter($sm->get('zfcuser_zend_db_adapter')); - $entityClass = $zfcUserOptions->getUserEntityClass(); - $mapper->setEntityPrototype(new $entityClass); - $mapper->setHydrator(new UserHydrator()); - } - - return $mapper; - }, - ), - ); + return include __DIR__ . '/config/services.config.php'; } } diff --git a/README.md b/README.md index db3c891..0ab41a6 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,19 @@ # ZfcUserAdmin Module for Zend Framework 2 -Version 0.0.1 Created by [Daniel Strøm](https://github.com/Danielss89) and [Martin Shwalbe](https://github.com/Hounddog) +Version 0.1 ## Introduction -This module provides an interface to create/edit new users. +This module provides an interface to create/edit/delete users. ## Installation -### Main Setup +### Using composer + +1. Add `danielss89/zfc-user-admin` (version `dev-master`) to requirements +2. Run `update` command on composer + +### Manually 1. Clone this project into your `./vendor/` directory and enable it in your `application.config.php` file. @@ -21,4 +26,39 @@ This module provides an interface to create/edit new users. 2. ZfcUser ## Usage -soon to come \ No newline at end of file + +### Override default module config + +Copy `/config/ZfcUserAdmin.global.php.dist` to `/autoload/ZfcUserAdmin.global.php` and +edit required module options (full list will be added to doc later, for now you can find all available options in +`/src/Options/ModuleOptions.php` - just look at class properties and convert upper case to +dash plus lower case, e.g. createUserAutoPassword -> create_user_auto_password). E.g.: + +``` + 'ZfcUserAdmin\Mapper\UserZendDb', + 'user_list_elements' => array('Id' => 'id', 'Name' => 'display_name', 'Email address' => 'email'), + 'create_user_auto_password' => false, + ... +); + +/** + * You do not need to edit below this line + */ +return array( + 'zfcuseradmin' => $settings +); + +``` + +TODO: add more usage information and module options list + +## Authors + +* [Daniel Strøm](https://github.com/Danielss89) +* [Martin Shwalbe](https://github.com/Hounddog) +* [Vladimir Garvardt](https://github.com/vgarvardt) \ No newline at end of file diff --git a/config/services.config.php b/config/services.config.php new file mode 100644 index 0000000..a294948 --- /dev/null +++ b/config/services.config.php @@ -0,0 +1,98 @@ + array( + 'ZfcUserAdmin\Form\EditUser' => 'ZfcUserAdmin\Form\EditUser', + 'zfcuseradmin_user_service' => 'ZfcUserAdmin\Service\User', + ), + 'factories' => array( + 'zfcuseradmin_module_options' => function (ServiceLocatorInterface $sm) { + $config = $sm->get('Config'); + return new Options\ModuleOptions(isset($config['zfcuseradmin']) ? $config['zfcuseradmin'] : array()); + }, + 'zfcuseradmin_edituser_form' => function (ServiceLocatorInterface $sm) { + /** @var $zfcUserOptions \ZfcUser\Options\UserServiceOptionsInterface */ + $zfcUserOptions = $sm->get('zfcuser_module_options'); + /** @var $zfcUserAdminOptions \ZfcUserAdmin\Options\ModuleOptions */ + $zfcUserAdminOptions = $sm->get('zfcuseradmin_module_options'); + $form = new Form\EditUser(null, $zfcUserAdminOptions, $zfcUserOptions, $sm); + $filter = new RegisterFilter( + new NoRecordExistsEdit(array( + 'mapper' => $sm->get('zfcuser_user_mapper'), + 'key' => 'email' + )), + new NoRecordExistsEdit(array( + 'mapper' => $sm->get('zfcuser_user_mapper'), + 'key' => 'username' + )), + $zfcUserOptions + ); + if (!$zfcUserAdminOptions->getAllowPasswordChange()) { + $filter->remove('password')->remove('passwordVerify'); + } else { + $filter->get('password')->setRequired(false); + $filter->remove('passwordVerify'); + } + $form->setInputFilter($filter); + return $form; + }, + 'zfcuseradmin_createuser_form' => function (ServiceLocatorInterface $sm) { + /** @var $zfcUserOptions \ZfcUser\Options\UserServiceOptionsInterface */ + $zfcUserOptions = $sm->get('zfcuser_module_options'); + /** @var $zfcUserAdminOptions \ZfcUserAdmin\Options\ModuleOptions */ + $zfcUserAdminOptions = $sm->get('zfcuseradmin_module_options'); + $form = new Form\CreateUser(null, $zfcUserAdminOptions, $zfcUserOptions, $sm); + $filter = new RegisterFilter( + new NoRecordExists(array( + 'mapper' => $sm->get('zfcuser_user_mapper'), + 'key' => 'email' + )), + new NoRecordExists(array( + 'mapper' => $sm->get('zfcuser_user_mapper'), + 'key' => 'username' + )), + $zfcUserOptions + ); + if ($zfcUserAdminOptions->getCreateUserAutoPassword()) { + $filter->remove('password')->remove('passwordVerify'); + } + $form->setInputFilter($filter); + return $form; + }, + 'zfcuser_user_mapper' => function (ServiceLocatorInterface $sm) { + /** @var $config \ZfcUserAdmin\Options\ModuleOptions */ + $config = $sm->get('zfcuseradmin_module_options'); + $mapperClass = $config->getUserMapper(); + if (stripos($mapperClass, 'doctrine') !== false) { + $mapper = new $mapperClass( + $sm->get('zfcuser_doctrine_em'), + $sm->get('zfcuser_module_options') + ); + } else { + /** @var $zfcUserOptions \ZfcUser\Options\UserServiceOptionsInterface */ + $zfcUserOptions = $sm->get('zfcuser_module_options'); + + /** @var $mapper \ZfcUserAdmin\Mapper\UserZendDb */ + $mapper = new $mapperClass(); + $mapper->setDbAdapter($sm->get('zfcuser_zend_db_adapter')); + $entityClass = $zfcUserOptions->getUserEntityClass(); + $mapper->setEntityPrototype(new $entityClass); + $mapper->setHydrator(new UserHydrator()); + } + + return $mapper; + }, + ), +); \ No newline at end of file diff --git a/src/ZfcUserAdmin/Controller/UserAdminController.php b/src/ZfcUserAdmin/Controller/UserAdminController.php index d49df55..4671f0b 100644 --- a/src/ZfcUserAdmin/Controller/UserAdminController.php +++ b/src/ZfcUserAdmin/Controller/UserAdminController.php @@ -4,12 +4,15 @@ use Zend\Mvc\Controller\AbstractActionController; use Zend\Paginator; +use Zend\Stdlib\Hydrator\ClassMethods; use ZfcUser\Mapper\UserInterface; +use ZfcUser\Options\ModuleOptions as ZfcUserModuleOptions; use ZfcUserAdmin\Options\ModuleOptions; class UserAdminController extends AbstractActionController { protected $options, $userMapper; + protected $zfcUserOptions; /** * @var \ZfcUserAdmin\Service\User */ @@ -35,46 +38,61 @@ public function listAction() public function createAction() { + /** @var $form \ZfcUserAdmin\Form\CreateUser */ $form = $this->getServiceLocator()->get('zfcuseradmin_createuser_form'); $request = $this->getRequest(); - $user = false; /** @var $request \Zend\Http\Request */ if ($request->isPost()) { - $user = $this->getAdminUserService()->create((array)$request->getPost()); - } - - if (!$user) { - return array( - 'createUserForm' => $form - ); + $zfcUserOptions = $this->getZfcUserOptions(); + $class = $zfcUserOptions->getUserEntityClass(); + $user = new $class(); + $form->setHydrator(new ClassMethods()); + $form->bind($user); + $form->setData($request->getPost()); + + if ($form->isValid()) { + $user = $this->getAdminUserService()->create($form, (array)$request->getPost()); + if ($user) { + $this->flashMessenger()->addSuccessMessage('The user was created'); + return $this->redirect()->toRoute('zfcadmin/zfcuseradmin/list'); + } + } } - $this->flashMessenger()->addSuccessMessage('The user was created'); - return $this->redirect()->toRoute('zfcadmin/zfcuseradmin/list'); + return array( + 'createUserForm' => $form + ); } public function editAction() { $userId = $this->getEvent()->getRouteMatch()->getParam('userId'); $user = $this->getUserMapper()->findById($userId); + + /** @var $form \ZfcUserAdmin\Form\EditUser */ $form = $this->getServiceLocator()->get('zfcuseradmin_edituser_form'); $form->setUser($user); /** @var $request \Zend\Http\Request */ $request = $this->getRequest(); - if (!$request->isPost()) { + if ($request->isPost()) { + $form->setData($request->getPost()); + if ($form->isValid()) { + $user = $this->getAdminUserService()->edit($form, (array)$request->getPost(), $user); + if ($user) { + $this->flashMessenger()->addSuccessMessage('The user was edited'); + return $this->redirect()->toRoute('zfcadmin/zfcuseradmin/list'); + } + } + } else { $form->populateFromUser($user); - return array( - 'editUserForm' => $form, - 'userId' => $userId - ); } - $this->getAdminUserService()->edit(get_object_vars($request->getPost()), $user); - - $this->flashMessenger()->addSuccessMessage('The user was edited'); - return $this->redirect()->toRoute('zfcadmin/zfcuseradmin/list'); + return array( + 'editUserForm' => $form, + 'userId' => $userId + ); } public function removeAction() @@ -137,4 +155,21 @@ public function setAdminUserService($service) $this->adminUserService = $service; return $this; } + + public function setZfcUserOptions(ZfcUserModuleOptions $options) + { + $this->zfcUserOptions = $options; + return $this; + } + + /** + * @return \ZfcUser\Options\ModuleOptions + */ + public function getZfcUserOptions() + { + if (!$this->zfcUserOptions instanceof ZfcUserModuleOptions) { + $this->setZfcUserOptions($this->getServiceLocator()->get('zfcuser_module_options')); + } + return $this->zfcUserOptions; + } } diff --git a/src/ZfcUserAdmin/Form/EditUser.php b/src/ZfcUserAdmin/Form/EditUser.php index 30865dd..17dafaf 100644 --- a/src/ZfcUserAdmin/Form/EditUser.php +++ b/src/ZfcUserAdmin/Form/EditUser.php @@ -2,26 +2,52 @@ namespace ZfcUserAdmin\Form; +use ZfcUser\Entity\UserInterface; +use ZfcUser\Form\Register; +use ZfcUser\Options\RegistrationOptionsInterface; use ZfcUserAdmin\Options\UserEditOptionsInterface; use Zend\Form\Form; use Zend\Form\Element; -use ZfcBase\Form\ProvidesEventsForm; -class EditUser extends ProvidesEventsForm +class EditUser extends Register { + /** + * @var \ZfcUserAdmin\Options\UserEditOptionsInterface + */ protected $userEditOptions; protected $userEntity; protected $serviceManager; - public function __construct($name = null, UserEditOptionsInterface $options, $serviceManager) + public function __construct($name = null, UserEditOptionsInterface $options, RegistrationOptionsInterface $registerOptions, $serviceManager) { $this->setUserEditOptions($options); - parent::__construct($name); - $this->setServiceManager($serviceManager); + parent::__construct($name, $registerOptions); + + $this->remove('captcha'); + + if ($this->userEditOptions->getAllowPasswordChange()) { + $this->add(array( + 'name' => 'reset_password', + 'type' => 'Zend\Form\Element\Checkbox', + 'options' => array( + 'label' => 'Reset password to random', + ), + )); + + $password = $this->get('password'); + $password->setAttribute('required', false); + $password->setOptions(array('label' => 'Password (only if want to change)')); + + $this->remove('passwordVerify'); + } else { + $this->remove('password')->remove('passwordVerify'); + } + + foreach ($this->getUserEditOptions()->getEditFormElements() as $name => $element) { + // avoid adding fields twice (e.g. email) + if ($this->get($element)) continue; - foreach($this->getUserEditOptions()->getEditFormElements() as $name => $element) - { $this->add(array( 'name' => $element, 'options' => array( @@ -33,16 +59,7 @@ public function __construct($name = null, UserEditOptionsInterface $options, $se )); } - $submitElement = new Element\Button('submit'); - $submitElement - ->setLabel('Edit') - ->setAttributes(array( - 'type' => 'submit', - )); - - $this->add($submitElement, array( - 'priority' => -100, - )); + $this->get('submit')->setLabel('Edit')->setValue('Edit'); $this->add(array( 'name' => 'userId', @@ -65,16 +82,33 @@ public function getUser() return $this->userEntity; } - public function populateFromUser($user) + public function populateFromUser(UserInterface $user) { - foreach($this->getUserEditOptions()->getEditFormElements() as $element) - { - $func = 'get' . ucfirst($element); - $this->get($element)->setValue($user->$func()); + foreach ($this->getElements() as $element) { + /** @var $element \Zend\Form\Element */ + $elementName = $element->getName(); + if (strpos($elementName, 'password') === 0) continue; + + $getter = $this->getAccessorName($elementName, false); + if (method_exists($user, $getter)) $element->setValue(call_user_func(array($user, $getter))); + } + + foreach ($this->getUserEditOptions()->getEditFormElements() as $element) { + $getter = $this->getAccessorName($element, false); + $this->get($element)->setValue(call_user_func(array($user, $getter))); } $this->get('userId')->setValue($user->getId()); } + protected function getAccessorName($property, $set = true) + { + $parts = explode('_', $property); + array_walk($parts, function (&$val) { + $val = ucfirst($val); + }); + return (($set ? 'set' : 'get') . implode('', $parts)); + } + public function setUserEditOptions(UserEditOptionsInterface $userEditOptions) { $this->userEditOptions = $userEditOptions; diff --git a/src/ZfcUserAdmin/Options/ModuleOptions.php b/src/ZfcUserAdmin/Options/ModuleOptions.php index 76e3725..77d382b 100644 --- a/src/ZfcUserAdmin/Options/ModuleOptions.php +++ b/src/ZfcUserAdmin/Options/ModuleOptions.php @@ -25,14 +25,14 @@ class ModuleOptions extends AbstractOptions implements * Key = form label * Value = entity property(expecting a 'getProperty()/setProperty()' function) */ - protected $editFormElements = array('Email' => 'email'); + protected $editFormElements = array(); /** * Array of form elements to show when creating a user * Key = form label * Value = entity property(expecting a 'getProperty()/setProperty()' function) */ - protected $createFormElements = array('Email' => 'email'); + protected $createFormElements = array(); /** * @var bool @@ -41,6 +41,12 @@ class ModuleOptions extends AbstractOptions implements */ protected $createUserAutoPassword = true; + /** + * @var bool + * Allow change user password on user edit form. + */ + protected $allowPasswordChange = true; + protected $userMapper = 'ZfcUserAdmin\Mapper\UserDoctrine'; public function setUserMapper($userMapper) @@ -92,4 +98,14 @@ public function getCreateUserAutoPassword() { return $this->createUserAutoPassword; } + + public function getAllowPasswordChange() + { + return $this->allowPasswordChange; + } + + public function setAdminPasswordChange($allowPasswordChange) + { + $this->allowPasswordChange = $allowPasswordChange; + } } diff --git a/src/ZfcUserAdmin/Options/UserEditOptionsInterface.php b/src/ZfcUserAdmin/Options/UserEditOptionsInterface.php index e07d901..6fe9ca0 100644 --- a/src/ZfcUserAdmin/Options/UserEditOptionsInterface.php +++ b/src/ZfcUserAdmin/Options/UserEditOptionsInterface.php @@ -7,4 +7,8 @@ interface UserEditOptionsInterface public function getEditFormElements(); public function setEditFormElements(array $elements); + + public function getAllowPasswordChange(); + + public function setAdminPasswordChange($allowPasswordChange); } diff --git a/src/ZfcUserAdmin/Service/User.php b/src/ZfcUserAdmin/Service/User.php index 56dd83d..2ca70c8 100644 --- a/src/ZfcUserAdmin/Service/User.php +++ b/src/ZfcUserAdmin/Service/User.php @@ -39,20 +39,9 @@ class User extends EventProvider implements ServiceManagerAwareInterface protected $zfcUserOptions; - public function create(array $data) + public function create(Form $form, array $data) { $zfcUserOptions = $this->getZfcUserOptions(); - $class = $zfcUserOptions->getUserEntityClass(); - /** @var $user UserInterface */ - $user = new $class(); - $form = $this->getServiceManager()->get('zfcuseradmin_createuser_form'); - $form->setHydrator(new ClassMethods()); - $form->bind($user); - $form->setData($data); - if (!$form->isValid()) { - return false; - } - $user = $form->getData(); $argv = array(); @@ -65,20 +54,8 @@ public function create(array $data) $bcrypt->setCost($zfcUserOptions->getPasswordCost()); $user->setPassword($bcrypt->create($argv['password'])); - if ($zfcUserOptions->getEnableUsername()) { - $user->setUsername($data['username']); - } - if ($zfcUserOptions->getEnableDisplayName()) { - $user->setDisplayName($data['display_name']); - } - foreach ($this->getOptions()->getCreateFormElements() as $element) { - $parts = explode('_', $element); - array_walk($parts, function (&$val) { - $val = ucfirst($val); - }); - $setter = 'set' . implode('', $parts); - call_user_func(array($user, $setter), $data[$element]); + call_user_func(array($user, $this->getAccessorName($element)), $data[$element]); } $argv += array('user' => $user, 'form' => $form, 'data' => $data); @@ -88,26 +65,52 @@ public function create(array $data) return $user; } - public function edit(array $data, UserInterface $user) + public function edit(Form $form, array $data, UserInterface $user) { - foreach ($this->getOptions()->getEditFormElements() as $element) { - if ($element === 'password') { - if ($data['password'] !== $user->getPassword()) { - // Password does not match, so password was changed - $bcrypt = new Bcrypt(); - $bcrypt->setCost($this->getZfcUserOptions()->getPasswordCost()); - $user->setPassword($bcrypt->create($data['password'])); - } - } else { - $func = 'set' . ucfirst($element); - $user->$func($data[$element]); + // first, process all form fields + foreach ($data as $key => $value) { + if ($key == 'password') continue; + + $setter = $this->getAccessorName($key); + if (method_exists($user, $setter)) call_user_func(array($user, $setter), $value); + } + + $argv = array(); + // then check if admin wants to change user password + if ($this->getOptions()->getAllowPasswordChange()) { + if (!empty($data['reset_password'])) { + $argv['password'] = Rand::getString(8); + } elseif (!empty($data['password'])) { + $argv['password'] = $data['password']; + } + + if (!empty($argv['password'])) { + $bcrypt = new Bcrypt(); + $bcrypt->setCost($this->getZfcUserOptions()->getPasswordCost()); + $user->setPassword($bcrypt->create($argv['password'])); } } + + // TODO: not sure if this code is required here - all fields that came from the form already saved + foreach ($this->getOptions()->getEditFormElements() as $element) { + call_user_func(array($user, $this->getAccessorName($element)), $data[$element]); + } + + $argv += array('user' => $user, 'form' => $form, 'data' => $data); + $this->getEventManager()->trigger(__FUNCTION__, $this, $argv); $this->getUserMapper()->update($user); - $this->getEventManager()->trigger(__FUNCTION__, $this, array('user' => $user, 'data' => $data)); + $this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, $argv); return $user; } + protected function getAccessorName($property, $set = true) + { + $parts = explode('_', $property); + array_walk($parts, function (&$val) { + $val = ucfirst($val); + }); + return (($set ? 'set' : 'get') . implode('', $parts)); + } public function getUserMapper() { diff --git a/src/ZfcUserAdmin/Validator/NoRecordExistsEdit.php b/src/ZfcUserAdmin/Validator/NoRecordExistsEdit.php new file mode 100644 index 0000000..f69d779 --- /dev/null +++ b/src/ZfcUserAdmin/Validator/NoRecordExistsEdit.php @@ -0,0 +1,29 @@ +setValue($value); + + /** @var $result \ZfcUser\Entity\UserInterface|null */ + $result = $this->query($value); + if ($result && $result->getId() != $context['userId']) { + $valid = false; + $this->error(self::ERROR_RECORD_FOUND); + } + + return $valid; + } +} \ No newline at end of file diff --git a/view/zfc-user-admin/user-admin/_form.phtml b/view/zfc-user-admin/user-admin/_form.phtml index b82d465..6ab1ea3 100644 --- a/view/zfc-user-admin/user-admin/_form.phtml +++ b/view/zfc-user-admin/user-admin/_form.phtml @@ -1,13 +1,17 @@ form()->openTag($form) ?>
- getLabel() != null && !($element instanceof Zend\Form\Element\Button)): ?> + + getAttribute('type') == 'checkbox'; ?> + getLabel() != null && !$isButton): ?>
formLabel($element) ?>
- +
formButton($element) ?>
formSelect($element) . $this->formElementErrors($element) ?>
+ +
formCheckbox($element) ?>
formMultiCheckbox($element) . $this->formElementErrors($element) ?>
@@ -16,6 +20,6 @@
redirect): ?> - + form()->closeTag() ?> \ No newline at end of file