replaced mailer library, added email templates (#1462)
This commit is contained in:
21
.env.dist
21
.env.dist
@@ -2,8 +2,6 @@
|
|||||||
# Copy this file to .env file for development, create environment variables when deploying to production
|
# Copy this file to .env file for development, create environment variables when deploying to production
|
||||||
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
|
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
|
||||||
|
|
||||||
MAILER_FROM=kimai@example.com
|
|
||||||
|
|
||||||
###> symfony/framework-bundle ###
|
###> symfony/framework-bundle ###
|
||||||
APP_ENV=prod
|
APP_ENV=prod
|
||||||
APP_SECRET=change_this_to_something_unique
|
APP_SECRET=change_this_to_something_unique
|
||||||
@@ -19,13 +17,18 @@ APP_SECRET=change_this_to_something_unique
|
|||||||
DATABASE_URL=sqlite:///%kernel.project_dir%/var/data/kimai.sqlite
|
DATABASE_URL=sqlite:///%kernel.project_dir%/var/data/kimai.sqlite
|
||||||
###< doctrine/doctrine-bundle ###
|
###< doctrine/doctrine-bundle ###
|
||||||
|
|
||||||
###> symfony/swiftmailer-bundle ###
|
|
||||||
# For Gmail as a transport, use: "gmail://username:password@localhost"
|
|
||||||
# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
|
|
||||||
# Delivery is disabled by default via "null://localhost"
|
|
||||||
MAILER_URL=null://localhost
|
|
||||||
###< symfony/swiftmailer-bundle ###
|
|
||||||
|
|
||||||
###> nelmio/cors-bundle ###
|
###> nelmio/cors-bundle ###
|
||||||
CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$
|
CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$
|
||||||
###< nelmio/cors-bundle ###
|
###< nelmio/cors-bundle ###
|
||||||
|
|
||||||
|
### Email configuration
|
||||||
|
# SMTP: smtp://localhost:25?encryption=&auth_mode=
|
||||||
|
# Google: gmail://username:password@default
|
||||||
|
# Amazon: ses://ACCESS_KEY:SECRET_KEY@default?region=eu-west-1
|
||||||
|
# Mailchimp: mandrill://KEY@default
|
||||||
|
# Mailgun: mailgun://KEY:DOMAIN@default
|
||||||
|
# Postmark: postmark://ID@default
|
||||||
|
# Sendgrid: sendgrid://KEY@default
|
||||||
|
# Disable emails: null://null
|
||||||
|
MAILER_URL=null://null
|
||||||
|
MAILER_FROM=kimai@example.com
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ Perform EACH version specific task between your version and the new one, otherwi
|
|||||||
|
|
||||||
## [1.8](https://github.com/kevinpapst/kimai2/releases/tag/1.8)
|
## [1.8](https://github.com/kevinpapst/kimai2/releases/tag/1.8)
|
||||||
|
|
||||||
New permissions:
|
- New mailer library: check if emails are still working (eg. by using the "password forgotten" function) or if you need to adjust your configuration, [see docs at symfony.com](https://symfony.com/doc/current/components/mailer.html#transport)
|
||||||
|
|
||||||
|
Permission changes:
|
||||||
|
|
||||||
- `comments_create_customer` - NEW: permission that allows to add new comments for customers
|
- `comments_create_customer` - NEW: permission that allows to add new comments for customers
|
||||||
- `comments_create_team_customer` - NEW: permission that allows to add new comments for team members of the current customer
|
- `comments_create_team_customer` - NEW: permission that allows to add new comments for team members of the current customer
|
||||||
|
|||||||
1348
assets/css/emails.css
Normal file
1348
assets/css/emails.css
Normal file
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,9 @@
|
|||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
"ext-zip": "*",
|
"ext-zip": "*",
|
||||||
"beberlei/doctrineextensions": "^1.2",
|
"beberlei/doctrineextensions": "^1.2",
|
||||||
|
"doctrine/doctrine-bundle": "^2.0",
|
||||||
|
"doctrine/doctrine-migrations-bundle": "^2.1",
|
||||||
|
"doctrine/orm": "^2.7",
|
||||||
"erusev/parsedown": "^1.6",
|
"erusev/parsedown": "^1.6",
|
||||||
"friendsofsymfony/rest-bundle": "^2.5",
|
"friendsofsymfony/rest-bundle": "^2.5",
|
||||||
"friendsofsymfony/user-bundle": "dev-master",
|
"friendsofsymfony/user-bundle": "dev-master",
|
||||||
@@ -29,6 +32,7 @@
|
|||||||
"kimai/kimai2-composer": "^0.1",
|
"kimai/kimai2-composer": "^0.1",
|
||||||
"laravolt/avatar": "^3.0",
|
"laravolt/avatar": "^3.0",
|
||||||
"league/csv": "^9.4",
|
"league/csv": "^9.4",
|
||||||
|
"league/html-to-markdown": "^4.9",
|
||||||
"mpdf/mpdf": "^8.0",
|
"mpdf/mpdf": "^8.0",
|
||||||
"nelmio/api-doc-bundle": "^3.2",
|
"nelmio/api-doc-bundle": "^3.2",
|
||||||
"nelmio/cors-bundle": "^1.5",
|
"nelmio/cors-bundle": "^1.5",
|
||||||
@@ -39,26 +43,33 @@
|
|||||||
"phpoffice/phpword": "^0.17",
|
"phpoffice/phpword": "^0.17",
|
||||||
"psr/log": "^1.1",
|
"psr/log": "^1.1",
|
||||||
"sensio/framework-extra-bundle": "^5.2",
|
"sensio/framework-extra-bundle": "^5.2",
|
||||||
"symfony/asset": "^4.0",
|
"symfony/amazon-mailer": "^4.4",
|
||||||
"symfony/console": "^4.0",
|
"symfony/asset": "^4.4",
|
||||||
"symfony/dotenv": "^4.0",
|
"symfony/console": "^4.4",
|
||||||
"symfony/expression-language": "^4.0",
|
"symfony/dotenv": "^4.4",
|
||||||
|
"symfony/expression-language": "^4.4",
|
||||||
"symfony/flex": "^1.0",
|
"symfony/flex": "^1.0",
|
||||||
"symfony/form": "^4.0",
|
"symfony/form": "^4.4",
|
||||||
"symfony/framework-bundle": "^4.0",
|
"symfony/framework-bundle": "^4.4",
|
||||||
"symfony/intl": "^4.0",
|
"symfony/google-mailer": "^4.4",
|
||||||
|
"symfony/intl": "^4.4",
|
||||||
|
"symfony/mailchimp-mailer": "^4.4",
|
||||||
|
"symfony/mailer": "^4.4",
|
||||||
|
"symfony/mailgun-mailer": "^4.4",
|
||||||
"symfony/monolog-bundle": "^3.4",
|
"symfony/monolog-bundle": "^3.4",
|
||||||
"symfony/orm-pack": "^1.0",
|
"symfony/postmark-mailer": "^4.4",
|
||||||
"symfony/security-bundle": "^4.0",
|
"symfony/security-bundle": "^4.4",
|
||||||
"symfony/security-csrf": "^4.0",
|
"symfony/security-csrf": "^4.4",
|
||||||
"symfony/serializer": "^4.0",
|
"symfony/sendgrid-mailer": "^4.4",
|
||||||
"symfony/swiftmailer-bundle": "^3.2",
|
"symfony/serializer": "^4.4",
|
||||||
"symfony/translation": "^4.0",
|
"symfony/translation": "^4.4",
|
||||||
"symfony/twig-bundle": "^4.0",
|
"symfony/twig-bundle": "^4.4",
|
||||||
"symfony/validator": "^4.0",
|
"symfony/validator": "^4.4",
|
||||||
"symfony/webpack-encore-bundle": "^1.5",
|
"symfony/webpack-encore-bundle": "^1.5",
|
||||||
"symfony/yaml": "^4.0",
|
"symfony/yaml": "^4.4",
|
||||||
|
"twig/cssinliner-extra": "^3.0",
|
||||||
"twig/extra-bundle": "^3.0",
|
"twig/extra-bundle": "^3.0",
|
||||||
|
"twig/inky-extra": "^3.0",
|
||||||
"twig/intl-extra": "^3.0",
|
"twig/intl-extra": "^3.0",
|
||||||
"twig/string-extra": "^3.0"
|
"twig/string-extra": "^3.0"
|
||||||
},
|
},
|
||||||
@@ -72,13 +83,13 @@
|
|||||||
"phpstan/phpstan-phpunit": "^0.12",
|
"phpstan/phpstan-phpunit": "^0.12",
|
||||||
"phpstan/phpstan-symfony": "^0.12",
|
"phpstan/phpstan-symfony": "^0.12",
|
||||||
"phpunit/phpunit": "^8.0",
|
"phpunit/phpunit": "^8.0",
|
||||||
"symfony/browser-kit": "^4.0",
|
"symfony/browser-kit": "^4.4",
|
||||||
"symfony/css-selector": "^4.0",
|
"symfony/css-selector": "^4.4",
|
||||||
"symfony/debug-bundle": "^4.0",
|
"symfony/debug-bundle": "^4.4",
|
||||||
"symfony/maker-bundle": "^1.12",
|
"symfony/maker-bundle": "^1.12",
|
||||||
"symfony/phpunit-bridge": "^4.0",
|
"symfony/phpunit-bridge": "^4.4",
|
||||||
"symfony/profiler-pack": "^1.0",
|
"symfony/profiler-pack": "^1.0",
|
||||||
"symfony/var-dumper": "^4.0"
|
"symfony/var-dumper": "^4.4"
|
||||||
},
|
},
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{
|
{
|
||||||
|
|||||||
1015
composer.lock
generated
1015
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,6 @@ return [
|
|||||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
|
|
||||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
|
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
|
||||||
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
|
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
|
||||||
|
|||||||
3
config/packages/dev/mailer.yaml
Normal file
3
config/packages/dev/mailer.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
framework:
|
||||||
|
mailer:
|
||||||
|
dsn: 'null://null'
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# See https://symfony.com/doc/current/email/dev_environment.html
|
|
||||||
swiftmailer:
|
|
||||||
# send all emails to a specific address
|
|
||||||
# delivery_addresses: ['me@example.com']
|
|
||||||
@@ -8,18 +8,8 @@ fos_user:
|
|||||||
registration:
|
registration:
|
||||||
confirmation:
|
confirmation:
|
||||||
enabled: false
|
enabled: false
|
||||||
template: '@FOSUser/Registration/email.txt.twig'
|
|
||||||
resetting:
|
resetting:
|
||||||
retry_ttl: 7200
|
retry_ttl: 7200
|
||||||
token_ttl: 86400
|
token_ttl: 86400
|
||||||
email:
|
|
||||||
template: '@FOSUser/Resetting/email.txt.twig'
|
|
||||||
service:
|
service:
|
||||||
mailer: fos_user.mailer.twig_swift
|
mailer: App\Mail\UserMails
|
||||||
# group:
|
|
||||||
# group_class: ~ # Required when using groups
|
|
||||||
# group_manager: fos_user.group_manager.default
|
|
||||||
# form:
|
|
||||||
# type: FOS\UserBundle\Form\Type\GroupFormType # or 'fos_user_group' on Symfony < 2.8
|
|
||||||
# name: fos_user_group_form
|
|
||||||
# validation_groups: [Registration, Default]
|
|
||||||
|
|||||||
3
config/packages/mailer.yaml
Normal file
3
config/packages/mailer.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
framework:
|
||||||
|
mailer:
|
||||||
|
dsn: '%env(MAILER_URL)%'
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
swiftmailer:
|
|
||||||
url: '%env(MAILER_URL)%'
|
|
||||||
spool: { type: 'memory' }
|
|
||||||
3
config/packages/test/mailer.yaml
Normal file
3
config/packages/test/mailer.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
framework:
|
||||||
|
mailer:
|
||||||
|
dsn: 'null://null'
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
swiftmailer:
|
|
||||||
disable_delivery: true
|
|
||||||
@@ -4,4 +4,7 @@ twig:
|
|||||||
strict_variables: '%kernel.debug%'
|
strict_variables: '%kernel.debug%'
|
||||||
form_themes:
|
form_themes:
|
||||||
- 'form/kimai-theme.html.twig'
|
- 'form/kimai-theme.html.twig'
|
||||||
exception_controller: null
|
exception_controller: null
|
||||||
|
paths:
|
||||||
|
# used in templates/emails/layout.html.twig
|
||||||
|
'%kernel.project_dir%/assets/css': css
|
||||||
|
|||||||
@@ -12,24 +12,13 @@
|
|||||||
<ini name="max_execution_time" value="-1" />
|
<ini name="max_execution_time" value="-1" />
|
||||||
<ini name="intl.default_locale" value="en_US" />
|
<ini name="intl.default_locale" value="en_US" />
|
||||||
<env name="KERNEL_CLASS" value="App\Kernel" force="true"/>
|
<env name="KERNEL_CLASS" value="App\Kernel" force="true"/>
|
||||||
|
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak"/>
|
||||||
<env name="APP_ENV" value="test" force="true"/>
|
<env name="APP_ENV" value="test" force="true"/>
|
||||||
<env name="APP_DEBUG" value="1" force="true"/>
|
<env name="APP_DEBUG" value="1" force="true"/>
|
||||||
<env name="APP_SECRET" value="5a79a1c866efef9ca1800f971d689f3e" force="true"/>
|
<env name="APP_SECRET" value="5a79a1c866efef9ca1800f971d689f3e" force="true"/>
|
||||||
<!-- define your env variables for the test env here -->
|
|
||||||
|
|
||||||
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak"/>
|
|
||||||
|
|
||||||
<!-- ###+ doctrine/doctrine-bundle ### -->
|
|
||||||
<env name="DATABASE_URL" value="sqlite:///%kernel.project_dir%/var/data/kimai_test.sqlite" force="true"/>
|
<env name="DATABASE_URL" value="sqlite:///%kernel.project_dir%/var/data/kimai_test.sqlite" force="true"/>
|
||||||
<!-- ###- doctrine/doctrine-bundle ### -->
|
|
||||||
|
|
||||||
<!-- ###+ symfony/swiftmailer-bundle ### -->
|
|
||||||
<env name="MAILER_URL" value="null://localhost" force="true"/>
|
|
||||||
<!-- ###- symfony/swiftmailer-bundle ### -->
|
|
||||||
|
|
||||||
<!-- ###+ nelmio/cors-bundle ### -->
|
|
||||||
<env name="CORS_ALLOW_ORIGIN" value="^https?://localhost(:[0-9]+)?$"/>
|
<env name="CORS_ALLOW_ORIGIN" value="^https?://localhost(:[0-9]+)?$"/>
|
||||||
<!-- ###- nelmio/cors-bundle ### -->
|
<env name="MAILER_URL" value="null://null"/>
|
||||||
</php>
|
</php>
|
||||||
|
|
||||||
<testsuites>
|
<testsuites>
|
||||||
|
|||||||
24
src/Configuration/MailConfiguration.php
Normal file
24
src/Configuration/MailConfiguration.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Configuration;
|
||||||
|
|
||||||
|
class MailConfiguration
|
||||||
|
{
|
||||||
|
public function getFromAddress(): ?string
|
||||||
|
{
|
||||||
|
$from = getenv('MAILER_FROM');
|
||||||
|
|
||||||
|
if ($from === false || empty($from)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $from;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -214,7 +214,6 @@ class DoctorController extends AbstractController
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'APP_ENV' => getenv('APP_ENV'),
|
'APP_ENV' => getenv('APP_ENV'),
|
||||||
'MAILER_FROM' => getenv('MAILER_FROM'),
|
|
||||||
'CORS_ALLOW_ORIGIN' => getenv('CORS_ALLOW_ORIGIN'),
|
'CORS_ALLOW_ORIGIN' => getenv('CORS_ALLOW_ORIGIN'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ use FOS\UserBundle\Model\UserManagerInterface;
|
|||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class intercepts the registration to make sure the first-ever
|
* This class intercepts the registration to make sure:
|
||||||
* registered user will get the SUPER_ADMIN role.
|
*
|
||||||
|
* - the first-ever registered user will get the SUPER_ADMIN role
|
||||||
|
* - the user uses the current request locale as initial language setting
|
||||||
*/
|
*/
|
||||||
class RegistrationSubscriber implements EventSubscriberInterface
|
class RegistrationSubscriber implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
@@ -49,7 +51,7 @@ class RegistrationSubscriber implements EventSubscriberInterface
|
|||||||
*/
|
*/
|
||||||
public function onRegistrationSuccess(FormEvent $event)
|
public function onRegistrationSuccess(FormEvent $event)
|
||||||
{
|
{
|
||||||
/** @var \FOS\UserBundle\Model\UserInterface $user */
|
/** @var User $user */
|
||||||
$user = $event->getForm()->getData();
|
$user = $event->getForm()->getData();
|
||||||
$roles = [User::ROLE_USER];
|
$roles = [User::ROLE_USER];
|
||||||
|
|
||||||
@@ -57,6 +59,7 @@ class RegistrationSubscriber implements EventSubscriberInterface
|
|||||||
$roles = [User::ROLE_SUPER_ADMIN];
|
$roles = [User::ROLE_SUPER_ADMIN];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$user->setLanguage($event->getRequest()->getLocale());
|
||||||
$user->setRoles($roles);
|
$user->setRoles($roles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/Mail/KimaiMailer.php
Normal file
43
src/Mail/KimaiMailer.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use App\Configuration\MailConfiguration;
|
||||||
|
use Symfony\Component\Mailer\Envelope;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
use Symfony\Component\Mime\RawMessage;
|
||||||
|
|
||||||
|
final class KimaiMailer implements MailerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var MailerInterface
|
||||||
|
*/
|
||||||
|
private $mailer;
|
||||||
|
/**
|
||||||
|
* @var MailConfiguration
|
||||||
|
*/
|
||||||
|
private $configuration;
|
||||||
|
|
||||||
|
public function __construct(MailConfiguration $configuration, MailerInterface $mailer)
|
||||||
|
{
|
||||||
|
$this->configuration = $configuration;
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(RawMessage $message, Envelope $envelope = null): void
|
||||||
|
{
|
||||||
|
if ($message instanceof Email) {
|
||||||
|
$message->from($this->configuration->getFromAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->mailer->send($message);
|
||||||
|
}
|
||||||
|
}
|
||||||
97
src/Mail/UserMails.php
Normal file
97
src/Mail/UserMails.php
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use FOS\UserBundle\Mailer\MailerInterface as FOSMailerInterface;
|
||||||
|
use FOS\UserBundle\Model\UserInterface;
|
||||||
|
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||||
|
use Symfony\Component\Mime\Address;
|
||||||
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
final class UserMails implements FOSMailerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var KimaiMailer
|
||||||
|
*/
|
||||||
|
private $mailer;
|
||||||
|
/**
|
||||||
|
* @var UrlGeneratorInterface
|
||||||
|
*/
|
||||||
|
private $router;
|
||||||
|
/**
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
public function __construct(KimaiMailer $mailer, UrlGeneratorInterface $router, TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
$this->router = $router;
|
||||||
|
$this->translator = $translator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendConfirmationEmailMessage(UserInterface $user)
|
||||||
|
{
|
||||||
|
$username = $user->getUsername();
|
||||||
|
$language = 'en';
|
||||||
|
|
||||||
|
if ($user instanceof User) {
|
||||||
|
$username = $user->getDisplayName();
|
||||||
|
$language = $user->getLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->router->generate('fos_user_registration_confirm', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||||
|
|
||||||
|
$email = (new TemplatedEmail())
|
||||||
|
->to(new Address($user->getEmail()))
|
||||||
|
->subject(
|
||||||
|
$this->translator->trans('registration.subject', ['%username%' => $username], 'email', $language)
|
||||||
|
)
|
||||||
|
->htmlTemplate('emails/confirmation.html.twig')
|
||||||
|
->context([
|
||||||
|
'user' => $user,
|
||||||
|
'username' => $username,
|
||||||
|
'confirmationUrl' => $url,
|
||||||
|
])
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->mailer->send($email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendResettingEmailMessage(UserInterface $user)
|
||||||
|
{
|
||||||
|
$username = $user->getUsername();
|
||||||
|
$language = 'en';
|
||||||
|
|
||||||
|
if ($user instanceof User) {
|
||||||
|
$username = $user->getDisplayName();
|
||||||
|
$language = $user->getLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->router->generate('fos_user_resetting_reset', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||||
|
|
||||||
|
$email = (new TemplatedEmail())
|
||||||
|
->to(new Address($user->getEmail()))
|
||||||
|
->subject(
|
||||||
|
$this->translator->trans('reset.subject', ['%username%' => $username], 'email', $language)
|
||||||
|
)
|
||||||
|
->htmlTemplate('emails/password-reset.html.twig')
|
||||||
|
->context([
|
||||||
|
'user' => $user,
|
||||||
|
'username' => $username,
|
||||||
|
'confirmationUrl' => $url,
|
||||||
|
])
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->mailer->send($email);
|
||||||
|
}
|
||||||
|
}
|
||||||
94
symfony.lock
94
symfony.lock
@@ -222,6 +222,12 @@
|
|||||||
"league/csv": {
|
"league/csv": {
|
||||||
"version": "9.4.1"
|
"version": "9.4.1"
|
||||||
},
|
},
|
||||||
|
"league/html-to-markdown": {
|
||||||
|
"version": "4.9.1"
|
||||||
|
},
|
||||||
|
"lorenzo/pinky": {
|
||||||
|
"version": "1.0.5"
|
||||||
|
},
|
||||||
"markbaker/complex": {
|
"markbaker/complex": {
|
||||||
"version": "1.4.6"
|
"version": "1.4.6"
|
||||||
},
|
},
|
||||||
@@ -417,8 +423,14 @@
|
|||||||
"setasign/fpdi": {
|
"setasign/fpdi": {
|
||||||
"version": "1.6.2"
|
"version": "1.6.2"
|
||||||
},
|
},
|
||||||
"swiftmailer/swiftmailer": {
|
"symfony/amazon-mailer": {
|
||||||
"version": "v6.0.2"
|
"version": "4.4",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "4.4",
|
||||||
|
"ref": "9648db3ecae5c8a6b1a5f74715d3907124348815"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"symfony/asset": {
|
"symfony/asset": {
|
||||||
"version": "v4.0.3"
|
"version": "v4.0.3"
|
||||||
@@ -510,6 +522,15 @@
|
|||||||
"ref": "137a14eeb6b3f5370e7147af8aff6518504f50c7"
|
"ref": "137a14eeb6b3f5370e7147af8aff6518504f50c7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"symfony/google-mailer": {
|
||||||
|
"version": "4.4",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "4.4",
|
||||||
|
"ref": "f8fd4ddb9b477510f8f4bce2b9c054ab428c0120"
|
||||||
|
}
|
||||||
|
},
|
||||||
"symfony/http-foundation": {
|
"symfony/http-foundation": {
|
||||||
"version": "v4.0.3"
|
"version": "v4.0.3"
|
||||||
},
|
},
|
||||||
@@ -525,6 +546,36 @@
|
|||||||
"symfony/ldap": {
|
"symfony/ldap": {
|
||||||
"version": "v4.2.8"
|
"version": "v4.2.8"
|
||||||
},
|
},
|
||||||
|
"symfony/mailchimp-mailer": {
|
||||||
|
"version": "4.4",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "4.4",
|
||||||
|
"ref": "1be490e068472fc87e226cf54bc95fb6f2e49117"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"symfony/mailer": {
|
||||||
|
"version": "4.3",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "4.3",
|
||||||
|
"ref": "15658c2a0176cda2e7dba66276a2030b52bd81b2"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/mailer.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"symfony/mailgun-mailer": {
|
||||||
|
"version": "4.4",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "4.4",
|
||||||
|
"ref": "91bc9c127983c37808481733bcf83b7651b1f941"
|
||||||
|
}
|
||||||
|
},
|
||||||
"symfony/maker-bundle": {
|
"symfony/maker-bundle": {
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
@@ -552,9 +603,6 @@
|
|||||||
"symfony/options-resolver": {
|
"symfony/options-resolver": {
|
||||||
"version": "v4.0.3"
|
"version": "v4.0.3"
|
||||||
},
|
},
|
||||||
"symfony/orm-pack": {
|
|
||||||
"version": "v1.0.6"
|
|
||||||
},
|
|
||||||
"symfony/phpunit-bridge": {
|
"symfony/phpunit-bridge": {
|
||||||
"version": "3.3",
|
"version": "3.3",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
@@ -588,6 +636,15 @@
|
|||||||
"symfony/polyfill-php73": {
|
"symfony/polyfill-php73": {
|
||||||
"version": "v1.11.0"
|
"version": "v1.11.0"
|
||||||
},
|
},
|
||||||
|
"symfony/postmark-mailer": {
|
||||||
|
"version": "4.4",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "4.4",
|
||||||
|
"ref": "84842f6791eaef80618dda244d4e3bbcbbcdf543"
|
||||||
|
}
|
||||||
|
},
|
||||||
"symfony/process": {
|
"symfony/process": {
|
||||||
"version": "v4.0.3"
|
"version": "v4.0.3"
|
||||||
},
|
},
|
||||||
@@ -630,6 +687,15 @@
|
|||||||
"symfony/security-http": {
|
"symfony/security-http": {
|
||||||
"version": "v4.3.3"
|
"version": "v4.3.3"
|
||||||
},
|
},
|
||||||
|
"symfony/sendgrid-mailer": {
|
||||||
|
"version": "4.4",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "4.4",
|
||||||
|
"ref": "b46e4f8f1adb2864f0aa9c0ff57aea3324a07199"
|
||||||
|
}
|
||||||
|
},
|
||||||
"symfony/serializer": {
|
"symfony/serializer": {
|
||||||
"version": "v4.1.2"
|
"version": "v4.1.2"
|
||||||
},
|
},
|
||||||
@@ -642,15 +708,6 @@
|
|||||||
"symfony/string": {
|
"symfony/string": {
|
||||||
"version": "v5.0.4"
|
"version": "v5.0.4"
|
||||||
},
|
},
|
||||||
"symfony/swiftmailer-bundle": {
|
|
||||||
"version": "2.5",
|
|
||||||
"recipe": {
|
|
||||||
"repo": "github.com/symfony/recipes",
|
|
||||||
"branch": "master",
|
|
||||||
"version": "2.5",
|
|
||||||
"ref": "8daf8ede72274989fb0dc66b83cb4bc1693afbb9"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"symfony/templating": {
|
"symfony/templating": {
|
||||||
"version": "v4.1.1"
|
"version": "v4.1.1"
|
||||||
},
|
},
|
||||||
@@ -720,9 +777,18 @@
|
|||||||
"theseer/tokenizer": {
|
"theseer/tokenizer": {
|
||||||
"version": "1.1.0"
|
"version": "1.1.0"
|
||||||
},
|
},
|
||||||
|
"tijsverkoyen/css-to-inline-styles": {
|
||||||
|
"version": "2.2.2"
|
||||||
|
},
|
||||||
|
"twig/cssinliner-extra": {
|
||||||
|
"version": "v3.0.1"
|
||||||
|
},
|
||||||
"twig/extra-bundle": {
|
"twig/extra-bundle": {
|
||||||
"version": "v3.0.1"
|
"version": "v3.0.1"
|
||||||
},
|
},
|
||||||
|
"twig/inky-extra": {
|
||||||
|
"version": "v3.0.1"
|
||||||
|
},
|
||||||
"twig/intl-extra": {
|
"twig/intl-extra": {
|
||||||
"version": "v3.0.1"
|
"version": "v3.0.1"
|
||||||
},
|
},
|
||||||
|
|||||||
21
templates/emails/confirmation.html.twig
Normal file
21
templates/emails/confirmation.html.twig
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{% extends 'emails/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{{ 'registration.title'|trans({'%username%': user.displayName}, 'email') }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<p>{{ 'registration.intro'|trans({'%email%': user.email}, 'email') }}<p>
|
||||||
|
|
||||||
|
<spacer size="16"></spacer>
|
||||||
|
|
||||||
|
<button class="large primary" href="{{ confirmationUrl }}">{{ 'registration.button'|trans({'%url%': confirmationUrl}, 'email') }}</button>
|
||||||
|
|
||||||
|
<spacer size="16"></spacer>
|
||||||
|
|
||||||
|
<p><small>{{ 'button_copy_url_to_browser'|trans({'%url%': confirmationUrl}, 'email') }}</small></p>
|
||||||
|
|
||||||
|
<spacer size="16"></spacer>
|
||||||
|
|
||||||
|
<p><small>{{ 'automated_email_dont_answer'|trans({}, 'email') }}</small></p>
|
||||||
|
{% endblock %}
|
||||||
29
templates/emails/layout.html.twig
Normal file
29
templates/emails/layout.html.twig
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{% apply inky_to_html|inline_css(source('@css/emails.css')) %}
|
||||||
|
<style type="text/css">
|
||||||
|
body, html, .body {
|
||||||
|
background: #f3f3f3 !important;
|
||||||
|
}
|
||||||
|
.container.header {
|
||||||
|
background: #f3f3f3;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<spacer size="32"></spacer>
|
||||||
|
|
||||||
|
<container class="header">
|
||||||
|
<row>
|
||||||
|
<columns>
|
||||||
|
<h1 class="text-center">{% block title %}{% endblock %}</h1>
|
||||||
|
</columns>
|
||||||
|
</row>
|
||||||
|
</container>
|
||||||
|
|
||||||
|
<container class="body-border">
|
||||||
|
<row>
|
||||||
|
<columns>
|
||||||
|
<spacer size="32"></spacer>
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
</columns>
|
||||||
|
</row>
|
||||||
|
</container>
|
||||||
|
{% endapply %}
|
||||||
21
templates/emails/password-reset.html.twig
Normal file
21
templates/emails/password-reset.html.twig
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{% extends 'emails/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{{ 'reset.title'|trans({'%username%': user.displayName}, 'email') }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<p>{{ 'reset.intro'|trans({'%username%': user.displayName, '%email%': user.email}, 'email') }}<p>
|
||||||
|
|
||||||
|
<spacer size="16"></spacer>
|
||||||
|
|
||||||
|
<button class="large primary" href="{{ confirmationUrl }}">{{ 'reset.button'|trans({'%url%': confirmationUrl}, 'email') }}</button>
|
||||||
|
|
||||||
|
<spacer size="16"></spacer>
|
||||||
|
|
||||||
|
<p><small>{{ 'button_copy_url_to_browser'|trans({'%url%': confirmationUrl}, 'email') }}</small></p>
|
||||||
|
|
||||||
|
<spacer size="16"></spacer>
|
||||||
|
|
||||||
|
<p><small>{{ 'automated_email_dont_answer'|trans({}, 'email') }}</small></p>
|
||||||
|
{% endblock %}
|
||||||
33
tests/Configuration/MailConfigurationTest.php
Normal file
33
tests/Configuration/MailConfigurationTest.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Tests\Configuration;
|
||||||
|
|
||||||
|
use App\Configuration\MailConfiguration;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \App\Configuration\MailConfiguration
|
||||||
|
*/
|
||||||
|
class MailConfigurationTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGetFromAddress()
|
||||||
|
{
|
||||||
|
$previous = getenv('MAILER_FROM');
|
||||||
|
putenv('MAILER_FROM=foo-bar123@example.com');
|
||||||
|
|
||||||
|
$sut = new MailConfiguration();
|
||||||
|
self::assertEquals('foo-bar123@example.com', $sut->getFromAddress());
|
||||||
|
|
||||||
|
putenv('MAILER_FROM=');
|
||||||
|
self::assertNull($sut->getFromAddress());
|
||||||
|
|
||||||
|
putenv('MAILER_FROM=' . $previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,7 @@ class RegistrationSubscriberTest extends TestCase
|
|||||||
$form->method('getData')->willReturn($user);
|
$form->method('getData')->willReturn($user);
|
||||||
|
|
||||||
$request = $this->createMock(Request::class);
|
$request = $this->createMock(Request::class);
|
||||||
|
$request->expects($this->any())->method('getLocale')->willReturn('ru');
|
||||||
|
|
||||||
$event = new FormEvent($form, $request);
|
$event = new FormEvent($form, $request);
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ class RegistrationSubscriberTest extends TestCase
|
|||||||
$sut->onRegistrationSuccess($event);
|
$sut->onRegistrationSuccess($event);
|
||||||
|
|
||||||
$this->assertEquals($expectedRoles, $user->getRoles());
|
$this->assertEquals($expectedRoles, $user->getRoles());
|
||||||
|
$this->assertEquals('ru', $user->getLanguage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTestData()
|
public function getTestData()
|
||||||
|
|||||||
51
tests/Mail/KimaiMailerTest.php
Normal file
51
tests/Mail/KimaiMailerTest.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Tests\Mail;
|
||||||
|
|
||||||
|
use App\Configuration\MailConfiguration;
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Mail\KimaiMailer;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mime\Address;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \App\Mail\KimaiMailer
|
||||||
|
*/
|
||||||
|
class KimaiMailerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function getSut(): KimaiMailer
|
||||||
|
{
|
||||||
|
$config = $this->createMock(MailConfiguration::class);
|
||||||
|
$config->expects($this->any())->method('getFromAddress')->willReturn('zippel@example.com');
|
||||||
|
|
||||||
|
$mailer = $this->createMock(MailerInterface::class);
|
||||||
|
|
||||||
|
return new KimaiMailer($config, $mailer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendSetsFrom()
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setUsername('Testing');
|
||||||
|
$user->setEmail('foo@example.com');
|
||||||
|
$user->setAlias('Super User');
|
||||||
|
|
||||||
|
$mailer = $this->getSut();
|
||||||
|
$message = new Email();
|
||||||
|
|
||||||
|
self::assertEquals([], $message->getFrom());
|
||||||
|
|
||||||
|
$mailer->send($message);
|
||||||
|
|
||||||
|
self::assertEquals([new Address('zippel@example.com')], $message->getFrom());
|
||||||
|
}
|
||||||
|
}
|
||||||
67
tests/Mail/UserMailsTest.php
Normal file
67
tests/Mail/UserMailsTest.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Tests\Mail;
|
||||||
|
|
||||||
|
use App\Configuration\MailConfiguration;
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Mail\KimaiMailer;
|
||||||
|
use App\Mail\UserMails;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mime\Address;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \App\Mail\UserMails
|
||||||
|
*/
|
||||||
|
class UserMailsTest extends TestCase
|
||||||
|
{
|
||||||
|
public function getSut(): UserMails
|
||||||
|
{
|
||||||
|
$config = $this->createMock(MailConfiguration::class);
|
||||||
|
$config->expects($this->any())->method('getFromAddress')->willReturn('zippel@example.com');
|
||||||
|
$mailer = $this->createMock(MailerInterface::class);
|
||||||
|
$mailer->expects($this->once())->method('send')->willReturnCallback(function (Email $message) {
|
||||||
|
self::assertEquals([new Address('zippel@example.com')], $message->getFrom());
|
||||||
|
self::assertEquals([new Address('foo@example.com')], $message->getTo());
|
||||||
|
self::assertEquals('foo', $message->getSubject());
|
||||||
|
});
|
||||||
|
$kimaiMailer = new KimaiMailer($config, $mailer);
|
||||||
|
$router = $this->createMock(UrlGeneratorInterface::class);
|
||||||
|
$translator = $this->createMock(TranslatorInterface::class);
|
||||||
|
$translator->expects($this->any())->method('trans')->willReturn('foo');
|
||||||
|
|
||||||
|
return new UserMails($kimaiMailer, $router, $translator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendConfirmationEmailMessage()
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setUsername('Testing');
|
||||||
|
$user->setEmail('foo@example.com');
|
||||||
|
$user->setAlias('Super User');
|
||||||
|
|
||||||
|
$mailer = $this->getSut();
|
||||||
|
$mailer->sendConfirmationEmailMessage($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendResettingEmailMessage()
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setUsername('Testing');
|
||||||
|
$user->setEmail('foo@example.com');
|
||||||
|
$user->setAlias('Super User');
|
||||||
|
|
||||||
|
$mailer = $this->getSut();
|
||||||
|
$mailer->sendResettingEmailMessage($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
47
translations/email.de.xlf
Normal file
47
translations/email.de.xlf
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<file source-language="en" target-language="de" datatype="plaintext" original="email.en.xlf">
|
||||||
|
<body>
|
||||||
|
<trans-unit id="button_copy_url_to_browser">
|
||||||
|
<source>button_copy_url_to_browser</source>
|
||||||
|
<target>Falls der Button nicht funktioniert, kopieren Sie bitte die folgende URL in ihren Browser: %url%</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="automated_email_dont_answer">
|
||||||
|
<source>automated_email_dont_answer</source>
|
||||||
|
<target>Dies ist eine automatisch generierte E-Mail. Bitte antworten Sie nicht, die Adresse wird nicht gelesen.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="registration.subject">
|
||||||
|
<source>registration.subject</source>
|
||||||
|
<target>Benutzerkonto aktivieren</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="registration.title">
|
||||||
|
<source>registration.title</source>
|
||||||
|
<target>Willkommen %username%</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="registration.button">
|
||||||
|
<source>registration.button</source>
|
||||||
|
<target>Aktivieren Sie Ihren Zugang</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="registration.intro">
|
||||||
|
<source>registration.intro</source>
|
||||||
|
<target>Sie haben sich für die Kimai Zeiterfassung mit der Email Adresse %email% registriert. Jetzt müssen Sie den Link in den nächsten Stunden anklicken, bevor der Link keine Gültigkeit mehr hat.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="reset.subject">
|
||||||
|
<source>reset.subject</source>
|
||||||
|
<target>Passwort zurücksetzen</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="reset.title">
|
||||||
|
<source>reset.title</source>
|
||||||
|
<target>Das kann passieren ...</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="reset.button">
|
||||||
|
<source>reset.button</source>
|
||||||
|
<target>Passwort zurücksetzen</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="reset.intro">
|
||||||
|
<source>reset.intro</source>
|
||||||
|
<target>... Du hast also Dein Passwort vergessen? Keine Sorge: Kimai wird Dir dabei helfen ein neues zu erstellen:</target>
|
||||||
|
</trans-unit>
|
||||||
|
</body>
|
||||||
|
</file>
|
||||||
|
</xliff>
|
||||||
47
translations/email.en.xlf
Normal file
47
translations/email.en.xlf
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<file source-language="en" target-language="en" datatype="plaintext" original="email.en.xlf">
|
||||||
|
<body>
|
||||||
|
<trans-unit id="button_copy_url_to_browser">
|
||||||
|
<source>button_copy_url_to_browser</source>
|
||||||
|
<target>If the button doesn't work, please copy & paste the following URL to your browser: %url%</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="automated_email_dont_answer">
|
||||||
|
<source>automated_email_dont_answer</source>
|
||||||
|
<target>This is an auto generated e-mail. Please do not reply, the address will not be read.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="registration.subject">
|
||||||
|
<source>registration.subject</source>
|
||||||
|
<target>Activate your account</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="registration.title">
|
||||||
|
<source>registration.title</source>
|
||||||
|
<target>Welcome %username%</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="registration.button">
|
||||||
|
<source>registration.button</source>
|
||||||
|
<target>Activate your account</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="registration.intro">
|
||||||
|
<source>registration.intro</source>
|
||||||
|
<target>You signed up at the Kimai time-tracker with the email %email%. Now you have to activate your account in the next few hours, before the link expires.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="reset.subject">
|
||||||
|
<source>reset.subject</source>
|
||||||
|
<target>Reset your password</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="reset.title">
|
||||||
|
<source>reset.title</source>
|
||||||
|
<target>That can happen ...</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="reset.button">
|
||||||
|
<source>reset.button</source>
|
||||||
|
<target>Reset your password</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="reset.intro">
|
||||||
|
<source>reset.intro</source>
|
||||||
|
<target>... so you forgot your password? Don't worry: Kimai will help you to create a new one:</target>
|
||||||
|
</trans-unit>
|
||||||
|
</body>
|
||||||
|
</file>
|
||||||
|
</xliff>
|
||||||
Reference in New Issue
Block a user