├── src ├── Resources │ ├── views │ │ ├── Group │ │ │ ├── new.html.twig │ │ │ ├── edit.html.twig │ │ │ ├── list.html.twig │ │ │ └── show.html.twig │ │ ├── Profile │ │ │ ├── edit.html.twig │ │ │ └── show.html.twig │ │ ├── Resetting │ │ │ ├── email.txt.twig │ │ │ ├── request.html.twig │ │ │ ├── reset.html.twig │ │ │ ├── checkEmail.html.twig │ │ │ └── passwordAlreadyRequested.html.twig │ │ ├── Security │ │ │ └── login.html.twig │ │ ├── Registration │ │ │ ├── email.txt.twig │ │ │ ├── register.html.twig │ │ │ ├── checkEmail.html.twig │ │ │ └── confirmed.html.twig │ │ ├── ChangePassword │ │ │ └── changePassword.html.twig │ │ ├── UserBundle │ │ │ ├── Group │ │ │ │ ├── show_content.html.twig │ │ │ │ ├── edit.html.twig │ │ │ │ ├── list.html.twig │ │ │ │ ├── new.html.twig │ │ │ │ ├── show.html.twig │ │ │ │ ├── list_content.html.twig │ │ │ │ ├── new_content.html.twig │ │ │ │ └── edit_content.html.twig │ │ │ ├── Profile │ │ │ │ ├── edit.html.twig │ │ │ │ ├── show.html.twig │ │ │ │ ├── show_content.html.twig │ │ │ │ └── edit_content.html.twig │ │ │ ├── Resetting │ │ │ │ ├── reset.html.twig │ │ │ │ ├── request.html.twig │ │ │ │ ├── checkEmail.html.twig │ │ │ │ ├── passwordAlreadyRequested.html.twig │ │ │ │ ├── reset_content.html.twig │ │ │ │ ├── email.txt.twig │ │ │ │ └── request_content.html.twig │ │ │ ├── Registration │ │ │ │ ├── register.html.twig │ │ │ │ ├── checkEmail.html.twig │ │ │ │ ├── register_content.html.twig │ │ │ │ ├── email.txt.twig │ │ │ │ └── confirmed.html.twig │ │ │ ├── ChangePassword │ │ │ │ ├── changePassword.html.twig │ │ │ │ └── changePassword_content.html.twig │ │ │ └── Security │ │ │ │ └── login.html.twig │ │ └── layout.html.twig │ ├── skeleton │ │ └── bundle │ │ │ ├── routing.yml.twig │ │ │ ├── Events.php.twig │ │ │ ├── routing.xml.twig │ │ │ ├── model │ │ │ ├── user_custom.php.twig │ │ │ ├── user_orm.php.twig │ │ │ ├── group_custom.php.twig │ │ │ ├── group_orm.php.twig │ │ │ ├── user_mongodb.php.twig │ │ │ ├── group_mongodb.php.twig │ │ │ ├── user_couchdb.php.twig │ │ │ └── group_couchdb.php.twig │ │ │ ├── routing.php.twig │ │ │ ├── Bundle.php.twig │ │ │ ├── Configuration.php.twig │ │ │ └── Extension.php.twig │ └── config │ │ └── container │ │ ├── templating_twig.xml │ │ ├── templating_php.xml │ │ ├── services.xml │ │ ├── forms.xml │ │ └── listeners.xml ├── Exception │ ├── MissingTemplateException.php │ ├── NoActiveUserSystemException.php │ └── UnexpectedTypeException.php ├── Command │ ├── Validators.php │ ├── DeactivateUserCommand.php │ ├── ActivateUserCommand.php │ ├── ChangePasswordCommand.php │ ├── CreateUserCommand.php │ ├── DemoteUserCommand.php │ ├── PromoteUserCommand.php │ └── GenerateUserSysCommand.php ├── Model │ ├── NoopGroupManager.php │ ├── UserDiscriminatorInterface.php │ ├── DelegatingGroupManager.php │ ├── UserDiscriminator.php │ ├── DelegatingUserManager.php │ └── UserConfig.php ├── Mailer │ └── DelegatingMailer.php ├── Doctrine │ ├── CouchDB │ │ └── UserListener.php │ ├── Orm │ │ └── UserListener.php │ ├── MongoDB │ │ └── UserListener.php │ └── AbstractUserListener.php ├── Form │ ├── Type │ │ ├── GroupFormType.php │ │ ├── ResettingFormType.php │ │ ├── RegistrationFormType.php │ │ ├── ChangePasswordFormType.php │ │ └── ProfileFormType.php │ └── FormFactory.php ├── EventListener │ ├── ProfileEditListener.php │ ├── ChangePasswordListener.php │ ├── RequestListener.php │ ├── RegistrationListener.php │ ├── ResettingListener.php │ ├── GroupListener.php │ ├── AuthenticationListener.php │ ├── EmailConfirmationListener.php │ └── EventDiscriminator.php ├── Templating │ └── MultiUserHelper.php ├── DependencyInjection │ ├── Compiler │ │ ├── RemoveParentServicesPass.php │ │ ├── OverrideServiceCompilerPass.php │ │ ├── RegisterFosUserMappingsPass.php │ │ └── RegisterUserPass.php │ └── RollerworksMultiUserExtension.php ├── Twig │ └── Extension │ │ └── MultiUserExtension.php ├── Routing │ └── UserDiscriminatorUrlGenerator.php ├── Controller │ └── ResettingController.php ├── RollerworksMultiUserBundle.php └── Generator │ └── UserBundleGenerator.php ├── Changelog.md ├── .gush.yml ├── .travis.yml ├── LICENSE ├── docs ├── user_manager.md ├── overriding_forms.md └── overriding_templates.md ├── scripts └── generate-events.php ├── composer.json └── README.md /src/Resources/views/Group/new.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("group.new") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Group/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("group.edit") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Group/list.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("group.list") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Group/show.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("group.show") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Profile/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("profile.edit") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Profile/show.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("profile.show") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Resetting/email.txt.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("resetting.email") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Security/login.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("security.login") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Resetting/request.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("resetting.request") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Resetting/reset.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("resetting.reset") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Registration/email.txt.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("registration.email") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Registration/register.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("registration.register") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Resetting/checkEmail.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("resetting.check_email") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Registration/checkEmail.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("registration.check_email") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Registration/confirmed.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("registration.confirmation.confirmed") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/ChangePassword/changePassword.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("change_password.change_password") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/Resetting/passwordAlreadyRequested.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("resetting.password_already_requested") %} 2 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Group/show_content.html.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | 3 |
4 |

{{ 'group.show.name'|trans }}: {{ group.getName() }}

5 |
6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Group/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/Group:edit_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Group/list.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/Group:list_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Group/new.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/Group:new_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Group/show.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/Group:show_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Profile/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/Profile:edit_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Profile/show.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/Profile:show_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Resetting/reset.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/Resetting:reset_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Resetting/request.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/Resetting:request_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Profile/show_content.html.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | 3 |
4 |

{{ 'profile.show.username'|trans }}: {{ user.username }}

5 |

{{ 'profile.show.email'|trans }}: {{ user.email }}

6 |
7 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Registration/register.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/Registration:register_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/ChangePassword/changePassword.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% block fos_user_content %} 4 | {% include "RollerworksMultiUserBundle:UserBundle/ChangePassword:changePassword_content.html.twig" %} 5 | {% endblock fos_user_content %} 6 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Resetting/checkEmail.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% trans_default_domain 'FOSUserBundle' %} 4 | 5 | {% block fos_user_content %} 6 |

7 | {{ 'resetting.check_email'|trans({'%email%': email}) }} 8 |

9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Resetting/passwordAlreadyRequested.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% trans_default_domain 'FOSUserBundle' %} 4 | 5 | {% block fos_user_content %} 6 |

{{ 'resetting.password_already_requested'|trans }}

7 | {% endblock fos_user_content %} 8 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Registration/checkEmail.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% trans_default_domain 'FOSUserBundle' %} 4 | 5 | {% block fos_user_content %} 6 |

{{ 'registration.check_email'|trans({'%email%': user.email}) }}

7 | {% endblock fos_user_content %} 8 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/routing.yml.twig: -------------------------------------------------------------------------------- 1 | {% for name, route in routes %} 2 | 3 | {{ extension_alias }}_{{ name }}: 4 | path: {{ route.path }} 5 | defaults: { _controller: "{{ route.controller_action }}" } 6 | {% if route.method is not empty %} 7 | methods: "{{ route.method|join('|') }}" 8 | {% endif %} 9 | {% endfor %} 10 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Group/list_content.html.twig: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/Events.php.twig: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | {{ form_end(form) }} 9 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Profile/edit_content.html.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | 3 | {{ form_start(form, {'action': path(rollerworks_multi_user_user().getRoutePrefix() ~ '_profile_edit'), 'attr': {'class': 'fos_user_profile_edit'} }) }} 4 | {{ form_widget(form) }} 5 |
6 | 7 |
8 | {{ form_end(form) }} 9 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | ### 1.0.0-BETA3 (2014-09-30) 5 | 6 | This version reflects the changes done in the FOSUserBundle done after 2.0.0-alpha1 7 | 8 | * The minimum requirement for Symfony has been bumped to 2.3 (older versions are already EOLed). 9 | * Lazy loading of UserSystems is enabled by default now. 10 | * [BC break] The templating engine configuration has been removed, as well as the related code. 11 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Group/edit_content.html.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | 3 | {{ form_start(form, {'action': path(rollerworks_multi_user_user().getRoutePrefix() ~ '_group_edit', {'groupName': group_name} ), 'attr': {'class': 'fos_user_group_edit'} }) }} 4 | {{ form_widget(form) }} 5 |
6 | 7 |
8 | {{ form_end(form) }} 9 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/ChangePassword/changePassword_content.html.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | 3 | {{ form_start(form, {'action': path(rollerworks_multi_user_user().getRoutePrefix() ~ '_change_password'), 'attr': {'class': 'fos_user_change_password'} }) }} 4 | {{ form_widget(form) }} 5 |
6 | 7 |
8 | {{ form_end(form) }} 9 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Registration/register_content.html.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | 3 | {{ form_start(form, {'action': path(rollerworks_multi_user_user().getRoutePrefix() ~ '_registration_register'), 'attr': {'class': 'fos_user_registration_register'} }) }} 4 | {{ form_widget(form) }} 5 |
6 | 7 |
8 | {{ form_end(form) }} 9 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Resetting/reset_content.html.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | 3 | {{ form_start(form, {'action': path(rollerworks_multi_user_user().getRoutePrefix() ~ '_resetting_reset', {'token': token}), 'attr': {'class': 'fos_user_resetting_reset'} }) }} 4 | {{ form_widget(form) }} 5 |
6 | 7 |
8 | {{ form_end(form) }} 9 | -------------------------------------------------------------------------------- /src/Exception/MissingTemplateException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Exception; 13 | 14 | class MissingTemplateException extends \RuntimeException 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/Exception/NoActiveUserSystemException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Exception; 13 | 14 | class NoActiveUserSystemException extends \RuntimeException 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Resetting/email.txt.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | {% block subject %} 3 | {% autoescape false %} 4 | {{ 'resetting.email.subject'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}) }} 5 | {% endautoescape %} 6 | {% endblock %} 7 | {% block body_text %} 8 | {% autoescape false %} 9 | {{ 'resetting.email.message'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}) }} 10 | {% endautoescape %} 11 | {% endblock %} 12 | {% block body_html %}{% endblock %} 13 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Registration/email.txt.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | {% block subject %} 3 | {% autoescape false %} 4 | {{ 'registration.email.subject'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}) }} 5 | {% endautoescape %} 6 | {% endblock %} 7 | {% block body_text %} 8 | {% autoescape false %} 9 | {{ 'registration.email.message'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}) }} 10 | {% endautoescape %} 11 | {% endblock %} 12 | {% block body_html %}{% endblock %} 13 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Registration/confirmed.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% trans_default_domain 'FOSUserBundle' %} 4 | 5 | {% block fos_user_content %} 6 |

{{ 'registration.confirmed'|trans({'%username%': user.username}) }}

7 | {% if app.session is not empty %} 8 | {% set targetUrl = app.session.get('_security.' ~ app.security.token.providerKey ~ '.target_path') %} 9 | {% if targetUrl is not empty %}

{{ 'registration.back'|trans }}

{% endif %} 10 | {% endif %} 11 | {% endblock fos_user_content %} 12 | -------------------------------------------------------------------------------- /src/Resources/config/container/templating_twig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Exception/UnexpectedTypeException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Exception; 13 | 14 | class UnexpectedTypeException extends \InvalidArgumentException 15 | { 16 | public function __construct($value, $expectedType) 17 | { 18 | parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/routing.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | {% block body %} 8 | {% for name, route in routes %} 9 | 10 | 11 | {{ route.controller_action }} 12 | 13 | {% endfor %} 14 | 15 | {% endblock body %} 16 | 17 | -------------------------------------------------------------------------------- /src/Resources/config/container/templating_php.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/model/user_custom.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\Model; 15 | 16 | { % block use_statements % } 17 | use FOS\UserBundle\Model\User as BaseUser; 18 | 19 | { % endblock use_statements % } 20 | 21 | { % block class_definition % } 22 | class User extends BaseUser 23 | { 24 | % endblock class_definition % 25 | } 26 | { 27 | { % block class_body % } 28 | protected $id; 29 | { % endblock class_body % } 30 | } 31 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/model/user_orm.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\Entity; 15 | 16 | { % block use_statements % } 17 | use FOS\UserBundle\Model\User as BaseUser; 18 | 19 | { % endblock use_statements % } 20 | 21 | { % block class_definition % } 22 | class User extends BaseUser 23 | { 24 | % endblock class_definition % 25 | } 26 | { 27 | { % block class_body % } 28 | protected $id; 29 | { % endblock class_body % } 30 | } 31 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/model/group_custom.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\Model; 15 | 16 | { % block use_statements % } 17 | use FOS\UserBundle\Model\Group as BaseGroup; 18 | 19 | { % endblock use_statements % } 20 | 21 | { % block class_definition % } 22 | class Group extends BaseGroup 23 | { 24 | % endblock class_definition % 25 | } 26 | { 27 | { % block class_body % } 28 | protected $id; 29 | { % endblock class_body % } 30 | } 31 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/model/group_orm.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\Entity; 15 | 16 | { % block use_statements % } 17 | use FOS\UserBundle\Model\Group as BaseGroup; 18 | 19 | { % endblock use_statements % } 20 | 21 | { % block class_definition % } 22 | class Group extends BaseGroup 23 | { 24 | % endblock class_definition % 25 | } 26 | { 27 | { % block class_body % } 28 | protected $id; 29 | { % endblock class_body % } 30 | } 31 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/model/user_mongodb.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\Document; 15 | 16 | { % block use_statements % } 17 | use FOS\UserBundle\Model\User as BaseUser; 18 | 19 | { % endblock use_statements % } 20 | 21 | { % block class_definition % } 22 | class User extends BaseUser 23 | { 24 | % endblock class_definition % 25 | } 26 | { 27 | { % block class_body % } 28 | protected $id; 29 | { % endblock class_body % } 30 | } 31 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/model/group_mongodb.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\Document; 15 | 16 | { % block use_statements % } 17 | use FOS\UserBundle\Model\Group as BaseGroup; 18 | 19 | { % endblock use_statements % } 20 | 21 | { % block class_definition % } 22 | class Group extends BaseGroup 23 | { 24 | % endblock class_definition % 25 | } 26 | { 27 | { % block class_body % } 28 | protected $id; 29 | { % endblock class_body % } 30 | } 31 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/model/user_couchdb.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\CouchDocument; 15 | 16 | { % block use_statements % } 17 | use FOS\UserBundle\Model\User as BaseUser; 18 | 19 | { % endblock use_statements % } 20 | 21 | { % block class_definition % } 22 | class User extends BaseUser 23 | { 24 | % endblock class_definition % 25 | } 26 | { 27 | { % block class_body % } 28 | protected $id; 29 | { % endblock class_body % } 30 | } 31 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/model/group_couchdb.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\CouchDocument; 15 | 16 | { % block use_statements % } 17 | use FOS\UserBundle\Model\Group as BaseGroup; 18 | 19 | { % endblock use_statements % } 20 | 21 | { % block class_definition % } 22 | class Group extends BaseGroup 23 | { 24 | % endblock class_definition % 25 | } 26 | { 27 | { % block class_body % } 28 | protected $id; 29 | { % endblock class_body % } 30 | } 31 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Resetting/request_content.html.twig: -------------------------------------------------------------------------------- 1 | {% trans_default_domain 'FOSUserBundle' %} 2 | 3 |
4 |
5 | {% if invalid_username is defined %} 6 |

{{ 'resetting.request.invalid_username'|trans({'%username%': invalid_username}) }}

7 | {% endif %} 8 | 9 | 10 |
11 |
12 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/routing.php.twig: -------------------------------------------------------------------------------- 1 | add('{{ extension_alias }}_{{ name }}', new Route('{{ route.path }}', array( 16 | '_controller' => '{{ route.controller_action }}' 17 | ) 18 | {% if route.method is not empty %} 19 | , array(), array(), '', array(), array('{{ route.method|join("', '") }}') 20 | {% endif %} 21 | )); 22 | {% endfor %} 23 | 24 | {% endblock body %} 25 | 26 | {% block return %} 27 | return $collection; 28 | {% endblock return %} 29 | -------------------------------------------------------------------------------- /.gush.yml: -------------------------------------------------------------------------------- 1 | # Gush configuration file, any comments will be lost. 2 | adapter: github 3 | issue_tracker: github 4 | meta-header: "This file is part of the RollerworksMultiUserBundle package.\n\n(c) Sebastiaan Stok \n\nThis source file is subject to the MIT license that is bundled\nwith this source code in the file LICENSE." 5 | # Note. Values are regexes, relative to the root-folder 6 | meta-exclude: 7 | - '/\.twig/' 8 | table-pr: 9 | bug_fix: ['Bug Fix?', 'no'] 10 | new_feature: ['New Feature?', 'no'] 11 | bc_breaks: ['BC Breaks?', 'no'] 12 | deprecations: ['Deprecations?', 'no'] 13 | fixed_tickets: ['Fixed Tickets', ''] 14 | license: [License, MIT] 15 | repo_adapter: github 16 | repo_org: rollerworks 17 | repo_name: RollerworksMultiUserBundle 18 | issue_org: rollerworks 19 | issue_repo_name: RollerworksMultiUserBundle 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | env: 4 | - SYMFONY_VERSION="2.3.*" 5 | - SYMFONY_VERSION="2.7.*" 6 | - SYMFONY_VERSION="2.8.*" 7 | - SYMFONY_VERSION="3.0.*" 8 | 9 | # We only install Mongo to ensure the container compiles properly with multiple db-drivers 10 | before_script: 11 | - echo "extension=mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini 12 | - COMPOSER_ROOT_VERSION=dev-master 13 | - composer self-update 14 | - composer require symfony/symfony:${SYMFONY_VERSION} --no-update 15 | - composer require doctrine/mongodb-odm:"1.0.*@dev" --no-update 16 | - composer require doctrine/mongodb-odm-bundle:"3.0.*@dev" --no-update 17 | - composer install 18 | 19 | php: 20 | - 5.3 21 | - 5.4 22 | - 5.5 23 | - 5.6 24 | - 7.0 25 | - hhvm 26 | 27 | matrix: 28 | allow_failures: 29 | - php: 7.0 30 | 31 | script: phpunit --configuration phpunit.xml.dist --verbose --exclude-group "" 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2016 Sebastiaan Stok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/Resources/views/UserBundle/Security/login.html.twig: -------------------------------------------------------------------------------- 1 | {% extends rollerworks_multi_user_user().getTemplate("layout") %} 2 | 3 | {% trans_default_domain 'FOSUserBundle' %} 4 | 5 | {% block fos_user_content %} 6 | {% if error %} 7 |
{{ error.messageKey|trans(error.messageData, 'security') }}
8 | {% endif %} 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | {% endblock fos_user_content %} 25 | -------------------------------------------------------------------------------- /src/Resources/views/layout.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} 9 | {{ 'layout.logged_in_as'|trans({'%username%': app.user.username}, 'FOSUserBundle') }} | 10 | 11 | {{ 'layout.logout'|trans({}, 'FOSUserBundle') }} 12 | 13 | {% else %} 14 | {{ 'layout.login'|trans({}, 'FOSUserBundle') }} 15 | {% endif %} 16 |
17 | 18 | {% for type, messages in app.session.flashbag.all() %} 19 | {% for message in messages %} 20 |
21 | {{ message }} 22 |
23 | {% endfor %} 24 | {% endfor %} 25 | 26 |
27 | {% block fos_user_content %} 28 | {% endblock fos_user_content %} 29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Command/Validators.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Command; 13 | 14 | use Sensio\Bundle\GeneratorBundle\Command\Validators as BaseValidators; 15 | 16 | /** 17 | * Validator functions. 18 | * 19 | * @author Sebastiaan Stok 20 | */ 21 | class Validators extends BaseValidators 22 | { 23 | public static function validateFormat($format) 24 | { 25 | $format = strtolower($format); 26 | 27 | if (!in_array($format, array('php', 'xml', 'yml'), true)) { 28 | throw new \RuntimeException(sprintf('Format "%s" is not supported.', $format)); 29 | } 30 | 31 | return $format; 32 | } 33 | 34 | public static function validateDbDriver($format) 35 | { 36 | $format = strtolower($format); 37 | 38 | if (!in_array($format, array('orm', 'mongodb', 'couchdb', 'custom'), true)) { 39 | throw new \RuntimeException(sprintf('Format "%s" is not supported.', $format)); 40 | } 41 | 42 | return $format; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Model/NoopGroupManager.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Model; 13 | 14 | use FOS\UserBundle\Model\GroupInterface; 15 | use FOS\UserBundle\Model\GroupManagerInterface; 16 | 17 | /** 18 | * NoopGroupManager, does nothing. 19 | * 20 | * Use this group manager as default when no group managing is used. 21 | * 22 | * @author Sebastiaan Stok 23 | */ 24 | class NoopGroupManager implements GroupManagerInterface 25 | { 26 | public function getClass() 27 | { 28 | // noop 29 | } 30 | 31 | public function createGroup($name) 32 | { 33 | // noop 34 | } 35 | 36 | public function deleteGroup(GroupInterface $group) 37 | { 38 | // noop 39 | } 40 | 41 | public function findGroupBy(array $criteria) 42 | { 43 | // noop 44 | } 45 | 46 | public function findGroupByName($name) 47 | { 48 | // noop 49 | } 50 | 51 | public function findGroups() 52 | { 53 | // noop 54 | } 55 | 56 | public function updateGroup(GroupInterface $group) 57 | { 58 | // noop 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Mailer/DelegatingMailer.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Mailer; 13 | 14 | use FOS\UserBundle\Mailer\MailerInterface; 15 | use FOS\UserBundle\Model\UserInterface; 16 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 17 | use Rollerworks\Component\SfContainerInjector\ContainerInjector; 18 | 19 | class DelegatingMailer implements MailerInterface 20 | { 21 | private $userDiscriminator; 22 | private $container; 23 | 24 | public function __construct(UserDiscriminatorInterface $userDiscriminator, ContainerInjector $container) 25 | { 26 | $this->userDiscriminator = $userDiscriminator; 27 | $this->container = $container; 28 | } 29 | 30 | public function sendConfirmationEmailMessage(UserInterface $user) 31 | { 32 | $this->container->get($this->userDiscriminator->getCurrentUserConfig()->getServicePrefix().'.mailer')->sendConfirmationEmailMessage($user); 33 | } 34 | 35 | public function sendResettingEmailMessage(UserInterface $user) 36 | { 37 | $this->container->get($this->userDiscriminator->getCurrentUserConfig()->getServicePrefix().'.mailer')->sendResettingEmailMessage($user); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Model/UserDiscriminatorInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Model; 13 | 14 | use Rollerworks\Bundle\MultiUserBundle\Exception\NoActiveUserSystemException; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | interface UserDiscriminatorInterface 20 | { 21 | /** 22 | * Adds a new user to the discriminator. 23 | * 24 | * @param string $name 25 | * @param UserConfig $user 26 | */ 27 | public function addUser($name, UserConfig $user); 28 | 29 | /** 30 | * Set the current user. 31 | * 32 | * @param string $name 33 | */ 34 | public function setCurrentUser($name); 35 | 36 | /** 37 | * Returns the name of the current user. 38 | * 39 | * @return string|null 40 | */ 41 | public function getCurrentUser(); 42 | 43 | /** 44 | * Returns the configuration of the current user. 45 | * 46 | * This must throw an NoActiveUserSystemException when there is no user-system active. 47 | * 48 | * @throws NoActiveUserSystemException when there is no user-system active 49 | * 50 | * @return UserConfig 51 | */ 52 | public function getCurrentUserConfig(); 53 | } 54 | -------------------------------------------------------------------------------- /src/Doctrine/CouchDB/UserListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Doctrine\CouchDB; 13 | 14 | use Doctrine\ODM\CouchDB\Event; 15 | use Doctrine\ODM\CouchDB\Event\LifecycleEventArgs; 16 | use FOS\UserBundle\Model\UserInterface; 17 | use Rollerworks\Bundle\MultiUserBundle\Doctrine\AbstractUserListener; 18 | 19 | /** 20 | * @author Sebastiaan Stok 21 | * @author Christophe Coevoet 22 | */ 23 | class UserListener extends AbstractUserListener 24 | { 25 | public function getSubscribedEvents() 26 | { 27 | return array( 28 | Event::prePersist, 29 | Event::preUpdate, 30 | ); 31 | } 32 | 33 | /** 34 | * @param LifecycleEventArgs $args 35 | */ 36 | public function prePersist($args) 37 | { 38 | $object = $args->getDocument(); 39 | if ($object instanceof UserInterface) { 40 | $this->updateUserFields($object); 41 | } 42 | } 43 | 44 | /** 45 | * @param LifecycleEventArgs $args 46 | */ 47 | public function preUpdate($args) 48 | { 49 | $object = $args->getDocument(); 50 | if ($object instanceof UserInterface) { 51 | $this->updateUserFields($object); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Form/Type/GroupFormType.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Form\Type; 13 | 14 | use Symfony\Component\Form\AbstractType; 15 | use Symfony\Component\Form\FormBuilderInterface; 16 | use Symfony\Component\OptionsResolver\OptionsResolverInterface; 17 | 18 | class GroupFormType extends AbstractType 19 | { 20 | /** 21 | * @var string 22 | */ 23 | private $class; 24 | 25 | /** 26 | * @var string 27 | */ 28 | private $typeName; 29 | 30 | /** 31 | * @param string $class The User class name 32 | * @param string $typeName The FormType name 33 | */ 34 | public function __construct($class, $typeName) 35 | { 36 | $this->class = $class; 37 | $this->typeName = $typeName; 38 | } 39 | 40 | public function buildForm(FormBuilderInterface $builder, array $options) 41 | { 42 | $builder->add('name', null, array('label' => 'form.group_name', 'translation_domain' => 'FOSUserBundle')); 43 | } 44 | 45 | public function setDefaultOptions(OptionsResolverInterface $resolver) 46 | { 47 | $resolver->setDefaults(array( 48 | 'data_class' => $this->class, 49 | 'intention' => 'group', 50 | )); 51 | } 52 | 53 | public function getName() 54 | { 55 | return $this->typeName; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/Bundle.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}; 15 | 16 | { % block use_statements % } 17 | use Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Compiler\RegisterFosUserMappingsPass; 18 | use Symfony\Component\DependencyInjection\ContainerBuilder; 19 | use Symfony\Component\HttpKernel\Bundle\Bundle; 20 | 21 | { % endblock use_statements % } 22 | 23 | { % block class_definition % } 24 | class 25 | { 26 | { bundle } 27 | } extends Bundle 28 | { % endblock class_definition % } 29 | { 30 | { % block class_body % } 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function build(ContainerBuilder $container) 35 | { 36 | parent::build($container); 37 | { % if { 38 | db_driver == 'orm' % 39 | } 40 | $container->addCompilerPass(RegisterFosUserMappingsPass::createOrmMappingDriver('{{ extension_alias }}')); } 41 | { % elseif { 42 | db_driver == 'mongodb' % 43 | } 44 | $container->addCompilerPass(RegisterFosUserMappingsPass::createMongoDBMappingDriver('{{ extension_alias }}')); } 45 | { % elseif { 46 | db_driver == 'couchdb' % 47 | } 48 | $container->addCompilerPass(RegisterFosUserMappingsPass::createCouchDBMappingDriver('{{ extension_alias }}')); } 49 | { % endif % } 50 | } 51 | { % endblock class_body % } 52 | } 53 | -------------------------------------------------------------------------------- /src/EventListener/ProfileEditListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\EventListener; 13 | 14 | use FOS\UserBundle\Event\FormEvent; 15 | use FOS\UserBundle\FOSUserEvents; 16 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 17 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 18 | use Symfony\Component\HttpFoundation\RedirectResponse; 19 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 20 | 21 | class ProfileEditListener implements EventSubscriberInterface 22 | { 23 | private $router; 24 | private $userDiscriminator; 25 | 26 | public function __construct(UrlGeneratorInterface $router, UserDiscriminatorInterface $userDiscriminator) 27 | { 28 | $this->router = $router; 29 | $this->userDiscriminator = $userDiscriminator; 30 | } 31 | 32 | public static function getSubscribedEvents() 33 | { 34 | return array( 35 | FOSUserEvents::PROFILE_EDIT_SUCCESS => array('onProfileEditSuccess', 1), 36 | ); 37 | } 38 | 39 | public function onProfileEditSuccess(FormEvent $event) 40 | { 41 | if (null === $event->getResponse()) { 42 | $url = $this->router->generate($this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_profile_show'); 43 | $event->setResponse(new RedirectResponse($url)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Templating/MultiUserHelper.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Templating; 13 | 14 | use Rollerworks\Bundle\MultiUserBundle\Model\UserConfig; 15 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 16 | use Symfony\Component\Templating\Helper\Helper; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | class MultiUserHelper extends Helper 22 | { 23 | private $userDiscriminator; 24 | 25 | /** 26 | * @param UserDiscriminatorInterface $userDiscriminator 27 | */ 28 | public function __construct(UserDiscriminatorInterface $userDiscriminator) 29 | { 30 | $this->userDiscriminator = $userDiscriminator; 31 | } 32 | 33 | /** 34 | * @return string|null 35 | */ 36 | public function getUser() 37 | { 38 | return $this->userDiscriminator->getCurrentUser(); 39 | } 40 | 41 | /** 42 | * @return UserConfig 43 | */ 44 | public function getUserConfig() 45 | { 46 | return $this->userDiscriminator->getCurrentUserConfig(); 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function __toString() 53 | { 54 | return (string) $this->getUser(); 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | */ 60 | public function getName() 61 | { 62 | return 'rollerworks_multi_user'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/EventListener/ChangePasswordListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\EventListener; 13 | 14 | use FOS\UserBundle\Event\FormEvent; 15 | use FOS\UserBundle\FOSUserEvents; 16 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 17 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 18 | use Symfony\Component\HttpFoundation\RedirectResponse; 19 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 20 | 21 | class ChangePasswordListener implements EventSubscriberInterface 22 | { 23 | private $router; 24 | private $userDiscriminator; 25 | 26 | public function __construct(UrlGeneratorInterface $router, UserDiscriminatorInterface $userDiscriminator) 27 | { 28 | $this->router = $router; 29 | $this->userDiscriminator = $userDiscriminator; 30 | } 31 | 32 | public static function getSubscribedEvents() 33 | { 34 | return array( 35 | FOSUserEvents::CHANGE_PASSWORD_SUCCESS => array('onChangePasswordSuccess', 1), 36 | ); 37 | } 38 | 39 | public function onChangePasswordSuccess(FormEvent $event) 40 | { 41 | if (null === $event->getResponse()) { 42 | $url = $this->router->generate($this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_profile_show'); 43 | $event->setResponse(new RedirectResponse($url)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/RemoveParentServicesPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Compiler; 13 | 14 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 15 | use Symfony\Component\DependencyInjection\ContainerBuilder; 16 | 17 | /** 18 | * RemoveParentServicesPass, marks the parent 'FOSUserBundle' services as abstract. 19 | * 20 | * By making them abstract they are removed and prevent any conflict or container bloat. 21 | * 22 | * @author Sebastiaan Stok 23 | * 24 | * @codeCoverageIgnore 25 | */ 26 | class RemoveParentServicesPass implements CompilerPassInterface 27 | { 28 | public function process(ContainerBuilder $container) 29 | { 30 | $container->getDefinition('fos_user.listener.authentication')->setAbstract(true)->clearTags(); 31 | $container->getDefinition('fos_user.listener.resetting')->setAbstract(true)->clearTags(); 32 | 33 | // Forms 34 | $container->getDefinition('fos_user.registration.form.type')->setAbstract(true); 35 | $container->getDefinition('fos_user.resetting.form.type')->setAbstract(true); 36 | $container->getDefinition('fos_user.profile.form.type')->setAbstract(true); 37 | $container->getDefinition('fos_user.change_password.form.type')->setAbstract(true); 38 | $container->getDefinition('fos_user.group.form.type')->setAbstract(true); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Twig/Extension/MultiUserExtension.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Twig\Extension; 13 | 14 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | class MultiUserExtension extends \Twig_Extension 20 | { 21 | private $userDiscriminator; 22 | 23 | /** 24 | * @param UserDiscriminatorInterface $userDiscriminator 25 | */ 26 | public function __construct(UserDiscriminatorInterface $userDiscriminator) 27 | { 28 | $this->userDiscriminator = $userDiscriminator; 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function getFunctions() 35 | { 36 | return array( 37 | new \Twig_SimpleFunction('rollerworks_multi_user_user', array($this, 'getCurrentUser')), 38 | ); 39 | } 40 | 41 | /** 42 | * @param bool $getName 43 | * 44 | * @return \Rollerworks\Bundle\MultiUserBundle\Model\UserConfig|string|null 45 | */ 46 | public function getCurrentUser($getName = false) 47 | { 48 | if ($getName) { 49 | return $this->userDiscriminator->getCurrentUser(); 50 | } 51 | 52 | return $this->userDiscriminator->getCurrentUserConfig(); 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function getName() 59 | { 60 | return 'rollerworks_multi_user'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Command/DeactivateUserCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Command; 13 | 14 | use FOS\UserBundle\Command\DeactivateUserCommand as BaseDeactivateUserCommand; 15 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 16 | use Symfony\Component\Console\Input\InputInterface; 17 | use Symfony\Component\Console\Input\InputOption; 18 | use Symfony\Component\Console\Output\OutputInterface; 19 | 20 | /** 21 | * @author Sebastiaan Stok 22 | */ 23 | class DeactivateUserCommand extends BaseDeactivateUserCommand 24 | { 25 | protected function configure() 26 | { 27 | parent::configure(); 28 | 29 | $definition = $this->getDefinition(); 30 | $definition->addOption( 31 | new InputOption('user-system', null, InputOption::VALUE_REQUIRED, 'The user-system to use') 32 | ); 33 | 34 | $this 35 | ->setHelp(<<fos:user:deactivate command deactivates a user (will not be able to log in) 37 | 38 | php app/console fos:user:deactivate --user-system=acme_user matthieu 39 | EOT 40 | ); 41 | } 42 | 43 | protected function interact(InputInterface $input, OutputInterface $output) 44 | { 45 | /** @var UserDiscriminatorInterface $discriminator */ 46 | $discriminator = $this->getContainer()->get('rollerworks_multi_user.user_discriminator'); 47 | $discriminator->setCurrentUser($input->getOption('user-system')); 48 | 49 | parent::interact($input, $output); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docs/user_manager.md: -------------------------------------------------------------------------------- 1 | About RollerworksMultiUserBundle User Manager 2 | ============================================= 3 | 4 | The RollerworksMultiUserBundle only provides one User Manager class: `DelegatingUserManager` 5 | which delegates all calls to the actual User Manager of the current user-system. 6 | 7 | The `fos_user.user_manager` service should only be used when you want to manage the 'current' user-system, 8 | to manage a specific user-system you must refer to the `[service-prefix].user_manager` service like `acme_user.user_manager`. 9 | 10 | > The `[service-prefix]` is what you have configured for the user-system, in this case 'acme_user' 11 | 12 | Handling of user groups is pretty much the same, except that you use `acme_user.group_manager` for handling groups. 13 | 14 | **Note:** 15 | 16 | > Groups support must be enabled per user-system, by default the system uses the `NoopGroupManager` which does nothing. 17 | > See also: [Using Groups With FOSUserBundle](https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/groups.md) 18 | 19 | ## Controller 20 | 21 | Because the FOSUserBundle Controllers always use the current user-system, 22 | you must either create your own or change the current user-system. 23 | 24 | The user-discriminator is available in the container as the rollerworks_multi_user.user_discriminator service. 25 | 26 | **Caution:** Make sure to use the user-system name for setCurrentUser(), and not the service-prefix. 27 | 28 | ```php 29 | $userDiscriminator = $container->get('rollerworks_multi_user.user_discriminator'); 30 | $userDiscriminator->setCurrentUser('user-system-name'); 31 | 32 | // Example 33 | $userDiscriminator->setCurrentUser('acme_user'); 34 | ``` 35 | 36 | **Note:** 37 | 38 | > As the group manager is connected to the user-system, this must also be done when 39 | > managing groups of another user-system. 40 | -------------------------------------------------------------------------------- /src/Form/Type/ResettingFormType.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Form\Type; 13 | 14 | use Symfony\Component\Form\AbstractType; 15 | use Symfony\Component\Form\FormBuilderInterface; 16 | use Symfony\Component\OptionsResolver\OptionsResolverInterface; 17 | 18 | class ResettingFormType extends AbstractType 19 | { 20 | /** 21 | * @var string 22 | */ 23 | private $class; 24 | 25 | /** 26 | * @var string 27 | */ 28 | private $typeName; 29 | 30 | /** 31 | * @param string $class The User class name 32 | * @param string $typeName The FormType name 33 | */ 34 | public function __construct($class, $typeName) 35 | { 36 | $this->class = $class; 37 | $this->typeName = $typeName; 38 | } 39 | 40 | public function buildForm(FormBuilderInterface $builder, array $options) 41 | { 42 | $builder->add('plainPassword', 'repeated', array( 43 | 'type' => 'password', 44 | 'options' => array('translation_domain' => 'FOSUserBundle'), 45 | 'first_options' => array('label' => 'form.new_password'), 46 | 'second_options' => array('label' => 'form.new_password_confirmation'), 47 | 'invalid_message' => 'fos_user.password.mismatch', 48 | )); 49 | } 50 | 51 | public function setDefaultOptions(OptionsResolverInterface $resolver) 52 | { 53 | $resolver->setDefaults(array( 54 | 'data_class' => $this->class, 55 | 'intention' => 'resetting', 56 | )); 57 | } 58 | 59 | public function getName() 60 | { 61 | return $this->typeName; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Command/ActivateUserCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Command; 13 | 14 | use FOS\UserBundle\Command\ActivateUserCommand as BaseActivateUserCommand; 15 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 16 | use Symfony\Component\Console\Input\InputInterface; 17 | use Symfony\Component\Console\Input\InputOption; 18 | use Symfony\Component\Console\Output\OutputInterface; 19 | 20 | /** 21 | * @author Sebastiaan Stok 22 | */ 23 | class ActivateUserCommand extends BaseActivateUserCommand 24 | { 25 | /** 26 | * @see Command 27 | */ 28 | protected function configure() 29 | { 30 | parent::configure(); 31 | 32 | $definition = $this->getDefinition(); 33 | $definition->addOption( 34 | new InputOption('user-system', null, InputOption::VALUE_REQUIRED, 'The user-system to use') 35 | ); 36 | 37 | $this 38 | ->setHelp(<<fos:user:activate command activates a user (so they will be able to log in): 40 | 41 | php app/console fos:user:activate --user-system=acme_user matthieu 42 | EOT 43 | ); 44 | } 45 | 46 | /** 47 | * @see Command 48 | */ 49 | protected function interact(InputInterface $input, OutputInterface $output) 50 | { 51 | /** @var UserDiscriminatorInterface $discriminator */ 52 | $discriminator = $this->getContainer()->get('rollerworks_multi_user.user_discriminator'); 53 | $discriminator->setCurrentUser($input->getOption('user-system')); 54 | 55 | parent::interact($input, $output); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/OverrideServiceCompilerPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Compiler; 13 | 14 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 15 | use Symfony\Component\DependencyInjection\ContainerBuilder; 16 | 17 | class OverrideServiceCompilerPass implements CompilerPassInterface 18 | { 19 | public function process(ContainerBuilder $container) 20 | { 21 | $this->changeService($container, 'fos_user.registration.form.factory', 'rollerworks_multi_user.registration.form.factory'); 22 | $this->changeService($container, 'fos_user.resetting.form.factory', 'rollerworks_multi_user.resetting.form.factory'); 23 | $this->changeService($container, 'fos_user.profile.form.factory', 'rollerworks_multi_user.profile.form.factory'); 24 | $this->changeService($container, 'fos_user.change_password.form.factory', 'rollerworks_multi_user.change_password.form.factory'); 25 | $this->changeService($container, 'fos_user.group.form.factory', 'rollerworks_multi_user.group.form.factory'); 26 | } 27 | 28 | /** 29 | * @param string $serviceName 30 | * @param string $newServiceName 31 | */ 32 | private function changeService(ContainerBuilder $container, $serviceName, $newServiceName) 33 | { 34 | if ($container->hasDefinition($serviceName) && $container->hasDefinition($newServiceName)) { 35 | $newService = $container->getDefinition($newServiceName); 36 | 37 | $container->removeDefinition($serviceName); 38 | $container->setDefinition($serviceName, $newService); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Doctrine/Orm/UserListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Doctrine\Orm; 13 | 14 | use Doctrine\ORM\Event\LifecycleEventArgs; 15 | use Doctrine\ORM\Event\PreUpdateEventArgs; 16 | use Doctrine\ORM\Events; 17 | use FOS\UserBundle\Model\UserInterface; 18 | use Rollerworks\Bundle\MultiUserBundle\Doctrine\AbstractUserListener; 19 | 20 | /** 21 | * Doctrine ORM listener updating the canonical fields and the password. 22 | * 23 | * @author Sebastiaan Stok 24 | * @author Christophe Coevoet 25 | */ 26 | class UserListener extends AbstractUserListener 27 | { 28 | public function getSubscribedEvents() 29 | { 30 | return array( 31 | Events::prePersist, 32 | Events::preUpdate, 33 | ); 34 | } 35 | 36 | /** 37 | * @param LifecycleEventArgs $args 38 | */ 39 | public function prePersist($args) 40 | { 41 | $object = $args->getEntity(); 42 | if ($object instanceof UserInterface) { 43 | $this->updateUserFields($object); 44 | } 45 | } 46 | 47 | /** 48 | * @param PreUpdateEventArgs $args 49 | */ 50 | public function preUpdate($args) 51 | { 52 | $object = $args->getEntity(); 53 | if ($object instanceof UserInterface) { 54 | $this->updateUserFields($object); 55 | // We are doing a update, so we must force Doctrine to update the 56 | // changeset in case we changed something above 57 | $em = $args->getEntityManager(); 58 | $uow = $em->getUnitOfWork(); 59 | $meta = $em->getClassMetadata(get_class($object)); 60 | $uow->recomputeSingleEntityChangeSet($meta, $object); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Doctrine/MongoDB/UserListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Doctrine\MongoDB; 13 | 14 | use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs; 15 | use Doctrine\ODM\MongoDB\Event\PreUpdateEventArgs; 16 | use Doctrine\ODM\MongoDB\Events; 17 | use FOS\UserBundle\Model\UserInterface; 18 | use Rollerworks\Bundle\MultiUserBundle\Doctrine\AbstractUserListener; 19 | 20 | /** 21 | * Doctrine MongoDB ODM listener updating the canonical fields and the password. 22 | * 23 | * @author Sebastiaan Stok 24 | * @author Christophe Coevoet 25 | */ 26 | class UserListener extends AbstractUserListener 27 | { 28 | public function getSubscribedEvents() 29 | { 30 | return array( 31 | Events::prePersist, 32 | Events::preUpdate, 33 | ); 34 | } 35 | 36 | /** 37 | * @param LifecycleEventArgs $args 38 | */ 39 | public function prePersist($args) 40 | { 41 | $object = $args->getDocument(); 42 | if ($object instanceof UserInterface) { 43 | $this->updateUserFields($object); 44 | } 45 | } 46 | 47 | /** 48 | * @param PreUpdateEventArgs $args 49 | */ 50 | public function preUpdate($args) 51 | { 52 | $object = $args->getDocument(); 53 | if ($object instanceof UserInterface) { 54 | $this->updateUserFields($object); 55 | // We are doing a update, so we must force Doctrine to update the 56 | // changeset in case we changed something above 57 | $dm = $args->getDocumentManager(); 58 | $uow = $dm->getUnitOfWork(); 59 | $meta = $dm->getClassMetadata(get_class($object)); 60 | $uow->recomputeSingleDocumentChangeSet($meta, $object); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Command/ChangePasswordCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Command; 13 | 14 | use FOS\UserBundle\Command\ChangePasswordCommand as BaseChangePasswordCommand; 15 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 16 | use Symfony\Component\Console\Input\InputInterface; 17 | use Symfony\Component\Console\Input\InputOption; 18 | use Symfony\Component\Console\Output\OutputInterface; 19 | 20 | /** 21 | * @author Sebastiaan Stok 22 | */ 23 | class ChangePasswordCommand extends BaseChangePasswordCommand 24 | { 25 | protected function configure() 26 | { 27 | parent::configure(); 28 | 29 | $definition = $this->getDefinition(); 30 | $definition->addOption( 31 | new InputOption('user-system', null, InputOption::VALUE_REQUIRED, 'The user-system to use') 32 | ); 33 | 34 | $this 35 | ->setHelp(<<fos:user:change-password command changes the password of a user: 37 | 38 | php app/console fos:user:change-password --user-system=acme_user matthieu 39 | 40 | This interactive shell will first ask you for a password. 41 | 42 | You can alternatively specify the password as a second argument: 43 | 44 | php app/console fos:user:change-password --user-system=acme_user matthieu mypassword 45 | 46 | EOT 47 | ); 48 | } 49 | 50 | protected function interact(InputInterface $input, OutputInterface $output) 51 | { 52 | /** @var UserDiscriminatorInterface $discriminator */ 53 | $discriminator = $this->getContainer()->get('rollerworks_multi_user.user_discriminator'); 54 | $discriminator->setCurrentUser($input->getOption('user-system')); 55 | 56 | parent::interact($input, $output); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/EventListener/RequestListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\EventListener; 13 | 14 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 15 | use Symfony\Component\HttpFoundation\RequestMatcherInterface; 16 | use Symfony\Component\HttpKernel\Event\GetResponseEvent; 17 | 18 | /** 19 | * Tries to determine the current user-system. 20 | * 21 | * @author Sebastiaan Stok 22 | */ 23 | class RequestListener 24 | { 25 | /** 26 | * @var UserDiscriminatorInterface 27 | */ 28 | protected $userDiscriminator; 29 | 30 | /** 31 | * @var RequestMatcherInterface[] 32 | */ 33 | protected $users; 34 | 35 | /** 36 | * @param UserDiscriminatorInterface $userDiscriminator 37 | */ 38 | public function __construct(UserDiscriminatorInterface $userDiscriminator) 39 | { 40 | $this->userDiscriminator = $userDiscriminator; 41 | $this->users = array(); 42 | } 43 | 44 | /** 45 | * @param string $name 46 | * @param RequestMatcherInterface $requestMatcher 47 | */ 48 | public function addUser($name, RequestMatcherInterface $requestMatcher) 49 | { 50 | $this->users[$name] = $requestMatcher; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function onKernelRequest(GetResponseEvent $event) 57 | { 58 | // Already set 59 | if (null !== $this->userDiscriminator->getCurrentUser()) { 60 | return; 61 | } 62 | 63 | $request = $event->getRequest(); 64 | foreach ($this->users as $name => $requestMatcher) { 65 | if ($requestMatcher->matches($request)) { 66 | $this->userDiscriminator->setCurrentUser($name); 67 | 68 | return; 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Form/FormFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Form; 13 | 14 | use FOS\UserBundle\Form\Factory\FactoryInterface; 15 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 16 | use Symfony\Component\Form\FormFactoryInterface; 17 | use Symfony\Component\Form\FormInterface; 18 | 19 | /** 20 | * @author Sebastiaan Stok 21 | * @author Leonardo Proietti 22 | */ 23 | class FormFactory implements FactoryInterface 24 | { 25 | /** 26 | * @var UserDiscriminatorInterface 27 | */ 28 | private $userDiscriminator; 29 | 30 | /** 31 | * @var FormFactoryInterface 32 | */ 33 | private $formFactory; 34 | 35 | /** 36 | * @var string 37 | */ 38 | private $type; 39 | 40 | /** 41 | * @param FormFactoryInterface $formFactory 42 | * @param string $type Form type name 43 | */ 44 | public function __construct(FormFactoryInterface $formFactory, $type) 45 | { 46 | $this->formFactory = $formFactory; 47 | $this->type = $type; 48 | } 49 | 50 | /** 51 | * @param UserDiscriminatorInterface $userDiscriminator 52 | */ 53 | public function setUserDiscriminator($userDiscriminator) 54 | { 55 | $this->userDiscriminator = $userDiscriminator; 56 | } 57 | 58 | /** 59 | * @return FormInterface 60 | */ 61 | public function createForm() 62 | { 63 | $user = $this->userDiscriminator->getCurrentUserConfig(); 64 | $type = $user->getFormType($this->type); 65 | $name = $user->getFormName($this->type); 66 | $validationGroups = $user->getFormValidationGroups($this->type); 67 | 68 | return $this->formFactory->createNamed($name, $type, null, array('validation_groups' => $validationGroups)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/Configuration.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\DependencyInjection; 15 | 16 | { % block use_statements % } 17 | use Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Configuration as UserConfiguration; 18 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; 19 | use Symfony\Component\Config\Definition\ConfigurationInterface; 20 | 21 | { % endblock use_statements % } 22 | 23 | /* 24 | {% block phpdoc_class_header %} 25 | * This is the class that validates and merges configuration from your app/config files 26 | {% endblock phpdoc_class_header %} 27 | * 28 | * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class} 29 | * To learn more about the user-configuration see {@link https://github.com/rollerworks/RollerworksMultiUserBundle/blob/master/docs/index.md#33-make-your-bundle-configurable} 30 | */ 31 | { % block class_definition % } 32 | class Configuration implements ConfigurationInterface 33 | { 34 | % endblock class_definition % 35 | } 36 | { 37 | { % block class_body % } 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | public function getConfigTreeBuilder() 42 | { 43 | $treeBuilder = new TreeBuilder(); 44 | $rootNode = $treeBuilder->root('{{ extension_alias }}'); 45 | 46 | $configuration = new UserConfiguration(); 47 | 48 | // Add the UserConfiguration tree 49 | // Enables everything except group 50 | $configuration->addUserConfig($rootNode, UserConfiguration::CONFIG_ALL ^ UserConfiguration::CONFIG_SECTION_GROUP); 51 | 52 | // Here you should define the parameters that are allowed to 53 | // configure your bundle. See the documentation linked above for 54 | // more information on that topic. 55 | 56 | return $treeBuilder; 57 | } 58 | { % endblock class_body % } 59 | } 60 | -------------------------------------------------------------------------------- /scripts/generate-events.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | if (!file_exists(__DIR__.'/../vendor/autoload.php')) { 13 | throw new \RuntimeException('Did not find vendor/autoload.php. Please Install vendors using command: composer.phar install --dev'); 14 | } 15 | 16 | /** 17 | * @var ClassLoader 18 | */ 19 | $loader = require_once __DIR__.'/../vendor/autoload.php'; 20 | 21 | $eventsClass = new \ReflectionClass('FOS\UserBundle\FOSUserEvents'); 22 | $events = $eventsClass->getConstants(); 23 | 24 | $underscoreToCamelCase = function ($string) { 25 | $string = strtolower($string); 26 | 27 | return preg_replace_callback('/_([a-z])/', function ($c) { 28 | return strtoupper($c[1]); 29 | }, $string); 30 | }; 31 | 32 | $date = date('Y-m-d'); 33 | 34 | echo << $eventName) { 42 | $event = ucfirst($underscoreToCamelCase($event)); 43 | $eventName = substr($eventName, 8); 44 | 45 | echo <<userDiscriminator->getCurrentUser()) { 50 | \$this->eventDispatcher->dispatch(\$userSys . '$eventName', \$e); 51 | } 52 | } 53 | 54 | EOT; 55 | } 56 | 57 | echo << $eventName) { 71 | $eventFunc = ucfirst($underscoreToCamelCase($event)); 72 | 73 | echo << 'dispatch$eventFunc', 75 | 76 | EOT; 77 | } 78 | 79 | echo << 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\EventListener; 13 | 14 | use FOS\UserBundle\Event\FormEvent; 15 | use FOS\UserBundle\Event\GetResponseUserEvent; 16 | use FOS\UserBundle\FOSUserEvents; 17 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 18 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 19 | use Symfony\Component\HttpFoundation\RedirectResponse; 20 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 21 | 22 | class RegistrationListener implements EventSubscriberInterface 23 | { 24 | private $router; 25 | private $userDiscriminator; 26 | 27 | public function __construct(UrlGeneratorInterface $router, UserDiscriminatorInterface $userDiscriminator) 28 | { 29 | $this->router = $router; 30 | $this->userDiscriminator = $userDiscriminator; 31 | } 32 | 33 | public static function getSubscribedEvents() 34 | { 35 | return array( 36 | FOSUserEvents::REGISTRATION_CONFIRM => array('onRegistrationConfirm', 1), 37 | FOSUserEvents::REGISTRATION_SUCCESS => array('onRegistrationSuccess', 1), 38 | ); 39 | } 40 | 41 | public function onRegistrationSuccess(FormEvent $event) 42 | { 43 | if (null === $event->getResponse()) { 44 | $url = $this->router->generate($this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_registration_confirmed'); 45 | $event->setResponse(new RedirectResponse($url)); 46 | } 47 | } 48 | 49 | public function onRegistrationConfirm(GetResponseUserEvent $event) 50 | { 51 | if (null === $event->getResponse()) { 52 | $url = $this->router->generate($this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_registration_confirmed'); 53 | $event->setResponse(new RedirectResponse($url)); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Form/Type/RegistrationFormType.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Form\Type; 13 | 14 | use Symfony\Component\Form\AbstractType; 15 | use Symfony\Component\Form\FormBuilderInterface; 16 | use Symfony\Component\OptionsResolver\OptionsResolverInterface; 17 | 18 | class RegistrationFormType extends AbstractType 19 | { 20 | /** 21 | * @var string 22 | */ 23 | private $class; 24 | 25 | /** 26 | * @var string 27 | */ 28 | private $typeName; 29 | 30 | /** 31 | * @param string $class The User class name 32 | * @param string $typeName The FormType name 33 | */ 34 | public function __construct($class, $typeName) 35 | { 36 | $this->class = $class; 37 | $this->typeName = $typeName; 38 | } 39 | 40 | public function buildForm(FormBuilderInterface $builder, array $options) 41 | { 42 | $builder 43 | ->add('email', 'email', array('label' => 'form.email', 'translation_domain' => 'FOSUserBundle')) 44 | ->add('username', null, array('label' => 'form.username', 'translation_domain' => 'FOSUserBundle')) 45 | ->add('plainPassword', 'repeated', array( 46 | 'type' => 'password', 47 | 'options' => array('translation_domain' => 'FOSUserBundle'), 48 | 'first_options' => array('label' => 'form.password'), 49 | 'second_options' => array('label' => 'form.password_confirmation'), 50 | 'invalid_message' => 'fos_user.password.mismatch', 51 | )) 52 | ; 53 | } 54 | 55 | public function setDefaultOptions(OptionsResolverInterface $resolver) 56 | { 57 | $resolver->setDefaults(array( 58 | 'data_class' => $this->class, 59 | 'intention' => 'registration', 60 | )); 61 | } 62 | 63 | public function getName() 64 | { 65 | return $this->typeName; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/EventListener/ResettingListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\EventListener; 13 | 14 | use FOS\UserBundle\Event\FormEvent; 15 | use FOS\UserBundle\Event\GetResponseUserEvent; 16 | use FOS\UserBundle\FOSUserEvents; 17 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 18 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 19 | use Symfony\Component\HttpFoundation\RedirectResponse; 20 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 21 | 22 | class ResettingListener implements EventSubscriberInterface 23 | { 24 | private $router; 25 | private $userDiscriminator; 26 | 27 | public function __construct(UrlGeneratorInterface $router, UserDiscriminatorInterface $userDiscriminator) 28 | { 29 | $this->router = $router; 30 | $this->userDiscriminator = $userDiscriminator; 31 | } 32 | 33 | public static function getSubscribedEvents() 34 | { 35 | return array( 36 | FOSUserEvents::RESETTING_RESET_INITIALIZE => array('onResettingResetInitialize', 1), 37 | FOSUserEvents::RESETTING_RESET_SUCCESS => array('onResettingResetSuccess', 1), 38 | ); 39 | } 40 | 41 | public function onResettingResetInitialize(GetResponseUserEvent $event) 42 | { 43 | $user = $this->userDiscriminator->getCurrentUserConfig(); 44 | 45 | $tokenTtl = $user->getConfig('resetting.token_ttl', 86400); 46 | 47 | if (!$event->getUser()->isPasswordRequestNonExpired($tokenTtl)) { 48 | $event->setResponse(new RedirectResponse($this->router->generate($user->getRoutePrefix().'_resetting_request'))); 49 | 50 | // Prevent the FOSUserBundle from overwriting 51 | $event->stopPropagation(); 52 | } 53 | } 54 | 55 | public function onResettingResetSuccess(FormEvent $event) 56 | { 57 | $event->setResponse(new RedirectResponse($this->router->generate($this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_profile_show'))); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollerworks/multi-user-bundle", 3 | "type": "symfony-bundle", 4 | "description": "Multi user management for the FOSUserBundle", 5 | "keywords": ["rollerworks", "multi", "user", "bundle", "FOSUserBundle"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Sebastiaan Stok", 10 | "email": "s.stok@rollercapes.net" 11 | }, 12 | { 13 | "name": "Contributors", 14 | "homepage": "https://github.com/rollerworks/RollerworksMultiUserBundle/graphs/contributors" 15 | } 16 | ], 17 | "minimum-stability": "dev", 18 | "prefer-stable": true, 19 | "require": { 20 | "php": ">=5.3.3", 21 | "symfony/framework-bundle": "~2.3|~3.0", 22 | "symfony/proxy-manager-bridge": "~2.3|~3.0", 23 | "ocramius/proxy-manager": "~0.4|~1.0", 24 | "symfony/finder": "~2.3|~3.0", 25 | "friendsofsymfony/user-bundle": "~2.0@dev,>2.0.0-alpha3", 26 | "rollerworks/sf-container-injector": "~1.0" 27 | }, 28 | "require-dev": { 29 | "twig/twig": "~1.5", 30 | "doctrine/doctrine-bundle": "~1.2", 31 | "doctrine/orm": "^2.2.3", 32 | "swiftmailer/swiftmailer": ">=4.3,<6.0", 33 | "symfony/yaml": "~2.3|~3.0", 34 | "symfony/validator": "~2.3|~3.0", 35 | "symfony/twig-bundle": "~2.3|~3.0", 36 | "symfony/monolog-bundle": "~2.3", 37 | "symfony/swiftmailer-bundle": "~2.3", 38 | "symfony/web-profiler-bundle": "~2.3|~3.0", 39 | "symfony/browser-kit": "~2.3|~3.0", 40 | "symfony/css-selector": "~2.3|~3.0", 41 | "symfony/process": "~2.3|~3.0", 42 | "symfony/expression-language": "~2.4|~3.0", 43 | "matthiasnoback/symfony-config-test": "^0.1.1", 44 | "sensio/generator-bundle": "~2.3|~3.0" 45 | }, 46 | "conflict": { 47 | "pugx/multi-user-bundle": "*" 48 | }, 49 | "autoload": { 50 | "psr-4": { 51 | "Rollerworks\\Bundle\\MultiUserBundle\\": "src/" 52 | } 53 | }, 54 | "autoload-dev": { 55 | "psr-4": { 56 | "Rollerworks\\Bundle\\MultiUserBundle\\Tests\\": "tests/" 57 | } 58 | }, 59 | "extra": { 60 | "branch-alias": { 61 | "dev-master": "1.1-dev" 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Command/CreateUserCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Command; 13 | 14 | use FOS\UserBundle\Command\CreateUserCommand as BaseCreateUserCommand; 15 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 16 | use Symfony\Component\Console\Input\InputInterface; 17 | use Symfony\Component\Console\Input\InputOption; 18 | use Symfony\Component\Console\Output\OutputInterface; 19 | 20 | /** 21 | * @author Sebastiaan Stok 22 | */ 23 | class CreateUserCommand extends BaseCreateUserCommand 24 | { 25 | protected function configure() 26 | { 27 | parent::configure(); 28 | 29 | $definition = $this->getDefinition(); 30 | $definition->addOption( 31 | new InputOption('user-system', null, InputOption::VALUE_REQUIRED, 'The user-system to use') 32 | ); 33 | 34 | $this 35 | ->setHelp(<<fos:user:create command creates a user: 37 | 38 | php app/console fos:user:create --user-system=acme_user matthieu 39 | 40 | This interactive shell will ask you for an email and then a password. 41 | 42 | You can alternatively specify the email and password as the second and third arguments: 43 | 44 | php app/console fos:user:create --user-system=acme_user matthieu matthieu@example.com mypassword 45 | 46 | You can create a super admin via the super-admin flag: 47 | 48 | php app/console fos:user:create admin --super-admin 49 | 50 | You can create an inactive user (will not be able to log in): 51 | 52 | php app/console fos:user:create thibault --inactive 53 | 54 | EOT 55 | ); 56 | } 57 | 58 | protected function interact(InputInterface $input, OutputInterface $output) 59 | { 60 | /** @var UserDiscriminatorInterface $discriminator */ 61 | $discriminator = $this->getContainer()->get('rollerworks_multi_user.user_discriminator'); 62 | $discriminator->setCurrentUser($input->getOption('user-system')); 63 | 64 | parent::interact($input, $output); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Command/DemoteUserCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Command; 13 | 14 | use FOS\UserBundle\Command\DemoteUserCommand as BaseDemoteUserCommand; 15 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 16 | use Symfony\Component\Console\Input\InputArgument; 17 | use Symfony\Component\Console\Input\InputDefinition; 18 | use Symfony\Component\Console\Input\InputInterface; 19 | use Symfony\Component\Console\Input\InputOption; 20 | use Symfony\Component\Console\Output\OutputInterface; 21 | 22 | /** 23 | * @author Sebastiaan Stok 24 | */ 25 | class DemoteUserCommand extends BaseDemoteUserCommand 26 | { 27 | protected function configure() 28 | { 29 | parent::configure(); 30 | 31 | $definition = new InputDefinition(); 32 | $definition->setDefinition(array( 33 | new InputArgument('username', InputArgument::REQUIRED, 'The username'), 34 | new InputArgument('role', InputArgument::OPTIONAL, 'The role'), 35 | new InputOption('user-system', null, InputOption::VALUE_REQUIRED, 'The user-system to use'), 36 | new InputOption('super', null, InputOption::VALUE_NONE, 'Instead specifying role, use this to quickly add the super administrator role'), 37 | )); 38 | 39 | $this->setDefinition($definition); 40 | 41 | $this 42 | ->setHelp(<<fos:user:demote command demotes a user by removing a role 44 | 45 | php app/console fos:user:demote --user-system=acme_user matthieu ROLE_CUSTOM 46 | php app/console fos:user:demote --user-system=acme_user --super matthieu 47 | EOT 48 | ); 49 | } 50 | 51 | protected function interact(InputInterface $input, OutputInterface $output) 52 | { 53 | /** @var UserDiscriminatorInterface $discriminator */ 54 | $discriminator = $this->getContainer()->get('rollerworks_multi_user.user_discriminator'); 55 | $discriminator->setCurrentUser($input->getOption('user-system')); 56 | 57 | parent::interact($input, $output); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Form/Type/ChangePasswordFormType.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Form\Type; 13 | 14 | use Symfony\Component\Form\AbstractType; 15 | use Symfony\Component\Form\FormBuilderInterface; 16 | use Symfony\Component\OptionsResolver\OptionsResolverInterface; 17 | use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; 18 | 19 | class ChangePasswordFormType extends AbstractType 20 | { 21 | /** 22 | * @var string 23 | */ 24 | private $class; 25 | 26 | /** 27 | * @var string 28 | */ 29 | private $typeName; 30 | 31 | /** 32 | * @param string $class The User class name 33 | * @param string $typeName The FormType name 34 | */ 35 | public function __construct($class, $typeName) 36 | { 37 | $this->class = $class; 38 | $this->typeName = $typeName; 39 | } 40 | 41 | public function buildForm(FormBuilderInterface $builder, array $options) 42 | { 43 | $constraint = new UserPassword(); 44 | 45 | $builder 46 | ->add('current_password', 'password', array( 47 | 'label' => 'form.current_password', 48 | 'translation_domain' => 'FOSUserBundle', 49 | 'mapped' => false, 50 | 'constraints' => $constraint, 51 | )) 52 | ->add('plainPassword', 'repeated', array( 53 | 'type' => 'password', 54 | 'options' => array('translation_domain' => 'FOSUserBundle'), 55 | 'first_options' => array('label' => 'form.new_password'), 56 | 'second_options' => array('label' => 'form.new_password_confirmation'), 57 | 'invalid_message' => 'fos_user.password.mismatch', 58 | )); 59 | } 60 | 61 | public function setDefaultOptions(OptionsResolverInterface $resolver) 62 | { 63 | $resolver->setDefaults(array( 64 | 'data_class' => $this->class, 65 | 'intention' => 'change_password', 66 | )); 67 | } 68 | 69 | public function getName() 70 | { 71 | return $this->typeName; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Model/DelegatingGroupManager.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Model; 13 | 14 | use FOS\UserBundle\Model\GroupInterface; 15 | use FOS\UserBundle\Model\GroupManagerInterface; 16 | 17 | /** 18 | * DelegatingGroupManager selects a GroupManager for the current user. 19 | * 20 | * Please don't use this manager as its only used for the original controllers. 21 | * 22 | * @author Sebastiaan Stok 23 | */ 24 | class DelegatingGroupManager implements GroupManagerInterface 25 | { 26 | private $userDiscriminator; 27 | 28 | public function __construct(UserDiscriminatorInterface $userDiscriminator) 29 | { 30 | $this->userDiscriminator = $userDiscriminator; 31 | } 32 | 33 | /** 34 | * @return UserDiscriminatorInterface 35 | */ 36 | public function getUserDiscriminator() 37 | { 38 | return $this->userDiscriminator; 39 | } 40 | 41 | public function getClass() 42 | { 43 | return $this->userDiscriminator->getCurrentUserConfig()->getGroupManager()->getClass(); 44 | } 45 | 46 | public function createGroup($name) 47 | { 48 | return $this->userDiscriminator->getCurrentUserConfig()->getGroupManager()->createGroup($name); 49 | } 50 | 51 | public function deleteGroup(GroupInterface $group) 52 | { 53 | $this->userDiscriminator->getCurrentUserConfig()->getGroupManager()->deleteGroup($group); 54 | } 55 | 56 | public function findGroupBy(array $criteria) 57 | { 58 | return $this->userDiscriminator->getCurrentUserConfig()->getGroupManager()->findGroupBy($criteria); 59 | } 60 | 61 | public function findGroupByName($name) 62 | { 63 | return $this->userDiscriminator->getCurrentUserConfig()->getGroupManager()->findGroupByName($name); 64 | } 65 | 66 | public function findGroups() 67 | { 68 | return $this->userDiscriminator->getCurrentUserConfig()->getGroupManager()->findGroups(); 69 | } 70 | 71 | public function updateGroup(GroupInterface $group) 72 | { 73 | $this->userDiscriminator->getCurrentUserConfig()->getGroupManager()->updateGroup($group); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Command/PromoteUserCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Command; 13 | 14 | use FOS\UserBundle\Command\PromoteUserCommand as BasePromoteUserCommand; 15 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 16 | use Symfony\Component\Console\Input\InputArgument; 17 | use Symfony\Component\Console\Input\InputDefinition; 18 | use Symfony\Component\Console\Input\InputInterface; 19 | use Symfony\Component\Console\Input\InputOption; 20 | use Symfony\Component\Console\Output\OutputInterface; 21 | 22 | /** 23 | * @author Sebastiaan Stok 24 | */ 25 | class PromoteUserCommand extends BasePromoteUserCommand 26 | { 27 | /** 28 | * @see Command 29 | */ 30 | protected function configure() 31 | { 32 | parent::configure(); 33 | 34 | $definition = new InputDefinition(); 35 | $definition->setDefinition(array( 36 | new InputArgument('username', InputArgument::REQUIRED, 'The username'), 37 | new InputArgument('role', InputArgument::OPTIONAL, 'The role'), 38 | new InputOption('user-system', null, InputOption::VALUE_REQUIRED, 'The user-system to use'), 39 | new InputOption('super', null, InputOption::VALUE_NONE, 'Instead specifying role, use this to quickly add the super administrator role'), 40 | )); 41 | 42 | $this->setDefinition($definition); 43 | $this 44 | ->setHelp(<<fos:user:promote command promotes a user by adding a role 46 | 47 | php app/console fos:user:promote --user-system=acme_user matthieu ROLE_CUSTOM 48 | php app/console fos:user:promote --user-system=acme_user --super matthieu 49 | EOT 50 | ); 51 | } 52 | 53 | protected function interact(InputInterface $input, OutputInterface $output) 54 | { 55 | /** @var UserDiscriminatorInterface $discriminator */ 56 | $discriminator = $this->getContainer()->get('rollerworks_multi_user.user_discriminator'); 57 | $discriminator->setCurrentUser($input->getOption('user-system')); 58 | 59 | parent::interact($input, $output); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docs/overriding_forms.md: -------------------------------------------------------------------------------- 1 | Overriding Default FOSUserBundle Forms 2 | ====================================== 3 | 4 | See [Overriding Default FOSUserBundle Forms](https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/overriding_forms.md) 5 | for full details. 6 | 7 | **Note:** 8 | 9 | > Each user-system must have its own form-types, you can not reuse the form types of UserA for UserB 10 | > as a form form-type can only be registered once, make sure `getName()` returns a unique name. 11 | 12 | Replace the `%fos_user.model.user.class%` with `%[service-prefix].model.user.class%` to get the correct model class. 13 | 14 | > `[service-prefix]` is what you have configured for the user-system. 15 | > And don't forget to do the same for `%fos_user.model.group.class%` 16 | 17 | ### Automatic registering 18 | 19 | The `UserServicesFactory` allows automatic registering of the Form service definition, 20 | to do this set the `class` configuration at the correct section with `UserServicesFactory::create()`. 21 | 22 | > Unless you have added additional constructor parameters to your class, this is preferred way of registering a type. 23 | 24 | ```php 25 | 26 | create('acme_user', array( 41 | array( 42 | // ... 43 | 44 | 'registration' => array( 45 | 'form' => array( 46 | 'type' => 'acme_user_registration', 47 | 'class' => 'Acme\UserBundle\Form\Type\RegistrationFormType', 48 | 'name' => 'acme_user_registration_form', 49 | ) 50 | ) 51 | ) 52 | )); 53 | } 54 | } 55 | ``` 56 | 57 | This will automatically register the `acme_user.registration.form.type` service with the correct user class. 58 | 59 | *Caution:* the `type` configuration is always required and must be the same as the returned value 60 | of the Form's `getName()` method. 61 | -------------------------------------------------------------------------------- /src/Form/Type/ProfileFormType.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Form\Type; 13 | 14 | use Symfony\Component\Form\AbstractType; 15 | use Symfony\Component\Form\FormBuilderInterface; 16 | use Symfony\Component\OptionsResolver\OptionsResolverInterface; 17 | use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; 18 | 19 | class ProfileFormType extends AbstractType 20 | { 21 | /** 22 | * @var string 23 | */ 24 | private $class; 25 | 26 | /** 27 | * @var string 28 | */ 29 | private $typeName; 30 | 31 | /** 32 | * @param string $class The User class name 33 | * @param string $typeName The FormType name 34 | */ 35 | public function __construct($class, $typeName) 36 | { 37 | $this->class = $class; 38 | $this->typeName = $typeName; 39 | } 40 | 41 | public function buildForm(FormBuilderInterface $builder, array $options) 42 | { 43 | $constraint = new UserPassword(); 44 | $this->buildUserForm($builder, $options); 45 | 46 | $builder->add('current_password', 'password', array( 47 | 'label' => 'form.current_password', 48 | 'translation_domain' => 'FOSUserBundle', 49 | 'mapped' => false, 50 | 'constraints' => $constraint, 51 | )); 52 | } 53 | 54 | public function setDefaultOptions(OptionsResolverInterface $resolver) 55 | { 56 | $resolver->setDefaults(array( 57 | 'data_class' => $this->class, 58 | 'intention' => 'profile', 59 | )); 60 | } 61 | 62 | public function getName() 63 | { 64 | return $this->typeName; 65 | } 66 | 67 | /** 68 | * Builds the embedded form representing the user. 69 | * 70 | * @param FormBuilderInterface $builder 71 | * @param array $options 72 | */ 73 | protected function buildUserForm(FormBuilderInterface $builder, array $options) 74 | { 75 | $builder 76 | ->add('username', null, array('label' => 'form.username', 'translation_domain' => 'FOSUserBundle')) 77 | ->add('email', 'email', array('label' => 'form.email', 'translation_domain' => 'FOSUserBundle')) 78 | ; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Resources/config/container/services.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Symfony\Component\HttpFoundation\RequestMatcher 9 | Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminator 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/EventListener/GroupListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\EventListener; 13 | 14 | use FOS\UserBundle\Event\FormEvent; 15 | use FOS\UserBundle\FOSUserEvents; 16 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 17 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 18 | use Symfony\Component\HttpFoundation\RedirectResponse; 19 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 20 | 21 | class GroupListener implements EventSubscriberInterface 22 | { 23 | private $router; 24 | private $userDiscriminator; 25 | 26 | public function __construct(UrlGeneratorInterface $router, UserDiscriminatorInterface $userDiscriminator) 27 | { 28 | $this->router = $router; 29 | $this->userDiscriminator = $userDiscriminator; 30 | } 31 | 32 | public static function getSubscribedEvents() 33 | { 34 | return array( 35 | FOSUserEvents::GROUP_CREATE_SUCCESS => array('onGroupCreateSuccess', 1), 36 | FOSUserEvents::GROUP_EDIT_SUCCESS => array('onGroupEditSuccess', 1), 37 | FOSUserEvents::GROUP_DELETE_COMPLETED => array('onGroupDeleteCompleted', 1), 38 | ); 39 | } 40 | 41 | public function onGroupCreateSuccess(FormEvent $event) 42 | { 43 | if (null === $event->getResponse()) { 44 | $url = $this->router->generate($this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_group_show', array('groupName' => $event->getForm()->getData()->getName())); 45 | $event->setResponse(new RedirectResponse($url)); 46 | } 47 | } 48 | 49 | public function onGroupEditSuccess(FormEvent $event) 50 | { 51 | if (null === $event->getResponse()) { 52 | $url = $this->router->generate($this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_group_show', array('groupName' => $event->getForm()->getData()->getName())); 53 | $event->setResponse(new RedirectResponse($url)); 54 | } 55 | } 56 | 57 | public function onGroupDeleteCompleted(FormEvent $event) 58 | { 59 | if ($event->getResponse() instanceof RedirectResponse) { 60 | $url = $this->router->generate($this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_group_list'); 61 | $event->setResponse(new RedirectResponse($url)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Routing/UserDiscriminatorUrlGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Routing; 13 | 14 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 15 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 16 | use Symfony\Component\Routing\RequestContext; 17 | use Symfony\Component\Routing\RouterInterface; 18 | 19 | /** 20 | * Changes the route name to a userDiscriminated version 21 | * and delegates to the real generator. 22 | * 23 | * @author Sebastiaan Stok 24 | */ 25 | class UserDiscriminatorUrlGenerator implements RouterInterface 26 | { 27 | private $userDiscriminator; 28 | private $generator; 29 | private $prefix; 30 | private $prefixLen; 31 | 32 | /** 33 | * @param UserDiscriminatorInterface $userDiscriminator 34 | * @param UrlGeneratorInterface $generator 35 | * @param string $prefix 36 | */ 37 | public function __construct(UserDiscriminatorInterface $userDiscriminator, UrlGeneratorInterface $generator, $prefix = 'fos_user') 38 | { 39 | $this->userDiscriminator = $userDiscriminator; 40 | $this->generator = $generator; 41 | $this->prefix = $prefix; 42 | $this->prefixLen = strlen($prefix); 43 | } 44 | 45 | /** 46 | * @codeCoverageIgnore 47 | */ 48 | public function setContext(RequestContext $context) 49 | { 50 | // noop 51 | } 52 | 53 | /** 54 | * @codeCoverageIgnore 55 | */ 56 | public function getContext() 57 | { 58 | // noop 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | */ 64 | public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) 65 | { 66 | if (substr($name, 0, $this->prefixLen) === $this->prefix) { 67 | $name = $this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().substr($name, $this->prefixLen); 68 | } 69 | 70 | return $this->generator->generate($name, $parameters, $referenceType); 71 | } 72 | 73 | /** 74 | * @codeCoverageIgnore 75 | */ 76 | public function getRouteCollection() 77 | { 78 | // noop 79 | } 80 | 81 | /** 82 | * @param string $pathinfo 83 | * 84 | * 85 | * @codeCoverageIgnore 86 | */ 87 | public function match($pathinfo) 88 | { 89 | // noop 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Model/UserDiscriminator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Model; 13 | 14 | use Rollerworks\Bundle\MultiUserBundle\Exception\NoActiveUserSystemException; 15 | use Symfony\Component\Form\Exception\UnexpectedTypeException; 16 | 17 | /** 18 | * @author Sebastiaan Stok 19 | */ 20 | class UserDiscriminator implements UserDiscriminatorInterface 21 | { 22 | /** 23 | * @var UserConfig[] 24 | */ 25 | protected $users = array(); 26 | 27 | /** 28 | * @var string 29 | */ 30 | protected $currentUser = null; 31 | 32 | /** 33 | * @param UserConfig[] $users 34 | */ 35 | public function __construct(array $users = null) 36 | { 37 | if ($users) { 38 | foreach ($users as $name => $user) { 39 | if (!$user instanceof UserConfig) { 40 | throw new UnexpectedTypeException($user, 'Rollerworks\Bundle\MultiUserBundle\Model\UserConfig'); 41 | } 42 | 43 | $this->users[$name] = $user; 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function addUser($name, UserConfig $user) 52 | { 53 | $this->users[$name] = $user; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function getCurrentUserConfig() 60 | { 61 | if (!$this->currentUser) { 62 | throw new NoActiveUserSystemException('Unable to get UserConfig, because there is no user-system active. Please call setCurrentUser() to activate a user-system or refer to the user-system services directly.'); 63 | } 64 | 65 | return $this->users[$this->currentUser]; 66 | } 67 | 68 | /** 69 | * {@inheritdoc} 70 | */ 71 | public function hasCurrentUserConfig() 72 | { 73 | return null !== $this->currentUser; 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | */ 79 | public function setCurrentUser($name) 80 | { 81 | if (!isset($this->users[$name])) { 82 | throw new \LogicException(sprintf('Impossible to set the user discriminator, because "%s" is not present in the users list.', $name)); 83 | } 84 | 85 | $this->currentUser = $name; 86 | } 87 | 88 | /** 89 | * {@inheritdoc} 90 | */ 91 | public function getCurrentUser() 92 | { 93 | return $this->currentUser; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/EventListener/AuthenticationListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\EventListener; 13 | 14 | use FOS\UserBundle\Event\UserEvent; 15 | use FOS\UserBundle\FOSUserEvents; 16 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 17 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 18 | use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; 19 | use Symfony\Component\Security\Http\SecurityEvents; 20 | 21 | /** 22 | * Determines the current user-object by the authentication. 23 | * 24 | * @author Sebastiaan Stok 25 | */ 26 | class AuthenticationListener implements EventSubscriberInterface 27 | { 28 | /** 29 | * @var UserDiscriminatorInterface 30 | */ 31 | private $userDiscriminator; 32 | 33 | /** 34 | * @var string[] 35 | */ 36 | private $users; 37 | 38 | /** 39 | * @param UserDiscriminatorInterface $userDiscriminator 40 | */ 41 | public function __construct(UserDiscriminatorInterface $userDiscriminator) 42 | { 43 | $this->userDiscriminator = $userDiscriminator; 44 | } 45 | 46 | /** 47 | * @param string $name 48 | * @param string $class 49 | */ 50 | public function addUser($name, $class) 51 | { 52 | $this->users[$class] = $name; 53 | } 54 | 55 | /** 56 | * @param UserEvent $event 57 | */ 58 | public function onSecurityImplicitLogin(UserEvent $event) 59 | { 60 | $this->discriminate($event->getUser()); 61 | } 62 | 63 | /** 64 | * @param InteractiveLoginEvent $event 65 | */ 66 | public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) 67 | { 68 | $this->discriminate($event->getAuthenticationToken()->getUser()); 69 | } 70 | 71 | /** 72 | * @return array 73 | * 74 | * @codeCoverageIgnore 75 | */ 76 | public static function getSubscribedEvents() 77 | { 78 | return array( 79 | FOSUserEvents::SECURITY_IMPLICIT_LOGIN => array('onSecurityImplicitLogin', 255), 80 | SecurityEvents::INTERACTIVE_LOGIN => array('onSecurityInteractiveLogin', 255), 81 | ); 82 | } 83 | 84 | /** 85 | * @param $user 86 | */ 87 | protected function discriminate($user) 88 | { 89 | $class = get_class($user); 90 | if (isset($this->users[$class])) { 91 | $this->userDiscriminator->setCurrentUser($this->users[$class]); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/EventListener/EmailConfirmationListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\EventListener; 13 | 14 | use FOS\UserBundle\Event\FormEvent; 15 | use FOS\UserBundle\FOSUserEvents; 16 | use FOS\UserBundle\Mailer\MailerInterface; 17 | use FOS\UserBundle\Util\TokenGeneratorInterface; 18 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 19 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 20 | use Symfony\Component\HttpFoundation\RedirectResponse; 21 | use Symfony\Component\HttpFoundation\Session\SessionInterface; 22 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 23 | 24 | class EmailConfirmationListener implements EventSubscriberInterface 25 | { 26 | private $mailer; 27 | private $tokenGenerator; 28 | private $router; 29 | private $session; 30 | 31 | /** 32 | * @var UserDiscriminatorInterface 33 | */ 34 | private $userDiscriminator; 35 | 36 | public function __construct(MailerInterface $mailer, TokenGeneratorInterface $tokenGenerator, UrlGeneratorInterface $router, SessionInterface $session, UserDiscriminatorInterface $userDiscriminator) 37 | { 38 | $this->mailer = $mailer; 39 | $this->tokenGenerator = $tokenGenerator; 40 | $this->router = $router; 41 | $this->session = $session; 42 | $this->userDiscriminator = $userDiscriminator; 43 | } 44 | 45 | public static function getSubscribedEvents() 46 | { 47 | return array( 48 | FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess', 49 | ); 50 | } 51 | 52 | public function onRegistrationSuccess(FormEvent $event) 53 | { 54 | if (!$this->userDiscriminator->getCurrentUserConfig()->getConfig('registering.confirmation.enabled', false)) { 55 | return; 56 | } 57 | 58 | /** @var $user \FOS\UserBundle\Model\UserInterface */ 59 | $user = $event->getForm()->getData(); 60 | 61 | $user->setEnabled(false); 62 | if (null === $user->getConfirmationToken()) { 63 | $user->setConfirmationToken($this->tokenGenerator->generateToken()); 64 | } 65 | 66 | $this->mailer->sendConfirmationEmailMessage($user); 67 | $this->session->set('fos_user_send_confirmation_email/email', $user->getEmail()); 68 | 69 | $url = $this->router->generate($this->userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_registration_check_email'); 70 | $event->setResponse(new RedirectResponse($url)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Controller/ResettingController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Controller; 13 | 14 | use FOS\UserBundle\Controller\ResettingController as BaseResettingController; 15 | use Symfony\Component\HttpFoundation\RedirectResponse; 16 | use Symfony\Component\HttpFoundation\Request; 17 | 18 | class ResettingController extends BaseResettingController 19 | { 20 | public function checkEmailAction(Request $request) 21 | { 22 | $userDiscriminator = $this->container->get('rollerworks_multi_user.user_discriminator'); 23 | $email = $request->query->get('email'); 24 | 25 | if (empty($email)) { 26 | // the user does not come from the sendEmail action 27 | return new RedirectResponse($this->container->get('router')->generate($userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_resetting_request')); 28 | } 29 | 30 | return $this->container->get('templating')->renderResponse('FOSUserBundle:Resetting:checkEmail.html.twig', array( 31 | 'email' => $email, 32 | )); 33 | } 34 | 35 | public function sendEmailAction(Request $request) 36 | { 37 | $userDiscriminator = $this->container->get('rollerworks_multi_user.user_discriminator'); 38 | $username = $request->request->get('username'); 39 | $user = $this->container->get('fos_user.user_manager')->findUserByUsernameOrEmail($username); 40 | 41 | if (null === $user) { 42 | return $this->container->get('templating')->renderResponse('FOSUserBundle:Resetting:request.html.twig', array('invalid_username' => $username)); 43 | } 44 | 45 | if ($user->isPasswordRequestNonExpired($userDiscriminator->getCurrentUserConfig()->getConfig('resetting.token_ttl'))) { 46 | return $this->container->get('templating')->renderResponse('FOSUserBundle:Resetting:passwordAlreadyRequested.html.twig'); 47 | } 48 | 49 | if (null === $user->getConfirmationToken()) { 50 | $tokenGenerator = $this->container->get('fos_user.util.token_generator'); 51 | $user->setConfirmationToken($tokenGenerator->generateToken()); 52 | } 53 | 54 | $this->container->get('fos_user.mailer')->sendResettingEmailMessage($user); 55 | $user->setPasswordRequestedAt(new \DateTime()); 56 | $this->container->get('fos_user.user_manager')->updateUser($user); 57 | 58 | return new RedirectResponse($this->container->get('router')->generate( 59 | $userDiscriminator->getCurrentUserConfig()->getRoutePrefix().'_resetting_check_email', 60 | array('email' => $this->getObfuscatedEmail($user)) 61 | )); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/RollerworksMultiUserBundle.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle; 13 | 14 | use Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Compiler\OverrideServiceCompilerPass; 15 | use Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Compiler\RegisterUserPass; 16 | use Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Compiler\RemoveParentServicesPass; 17 | use Symfony\Component\Console\Application; 18 | use Symfony\Component\DependencyInjection\ContainerBuilder; 19 | use Symfony\Component\Finder\Finder; 20 | use Symfony\Component\HttpKernel\Bundle\Bundle; 21 | 22 | /** 23 | * RollerworksMultiUserBundle. 24 | * 25 | * Provides user management functionality (authentication, authorization, etc). 26 | * 27 | * @author Sebastiaan Stok 28 | */ 29 | class RollerworksMultiUserBundle extends Bundle 30 | { 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function build(ContainerBuilder $container) 35 | { 36 | parent::build($container); 37 | 38 | $container->addCompilerPass(new OverrideServiceCompilerPass()); 39 | $container->addCompilerPass(new RemoveParentServicesPass()); 40 | $container->addCompilerPass(new RegisterUserPass()); 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | public function getParent() 47 | { 48 | return 'FOSUserBundle'; 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public function registerCommands(Application $application) 55 | { 56 | if (!is_dir($dir = $this->getPath().'/Command')) { 57 | return; 58 | } 59 | 60 | $finder = new Finder(); 61 | $finder->files()->name('*Command.php')->in($dir); 62 | 63 | // Don't enable the generate command when SensioGeneratorBundle is not installed 64 | if (!class_exists('Sensio\\Bundle\\GeneratorBundle\\Command\\GenerateBundleCommand')) { 65 | $finder->notName('GenerateUserSysCommand.php'); 66 | } 67 | 68 | $prefix = $this->getNamespace().'\\Command'; 69 | foreach ($finder as $file) { 70 | $ns = $prefix; 71 | if ($relativePath = $file->getRelativePath()) { 72 | $ns .= '\\'.strtr($relativePath, '/', '\\'); 73 | } 74 | $class = $ns.'\\'.$file->getBasename('.php'); 75 | 76 | $r = new \ReflectionClass($class); 77 | if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) { 78 | $application->add($r->newInstance()); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Resources/config/container/forms.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminator 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | registration 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | resetting 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | profile 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | change_password 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | group 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/Model/DelegatingUserManager.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Model; 13 | 14 | use FOS\UserBundle\Model\UserInterface; 15 | use FOS\UserBundle\Model\UserManagerInterface; 16 | 17 | /** 18 | * DelegatingUserManager selects a UserManager for the current user. 19 | * 20 | * Please don't use this manager as its only used for the original controllers. 21 | * 22 | * @author Sebastiaan Stok 23 | */ 24 | class DelegatingUserManager implements UserManagerInterface 25 | { 26 | private $userDiscriminator; 27 | 28 | public function __construct(UserDiscriminatorInterface $userDiscriminator) 29 | { 30 | $this->userDiscriminator = $userDiscriminator; 31 | } 32 | 33 | /** 34 | * @return UserDiscriminatorInterface 35 | */ 36 | public function getUserDiscriminator() 37 | { 38 | return $this->userDiscriminator; 39 | } 40 | 41 | public function createUser() 42 | { 43 | return $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->createUser(); 44 | } 45 | 46 | public function deleteUser(UserInterface $user) 47 | { 48 | $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->deleteUser($user); 49 | } 50 | 51 | public function findUserBy(array $criteria) 52 | { 53 | return $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->findUserBy($criteria); 54 | } 55 | 56 | public function findUserByUsername($username) 57 | { 58 | return $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->findUserByUsername($username); 59 | } 60 | 61 | public function findUserByEmail($email) 62 | { 63 | return $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->findUserByEmail($email); 64 | } 65 | 66 | public function findUserByUsernameOrEmail($usernameOrEmail) 67 | { 68 | return $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->findUserByUsernameOrEmail($usernameOrEmail); 69 | } 70 | 71 | public function findUserByConfirmationToken($token) 72 | { 73 | return $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->findUserByConfirmationToken($token); 74 | } 75 | 76 | public function findUsers() 77 | { 78 | return $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->findUsers(); 79 | } 80 | 81 | public function getClass() 82 | { 83 | return $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->getClass(); 84 | } 85 | 86 | public function reloadUser(UserInterface $user) 87 | { 88 | $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->reloadUser($user); 89 | } 90 | 91 | public function updateUser(UserInterface $user) 92 | { 93 | $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->updateUser($user); 94 | } 95 | 96 | public function updateCanonicalFields(UserInterface $user) 97 | { 98 | $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->updateCanonicalFields($user); 99 | } 100 | 101 | public function updatePassword(UserInterface $user) 102 | { 103 | $this->userDiscriminator->getCurrentUserConfig()->getUserManager()->updatePassword($user); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/RegisterFosUserMappingsPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Compiler; 13 | 14 | use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterMappingsPass; 15 | use Symfony\Component\DependencyInjection\Definition; 16 | 17 | class RegisterFosUserMappingsPass extends RegisterMappingsPass 18 | { 19 | public static function createOrmMappingDriver($servicePrefix) 20 | { 21 | $namespaces = self::getMappings(); 22 | 23 | $arguments = array($namespaces, '.orm.xml'); 24 | $locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments); 25 | $driver = new Definition('Doctrine\ORM\Mapping\Driver\XmlDriver', array($locator)); 26 | 27 | $managerParameters = array($servicePrefix.'.model_manager_name', 'doctrine.default_entity_manager'); 28 | $enabledParameter = $servicePrefix.'.backend_type_orm'; 29 | 30 | return new self( 31 | $driver, 32 | $namespaces, 33 | $managerParameters, 34 | 'doctrine.orm.%s_metadata_driver', 35 | $enabledParameter, 36 | 'doctrine.orm.%s_configuration' 37 | ); 38 | } 39 | 40 | public static function createMongoDBMappingDriver($servicePrefix) 41 | { 42 | $namespaces = self::getMappings(); 43 | 44 | $arguments = array($namespaces, '.mongodb.xml'); 45 | $locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments); 46 | $driver = new Definition('Doctrine\ODM\MongoDB\Mapping\Driver\XmlDriver', array($locator)); 47 | 48 | $managerParameters = array($servicePrefix.'.model_manager_name', 'doctrine_mongodb.odm.default_document_manager'); 49 | $enabledParameter = $servicePrefix.'.backend_type_mongodb'; 50 | 51 | return new self( 52 | $driver, 53 | $namespaces, 54 | $managerParameters, 55 | 'doctrine_mongodb.odm.%s_metadata_driver', 56 | $enabledParameter, 57 | 'doctrine_mongodb.odm.%s_configuration' 58 | ); 59 | } 60 | 61 | public static function createCouchDBMappingDriver($servicePrefix) 62 | { 63 | $namespaces = self::getMappings(); 64 | 65 | $arguments = array($namespaces, '.couchdb.xml'); 66 | $locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments); 67 | $driver = new Definition('Doctrine\ODM\CouchDB\Mapping\Driver\XmlDriver', array($locator)); 68 | 69 | $managerParameters = array($servicePrefix.'.model_manager_name', 'doctrine_couchdb.default_document_manager'); 70 | $enabledParameter = $servicePrefix.'.backend_type_couchdb'; 71 | 72 | return new self( 73 | $driver, 74 | $namespaces, 75 | $managerParameters, 76 | 'doctrine_couchdb.odm.%s_metadata_driver', 77 | $enabledParameter, 78 | 'doctrine_couchdb.odm.%s_configuration' 79 | ); 80 | } 81 | 82 | private static function getMappings() 83 | { 84 | static $mappings; 85 | 86 | if (null === $mappings) { 87 | $r = new \ReflectionClass('FOS\UserBundle\FOSUserBundle'); 88 | $mappings = array( 89 | realpath(dirname($r->getFilename()).'/Resources/config/doctrine-mapping') => 'FOS\UserBundle\Model', 90 | ); 91 | } 92 | 93 | return $mappings; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/DependencyInjection/RollerworksMultiUserExtension.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\DependencyInjection; 13 | 14 | use Symfony\Component\Config\FileLocator; 15 | use Symfony\Component\DependencyInjection\ContainerBuilder; 16 | use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; 17 | use Symfony\Component\DependencyInjection\Loader; 18 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; 19 | 20 | /** 21 | * @author Sebastiaan Stok 22 | */ 23 | class RollerworksMultiUserExtension extends Extension implements PrependExtensionInterface 24 | { 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function load(array $configs, ContainerBuilder $container) 29 | { 30 | $configuration = new Configuration(); 31 | $config = $this->processConfiguration($configuration, $configs); 32 | 33 | $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 34 | $loader->load('container/services.xml'); 35 | $loader->load('container/listeners.xml'); 36 | $loader->load('container/forms.xml'); 37 | $loader->load('container/templating_twig.xml'); 38 | $loader->load('container/templating_php.xml'); 39 | 40 | $container->setParameter('rollerworks_multi_user.from_email.address', $config['from_email']['address']); 41 | $container->setParameter('rollerworks_multi_user.from_email.sender_name', $config['from_email']['sender_name']); 42 | 43 | // add some required classes for compilation 44 | $this->addClassesToCompile(array( 45 | 'Rollerworks\\Bundle\\MultiUserBundle\\Model\\UserConfig', 46 | 'Rollerworks\\Bundle\\MultiUserBundle\\Model\\UserDiscriminator', 47 | 'Rollerworks\\Bundle\\MultiUserBundle\\EventListener\\RequestListener', 48 | 'Rollerworks\\Bundle\\MultiUserBundle\\EventListener\\AuthenticationListener', 49 | )); 50 | } 51 | 52 | public function prepend(ContainerBuilder $container) 53 | { 54 | $configs = $container->getExtensionConfig($this->getAlias()); 55 | $config = $this->processConfiguration(new Configuration(), $configs); 56 | 57 | $fosConfig = array( 58 | 'db_driver' => $config['db_driver'], // Pass the driver until we have a proper fix for issue multiple drivers 59 | 'use_listener' => false, 60 | 'use_flash_notifications' => $config['use_flash_notifications'], 61 | 'firewall_name' => 'dummy', 62 | 'user_class' => 'Acme\UserBundle\Entity\User', 63 | 'from_email' => $config['from_email'], 64 | 'registration' => array( 65 | 'confirmation' => array( 66 | 'enabled' => false, 67 | ), 68 | ), 69 | 'service' => array( 70 | 'mailer' => 'rollerworks_multi_user.mailer.delegating', 71 | 'user_manager' => 'rollerworks_multi_user.user_manager.delegating', 72 | ), 73 | 'group' => array( 74 | 'group_class' => 'Acme\UserBundle\Entity\Group', 75 | 'group_manager' => 'rollerworks_multi_user.group_manager.delegating', 76 | ), 77 | ); 78 | 79 | /* 80 | * We provide the FosUserBundle with a dummy configuration to load everything. 81 | * Later the parent services are removed. 82 | */ 83 | 84 | $container->prependExtensionConfig('fos_user', $fosConfig); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Doctrine/AbstractUserListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Doctrine; 13 | 14 | use Doctrine\Common\EventSubscriber; 15 | use Doctrine\Common\Persistence\Event\LifecycleEventArgs; 16 | use FOS\UserBundle\Model\UserInterface; 17 | use FOS\UserBundle\Model\UserManagerInterface; 18 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 19 | use Rollerworks\Component\SfContainerInjector\ContainerInjector; 20 | 21 | /** 22 | * Base Doctrine listener updating the canonical username and password fields. 23 | * 24 | * Overwritten by database specific listeners to register the right events and 25 | * to let the UoW recalculate the change set if needed. 26 | * 27 | * @author Sebastiaan Stok 28 | * @author Christophe Coevoet 29 | * @author David Buchmann 30 | */ 31 | abstract class AbstractUserListener implements EventSubscriber 32 | { 33 | /** 34 | * @var UserManagerInterface 35 | */ 36 | private $userManager; 37 | 38 | /** 39 | * @var UserDiscriminatorInterface 40 | */ 41 | private $userDiscriminator; 42 | 43 | /** 44 | * @var ContainerInjector 45 | */ 46 | private $container; 47 | 48 | /** 49 | * Constructor. 50 | * 51 | * @param ContainerInjector $container 52 | */ 53 | public function __construct(ContainerInjector $container) 54 | { 55 | $this->container = $container; 56 | } 57 | 58 | /** 59 | * {inheritdoc}. 60 | */ 61 | public function prePersist($args) 62 | { 63 | if (null === $this->userDiscriminator->getCurrentUser()) { 64 | return; 65 | } 66 | 67 | $object = $args->getObject(); 68 | if ($object instanceof UserInterface && $this->userDiscriminator->getCurrentUserConfig()->getConfig('use_listener', true)) { 69 | $this->updateUserFields($object); 70 | } 71 | } 72 | 73 | /** 74 | * Pre update listener based on doctrine commons, overwrite to update 75 | * the changeset in the UoW and to handle non-common event argument 76 | * class. 77 | * 78 | * @param LifecycleEventArgs $args weak typed to allow overwriting 79 | */ 80 | public function preUpdate($args) 81 | { 82 | if (null === $this->userDiscriminator->getCurrentUser()) { 83 | return; 84 | } 85 | 86 | $object = $args->getObject(); 87 | if ($object instanceof UserInterface && $this->userDiscriminator->getCurrentUserConfig()->getConfig('use_listener', true)) { 88 | $this->updateUserFields($object); 89 | } 90 | } 91 | 92 | /** 93 | * This must be called on prePersist and preUpdate if the event is about a 94 | * user. 95 | * 96 | * @param UserInterface $user 97 | */ 98 | protected function updateUserFields(UserInterface $user) 99 | { 100 | if (null === $this->userDiscriminator) { 101 | $this->userDiscriminator = $this->container->get('rollerworks_multi_user.user_discriminator'); 102 | } 103 | 104 | // Can only use the user manager when there is an user-system active 105 | if (null === $this->userDiscriminator->getCurrentUser() || true !== $this->userDiscriminator->getCurrentUserConfig()->getConfig('use_listener', true)) { 106 | return; 107 | } 108 | 109 | if (null === $this->userManager) { 110 | $this->userManager = $this->container->get('fos_user.user_manager'); 111 | } 112 | 113 | $this->userManager->updateCanonicalFields($user); 114 | $this->userManager->updatePassword($user); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Resources/config/container/listeners.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Symfony\Component\HttpFoundation\RequestMatcher 9 | Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminator 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/RegisterUserPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Compiler; 13 | 14 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 15 | use Symfony\Component\DependencyInjection\ContainerBuilder; 16 | use Symfony\Component\DependencyInjection\Reference; 17 | 18 | /** 19 | * RegisterUserPass, registers the user-systems with the UserDiscriminator. 20 | * 21 | * @author Sebastiaan Stok 22 | */ 23 | class RegisterUserPass implements CompilerPassInterface 24 | { 25 | private $requestMatchers = array(); 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function process(ContainerBuilder $container) 31 | { 32 | if (!$container->hasDefinition('rollerworks_multi_user.user_discriminator')) { 33 | return; 34 | } 35 | 36 | $requestListener = null; 37 | $authenticationListener = null; 38 | 39 | if ($container->hasDefinition('rollerworks_multi_user.listener.request')) { 40 | $requestListener = $container->getDefinition('rollerworks_multi_user.listener.request'); 41 | } 42 | 43 | if ($container->hasDefinition('rollerworks_multi_user.listener.authentication')) { 44 | $authenticationListener = $container->getDefinition('rollerworks_multi_user.listener.authentication'); 45 | } 46 | 47 | $userDiscriminator = $container->getDefinition('rollerworks_multi_user.user_discriminator'); 48 | 49 | foreach ($container->findTaggedServiceIds('rollerworks_multi_user.user_system') as $id => $attributes) { 50 | $name = $attributes[0]['alias']; 51 | 52 | if (!isset($attributes[0]['request_matcher'])) { 53 | $requestMatcher = $this->createRequestMatcher($container, $container->getParameterBag()->resolveValue($attributes[0]['path']), $container->getParameterBag()->resolveValue($attributes[0]['host'])); 54 | } else { 55 | $requestMatcher = new Reference($attributes[0]['request_matcher']); 56 | } 57 | 58 | if ($authenticationListener) { 59 | $authenticationListener->addMethodCall('addUser', array($name, $container->getParameterBag()->resolveValue($attributes[0]['class']))); 60 | } 61 | 62 | if ($requestListener) { 63 | $requestListener->addMethodCall('addUser', array($name, $requestMatcher)); 64 | } 65 | 66 | $userDiscriminator->addMethodCall('addUser', array($name, new Reference($id))); 67 | } 68 | } 69 | 70 | /** 71 | * Copied from the SymfonySecurityBundle. 72 | */ 73 | private function createRequestMatcher(ContainerBuilder $container, $path = null, $host = null, $methods = array(), $ip = null, array $attributes = array()) 74 | { 75 | $serialized = serialize(array($path, $host, $methods, $ip, $attributes)); 76 | $id = 'rollerworks_multi_user.request_matcher.'.md5($serialized).sha1($serialized); 77 | 78 | // Matcher already exist, which is not allowed 79 | if (isset($this->requestMatchers[$id])) { 80 | throw new \RuntimeException(sprintf( 81 | 'There is already a request-matcher for this configuration: path: "%s", host: "%s". Only one request matcher should match for the user system.', 82 | $path, 83 | $host 84 | )); 85 | } 86 | 87 | if ($methods) { 88 | $methods = array_map('strtoupper', (array) $methods); 89 | } 90 | 91 | // only add arguments that are necessary 92 | $arguments = array($path, $host, $methods, $ip, $attributes); 93 | while (count($arguments) > 0 && !end($arguments)) { 94 | array_pop($arguments); 95 | } 96 | 97 | $container 98 | ->register($id, '%rollerworks_multi_user.matcher.class%') 99 | ->setPublic(false) 100 | ->setArguments($arguments) 101 | ; 102 | 103 | return $this->requestMatchers[$id] = new Reference($id); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RollerworksMultiUserBundle 2 | ========================== 3 | 4 | # !! MAINTAINERS WANTED !! 5 | 6 | **This project is no longer actively maintained!** 7 | If you would like to takeover this project please leave a comment in: 8 | https://github.com/rollerworks/RollerworksMultiUserBundle/issues/96 9 | 10 | [![Join the chat at https://gitter.im/rollerworks/RollerworksMultiUserBundle](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/rollerworks/RollerworksMultiUserBundle?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 11 | 12 | [![Build Status](https://travis-ci.org/rollerworks/RollerworksMultiUserBundle.png?branch=master)](https://travis-ci.org/rollerworks/RollerworksMultiUserBundle) 13 | [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/rollerworks/RollerworksMultiUserBundle/badges/quality-score.png?s=d98ca957ce5deb8b4bd41532cae263b8a1639121)](https://scrutinizer-ci.com/g/rollerworks/RollerworksMultiUserBundle/) 14 | [![SensioLabsInsight](https://insight.sensiolabs.com/projects/9a47cef8-7640-4f20-9efe-0153325d66ba/mini.png)](https://insight.sensiolabs.com/projects/9a47cef8-7640-4f20-9efe-0153325d66ba) 15 | 16 | The RollerworksMultiUserBundle adds support for a multi-user set-up using the FOSUserBundle. 17 | It provides a fully compatible in-place replacement for the 'fos_user' services. 18 | 19 | In practice it is build on-top of the FOSUserBundle, and uses the original controllers, forms and UserManager. 20 | 21 | Features include: 22 | 23 | - Fully compatible with existing bundles that already use the FOSUserBundle 24 | - Unlimited user-systems, each with there own configuration, storage engine, templates, forms, etc. 25 | - Easy generation of new user-systems 26 | - Unit tested 27 | 28 | **Caution:** This bundle is developed in sync with [FOSUserBundle's repository](https://github.com/FriendsOfSymfony/FOSUserBundle). 29 | For FOSUserBundle 2.0.x, you need to use the 1.0 release of the bundle (or lower). 30 | 31 | **As the FOSUserBundle 2.0 is not stable yet, multi-user support is considered experimental!** 32 | 33 | **Warning:** This bundle can not be used in combination with the PUGXMultiUserBundle. 34 | If the PUGXMultiUserBundle is installed, then please remove it before continuing. 35 | 36 | Documentation 37 | ------------- 38 | 39 | The bulk of the documentation is stored in the `doc/index.md` 40 | file in this bundle: 41 | 42 | [Read the Documentation for master](https://github.com/rollerworks/RollerworksMultiUserBundle/blob/master/docs/index.md) 43 | 44 | Installation 45 | ------------ 46 | 47 | All the installation instructions are located in [documentation](https://github.com/rollerworks/RollerworksMultiUserBundle/blob/master/docs/index.md). 48 | 49 | License 50 | ------- 51 | 52 | This bundle is released under the MIT license. 53 | See the bundled LICENSE file for details. 54 | 55 | About 56 | ----- 57 | 58 | RollerworksMultiUserBundle was designed as an alternative to the PUGXMultiUserBundle. 59 | 60 | A major difference to the PUGXMultiUserBundle is that RollerworksMultiUserBundle 61 | does not use Doctrine ORM Joined-Entity inheritance and provides a richer set of features. 62 | 63 | Reporting an issue or a feature request 64 | --------------------------------------- 65 | 66 | Issues and feature requests are tracked in the [Github issue tracker](https://github.com/Rollerworks/RollerworksMultiUserBundle/issues). 67 | 68 | When reporting a bug, it may be a good idea to reproduce it in a basic project 69 | built using the [Symfony Standard Edition](https://github.com/symfony/symfony-standard) 70 | to allow developers of the bundle to reproduce the issue by simply cloning it 71 | and following some steps. 72 | 73 | For opening a PR please use [Gush](http://gushphp.org/) so the information template is included. 74 | 75 | Credits 76 | ------- 77 | 78 | The original idea of the UserDiscriminator came from the PUGXMultiUserBundle. 79 | 80 | A major difference to the PUGXMultiUserBundle is as that RollerworksMultiUserBundle 81 | does not use Doctrine ORM Joined-Entity inheritance and every user-manager is accessible without 'discriminating'. 82 | 83 | This bundle contains source originally designed by the FOSUserBundle developers. 84 | 85 | Running the Tests 86 | ------- 87 | 88 | Before running the tests, you will need to install the bundle dependencies. Do this using composer: 89 | 90 | > The doctrine/mongodb-odm is required for functional tests 91 | > but are not installed by default as it fails with some of the automated code analyzers. 92 | 93 | ``` bash 94 | $ php composer.phar composer require doctrine/mongodb-odm:"1.0.*@dev" --no-update 95 | $ php composer.phar composer require doctrine/mongodb-odm-bundle:"3.0.*@dev" --no-update 96 | $ php composer.phar --dev install 97 | ``` 98 | 99 | Then you can launch phpunit (make sure its installed https://github.com/sebastianbergmann/phpunit/#installation) 100 | 101 | > Using the Composer version of PHPUnit currently fails so make sure to either use the Phar archive or PEAR version. 102 | > You need at least version 3.6 of PHPUnit and MockObject plugin 1.0.8 103 | 104 | ``` bash 105 | $ phpunit -c phpunit.xml.dist 106 | ``` 107 | 108 | **Note:** Functional test are not run by default, to run all tests make sure both 109 | PDO_SQLite and the PHP extension for MongoDB are installed, and launch phpunit with: 110 | 111 | ``` bash 112 | $ bin/phpunit -c phpunit.xml.dist --exclude-group "" 113 | ``` 114 | 115 | > Optionally you may skip the functional tests as these are always run automatically on Travis-CI 116 | > when opening a Pull Request. 117 | -------------------------------------------------------------------------------- /src/Model/UserConfig.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Model; 13 | 14 | use FOS\UserBundle\Model\GroupManagerInterface; 15 | use FOS\UserBundle\Model\UserManagerInterface; 16 | use Rollerworks\Bundle\MultiUserBundle\Exception\MissingTemplateException; 17 | 18 | /** 19 | * UserConfiguration, holds the configuration of a user in the system. 20 | * 21 | * @author Sebastiaan Stok 22 | */ 23 | class UserConfig 24 | { 25 | protected $servicePrefix; 26 | protected $routePrefix; 27 | protected $templates; 28 | protected $forms; 29 | protected $config; 30 | 31 | protected $userManager; 32 | protected $groupManager; 33 | 34 | /** 35 | * Constructor. 36 | * 37 | * @param string $servicePrefix 38 | * @param string $routePrefix 39 | * @param UserManagerInterface $userManager 40 | * @param GroupManagerInterface $groupManager 41 | */ 42 | public function __construct($servicePrefix, $routePrefix, UserManagerInterface $userManager, GroupManagerInterface $groupManager) 43 | { 44 | $this->servicePrefix = $servicePrefix; 45 | $this->routePrefix = $routePrefix; 46 | $this->userManager = $userManager; 47 | $this->groupManager = $groupManager; 48 | $this->templates = array(); 49 | $this->forms = array(); 50 | $this->config = array(); 51 | } 52 | 53 | /** 54 | * Set a config. 55 | * 56 | * @param string $name 57 | * @param string|int|bool|array $value 58 | */ 59 | public function setConfig($name, $value) 60 | { 61 | $this->config[$name] = $value; 62 | } 63 | 64 | /** 65 | * @param string $name 66 | * @param string|int|bool|array $defaultValue 67 | * 68 | * @return string|int|bool|array 69 | */ 70 | public function getConfig($name, $defaultValue = null) 71 | { 72 | return array_key_exists($name, $this->config) ? $this->config[$name] : $defaultValue; 73 | } 74 | 75 | /** 76 | * Set the form configuration. 77 | * 78 | * @param string $name 79 | * @param string $formName 80 | * @param string $type 81 | * @param array $validationGroups 82 | * 83 | * @return static 84 | */ 85 | public function setForm($name, $formName, $type, array $validationGroups) 86 | { 87 | $this->forms[$name] = array('type' => $type, 'name' => $formName, 'validation_groups' => $validationGroups); 88 | 89 | return $this; 90 | } 91 | 92 | /** 93 | * Set the template configuration. 94 | * 95 | * @param string $name 96 | * @param string $resource 97 | * 98 | * @return static 99 | */ 100 | public function setTemplate($name, $resource) 101 | { 102 | $this->templates[$name] = $resource; 103 | 104 | return $this; 105 | } 106 | 107 | /** 108 | * Get the form-type name for the given config-name. 109 | * 110 | * @param string $name 111 | * 112 | * @return string|null 113 | */ 114 | public function getFormType($name) 115 | { 116 | return isset($this->forms[$name]['type']) ? $this->forms[$name]['type'] : null; 117 | } 118 | 119 | /** 120 | * Get the form name for the given config-name. 121 | * 122 | * @param string $name 123 | * 124 | * @return string|null 125 | */ 126 | public function getFormName($name) 127 | { 128 | return isset($this->forms[$name]['name']) ? $this->forms[$name]['name'] : null; 129 | } 130 | 131 | /** 132 | * Get the validation groups for the given config-name. 133 | * 134 | * @param string $name 135 | * 136 | * @return array|null 137 | */ 138 | public function getFormValidationGroups($name) 139 | { 140 | return isset($this->forms[$name]['validation_groups']) ? $this->forms[$name]['validation_groups'] : null; 141 | } 142 | 143 | /** 144 | * Get the View template for the given config-name. 145 | * 146 | * @param string $name 147 | * 148 | * @return string 149 | */ 150 | public function getTemplate($name) 151 | { 152 | if (!isset($this->templates[$name])) { 153 | throw new MissingTemplateException(sprintf('Unable to get template for "%s", there is no such template configured.', $name)); 154 | } 155 | 156 | $template = (string) $this->templates[$name]; 157 | if ('' === $template) { 158 | throw new MissingTemplateException(sprintf('Unable to get template for "%s", it seems the template value is empty. Make sure you enabled the proper section and configured a proper value.', $name)); 159 | } 160 | 161 | return $template; 162 | } 163 | 164 | /** 165 | * @return UserManagerInterface 166 | */ 167 | public function getUserManager() 168 | { 169 | return $this->userManager; 170 | } 171 | 172 | /** 173 | * @return GroupManagerInterface 174 | */ 175 | public function getGroupManager() 176 | { 177 | return $this->groupManager; 178 | } 179 | 180 | /** 181 | * @return string 182 | */ 183 | public function getServicePrefix() 184 | { 185 | return $this->servicePrefix; 186 | } 187 | 188 | /** 189 | * @return string 190 | */ 191 | public function getRoutePrefix() 192 | { 193 | return $this->routePrefix; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /docs/overriding_templates.md: -------------------------------------------------------------------------------- 1 | Overriding Default RollerworksMultiUserBundle Templates 2 | ======================================================= 3 | 4 | **Tip:** Each UserBundle can have it's own templates, the technique 5 | described in this section is only meant for when you want to "keep things simple". 6 | 7 | As you start to incorporate RollerworksMultiUserBundle into your application, 8 | you will probably find that you need to override the default templates that are 9 | provided by the bundle. Although the template names are not configurable, the Symfony2 10 | framework provides two ways to override the templates of a bundle. 11 | 12 | > Because of the Bundle inheritance you should only overwrite the 13 | > RollerworksMultiUserBundle (not the FOSUserBundle) in your `app/Resources`. 14 | 15 | 1. Define a new template of the same name in the `app/Resources` directory 16 | 2. Create a new bundle that is defined as a child of `RollerworksMultiUserBundle` 17 | 18 | **Caution:** 19 | 20 | Unlike the FOSUserBundle template files (except `layout.html.twig`) 21 | should be placed inside `views/UserBundle` (instead of directly 22 | in the `views` folder). 23 | 24 | Failing to do so will break the automatic loading of the configured template. 25 | 26 | ### Example: Overriding The Default layout.html.twig 27 | 28 | It is highly recommended that you override the `Resources/views/layout.html.twig` 29 | template so that the pages provided by the RollerworksMultiUserBundle have a similar look and 30 | feel to the rest of your application. An example of overriding this layout template 31 | is demonstrated below using both of the overriding options listed above. 32 | 33 | Here is the default `layout.html.twig` provided by the RollerworksMultiUserBundle: 34 | 35 | ``` html+jinja 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} 44 | {{ 'layout.logged_in_as'|trans({'%username%': app.user.username}, 'FOSUserBundle') }} | 45 | 46 | {{ 'layout.logout'|trans({}, 'FOSUserBundle') }} 47 | 48 | {% else %} 49 | {{ 'layout.login'|trans({}, 'FOSUserBundle') }} 50 | {% endif %} 51 |
52 | 53 | {% for type, messages in app.session.flashbag.all() %} 54 | {% for key, message in messages %} 55 |
56 | {{ message|trans({}, 'FOSUserBundle') }} 57 |
58 | {% endfor %} 59 | {% endfor %} 60 | 61 |
62 | {% block fos_user_content %} 63 | {% endblock fos_user_content %} 64 |
65 | 66 | 67 | ``` 68 | 69 | As you can see its pretty basic and doesn't really have much structure, so you will 70 | want to replace it with a layout file that is appropriate for your application. The 71 | main thing to note in this template is the block named `fos_user_content`. This is 72 | the block where the content from each of the different bundle's actions will be 73 | displayed, so you must make sure to include this block in the layout file you will 74 | use to override the default one. 75 | 76 | > The `{{ path(rollerworks_multi_user_user().getRoutePrefix() ~ '_security_login') }}` will generate the `_security_login` 77 | > route for the active user-system. If you want your 'own' templates to support multiple user-systems, 78 | > this the recommended way to generate routes for your user-systems. 79 | 80 | The following Twig template file is an example of a layout file that might be used 81 | to override the one provided by the bundle. 82 | 83 | ``` html+jinja 84 | {% extends 'AcmeDemoBundle::layout.html.twig' %} 85 | 86 | {% block title %}Acme Demo Application{% endblock %} 87 | 88 | {% block content %} 89 | {% block fos_user_content %}{% endblock %} 90 | {% endblock %} 91 | ``` 92 | 93 | This example extends the layout template from a fictional application bundle named 94 | `AcmeDemoBundle`. The `content` block is where the main content of each page is rendered. 95 | This is why the `fos_user_content` block has been placed inside of it. This will 96 | lead to the desired effect of having the output from the RollerworksMultiUserBundle actions 97 | integrated into our applications layout, preserving the look and feel of the 98 | application. 99 | 100 | **a) Define New Template In app/Resources** 101 | 102 | The easiest way to override a bundle's template is to simply place a new one in 103 | your `app/Resources` folder. To override the layout template located at 104 | `Resources/views/layout.html.twig` in the `RollerworksMultiUserBundle` directory, you would place 105 | your new layout template at `app/Resources/RollerworksMultiUserBundle/views/layout.html.twig`. 106 | 107 | As you can see the pattern for overriding templates in this way is to 108 | create a folder with the name of the bundle class in the `app/Resources` directory. 109 | Then add your new template to this folder, preserving the directory structure from the 110 | original bundle. 111 | 112 | **b) Create A Child Bundle And Override Template** 113 | 114 | **Note:** 115 | 116 | ``` 117 | This method is more complicated than the one outlined above. Unless you are 118 | planning to override the controllers as well as the templates, it is recommended 119 | that you use the other method. 120 | ``` 121 | 122 | As listed above, you can also create a bundle defined as child of RollerworksMultiUserBundle 123 | and place the new template in the same location that is resides in the RollerworksMultiUserBundle. 124 | The first thing you want to do is override the `getParent` method to your bundle 125 | class. 126 | 127 | ``` php 128 | // src/Acme/UserBundle/AcmeUserBundle.php 129 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\EventListener; 13 | 14 | use FOS\UserBundle\FOSUserEvents; 15 | use Rollerworks\Bundle\MultiUserBundle\Model\UserDiscriminatorInterface; 16 | use Symfony\Component\EventDispatcher\Event; 17 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; 18 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 19 | 20 | /** 21 | * @author Sebastiaan Stok 22 | */ 23 | class EventDiscriminator implements EventSubscriberInterface 24 | { 25 | /** 26 | * @var UserDiscriminatorInterface 27 | */ 28 | private $userDiscriminator; 29 | 30 | /** 31 | * @var EventDispatcherInterface 32 | */ 33 | private $eventDispatcher; 34 | 35 | /** 36 | * @param UserDiscriminatorInterface $userDiscriminator 37 | * @param EventDispatcherInterface $eventDispatcher 38 | */ 39 | public function __construct(UserDiscriminatorInterface $userDiscriminator, EventDispatcherInterface $eventDispatcher) 40 | { 41 | $this->userDiscriminator = $userDiscriminator; 42 | $this->eventDispatcher = $eventDispatcher; 43 | } 44 | 45 | // dispatchers and event-subscriber list is generated using scripts/generate-events.php 46 | // last updated on 2014-03-06 47 | 48 | public function dispatchChangePasswordInitialize(Event $e) 49 | { 50 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 51 | $this->eventDispatcher->dispatch($userSys.'.change_password.edit.initialize', $e); 52 | } 53 | } 54 | 55 | public function dispatchChangePasswordSuccess(Event $e) 56 | { 57 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 58 | $this->eventDispatcher->dispatch($userSys.'.change_password.edit.success', $e); 59 | } 60 | } 61 | 62 | public function dispatchChangePasswordCompleted(Event $e) 63 | { 64 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 65 | $this->eventDispatcher->dispatch($userSys.'.change_password.edit.completed', $e); 66 | } 67 | } 68 | 69 | public function dispatchGroupCreateInitialize(Event $e) 70 | { 71 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 72 | $this->eventDispatcher->dispatch($userSys.'.group.create.initialize', $e); 73 | } 74 | } 75 | 76 | public function dispatchGroupCreateSuccess(Event $e) 77 | { 78 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 79 | $this->eventDispatcher->dispatch($userSys.'.group.create.success', $e); 80 | } 81 | } 82 | 83 | public function dispatchGroupCreateCompleted(Event $e) 84 | { 85 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 86 | $this->eventDispatcher->dispatch($userSys.'.group.create.completed', $e); 87 | } 88 | } 89 | 90 | public function dispatchGroupDeleteCompleted(Event $e) 91 | { 92 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 93 | $this->eventDispatcher->dispatch($userSys.'.group.delete.completed', $e); 94 | } 95 | } 96 | 97 | public function dispatchGroupEditInitialize(Event $e) 98 | { 99 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 100 | $this->eventDispatcher->dispatch($userSys.'.group.edit.initialize', $e); 101 | } 102 | } 103 | 104 | public function dispatchGroupEditSuccess(Event $e) 105 | { 106 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 107 | $this->eventDispatcher->dispatch($userSys.'.group.edit.success', $e); 108 | } 109 | } 110 | 111 | public function dispatchGroupEditCompleted(Event $e) 112 | { 113 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 114 | $this->eventDispatcher->dispatch($userSys.'.group.edit.completed', $e); 115 | } 116 | } 117 | 118 | public function dispatchProfileEditInitialize(Event $e) 119 | { 120 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 121 | $this->eventDispatcher->dispatch($userSys.'.profile.edit.initialize', $e); 122 | } 123 | } 124 | 125 | public function dispatchProfileEditSuccess(Event $e) 126 | { 127 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 128 | $this->eventDispatcher->dispatch($userSys.'.profile.edit.success', $e); 129 | } 130 | } 131 | 132 | public function dispatchProfileEditCompleted(Event $e) 133 | { 134 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 135 | $this->eventDispatcher->dispatch($userSys.'.profile.edit.completed', $e); 136 | } 137 | } 138 | 139 | public function dispatchRegistrationInitialize(Event $e) 140 | { 141 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 142 | $this->eventDispatcher->dispatch($userSys.'.registration.initialize', $e); 143 | } 144 | } 145 | 146 | public function dispatchRegistrationSuccess(Event $e) 147 | { 148 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 149 | $this->eventDispatcher->dispatch($userSys.'.registration.success', $e); 150 | } 151 | } 152 | 153 | public function dispatchRegistrationCompleted(Event $e) 154 | { 155 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 156 | $this->eventDispatcher->dispatch($userSys.'.registration.completed', $e); 157 | } 158 | } 159 | 160 | public function dispatchRegistrationConfirm(Event $e) 161 | { 162 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 163 | $this->eventDispatcher->dispatch($userSys.'.registration.confirm', $e); 164 | } 165 | } 166 | 167 | public function dispatchRegistrationConfirmed(Event $e) 168 | { 169 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 170 | $this->eventDispatcher->dispatch($userSys.'.registration.confirmed', $e); 171 | } 172 | } 173 | 174 | public function dispatchResettingResetInitialize(Event $e) 175 | { 176 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 177 | $this->eventDispatcher->dispatch($userSys.'.resetting.reset.initialize', $e); 178 | } 179 | } 180 | 181 | public function dispatchResettingResetSuccess(Event $e) 182 | { 183 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 184 | $this->eventDispatcher->dispatch($userSys.'.resetting.reset.success', $e); 185 | } 186 | } 187 | 188 | public function dispatchResettingResetCompleted(Event $e) 189 | { 190 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 191 | $this->eventDispatcher->dispatch($userSys.'.resetting.reset.completed', $e); 192 | } 193 | } 194 | 195 | public function dispatchSecurityImplicitLogin(Event $e) 196 | { 197 | if ($userSys = $this->userDiscriminator->getCurrentUser()) { 198 | $this->eventDispatcher->dispatch($userSys.'.security.implicit_login', $e); 199 | } 200 | } 201 | 202 | /** 203 | * Subscribes to all events defined in FOS\UserBundle\FOSUserEvents. 204 | * 205 | * @return array 206 | */ 207 | public static function getSubscribedEvents() 208 | { 209 | return array( 210 | FOSUserEvents::CHANGE_PASSWORD_INITIALIZE => 'dispatchChangePasswordInitialize', 211 | FOSUserEvents::CHANGE_PASSWORD_SUCCESS => 'dispatchChangePasswordSuccess', 212 | FOSUserEvents::CHANGE_PASSWORD_COMPLETED => 'dispatchChangePasswordCompleted', 213 | FOSUserEvents::GROUP_CREATE_INITIALIZE => 'dispatchGroupCreateInitialize', 214 | FOSUserEvents::GROUP_CREATE_SUCCESS => 'dispatchGroupCreateSuccess', 215 | FOSUserEvents::GROUP_CREATE_COMPLETED => 'dispatchGroupCreateCompleted', 216 | FOSUserEvents::GROUP_DELETE_COMPLETED => 'dispatchGroupDeleteCompleted', 217 | FOSUserEvents::GROUP_EDIT_INITIALIZE => 'dispatchGroupEditInitialize', 218 | FOSUserEvents::GROUP_EDIT_SUCCESS => 'dispatchGroupEditSuccess', 219 | FOSUserEvents::GROUP_EDIT_COMPLETED => 'dispatchGroupEditCompleted', 220 | FOSUserEvents::PROFILE_EDIT_INITIALIZE => 'dispatchProfileEditInitialize', 221 | FOSUserEvents::PROFILE_EDIT_SUCCESS => 'dispatchProfileEditSuccess', 222 | FOSUserEvents::PROFILE_EDIT_COMPLETED => 'dispatchProfileEditCompleted', 223 | FOSUserEvents::REGISTRATION_INITIALIZE => 'dispatchRegistrationInitialize', 224 | FOSUserEvents::REGISTRATION_SUCCESS => 'dispatchRegistrationSuccess', 225 | FOSUserEvents::REGISTRATION_COMPLETED => 'dispatchRegistrationCompleted', 226 | FOSUserEvents::REGISTRATION_CONFIRM => 'dispatchRegistrationConfirm', 227 | FOSUserEvents::REGISTRATION_CONFIRMED => 'dispatchRegistrationConfirmed', 228 | FOSUserEvents::RESETTING_RESET_INITIALIZE => 'dispatchResettingResetInitialize', 229 | FOSUserEvents::RESETTING_RESET_SUCCESS => 'dispatchResettingResetSuccess', 230 | FOSUserEvents::RESETTING_RESET_COMPLETED => 'dispatchResettingResetCompleted', 231 | FOSUserEvents::SECURITY_IMPLICIT_LOGIN => 'dispatchSecurityImplicitLogin', 232 | ); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/Resources/skeleton/bundle/Extension.php.twig: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace {{ 13 | 14 | namespace }}\DependencyInjection; 15 | 16 | { % block use_statements % } 17 | use Rollerworks\Bundle\MultiUserBundle\DependencyInjection\Factory\UserServicesFactory; 18 | use Symfony\Component\Config\FileLocator; 19 | use Symfony\Component\DependencyInjection\ContainerBuilder; 20 | use Symfony\Component\DependencyInjection\Loader; 21 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; 22 | 23 | { % endblock use_statements % } 24 | 25 | /* 26 | {% block phpdoc_class_header %} 27 | * This is the class that loads and manages your bundle configuration 28 | {% endblock phpdoc_class_header %} 29 | * 30 | * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} 31 | */ 32 | { % block class_definition % } 33 | class 34 | { 35 | { bundle_basename } 36 | }Extension extends Extension 37 | { % endblock class_definition % } 38 | { 39 | { % block class_body % } 40 | /** 41 | * {@inheritdoc} 42 | */ 43 | public function load(array $configs, ContainerBuilder $container) 44 | { 45 | $configuration = new Configuration(); 46 | $config = $this->processConfiguration($configuration, $configs); 47 | 48 | $factory = new UserServicesFactory($container); 49 | 50 | // The first parameter is the name of the user-system, the second is the configuration which internally 51 | // is normalized by the Symfony Config component. 52 | 53 | /* 54 | The `UserServicesFactory::create()` will also register the form-types you need, 55 | any form-type/name/class that belongs to the FOSUserBundle will be converted to an ready 56 | to use form-type. Remember form types and names start with the service-prefix of the user-system. 57 | 58 | ``` 59 | 'form' => array( 60 | 'type' => 'fos_user_profile', 61 | 'class' => 'FOS\UserBundle\Form\Type\ProfileFormType', 62 | 'name' => 'fos_user_profile_form', 63 | ), 64 | ``` 65 | 66 | Will internally get converted to an '{{ extension_alias }}_profile' form-type service definition. 67 | Never set the template namespace to FOSUserBundle or RollerworksMultiUserBundle as this will create an endless recursion! 68 | */ 69 | 70 | $factory->create('{{ extension_alias }}', array( 71 | array( 72 | /* 73 | * `path` and `host` are used by the `RequestListener` service for finding the correct user-type. 74 | * You can either set only a path or host, or both. Or you can choose to use your own matcher-service 75 | * by setting the `request_matcher`, and giving it the service-id. 76 | * 77 | * A request-matcher must always implement the `Symfony\Component\HttpFoundation\RequestMatcherInterface`. 78 | */ 79 | 80 | 'path' => '^/', // path-regex, must match the firewall pattern 81 | 'host' => null, 82 | 'request_matcher' => null, 83 | 84 | // When not set this will inherit from the user-system name provided above 85 | 'services_prefix' => '{{ extension_alias }}', 86 | 'routes_prefix' => '{{ extension_alias }}', 87 | 88 | 'db_driver' => '{{ db_driver }}', // can be either: orm, mongodb, couchdb or custom (Propel is not supported) 89 | 'model_manager_name' => 'default', 90 | 'use_listener' => true, 91 | 92 | 'user_class' => '{{ model_namespace }}\User', 93 | 'firewall_name' => 'main', // this must equal to the firewall-name used for this user-system 94 | 95 | 'use_username_form_type' => true, 96 | 97 | // When not set these will inherit from the system wide configuration 98 | 'from_email' => array( 99 | 'address' => null, 100 | 'sender_name' => null, 101 | ), 102 | 103 | 'security' => array( 104 | 'login' => array( 105 | 'template' => 'RollerworksMultiUserBundle:UserBundle/Security:login.html.twig', 106 | ), 107 | ), 108 | 109 | 'service' => array( 110 | 'mailer' => 'fos_user.mailer.default', 111 | 'email_canonicalizer' => 'fos_user.util.canonicalizer.default', 112 | 'username_canonicalizer' => 'fos_user.util.canonicalizer.default', 113 | 'user_manager' => 'fos_user.user_manager.default', 114 | ), 115 | 116 | 'template' => array( 117 | 'layout' => 'RollerworksMultiUserBundle::layout.html.twig', 118 | ), 119 | 120 | 'profile' => array( 121 | 'form' => array( 122 | 'class' => 'FOS\\UserBundle\\Form\\Type\\ProfileFormType', 123 | 'type' => 'fos_user_profile', 124 | 'name' => 'fos_user_profile_form', 125 | 'validation_groups' => array('Profile', 'Default'), 126 | ), 127 | 'template' => array( 128 | 'edit' => 'RollerworksMultiUserBundle:UserBundle/Profile:edit.html.twig', 129 | 'show' => 'RollerworksMultiUserBundle:UserBundle/Profile:show.html.twig', 130 | ), 131 | ), 132 | 133 | 'change_password' => array( 134 | 'form' => array( 135 | 'class' => 'FOS\\UserBundle\\Form\\Type\\ChangePasswordFormType', 136 | 'type' => 'fos_user_change_password', 137 | 'name' => 'fos_user_change_password_form', 138 | 'validation_groups' => array('ChangePassword', 'Default'), 139 | ), 140 | 'template' => array( 141 | 'change_password' => 'RollerworksMultiUserBundle:UserBundle/ChangePassword:changePassword.html.twig', 142 | ), 143 | ), 144 | 145 | 'registration' => array( 146 | 'confirmation' => array( 147 | 'enabled' => false, 148 | 'template' => array( 149 | 'email' => 'RollerworksMultiUserBundle:UserBundle/Registration:email.txt.twig', 150 | 'confirmed' => 'RollerworksMultiUserBundle:UserBundle/Registration:confirmed.html.twig', 151 | ), 152 | 'from_email' => array( 153 | 'address' => null, 154 | 'sender_name' => null, 155 | ), 156 | ), 157 | 'form' => array( 158 | 'class' => 'FOS\\UserBundle\\Form\\Type\\RegistrationFormType', 159 | 'type' => 'fos_user_registration', 160 | 'name' => 'fos_user_registration_form', 161 | 'validation_groups' => array('Registration', 'Default'), 162 | ), 163 | 'template' => array( 164 | 'register' => 'RollerworksMultiUserBundle:UserBundle/Registration:register.html.twig', 165 | 'check_email' => 'RollerworksMultiUserBundle:UserBundle/Registration:checkEmail.html.twig', 166 | ), 167 | ), 168 | 169 | 'resetting' => array( 170 | 'token_ttl' => 86400, 171 | 'email' => array( 172 | 'from_email' => array( 173 | 'address' => null, 174 | 'sender_name' => null, 175 | ), 176 | ), 177 | 'form' => array( 178 | 'template' => null, 179 | 'class' => 'FOS\\UserBundle\\Form\\Type\\ResettingFormType', 180 | 'type' => 'fos_user_resetting', 181 | 'name' => 'fos_user_resetting_form', 182 | 'validation_groups' => array('ResetPassword', 'Default'), 183 | ), 184 | 'template' => array( 185 | 'check_email' => 'RollerworksMultiUserBundle:UserBundle/Resetting:checkEmail.html.twig', 186 | 'email' => 'RollerworksMultiUserBundle:UserBundle/Resetting:email.txt.twig', 187 | 'password_already_requested' => 'RollerworksMultiUserBundle:UserBundle/Resetting:passwordAlreadyRequested.html.twig', 188 | 'request' => 'RollerworksMultiUserBundle:UserBundle/Resetting:request.html.twig', 189 | 'reset' => 'RollerworksMultiUserBundle:UserBundle/Resetting:reset.html.twig', 190 | ), 191 | ), 192 | 193 | // Optional 194 | /* 195 | 'group' => array( 196 | 'group_class' => '{{ model_namespace }}\Group', 197 | 'group_manager' => 'fos_user.group_manager.default', 198 | 199 | 'form' => array( 200 | 'class' => 'FOS\\UserBundle\\Form\\Type\\GroupFormType', 201 | 'type' => 'fos_user_group', 202 | 'name' => 'fos_user_group_form', 203 | 'validation_groups' => array('Registration', 'Default'), 204 | ), 205 | 'template' => array( 206 | 'edit' => 'RollerworksMultiUserBundle:UserBundle/Group:edit.html.twig', 207 | 'list' => 'RollerworksMultiUserBundle:UserBundle/Group:list.html.twig', 208 | 'new' => 'RollerworksMultiUserBundle:UserBundle/Group:new.html.twig', 209 | 'show' => 'RollerworksMultiUserBundle:UserBundle/Group:show.html.twig', 210 | ), 211 | ), 212 | */ 213 | ), 214 | $config, 215 | )); 216 | 217 | { % if { 218 | format == 'yml' - % 219 | } 220 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); } 221 | $loader->load('services.yml'); 222 | { % - elseif { 223 | format == 'xml' - % 224 | } 225 | $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); } 226 | $loader->load('services.xml'); 227 | { % - elseif { 228 | format == 'php' - % 229 | } 230 | $loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); } 231 | $loader->load('services.php'); 232 | { % -endif % } 233 | } 234 | { % endblock class_body % } 235 | } 236 | -------------------------------------------------------------------------------- /src/Generator/UserBundleGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Generator; 13 | 14 | use Sensio\Bundle\GeneratorBundle\Generator\Generator; 15 | use Symfony\Component\DependencyInjection\Container; 16 | use Symfony\Component\Filesystem\Filesystem; 17 | 18 | /** 19 | * Generates a bundle. 20 | * 21 | * @author Sebastiaan Stok 22 | * @author Fabien Potencier 23 | */ 24 | class UserBundleGenerator extends Generator 25 | { 26 | private $filesystem; 27 | 28 | public function __construct(Filesystem $filesystem) 29 | { 30 | $this->filesystem = $filesystem; 31 | } 32 | 33 | /** 34 | * @param string $namespace 35 | * @param string $bundle 36 | * @param string $dir 37 | * @param string $format 38 | * @param string $structure 39 | * @param string $dbDriver 40 | * 41 | * @throws \RuntimeException 42 | */ 43 | public function generate($namespace, $bundle, $dir, $format, $structure, $dbDriver) 44 | { 45 | $dir .= '/'.strtr($namespace, '\\', '/'); 46 | if (file_exists($dir)) { 47 | if (!is_dir($dir)) { 48 | throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" exists but is a file.', realpath($dir))); 49 | } 50 | $files = scandir($dir); 51 | if ($files != array('.', '..')) { 52 | throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" is not empty.', realpath($dir))); 53 | } 54 | if (!is_writable($dir)) { 55 | throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" is not writable.', realpath($dir))); 56 | } 57 | } 58 | 59 | $driverDir = array( 60 | 'orm' => 'Entity', 61 | 'mongodb' => 'Document', 62 | 'couchdb' => 'CouchDocument', 63 | 'custom' => 'Model', 64 | ); 65 | 66 | $basename = substr($bundle, 0, -6); 67 | $parameters = array( 68 | 'namespace' => $namespace, 69 | 'bundle' => $bundle, 70 | 'format' => $format, 71 | 'db_driver' => $dbDriver, 72 | 'model_namespace' => $namespace.'\\'.$driverDir[$dbDriver], 73 | 'bundle_basename' => $basename, 74 | 'extension_alias' => Container::underscore($basename), 75 | ); 76 | 77 | $this->renderFile('bundle/Bundle.php.twig', $dir.'/'.$bundle.'.php', $parameters); 78 | $this->renderFile('bundle/Extension.php.twig', $dir.'/DependencyInjection/'.$basename.'Extension.php', $parameters); 79 | $this->renderFile('bundle/Configuration.php.twig', $dir.'/DependencyInjection/Configuration.php', $parameters); 80 | $this->renderFile('bundle/DefaultController.php.twig', $dir.'/Controller/DefaultController.php', $parameters); 81 | $this->renderFile('bundle/DefaultControllerTest.php.twig', $dir.'/Tests/Controller/DefaultControllerTest.php', $parameters); 82 | $this->renderFile('bundle/index.html.twig.twig', $dir.'/Resources/views/Default/index.html.twig', $parameters); 83 | $this->renderFile('bundle/services.'.$format.'.twig', $dir.'/Resources/config/services.'.$format, $parameters); 84 | 85 | $this->generateRoutes($namespace, $bundle, $dir, $format, $basename); 86 | $this->generateModels($namespace, $bundle, $dir, $dbDriver, $basename); 87 | $this->generateEventsClass($namespace, $bundle, $dir, $dbDriver, $basename); 88 | 89 | if ($structure) { 90 | $this->renderFile('bundle/messages.fr.xlf', $dir.'/Resources/translations/messages.fr.xlf', $parameters); 91 | 92 | $this->filesystem->mkdir($dir.'/Resources/doc'); 93 | $this->filesystem->touch($dir.'/Resources/doc/index.rst'); 94 | $this->filesystem->mkdir($dir.'/Resources/translations'); 95 | $this->filesystem->mkdir($dir.'/Resources/public/css'); 96 | $this->filesystem->mkdir($dir.'/Resources/public/images'); 97 | $this->filesystem->mkdir($dir.'/Resources/public/js'); 98 | } 99 | } 100 | 101 | /** 102 | * @param string $namespace 103 | * @param string $bundle 104 | * @param string $dir 105 | * @param string $dbDriver 106 | * @param string $basename 107 | */ 108 | private function generateModels($namespace, $bundle, $dir, $dbDriver, $basename) 109 | { 110 | $parameters = array( 111 | 'namespace' => $namespace, 112 | 'bundle' => $bundle, 113 | 'bundle_basename' => $basename, 114 | 'extension_alias' => Container::underscore($basename), 115 | ); 116 | 117 | $driverDir = array( 118 | 'orm' => 'Entity', 119 | 'mongodb' => 'Document', 120 | 'couchdb' => 'CouchDocument', 121 | 'custom' => 'Model', 122 | ); 123 | 124 | $dir = $dir.'/'.$driverDir[$dbDriver]; 125 | 126 | $this->renderFile('bundle/model/user_'.$dbDriver.'.php.twig', $dir.'/User.php', $parameters); 127 | $this->renderFile('bundle/model/group_'.$dbDriver.'.php.twig', $dir.'/Group.php', $parameters); 128 | } 129 | 130 | /** 131 | * @param string $namespace 132 | * @param string $bundle 133 | * @param string $dir 134 | * @param string $format 135 | * @param string $basename 136 | */ 137 | private function generateRoutes($namespace, $bundle, $dir, $format, $basename) 138 | { 139 | $baseRoutes = array( 140 | 'change_password' => array( 141 | 'change_password' => array( 142 | 'path' => '/change-password', 143 | 'method' => array('GET', 'POST'), 144 | 'controller_action' => 'FOSUserBundle:ChangePassword:changePassword', 145 | ), 146 | ), 147 | 148 | 'group' => array( 149 | 'group_list' => array( 150 | 'path' => '/list', 151 | 'method' => array('GET'), 152 | 'controller_action' => 'FOSUserBundle:Group:list', 153 | ), 154 | 'group_new' => array( 155 | 'path' => '/new', 156 | 'method' => array(), 157 | 'controller_action' => 'FOSUserBundle:Group:new', 158 | ), 159 | 'group_show' => array( 160 | 'path' => '/{groupName}', 161 | 'method' => array('GET'), 162 | 'controller_action' => 'FOSUserBundle:Group:show', 163 | ), 164 | 'group_edit' => array( 165 | 'path' => '/{groupName}/edit', 166 | 'method' => array(), 167 | 'controller_action' => 'FOSUserBundle:Group:edit', 168 | ), 169 | 'group_delete' => array( 170 | 'path' => '/{groupName}/delete', 171 | 'method' => array('GET'), 172 | 'controller_action' => 'FOSUserBundle:Group:delete', 173 | ), 174 | ), 175 | 176 | 'profile' => array( 177 | 'profile_show' => array( 178 | 'path' => '/', 179 | 'method' => array('GET'), 180 | 'controller_action' => 'FOSUserBundle:Profile:show', 181 | ), 182 | 'profile_edit' => array( 183 | 'path' => '/edit', 184 | 'method' => array(), 185 | 'controller_action' => 'FOSUserBundle:Profile:edit', 186 | ), 187 | ), 188 | 189 | 'registration' => array( 190 | 'registration_register' => array( 191 | 'path' => '/', 192 | 'method' => array(), 193 | 'controller_action' => 'FOSUserBundle:Registration:register', 194 | ), 195 | 'registration_check_email' => array( 196 | 'path' => '/check-email', 197 | 'method' => array('GET'), 198 | 'controller_action' => 'FOSUserBundle:Registration:checkEmail', 199 | ), 200 | 'registration_confirm' => array( 201 | 'path' => '/confirm/{token}', 202 | 'method' => array('GET'), 203 | 'controller_action' => 'FOSUserBundle:Registration:confirm', 204 | ), 205 | 'registration_confirmed' => array( 206 | 'path' => '/confirmed', 207 | 'method' => array('GET'), 208 | 'controller_action' => 'FOSUserBundle:Registration:confirmed', 209 | ), 210 | ), 211 | 212 | 'resetting' => array( 213 | 'resetting_request' => array( 214 | 'path' => '/request', 215 | 'method' => array('GET'), 216 | 'controller_action' => 'FOSUserBundle:Resetting:request', 217 | ), 218 | 'resetting_send_email' => array( 219 | 'path' => '/send-email', 220 | 'method' => array('POST'), 221 | 'controller_action' => 'FOSUserBundle:Resetting:sendEmail', 222 | ), 223 | 'resetting_check_email' => array( 224 | 'path' => '/check-email', 225 | 'method' => array('GET'), 226 | 'controller_action' => 'FOSUserBundle:Resetting:checkEmail', 227 | ), 228 | 'resetting_reset' => array( 229 | 'path' => '/reset/{token}', 230 | 'method' => array('GET', 'POST'), 231 | 'controller_action' => 'FOSUserBundle:Resetting:reset', 232 | ), 233 | ), 234 | 235 | 'security' => array( 236 | 'security_login' => array( 237 | 'path' => '/login', 238 | 'method' => array(), 239 | 'controller_action' => 'FOSUserBundle:Security:login', 240 | ), 241 | 'security_check' => array( 242 | 'path' => '/login_check', 243 | 'method' => array('POST'), 244 | 'controller_action' => 'FOSUserBundle:Security:check', 245 | ), 246 | 'security_logout' => array( 247 | 'path' => '/logout', 248 | 'method' => array(), 249 | 'controller_action' => 'FOSUserBundle:Security:logout', 250 | ), 251 | ), 252 | ); 253 | 254 | foreach ($baseRoutes as $fileName => $routes) { 255 | $parameters = array( 256 | 'namespace' => $namespace, 257 | 'bundle' => $bundle, 258 | 'format' => $format, 259 | 'bundle_basename' => $basename, 260 | 'extension_alias' => Container::underscore($basename), 261 | 'routes' => $routes, 262 | ); 263 | 264 | $this->renderFile('bundle/routing.'.$format.'.twig', $dir.'/Resources/config/routing/'.$fileName.'.'.$format, $parameters); 265 | } 266 | } 267 | 268 | private function generateEventsClass($namespace, $bundle, $dir, $format, $basename) 269 | { 270 | $eventsClass = new \ReflectionClass('FOS\UserBundle\FOSUserEvents'); 271 | $events = $eventsClass->getConstants(); 272 | 273 | foreach ($events as $name => $event) { 274 | $events[$name] = Container::underscore($basename).substr($event, 8); 275 | } 276 | 277 | $parameters = array( 278 | 'namespace' => $namespace, 279 | 'bundle' => $bundle, 280 | 'format' => $format, 281 | 'bundle_basename' => $basename, 282 | 'extension_alias' => Container::underscore($basename), 283 | 'events' => $events, 284 | ); 285 | 286 | $this->renderFile('bundle/Events.php.twig', $dir.'/'.$basename.'Events.php', $parameters); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /src/Command/GenerateUserSysCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Rollerworks\Bundle\MultiUserBundle\Command; 13 | 14 | use Rollerworks\Bundle\MultiUserBundle\Generator\UserBundleGenerator; 15 | use Sensio\Bundle\GeneratorBundle\Command\GenerateBundleCommand; 16 | use Symfony\Component\Console\Input\InputInterface; 17 | use Symfony\Component\Console\Input\InputOption; 18 | use Symfony\Component\Console\Output\OutputInterface; 19 | use Symfony\Component\HttpKernel\Bundle\BundleInterface; 20 | 21 | class GenerateUserSysCommand extends GenerateBundleCommand 22 | { 23 | protected function configure() 24 | { 25 | parent::configure(); 26 | $this->setName('rollerworks:multi-user:generate:usersys'); 27 | 28 | $definition = $this->getDefinition(); 29 | $definition->addOption(new InputOption('db-driver', '', InputOption::VALUE_REQUIRED, 'DB-driver to use')); 30 | 31 | $this 32 | ->setHelp(<<rollerworks:multi-user:generate:usersys command helps you generates new user-system bundles. 34 | 35 | By default, the command interacts with the developer to tweak the generation. 36 | Any passed option will be used as a default value for the interaction 37 | (--namespace is the only one needed if you follow the 38 | conventions): 39 | 40 | php app/console rollerworks:multi-user:generate:usersys --namespace=Acme/UserBundle 41 | 42 | Note that you can use / instead of \\ for the namespace delimiter to avoid any 43 | problem. 44 | 45 | If you want to disable any user interaction, use --no-interaction but don't forget to pass all needed options: 46 | 47 | php app/console rollerworks:multi-user:generate:usersys --namespace=Acme/UserBundle --dir=src [--db-driver=...] [--bundle-name=...] --no-interaction 48 | 49 | Note that the bundle namespace must end with "Bundle". 50 | EOT 51 | ); 52 | } 53 | 54 | /** 55 | * @see Command 56 | * 57 | * @throws \InvalidArgumentException When namespace doesn't end with Bundle 58 | * @throws \RuntimeException When bundle can't be executed 59 | */ 60 | protected function execute(InputInterface $input, OutputInterface $output) 61 | { 62 | $questionHelper = $this->getQuestionHelper(); 63 | $dialog = $this->getHelper('dialog'); 64 | 65 | if ($input->isInteractive()) { 66 | if (!$dialog->askConfirmation($output, $questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true)) { 67 | $output->writeln('Command aborted'); 68 | 69 | return 1; 70 | } 71 | } 72 | 73 | foreach (array('namespace', 'dir') as $option) { 74 | if (null === $input->getOption($option)) { 75 | throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option)); 76 | } 77 | } 78 | 79 | $namespace = Validators::validateBundleNamespace($input->getOption('namespace')); 80 | if (!$bundle = $input->getOption('bundle-name')) { 81 | $bundle = strtr($namespace, array('\\' => '')); 82 | } 83 | $bundle = Validators::validateBundleName($bundle); 84 | $dir = Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace); 85 | 86 | if (null === $input->getOption('format')) { 87 | $input->setOption('format', 'xml'); 88 | } 89 | $format = Validators::validateFormat($input->getOption('format')); 90 | $structure = $input->getOption('structure'); 91 | 92 | if (null === $input->getOption('db-driver')) { 93 | $input->setOption('db-driver', 'orm'); 94 | } 95 | $dbDriver = Validators::validateDbDriver($input->getOption('db-driver')); 96 | 97 | $questionHelper->writeSection($output, 'Bundle generation'); 98 | 99 | if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) { 100 | $dir = getcwd().'/'.$dir; 101 | } 102 | 103 | $generator = $this->getGenerator(); 104 | $generator->generate($namespace, $bundle, $dir, $format, $structure, $dbDriver); 105 | 106 | $output->writeln('Generating the bundle code: OK'); 107 | 108 | $errors = array(); 109 | $runner = $questionHelper->getRunner($output, $errors); 110 | 111 | // check that the namespace is already autoloaded 112 | $runner($this->checkAutoloader($output, $namespace, $bundle, $dir)); 113 | 114 | // register the bundle in the Kernel class 115 | $runner($this->updateKernel($questionHelper, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle)); 116 | 117 | $questionHelper->writeGeneratorSummary($output, $errors); 118 | } 119 | 120 | protected function interact(InputInterface $input, OutputInterface $output) 121 | { 122 | $questionHelper = $this->getQuestionHelper(); 123 | $dialog = $this->getHelper('dialog'); 124 | 125 | $questionHelper->writeSection($output, 'Welcome to the RollerworksMultiUser UserSys-bundle generator'); 126 | 127 | // namespace 128 | $namespace = null; 129 | try { 130 | $namespace = $input->getOption('namespace') ? Validators::validateBundleNamespace($input->getOption('namespace')) : null; 131 | } catch (\Exception $error) { 132 | $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); 133 | } 134 | 135 | if (null === $namespace) { 136 | $output->writeln(array( 137 | '', 138 | 'Your user-systems code must be written in bundles. This command helps', 139 | 'you generate them easily.', 140 | '', 141 | 'Each user-system bundle is hosted under a namespace (like Acme/Bundle/UserBundle).', 142 | 'The namespace should begin with a "vendor" name like your company name, your', 143 | 'project name, or your client name, followed by one or more optional category', 144 | 'sub-namespaces, and it should end with the bundle name itself', 145 | '(which must have Bundle as a suffix).', 146 | '', 147 | 'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#index-1 for more', 148 | 'details on bundle naming conventions.', 149 | '', 150 | 'Use / instead of \\ for the namespace delimiter to avoid any problem.', 151 | '', 152 | )); 153 | 154 | $namespace = $dialog->askAndValidate($output, $questionHelper->getQuestion('Bundle namespace', $input->getOption('namespace')), array('Rollerworks\Bundle\MultiUserBundle\Command\Validators', 'validateBundleNamespace'), false, $input->getOption('namespace')); 155 | $input->setOption('namespace', $namespace); 156 | } 157 | 158 | // bundle name 159 | $bundle = null; 160 | try { 161 | $bundle = $input->getOption('bundle-name') ? Validators::validateBundleName($input->getOption('bundle-name')) : null; 162 | } catch (\Exception $error) { 163 | $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); 164 | } 165 | 166 | if (null === $bundle) { 167 | $bundle = strtr($namespace, array('\\Bundle\\' => '', '\\' => '')); 168 | 169 | $output->writeln(array( 170 | '', 171 | 'In your code, a bundle is often referenced by its name. It can be the', 172 | 'concatenation of all namespace parts but it\'s really up to you to come', 173 | 'up with a unique name (a good practice is to start with the vendor name).', 174 | 'Based on the namespace, we suggest '.$bundle.'.', 175 | '', 176 | )); 177 | $bundle = $dialog->askAndValidate($output, $questionHelper->getQuestion('Bundle name', $bundle), array('Rollerworks\Bundle\MultiUserBundle\Command\Validators', 'validateBundleName'), false, $bundle); 178 | $input->setOption('bundle-name', $bundle); 179 | } 180 | 181 | // target dir 182 | $dir = null; 183 | try { 184 | $dir = $input->getOption('dir') ? Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace) : null; 185 | } catch (\Exception $error) { 186 | $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); 187 | } 188 | 189 | if (null === $dir) { 190 | $dir = dirname($this->getContainer()->getParameter('kernel.root_dir')).'/src'; 191 | 192 | $output->writeln(array( 193 | '', 194 | 'The bundle can be generated anywhere. The suggested default directory uses', 195 | 'the standard conventions.', 196 | '', 197 | )); 198 | $dir = $dialog->askAndValidate($output, $questionHelper->getQuestion('Target directory', $dir), function ($dir) use ($bundle, $namespace) { return Validators::validateTargetDir($dir, $bundle, $namespace); }, false, $dir); 199 | $input->setOption('dir', $dir); 200 | } 201 | 202 | // format 203 | $format = null; 204 | try { 205 | $format = $input->getOption('format') ? Validators::validateFormat($input->getOption('format')) : null; 206 | } catch (\Exception $error) { 207 | $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); 208 | } 209 | 210 | if (null === $format) { 211 | $output->writeln(array( 212 | '', 213 | 'Determine the format to use for the generated configuration, note that annotation is not supported.', 214 | '', 215 | )); 216 | $format = $dialog->askAndValidate($output, $questionHelper->getQuestion('Configuration format (yml, xml or php)', $input->getOption('format')), array('Rollerworks\Bundle\MultiUserBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format')); 217 | $input->setOption('format', $format); 218 | } 219 | 220 | // db-driver 221 | $dbDriver = null; 222 | try { 223 | $dbDriver = $input->getOption('db-driver') ?: null; 224 | } catch (\Exception $error) { 225 | $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); 226 | } 227 | 228 | if (null === $dbDriver) { 229 | $output->writeln(array( 230 | '', 231 | 'For the user-system to work you need to configure a db-driver.', 232 | '', 233 | )); 234 | $dbDriver = $dialog->askAndValidate($output, $questionHelper->getQuestion('Db-driver (orm, mongodb, couchdb or custom)', $input->getOption('db-driver')), array('Rollerworks\Bundle\MultiUserBundle\Command\Validators', 'validateDbDriver'), false, $input->getOption('db-driver')); 235 | $input->setOption('db-driver', $dbDriver); 236 | } 237 | 238 | // optional files to generate 239 | $output->writeln(array( 240 | '', 241 | 'To help you get started faster, the command can generate some', 242 | 'code snippets for you.', 243 | '', 244 | )); 245 | 246 | $structure = $input->getOption('structure'); 247 | if (!$structure && $dialog->askConfirmation($output, $questionHelper->getQuestion('Do you want to generate the whole directory structure', 'no', '?'), false)) { 248 | $structure = true; 249 | } 250 | $input->setOption('structure', $structure); 251 | 252 | // summary 253 | $output->writeln(array( 254 | '', 255 | $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), 256 | '', 257 | sprintf("You are going to generate a \"%s\\%s\" bundle\nin \"%s\" using the \"%s\" format with db-driver \"%s\".", $namespace, $bundle, $dir, $format, $dbDriver), 258 | '', 259 | )); 260 | } 261 | 262 | protected function createGenerator() 263 | { 264 | return new UserBundleGenerator($this->getContainer()->get('filesystem')); 265 | } 266 | 267 | /** 268 | * add this bundle skeleton dirs to the beginning of the parent skeletonDirs array. 269 | * 270 | * @param BundleInterface $bundle 271 | * 272 | * @return string[] 273 | */ 274 | protected function getSkeletonDirs(BundleInterface $bundle = null) 275 | { 276 | $baseSkeletonDirs = parent::getSkeletonDirs($bundle); 277 | $skeletonDirs = array(); 278 | 279 | if (is_dir($dir = $this->getContainer()->get('kernel')->getRootdir().'/Resources/MultiUserBundleMultiUserBundle/skeleton')) { 280 | $skeletonDirs[] = $dir; 281 | } 282 | 283 | $reflClass = new \ReflectionClass($this); 284 | $dir = dirname($reflClass->getFileName()); 285 | 286 | $skeletonDirs[] = $dir.'/../Resources/skeleton'; 287 | $skeletonDirs[] = $dir.'/../Resources'; 288 | 289 | return array_merge($skeletonDirs, $baseSkeletonDirs); 290 | } 291 | } 292 | --------------------------------------------------------------------------------