From 756ad98a1886d3774e47661880f790bbf3ba318f Mon Sep 17 00:00:00 2001 From: kshishkin Date: Sat, 31 Oct 2015 19:15:07 +0100 Subject: [PATCH 1/2] adding i18n support More specifically: - adding dependency to TranslationServiceProvider in composer.json - creating translation messages files under src/SimpleUser/translations/{cultureCode}/* - loading translation files in src/SimpleUser/UserServiceProvider.php - externalizing strings in all templates to translation files - adding language pick dropdown in layout.twig (conditional, only present when more than 1 locale in place) - updating unit tests - updating readme.md with how to example --- README.md | 27 ++++- composer.json | 3 +- src/SimpleUser/UserServiceProvider.php | 30 +++++ src/SimpleUser/translations/en-EN/labels.yml | 103 ++++++++++++++++++ src/SimpleUser/translations/pl-PL/labels.yml | 99 +++++++++++++++++ src/SimpleUser/views/edit.twig | 38 +++---- src/SimpleUser/views/email/confirm-email.twig | 12 +- .../views/email/reset-password.twig | 29 ++--- src/SimpleUser/views/forgot-password.twig | 17 ++- src/SimpleUser/views/layout.twig | 31 ++++-- src/SimpleUser/views/list.twig | 21 ++-- .../views/login-confirmation-needed.twig | 16 +-- src/SimpleUser/views/login.twig | 24 ++-- .../views/register-confirmation-sent.twig | 8 +- src/SimpleUser/views/register.twig | 38 ++++--- src/SimpleUser/views/reset-password.twig | 14 +-- src/SimpleUser/views/view.twig | 8 +- .../Tests/UserServiceProviderTest.php | 10 +- 18 files changed, 404 insertions(+), 124 deletions(-) create mode 100644 src/SimpleUser/translations/en-EN/labels.yml create mode 100644 src/SimpleUser/translations/pl-PL/labels.yml diff --git a/README.md b/README.md index 21452fb..e94819c 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,18 @@ Set up your Silex application something like this: $app->register(new Provider\UrlGeneratorServiceProvider()); $app->register(new Provider\TwigServiceProvider()); $app->register(new Provider\SwiftmailerServiceProvider()); + $app->register(new Provider\TranslationServiceProvider(), array( + 'locale_fallbacks' => array('en-EN', 'pl-PL'), + )); + + // locale session storage + $currentLocale = 'en-EN'; + if ($app['session']->get('current_language')) { + $currentLocale = $app['session']->get('current_language'); + } + // + /* sets current language */ + $app['translator']->setLocale($currentLocale); // Register the SimpleUser service provider. $simpleUserProvider = new SimpleUser\UserServiceProvider(); @@ -68,11 +80,22 @@ Set up your Silex application something like this: // Mount the user controller routes: $app->mount('/user', $simpleUserProvider); - /* - // Other routes and controllers... + + + + /* Other routes and controllers... + + // main page $app->get('/', function () use ($app) { return $app['twig']->render('index.twig', array()); }); + + // switch language + $app->match('/{lang}/', function ($lang) use ($app) { + + $app['session']->set('current_language', $lang); + return $app->redirect($_SERVER['HTTP_REFERER']); + })->assert('lang','[\w-]{2,5}'); */ // ... diff --git a/composer.json b/composer.json index 62c4d2f..cb7ecfc 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "doctrine/dbal": "~2.4", "symfony/twig-bridge": "~2.3", "jasongrimes/paginator": "~1.0", - "swiftmailer/swiftmailer": "~5.3" + "swiftmailer/swiftmailer": "~5.3", + "symfony/translation": "^2.7" }, "require-dev": { "phpunit/phpunit": "~4.2", diff --git a/src/SimpleUser/UserServiceProvider.php b/src/SimpleUser/UserServiceProvider.php index f969a6c..48a9538 100644 --- a/src/SimpleUser/UserServiceProvider.php +++ b/src/SimpleUser/UserServiceProvider.php @@ -92,6 +92,9 @@ public function register(Application $app) 'attribute' => 'attribute', 'value' => 'value', ), + + 'availableLocales' => array('en_EN', 'pl_PL'), + ); // Initialize $app['user.options']. @@ -184,6 +187,33 @@ public function register(Application $app) return $mailer; }); + $app['translator'] = $app->extend('translator', function($translator) { + + $appCurrentlocale = $translator->getLocale(); + + // if app locale is in a short form (e.g. en, de, pl) transform it to valid culture code (en_EN) + $localeCodeLen = strlen($appCurrentlocale); + if($localeCodeLen===2) + $cultureCode = strtolower($appCurrentlocale) . '_' . strtoupper($appCurrentlocale); + elseif($localeCodeLen===5) + $cultureCode = $appCurrentlocale; + else + $cultureCode = 'en-EN'; + + // if there is no translation for determined lang, switch to en_EN as default + if(!is_dir(__DIR__."/translations/$cultureCode")) + $cultureCode = 'en-EN'; + + // load User Service Provider translations + $translator->addLoader('yaml', new \Symfony\Component\Translation\Loader\YamlFileLoader()); + + foreach (glob(__DIR__.'/translations/'. $cultureCode . '/*.yml') as $translationFile) { + $translator->addResource('yaml', $translationFile, $cultureCode); + } + + return $translator; + }); + // Add a custom security voter to support testing user attributes. $app['security.voters'] = $app->extend('security.voters', function($voters) use ($app) { foreach ($voters as $voter) { diff --git a/src/SimpleUser/translations/en-EN/labels.yml b/src/SimpleUser/translations/en-EN/labels.yml new file mode 100644 index 0000000..58f7412 --- /dev/null +++ b/src/SimpleUser/translations/en-EN/labels.yml @@ -0,0 +1,103 @@ +user: + List users: Users + Toggle navigation: Toggle Navigation + Create account: Create account + Sign in: Sign in + View your profile: View your profile + Edit your profile: Edit your profile + Sign out: Sign out + username: username + email: email + password: password + remember: remember me on this computer + not_registerd_question: don't have an account? + forgot_password_question: forgot password? + create_now: register now + hello: hello + + #register + alreadyRegistered: Hello, %displayName%. You are already registered and signed in. + create_account: create an account + already_have_account_question: already have an account? + public_name: Name + public_name_note: Shown publicly + never_shared_note: Never shared + retype_password_label: re-type password + confirm_password_label: confirm password + register: register + sign_in_now: Sign in now + + #forgot password + enter_password_note: Enter your email address below and we'll send you password reset instructions. + your_email_address: Your email address + send_me_instructions: Send me reset instructions + backToLogin: Back to login + spamboxNote: If you don't get an email within a few minutes, make sure to check your spam or junk folder. The sender is + + #login-confirmation + accountNotYetActiveTitle: Your account is not yet active + emailSentToAddressNoteWhenRegistered: An email should have been sent to you at %email% when you registered. + clickLinkInMessageNote: Please click the link in that message to confirm your email address and activate your account. + resendTheMessage: Resend the message + checkJunkEmailBox: Make sure to check your junk or spam folder. The sender is %fromAddress%. + emailConfirmationNeeded: Email confirmation needed + + #registerConfirmation + thankYou: Thank you! + emailSentToAddress: An email has been sent to you at %email%. + + #resetPassword + chooseNewPassword: Choose a new password + newPassword: New password + retypePassword: Re-type password + savePassword: Save password + + #view + edit: edit + registered: registered + pendingEmailConfirmation: Pending email confirmation + visibleToAdminsOnly: visible to admins only + + #email - confirm-email + welcomePleaseConfirmEmail: Welcome! Please confirm your email. + thanksForJoiningSite: Thanks for joining our site! + pleaseClickLinkBelow: Please click the link below to confirm your email + pleaseClickLinkHtml: Please click here to confirm your email. + + #edit + editUser: Edit User + gravatarImage: Gravatar image + change: Change + name: Name + shownPublicly: Shown publicly + neverShared: Never shared + leavBlankExceptToChange: Leave blank except to change + adminOnly: Admin-only + backToProfile: Back to profile + save: Save + resetYourPassword: Reset your password + clickTheLinkToResetYourPassword: Click the link below to reset your password + pleaseClickHereToResetPassword: Please click here to reset your password. + didntAskToReset: Didn't ask to reset your password? + didntAskToResetNote: > + If you didn't ask to reset your password, it's likely that + another user entered your username or email address by mistake + while trying to reset their password. If that's the case, you + don't need to take any further action and you can safely + disregard this email. + + #list + listUsers: List users + oneUserFound: 1 user found. + numberOfUsersFound: %totalItems% users found. + showing: Showing + pendingConfirmation: Pending email confirmation + +langs: + en-EN: EN + pl-PL: PL + de-DE: DE + + en: EN + pl: PL + de: DE diff --git a/src/SimpleUser/translations/pl-PL/labels.yml b/src/SimpleUser/translations/pl-PL/labels.yml new file mode 100644 index 0000000..b4631e1 --- /dev/null +++ b/src/SimpleUser/translations/pl-PL/labels.yml @@ -0,0 +1,99 @@ +user: + List users: Użytkownicy + Toggle navigation: Przełącz Nawigację + Create account: Zarejestruj + Sign in: Zaloguj + View your profile: Zobacz profil + Edit your profile: Edytuj profil + Sign out: Wyloguj + username: nazwa użytkownika + email: email + password: hasło + remember: Zapamiętaj mnie na tym komputerze + not_registerd_question: Nie masz konta? + forgot_password_question: Nie pamiętasz hasła? + create_now: zarejestruj teraz + + #register + create_account: utwórz konto + already_have_account_question: masz już konto? + public_name: nazwa + public_name_note: pokazywana publicznie + never_shared_note: nigdy nie pokazywany + retype_password_label: potwierdź hasło + confirm_password_label: potwierdź hasło + register: zarejestruj + sign_in_now: zaloguj teraz + + #forgot password + enter_password_note: Wprowadź swój adres poniżej, wyślemy Ci instrukcję ustawienia nowego hasła. + your_email_address: Twój adres email + send_me_instructions: Wyślij mi instrukcję ustanowienia nowego hasła + backToLogin: Spowrotem do logowania + spamboxNote: Jeśli nie dostaniesz wiadomości w ciągu kilku minut, sprawdź folder ze spamem. Nadawcą wiadomości jest + + #login-confirmation + accountNotYetActiveTitle: Twoje konto nie jest jeszcze aktywne + emailSentToAddressNoteWhenRegistered: Email powinien zostać do Ciebie wysłany na adres %email% kiedy się zarejestrowaleś. + clickLinkInMessageNote: Proszę kliknij w link w treści wiadomości by potwierdzić adres email i aktywować konto. + resendTheMessage: Wyślij ponownie + checkJunkEmailBox: Upewnij się, że sprawdziłeś folder ze spamem. Nadawcą jest %fromAddress%. + emailConfirmationNeeded: Potrzebne jest potwierdzenie adresu email. + + #registerConfirmation + thankYou: Dziękujemy! + emailSentToAddress: Email został wysłany na adres %email%. + + #resetPassword + chooseNewPassword: Wybierz nowe haslo + newPassword: Nowe hasło + retypePassword: Wprowadź ponownie hasło + savePassword: Zapisz hasło + + #view + edit: edytuj + registered: zarejestrowany + pendingEmailConfirmation: Oczekuje na potwierdzenie adresu email + visibleToAdminsOnly: widoczne tylko dla administratorów + + #email - confirm-email + welcomePleaseConfirmEmail: Witaj! Potwierdź proszę swój email. + thanksForJoiningSite: Dziękujemy za dołączenie do użytkowników. + pleaseClickLinkBelow: Proszę, kliknij w link poniżej by potwierdzić adres email. + pleaseClickLinkHtml: Proszę, kliknij tutaj by potwierdzić adres email. + + #edit + editUser: Edytuj profil + gravatarImage: Obrazek Gravatar + change: Zmień + name: Nazwa + shownPublicly: Pokazywane publicznie + neverShared: Nigdy nie ujawniane + leavBlankExceptToChange: Pozostaw puste, chyba że chcesz zmienić + adminOnly: Admin-tylko + backToProfile: Powrót do profilu + save: Zapisz + resetYourPassword: Ustaw nowe hasło + clickTheLinkToResetYourPassword: Kliknij w link poniżej by ustawić nowe hasło. + pleaseClickHereToResetPassword: Proszę kliknij tutaj by ustawić nowe hasło. + didntAskToReset: Nie prosiłeś o zmianę hasła? + didntAskToResetNote: > + Jeśli nie prosiłeś o zmianę hasła, jest prawdopodobne, + że ktoś inny wprowadził Twoją nazwę użytkownika przez pomyłkę. + W takim przypadku możesz spokojnie zignorować tą wiadomość. + #list + listUsers: Lista użytkowników + oneUserFound: 1 znaleziony użytkownik. + numberOfUsersFound: %totalItems% znalezieni użytkownicy. + showing: Pokazywane + pendingConfirmation: Oczekuje na potwierdzenie adresu email. + + +langs: + en-EN: EN + pl-PL: PL + de-DE: DE + + en: EN + pl: PL + de: DE \ No newline at end of file diff --git a/src/SimpleUser/views/edit.twig b/src/SimpleUser/views/edit.twig index 657d929..f5f9684 100644 --- a/src/SimpleUser/views/edit.twig +++ b/src/SimpleUser/views/edit.twig @@ -1,10 +1,10 @@ {% extends layout_template %} -{% block title %}Edit User: {{ user.displayName }}{% endblock %} +{% block title %}{{ 'user.editUser' | trans }}: {{ user.displayName }}{% endblock %} {% block content %} -

Edit User: {{ user.displayName }}

+

{{ 'user.editUser' | trans }}: {{ user.displayName }}

{% if error %}
{{ error|nl2br }}
@@ -14,8 +14,8 @@
@@ -23,42 +23,42 @@
- +
-

Shown publicly

+

{{ 'user.shownPublicly' | trans }}

- +
- -

Never shared

+ +

{{ 'user.neverShared' | trans }}

{% if isUsernameRequired or user.hasRealUsername %}
- +
- +
{% endif %}
- +
- -

Leave blank except to change

+ +

{{ 'user.leavBlankExceptToChange' | trans }}

- +
- +
@@ -66,7 +66,7 @@
+
- - Back to login + + {{ 'user.backToLogin' | trans | capitalize }}

- If you don't get an email within a few minutes, make sure to check your spam or junk folder. - The sender is {{ fromAddress }}. + {{ 'user.spamboxNote' | trans | capitalize }} {{ fromAddress }}.
diff --git a/src/SimpleUser/views/layout.twig b/src/SimpleUser/views/layout.twig index 67bc15b..972a89f 100644 --- a/src/SimpleUser/views/layout.twig +++ b/src/SimpleUser/views/layout.twig @@ -1,5 +1,5 @@ - + @@ -14,7 +14,7 @@ -

Make sure to check your junk or spam folder. The sender is {{ fromAddress }}.

+

{{ 'user.checkJunkEmailBox' | trans }}

{% endblock %} diff --git a/src/SimpleUser/views/login.twig b/src/SimpleUser/views/login.twig index aee7068..eb3e09f 100644 --- a/src/SimpleUser/views/login.twig +++ b/src/SimpleUser/views/login.twig @@ -1,20 +1,20 @@ {% extends layout_template %} -{% block title %}Sign in{% endblock %} +{% block title %}{{ 'user.Sign in' | trans | capitalize }}{% endblock %} {% block content %} {% if app.user %} -

Hello, {{ app.user.displayName }}.

-

Sign out

+

{{ 'user.hello' | trans | capitalize }}, {{ app.user.displayName }}.

+

{{ 'user.Sign out' | trans }}

{% else %}

- Sign in + {{ 'user.Sign in' | trans }}

- Don't have an account? Register now. + {{ 'user.not_registerd_question' | trans | capitalize }} {{ 'user.create_now' | trans | capitalize }}

{% if error %} @@ -24,16 +24,16 @@
- +
- +
- +
- +
@@ -41,7 +41,7 @@
@@ -49,9 +49,9 @@
- + {% if allowPasswordReset %} - Forgot your password? + {{ 'user.forgot_password_question' | trans | capitalize }} {% endif %}
diff --git a/src/SimpleUser/views/register-confirmation-sent.twig b/src/SimpleUser/views/register-confirmation-sent.twig index 26c7fba..ded932b 100644 --- a/src/SimpleUser/views/register-confirmation-sent.twig +++ b/src/SimpleUser/views/register-confirmation-sent.twig @@ -4,10 +4,12 @@ {% block content %} -

Thank you!

+

{{ 'user.thankYou' | trans }}

-

An email has been sent to you at {{ email }}.

+ {% autoescape false %} +

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

+ {% endautoescape %} -

Please click the link in that message to activate your account.

+

{{ 'user.clickLinkInMessageNote' | trans }}

{% endblock %} diff --git a/src/SimpleUser/views/register.twig b/src/SimpleUser/views/register.twig index 8e11cbd..09bcfe9 100644 --- a/src/SimpleUser/views/register.twig +++ b/src/SimpleUser/views/register.twig @@ -1,20 +1,22 @@ {% extends layout_template %} -{% block title %}Register{% endblock %} +{% block title %}{{ 'user.register' | trans | capitalize }}{% endblock %} {% block content %} {% if app.user %} -

Hello, {{ app.user.displayName }}. You are already registered and signed in.

-

Sign out

+ {% autoescape false %} +

{{ 'user.alreadyRegistered' | trans( { '%displayName%': app.user.displayName} ) }}

+ {% endautoescape %} +

{{ 'user.Sign out' | trans | capitalize }}

{% else %}

- Create an account + {{ 'user.create_account' | trans | capitalize }}

- Already have an account? Sign in now. + {{ 'user.already_have_account_question' | trans | capitalize }} {{ 'user.sign_in_now' | trans | capitalize }}.

{% if error %} @@ -24,47 +26,47 @@
- +
- - Shown publicly + + {{ 'user.public_name_note' | trans | capitalize }}
- +
- - Never shared + + {{ 'user.never_shared_note' | trans | capitalize }}
{% if isUsernameRequired %}
- +
- +
{% endif %}
- +
- +
- +
- +
- +
diff --git a/src/SimpleUser/views/reset-password.twig b/src/SimpleUser/views/reset-password.twig index fbc916f..5c263c8 100644 --- a/src/SimpleUser/views/reset-password.twig +++ b/src/SimpleUser/views/reset-password.twig @@ -1,11 +1,11 @@ {% extends layout_template %} -{% block title %}Reset your password{% endblock %} +{% block title %}{{ 'user.resetYourPassword' | trans }}{% endblock %} {% block content %}

- Choose a new password, {{ user.displayName }} + {{ 'user.chooseNewPassword' | trans | capitalize }}, {{ user.displayName }}

{% if error %} @@ -14,22 +14,22 @@
- +
- +
- +
- +
- +
diff --git a/src/SimpleUser/views/view.twig b/src/SimpleUser/views/view.twig index 45ded1a..7cbd856 100644 --- a/src/SimpleUser/views/view.twig +++ b/src/SimpleUser/views/view.twig @@ -14,8 +14,8 @@ {% if not user.isEnabled %}
- Pending email confirmation - (visible to admins only) + {{ 'user.pendingEmailConfirmation' | trans | capitalize }} + ({{ 'user.visibleToAdminsOnly' | trans | capitalize }})
{% endif %} @@ -27,12 +27,12 @@ {% endif %}
- Registered {{ user.timeCreated|date('F j, Y') }} + {{ 'user.registered' | trans | capitalize }} {{ user.timeCreated|date('F j, Y') }}
{% if is_granted('EDIT_USER', user) %}
- Edit + {{ 'user.edit' | trans | capitalize }}
{% endif %}
diff --git a/tests/SimpleUser/Tests/UserServiceProviderTest.php b/tests/SimpleUser/Tests/UserServiceProviderTest.php index fee8992..4b954a5 100644 --- a/tests/SimpleUser/Tests/UserServiceProviderTest.php +++ b/tests/SimpleUser/Tests/UserServiceProviderTest.php @@ -16,6 +16,11 @@ protected function getMinimalApp() array('security.firewalls' => array('dummy-firewall' => array('form' => array()))) ); $app->register(new Provider\DoctrineServiceProvider()); + + $app->register(new Provider\TranslationServiceProvider(), array( + 'locale_fallbacks' => array('en-EN'), + )); + $app->register(new UserServiceProvider(), array( 'db.options' => array( 'driver' => 'pdo_sqlite', @@ -36,7 +41,10 @@ protected function getAppWithAllDependencies() $app->register(new Provider\UrlGeneratorServiceProvider()); $app->register(new Provider\TwigServiceProvider()); $app->register(new Provider\SwiftmailerServiceProvider()); - + $app->register(new Provider\TranslationServiceProvider(), array( + 'locale_fallbacks' => array('en-EN'), + )); + return $app; } From 5f32fd29b5080d8c4b8389dea0e6a59fb838382d Mon Sep 17 00:00:00 2001 From: kshishkin Date: Sat, 7 Nov 2015 04:02:42 +0100 Subject: [PATCH 2/2] resolving columns escaping issue #64 More specifically: 1. UserManager.php - removed escaping part from setColumns - created getCol,umnsEscaped method - it operates on getColumns and applies escaping - changed SQL statements to use getUserColumns and getUserColumnsEscaped where needed - sanitized :param_name 2. UserManagerTest.php - created $app protected property - added test case for custom email column name containing risky character (space). - referencing getUserColumns instead of hardcoded column name in two present testcases --- src/SimpleUser/UserManager.php | 74 +++++++++++++--------- tests/SimpleUser/Tests/UserManagerTest.php | 35 ++++++++-- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/SimpleUser/UserManager.php b/src/SimpleUser/UserManager.php index e1fc43a..0795a01 100644 --- a/src/SimpleUser/UserManager.php +++ b/src/SimpleUser/UserManager.php @@ -456,9 +456,9 @@ protected function createCommonFindSql(array $criteria = array()) $i++; $alias = 'custom' . $i; $sql .= 'JOIN ' . $this->conn->quoteIdentifier($this->userCustomFieldsTableName). ' ' . $alias . ' '; - $sql .= 'ON ' . $this->conn->quoteIdentifier($this->userTableName). '.' . $this->getUserColumns('id') . ' = ' . $alias . '.'. $this->getUserColumns('user_id').' '; - $sql .= 'AND ' . $alias . '.'.$this->getUserColumns('attribute').' = :attribute' . $i . ' '; - $sql .= 'AND ' . $alias . '.'.$this->getUserColumns('value').' = :value' . $i . ' '; + $sql .= 'ON ' . $this->conn->quoteIdentifier($this->userTableName). '.' . $this->getUserColumnsEscaped('id') . ' = ' . $alias . '.'. $this->getUserColumnsEscaped('user_id').' '; + $sql .= 'AND ' . $alias . '.'.$this->getUserColumnsEscaped('attribute').' = :attribute' . $i . ' '; + $sql .= 'AND ' . $alias . '.'.$this->getUserColumnsEscaped('value').' = :value' . $i . ' '; $params['attribute' . $i] = $attribute; $params['value' . $i] = $value; } @@ -469,8 +469,9 @@ protected function createCommonFindSql(array $criteria = array()) if ($key == 'customFields') { continue; } else { - $sql .= ($first_crit ? 'WHERE' : 'AND') . ' ' . $key . ' = :' . $key . ' '; - $params[$key] = $val; + $param_key = preg_replace('/[^\w]/', '', $key); + $sql .= ($first_crit ? 'WHERE' : 'AND') . ' ' . $this->conn->quoteIdentifier($key) . ' = :' . $param_key . ' '; + $params[$param_key] = $val; } $first_crit = false; } @@ -503,9 +504,9 @@ public function insert(User $user) $this->dispatcher->dispatch(UserEvents::BEFORE_INSERT, new UserEvent($user)); $sql = 'INSERT INTO ' . $this->conn->quoteIdentifier($this->userTableName) . ' - ('.$this->getUserColumns('email').', '.$this->getUserColumns('password').', '.$this->getUserColumns('salt').', '.$this->getUserColumns('name'). - ', '.$this->getUserColumns('roles').', '.$this->getUserColumns('time_created').', '.$this->getUserColumns('username').', '.$this->getUserColumns('isEnabled'). - ', '.$this->getUserColumns('confirmationToken').', '.$this->getUserColumns('timePasswordResetRequested').') + ('.$this->getUserColumnsEscaped('email').', '.$this->getUserColumnsEscaped('password').', '.$this->getUserColumnsEscaped('salt').', '.$this->getUserColumnsEscaped('name'). + ', '.$this->getUserColumnsEscaped('roles').', '.$this->getUserColumnsEscaped('time_created').', '.$this->getUserColumnsEscaped('username').', '.$this->getUserColumnsEscaped('isEnabled'). + ', '.$this->getUserColumnsEscaped('confirmationToken').', '.$this->getUserColumnsEscaped('timePasswordResetRequested').') VALUES (:email, :password, :salt, :name, :roles, :timeCreated, :username, :isEnabled, :confirmationToken, :timePasswordResetRequested) '; $params = array( @@ -542,17 +543,17 @@ public function update(User $user) $this->dispatcher->dispatch(UserEvents::BEFORE_UPDATE, new UserEvent($user)); $sql = 'UPDATE ' . $this->conn->quoteIdentifier($this->userTableName). ' - SET '.$this->getUserColumns('email').' = :email - , '.$this->getUserColumns('password').' = :password - , '.$this->getUserColumns('salt').' = :salt - , '.$this->getUserColumns('name').' = :name - , '.$this->getUserColumns('roles').' = :roles - , '.$this->getUserColumns('time_created').' = :timeCreated - , '.$this->getUserColumns('username').' = :username - , '.$this->getUserColumns('isEnabled').' = :isEnabled - , '.$this->getUserColumns('confirmationToken').' = :confirmationToken - , '.$this->getUserColumns('timePasswordResetRequested').' = :timePasswordResetRequested - WHERE '.$this->getUserColumns('id').' = :id'; + SET '.$this->getUserColumnsEscaped('email').' = :email + , '.$this->getUserColumnsEscaped('password').' = :password + , '.$this->getUserColumnsEscaped('salt').' = :salt + , '.$this->getUserColumnsEscaped('name').' = :name + , '.$this->getUserColumnsEscaped('roles').' = :roles + , '.$this->getUserColumnsEscaped('time_created').' = :timeCreated + , '.$this->getUserColumnsEscaped('username').' = :username + , '.$this->getUserColumnsEscaped('isEnabled').' = :isEnabled + , '.$this->getUserColumnsEscaped('confirmationToken').' = :confirmationToken + , '.$this->getUserColumnsEscaped('timePasswordResetRequested').' = :timePasswordResetRequested + WHERE '.$this->getUserColumnsEscaped('id').' = :id'; $params = array( 'email' => $user->getEmail(), @@ -581,11 +582,11 @@ public function update(User $user) protected function saveUserCustomFields(User $user) { $this->conn->executeUpdate('DELETE FROM ' . $this->conn->quoteIdentifier($this->userCustomFieldsTableName). ' - WHERE '.$this->getUserColumns('user_id').' = ?', array($user->getId())); + WHERE '.$this->getUserColumnsEscaped('user_id').' = ?', array($user->getId())); foreach ($user->getCustomFields() as $attribute => $value) { $this->conn->executeUpdate('INSERT INTO ' . $this->conn->quoteIdentifier($this->userCustomFieldsTableName). - ' ('.$this->getUserColumns('user_id').', '.$this->getUserColumns('attribute').', '.$this->getUserColumns('value').') VALUES (?, ?, ?) ', + ' ('.$this->getUserColumnsEscaped('user_id').', '.$this->getUserColumnsEscaped('attribute').', '.$this->getUserColumnsEscaped('value').') VALUES (?, ?, ?) ', array($user->getId(), $attribute, $value)); } } @@ -706,22 +707,33 @@ public function getUserTableName() } public function setUserColumns(array $userColumns){ - $conn = $this->conn; - //Escape the column names - - $escapedUserColumns = array_map(function($column) use ($conn){ - return $conn->quoteIdentifier($column,\PDO::PARAM_STR); - }, $userColumns); - //Merge the existing column names - $this->userColumns = array_merge($this->userColumns, $escapedUserColumns); + $this->userColumns = array_merge($this->userColumns, $userColumns); } - public function getUserColumns($column = ""){ - if ($column == "") return $this->userColumns; + public function getUserColumns($column = null){ + if ($column === null) return $this->userColumns; else return $this->userColumns[$column]; } + public function getUserColumnsEscaped($column = null) + { + $userColumns = $this->getUserColumns(); + + $conn = $this->conn; + + if ($column === null){ + $columns = $userColumns; + $escapedColumns = array_map(function($column) use ($conn){ + return $conn->quoteIdentifier($column,\PDO::PARAM_STR); + }, $columns); + + return $escapedColumns; + } + else + return $conn->quoteIdentifier($userColumns[$column],\PDO::PARAM_STR); + } + public function setUserCustomFieldsTableName($userCustomFieldsTableName) { $this->userCustomFieldsTableName = $userCustomFieldsTableName; diff --git a/tests/SimpleUser/Tests/UserManagerTest.php b/tests/SimpleUser/Tests/UserManagerTest.php index 56695d2..6765e4d 100644 --- a/tests/SimpleUser/Tests/UserManagerTest.php +++ b/tests/SimpleUser/Tests/UserManagerTest.php @@ -29,6 +29,9 @@ class UserManagerTest extends \PHPUnit_Framework_TestCase /** @var EventDispatcher */ protected $dispatcher; + /* @var $app Application */ + protected $app; + public function setUp() { $app = new Application(); @@ -44,6 +47,7 @@ public function setUp() $this->userManager = new UserManager($app['db'], $app); $this->conn = $app['db']; $this->dispatcher = $app['dispatcher']; + $this->app = $app; } public function testCreateUser() @@ -84,10 +88,11 @@ public function testDeleteUser() $user = $this->userManager->createUser($email, 'password'); $this->userManager->insert($user); - $this->assertEquals($user, $this->userManager->findOneBy(array('email' => $email))); + $emailColumn = $this->userManager->getUserColumns('email'); + $this->assertEquals($user, $this->userManager->findOneBy(array($emailColumn => $email))); $this->userManager->delete($user); - $this->assertNull($this->userManager->findOneBy(array('email' => $email))); + $this->assertNull($this->userManager->findOneBy(array($emailColumn => $email))); } public function testCustomFields() @@ -226,8 +231,9 @@ public function testFindAndCount() $user2 = $this->userManager->createUser($email2, 'password'); $user2->setCustomField($customField, $customVal); $this->userManager->insert($user2); - - $criteria = array('email' => $email1); + + $emailColumn = $this->userManager->getUserColumns('email'); + $criteria = array($emailColumn => $email1); $results = $this->userManager->findBy($criteria); $numResults = $this->userManager->findCount($criteria); $this->assertCount(1, $results); @@ -412,7 +418,24 @@ public function testPasswordStrengthValidator() public function testChangeUserColumns() { - $this->userManager->setUserColumns(array('email' => 'foo')); - $this->assertEquals('"foo"', $this->userManager->getUserColumns('email')); + $this->userManager->setUserColumns(array('email' => 'foo ')); + $this->assertEquals('"foo "', $this->userManager->getUserColumnsEscaped('email')); + } + + public function testValidationWhenChangedUserColumns() + { + + $statement = file_get_contents(__DIR__ . '/../../../sql/sqlite.sql'); + $statementModified = str_replace('email', '"foo "', $statement); + $this->app['db']->executeUpdate('DROP TABLE user_custom_fields;'); + $this->app['db']->executeUpdate('DROP TABLE users;'); + $this->app['db']->executeUpdate($statementModified); + + $this->userManager->setUserColumns(array('email' => 'foo ')); + + $user = $this->userManager->createUser('test@example.com', 'password'); + + $errors = $this->userManager->validate($user); + $this->assertEmpty($errors); } }