Merge branch 'master' into bugfix/commands-6593

This commit is contained in:
Eric Lippmann 2014-09-19 13:18:47 +02:00
commit c51b05296e
13 changed files with 412 additions and 43 deletions

View File

@ -918,10 +918,21 @@ class Module
* Translate a string with the global mt() * Translate a string with the global mt()
* *
* @param $string * @param $string
* @param null $context
*
* @return mixed|string * @return mixed|string
*/ */
protected function translate($string) protected function translate($string, $context = null)
{ {
return mt($this->name, $string); return mt($this->name, $string, $context);
}
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
protected function translatePlural($textSingular, $textPlural, $number, $context = null)
{
return mtp($this->name, $textSingular, $textPlural, $number, $context);
} }
} }

View File

@ -5,23 +5,85 @@
use Icinga\Util\Translator; use Icinga\Util\Translator;
if (extension_loaded('gettext')) { if (extension_loaded('gettext')) {
function t($messageId)
/**
* (non-PHPDoc)
* @see Translator::translate() For the function documentation.
*/
function t($messageId, $context = null)
{ {
return Translator::translate($messageId, Translator::DEFAULT_DOMAIN); return Translator::translate($messageId, Translator::DEFAULT_DOMAIN, $context);
} }
function mt($domain, $messageId) /**
* (non-PHPDoc)
* @see Translator::translate() For the function documentation.
*/
function mt($domain, $messageId, $context = null)
{ {
return Translator::translate($messageId, $domain); return Translator::translate($messageId, $domain, $context);
} }
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
function tp($messageId, $messageId2, $number, $context = null)
{
return Translator::translatePlural($messageId, $messageId2, $number, Translator::DEFAULT_DOMAIN, $context);
}
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
function mtp($domain, $messageId, $messageId2, $number, $context = null)
{
return Translator::translatePlural($messageId, $messageId2, $number, $domain, $context);
}
} else { } else {
function t($messageId)
/**
* (non-PHPDoc)
* @see Translator::translate() For the function documentation.
*/
function t($messageId, $context = null)
{ {
return $messageId; return $messageId;
} }
function mt($domain, $messageId) /**
* (non-PHPDoc)
* @see Translator::translate() For the function documentation.
*/
function mt($domain, $messageId, $context = null)
{ {
return $messageId; return $messageId;
} }
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
function tp($messageId, $messageId2, $number, $context = null)
{
if ((int) $number !== 1) {
return $messageId2;
}
return $messageId;
}
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
function mtp($domain, $messageId, $messageId2, $number, $context = null)
{
if ((int) $number !== 1) {
return $messageId2;
}
return $messageId;
}
} }

View File

@ -113,30 +113,32 @@ class Manager
} }
/** /**
* Tries to authenticate the user with the current session * Try to authenticate the user with the current session
*
* Authentication for externally-authenticated users will be revoked if the username changed or external
* authentication is no longer in effect
*/ */
public function authenticateFromSession() public function authenticateFromSession()
{ {
$this->user = Session::getSession()->get('user'); $this->user = Session::getSession()->get('user');
if ($this->user !== null && $this->user->isRemoteUser() === true) { if ($this->user !== null && $this->user->isRemoteUser() === true) {
list($originUsername, $field) = $this->user->getRemoteUserInformation(); list($originUsername, $field) = $this->user->getRemoteUserInformation();
if (array_key_exists($field, $_SERVER) && $_SERVER[$field] !== $originUsername) { if (! array_key_exists($field, $_SERVER) || $_SERVER[$field] !== $originUsername) {
$this->removeAuthorization(); $this->removeAuthorization();
} }
} }
} }
/** /**
* Returns true when the user is currently authenticated * Whether the user is authenticated
* *
* @param Boolean $ignoreSession Set to true to prevent authentication by session * @param bool $ignoreSession True to prevent session authentication
* *
* @return bool * @return bool
*/ */
public function isAuthenticated($ignoreSession = false) public function isAuthenticated($ignoreSession = false)
{ {
if ($this->user === null && !$ignoreSession) { if ($this->user === null && ! $ignoreSession) {
$this->authenticateFromSession(); $this->authenticateFromSession();
} }
return is_object($this->user); return is_object($this->user);
@ -145,25 +147,16 @@ class Manager
/** /**
* Whether an authenticated user has a given permission * Whether an authenticated user has a given permission
* *
* This is true if the user owns this permission, false if not.
* Also false if there is no authenticated user
*
* TODO: I'd like to see wildcard support, e.g. module/*
*
* @param string $permission Permission name * @param string $permission Permission name
* @return bool *
* @return bool True if the user owns the given permission, false if not or if not authenticated
*/ */
public function hasPermission($permission) public function hasPermission($permission)
{ {
if (! $this->isAuthenticated()) { if (! $this->isAuthenticated()) {
return false; return false;
} }
foreach ($this->user->getPermissions() as $p) { return $this->user->can($permission);
if ($p === $permission) {
return true;
}
}
return false;
} }
/** /**

View File

@ -179,7 +179,7 @@ class User
} }
/** /**
* Return permission information for this user * Get the user's permissions
* *
* @return array * @return array
*/ */
@ -189,13 +189,17 @@ class User
} }
/** /**
* Setter for permissions * Set the user's permissions
* *
* @param array $permissions * @param array $permissions
*
* @return $this
*/ */
public function setPermissions(array $permissions) public function setPermissions(array $permissions)
{ {
$this->permissions = $permissions; natcasesort($permissions);
$this->permissions = array_combine($permissions, $permissions);
return $this;
} }
/** /**
@ -402,6 +406,33 @@ class User
*/ */
public function isRemoteUser() public function isRemoteUser()
{ {
return (count($this->remoteUserInformation)) ? true : false; return ! empty($this->remoteUserInformation);
}
/**
* Whether the user has a given permission
*
* @param string $permission
*
* @return bool
*/
public function can($permission)
{
if (isset($this->permissions['*']) || isset($this->permissions[$permission])) {
return true;
}
foreach ($this->permissions as $permitted) {
$wildcard = strpos($permitted, '*');
if ($wildcard !== false) {
if (substr($permission, 0, $wildcard) === substr($permitted, 0, $wildcard)) {
return true;
} else {
if ($permission === $permitted) {
return true;
}
}
}
}
return false;
} }
} }

View File

@ -36,11 +36,16 @@ class Translator
* *
* @param string $text The string to translate * @param string $text The string to translate
* @param string $domain The primary domain to use * @param string $domain The primary domain to use
* @param string|null $context Optional parameter for context based translation
* *
* @return string The translated string * @return string The translated string
*/ */
public static function translate($text, $domain) public static function translate($text, $domain, $context = null)
{ {
if ($context !== null) {
return self::pgettext($text, $domain, $context);
}
$res = dgettext($domain, $text); $res = dgettext($domain, $text);
if ($res === $text && $domain !== self::DEFAULT_DOMAIN) { if ($res === $text && $domain !== self::DEFAULT_DOMAIN) {
return dgettext(self::DEFAULT_DOMAIN, $text); return dgettext(self::DEFAULT_DOMAIN, $text);
@ -48,6 +53,77 @@ class Translator
return $res; return $res;
} }
/**
* Translate a plural string
*
* @param string $textSingular The string in singular form to translate
* @param string $textPlural The string in plural form to translate
* @param integer $number The number to get the plural or singular string
* @param string $domain The primary domain to use
* @param string|null $context Optional parameter for context based translation
*
* @return string The translated string
*/
public static function translatePlural($textSingular, $textPlural, $number, $domain, $context = null)
{
if ($context !== null) {
return self::pngettext($textSingular, $textPlural, $number, $domain, $context);
}
$res = dngettext($domain, $textSingular, $textPlural, $number);
return $res;
}
/**
* Emulated pgettext()
*
* @link http://php.net/manual/de/book.gettext.php#89975
*
* @param $text
* @param $domain
* @param $context
*
* @return string
*/
public static function pgettext($text, $domain, $context)
{
$contextString = "{$context}\004{$text}";
$translation = dcgettext($domain, $contextString, LC_MESSAGES);
if ($translation == $contextString) {
return $text;
} else {
return $translation;
}
}
/**
* Emulated pngettext()
*
* @link http://php.net/manual/de/book.gettext.php#89975
*
* @param $textSingular
* @param $textPlural
* @param $number
* @param $domain
* @param $context
*
* @return string
*/
public static function pngettext($textSingular, $textPlural, $number, $domain, $context)
{
$contextString = "{$context}\004{$textSingular}";
$translation = dcngettext($domain, $contextString, $textPlural, $number, LC_MESSAGES);
if ($translation == $contextString || $translation == $textPlural) {
return ($number == 1 ? $textSingular : $textPlural);
} else {
return $translation;
}
}
/** /**
* Register a new gettext domain * Register a new gettext domain
* *

View File

@ -230,12 +230,28 @@ class ActionController extends Zend_Controller_Action
* Autoselects the module domain, if any, and falls back to the global one if no translation could be found. * Autoselects the module domain, if any, and falls back to the global one if no translation could be found.
* *
* @param string $text The string to translate * @param string $text The string to translate
* @param string|null $context Optional parameter for context based translation
* *
* @return string The translated string * @return string The translated string
*/ */
public function translate($text) public function translate($text, $context = null)
{ {
return Translator::translate($text, $this->view->translationDomain); return Translator::translate($text, $this->view->translationDomain, $context);
}
/**
* Translate a plural string
*
* @param string $textSingular The string in singular form to translate
* @param string $textPlural The string in plural form to translate
* @param string $number The number to get the plural or singular string
* @param string|null $context Optional parameter for context based translation
*
* @return string The translated string
*/
public function translatePlural($textSingular, $textPlural, $number, $context = null)
{
return Translator::translatePlural($textSingular, $textPlural, $number, $this->view->translationDomain, $context);
} }
protected function ignoreXhrBody() protected function ignoreXhrBody()

View File

@ -127,9 +127,19 @@ class View extends Zend_View_Abstract
); );
} }
public function translate($text) public function translate($text, $context = null)
{ {
return Translator::translate($text, $this->translationDomain); return Translator::translate($text, $this->translationDomain, $context);
}
/**
* Translate a plural string
*
* @see Translator::translatePlural()
*/
public function translatePlural($textSingular, $textPlural, $number, $context = null)
{
return Translator::translatePlural($textSingular, $textPlural, $number, $this->translationDomain, $context);
} }
/** /**

View File

@ -42,6 +42,40 @@ The same works also for views:
If you need to provide placeholders in your messages, you should wrap the `$this->translate()` with `sprintf()` for e.g. If you need to provide placeholders in your messages, you should wrap the `$this->translate()` with `sprintf()` for e.g.
sprintf($this->translate('Hello User: (%s)'), $user->getName()) sprintf($this->translate('Hello User: (%s)'), $user->getName())
## Translating plural forms
To provide a plural translation, just use the `translatePlural()` function.
```php
<?php
class ExampleController extends Controller
{
public function indexAction()
{
$this->view->message = $this->translatePlural('Service', 'Services', 3);
}
}
```
## Context based translation
If you want to provide context based translations, you can easily do it with an extra parameter in both methods
`translate()` and `translatePlural()`.
```php
<?php
class ExampleController extends Controller
{
public function indexAction()
{
$this->view->title = $this->translate('My Titile', 'mycontext');
$this->view->message = $this->translatePlural('Service', 'Services', 3, 'mycontext');
}
}
```
# Translation for Translators # Translation for Translators
Icinga Web 2 internally uses the UNIX standard gettext tool to perform internationalization, this means translation Icinga Web 2 internally uses the UNIX standard gettext tool to perform internationalization, this means translation

View File

@ -237,8 +237,16 @@ class GettextTranslationHelper
'/usr/bin/xgettext', '/usr/bin/xgettext',
'--language=PHP', '--language=PHP',
'--keyword=translate', '--keyword=translate',
'--keyword=translate:1,2c',
'--keyword=translatePlural:1,2',
'--keyword=translatePlural:1,2,4c',
'--keyword=mt:2', '--keyword=mt:2',
'--keyword=mtp:2,3',
'--keyword=mtp:2,3,5c',
'--keyword=t', '--keyword=t',
'--keyword=t:1,2c',
'--keyword=tp:1,2',
'--keyword=tp:1,2,4c',
'--sort-output', '--sort-output',
'--force-po', '--force-po',
'--omit-header', '--omit-header',
@ -332,6 +340,7 @@ class GettextTranslationHelper
'"MIME-Version: 1.0\n"', '"MIME-Version: 1.0\n"',
'"Content-Type: text/plain; charset=' . $headerInfo['charset'] . '\n"', '"Content-Type: text/plain; charset=' . $headerInfo['charset'] . '\n"',
'"Content-Transfer-Encoding: 8bit\n"', '"Content-Transfer-Encoding: 8bit\n"',
'"Plural-Forms: nplurals=2; plural=(n != 1);\n"',
'' ''
) )
) . PHP_EOL . substr($content, strpos($content, '#: ')) ) . PHP_EOL . substr($content, strpos($content, '#: '))

View File

@ -60,4 +60,19 @@ class UserTest extends BaseTestCase
$user = new User('unittest'); $user = new User('unittest');
$user->setEmail('mySampleEmail at someDomain dot org'); $user->setEmail('mySampleEmail at someDomain dot org');
} }
public function testPermissions()
{
$user = new User('test');
$user->setPermissions(array(
'test',
'test/some/specific',
'test/more/*'
));
$this->assertTrue($user->can('test'));
$this->assertTrue($user->can('test/some/specific'));
$this->assertTrue($user->can('test/more/everything'));
$this->assertFalse($user->can('not/test'));
$this->assertFalse($user->can('test/some/not/so/specific'));
}
} }

View File

@ -213,4 +213,76 @@ class TranslatorTest extends BaseTestCase
'Translator::getPreferredLocaleCode does not return the default locale if no match could be found' 'Translator::getPreferredLocaleCode does not return the default locale if no match could be found'
); );
} }
/**
* @depends testWhetherSetupLocaleSetsUpTheGivenLocale
*/
public function testWhetherTranslatePluralReturnsTheSingularForm()
{
Translator::setupLocale('de_DE');
$result = Translator::translatePlural('test service', 'test services', 1, 'icingatest');
$expected = 'test dienst';
$this->assertEquals(
$expected,
$result,
'Translator::translatePlural() could not return the translated singular form'
);
}
/**
* @depends testWhetherSetupLocaleSetsUpTheGivenLocale
*/
public function testWhetherTranslatePluralReturnsThePluralForm()
{
Translator::setupLocale('de_DE');
$result = Translator::translatePlural('test service', 'test services', 2, 'icingatest');
$expected = 'test dienste';
$this->assertEquals(
$expected,
$result,
'Translator::translatePlural() could not return the translated plural form'
);
}
/**
* @depends testWhetherSetupLocaleSetsUpTheGivenLocale
*/
public function testWhetherTranslateReturnsTheContextForm()
{
Translator::setupLocale('de_DE');
$result = Translator::translate('context service', 'icingatest', 'test2');
$expected = 'context dienst test2';
$this->assertEquals(
$expected,
$result,
'Translator::translate() could not return the translated context form'
);
}
/**
* @depends testWhetherSetupLocaleSetsUpTheGivenLocale
*/
public function testWhetherTranslatePluralReturnsTheContextForm()
{
Translator::setupLocale('de_DE');
$result = Translator::translatePlural('context service', 'context services', 3, 'icingatest', 'test-context');
$expected = 'context plural dienste';
$this->assertEquals(
$expected,
$result,
'Translator::translatePlural() could not return the translated context form'
);
}
} }

View File

@ -1,2 +1,42 @@
msgid ""
msgstr ""
"Project-Id-Version: Icinga Web 2 Test (0.0.1)\n"
"Report-Msgid-Bugs-To: dev@icinga.org\n"
"POT-Creation-Date: 2014-09-16 13:29+0200\n"
"PO-Revision-Date: 2014-09-16 16:08+0100\n"
"Last-Translator: Alexander Fuhr <alexander.fuhr@netways.de>\n"
"Language: de_DE\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.5.4\n"
msgid "Lorem ipsum dolor sit amet" msgid "Lorem ipsum dolor sit amet"
msgstr "Lorem ipsum dolor sit amet!" msgstr "Lorem ipsum dolor sit amet!"
msgid "test service"
msgid_plural "test services"
msgstr[0] "test dienst"
msgstr[1] "test dienste"
msgctxt "test"
msgid "context service"
msgstr "context dienst test"
msgctxt "test2"
msgid "context service"
msgstr "context dienst test2"
msgctxt "test-contextu"
msgid "context service"
msgid_plural "context services"
msgstr[0] "context plural dienstu"
msgstr[1] "context plural diensteu"
msgctxt "test-context"
msgid "context service"
msgid_plural "context services"
msgstr[0] "context plural dienst"
msgstr[1] "context plural dienste"