diff --git a/.env.dist b/.env.dist index 61b5da2e..f9b807c8 100644 --- a/.env.dist +++ b/.env.dist @@ -2,8 +2,6 @@ # 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 -MAILER_FROM=kimai@example.com - ###> symfony/framework-bundle ### APP_ENV=prod 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 ###< 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 ### CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$ ###< 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 diff --git a/UPGRADING.md b/UPGRADING.md index 32b1cae1..f72ae9c0 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -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) -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_team_customer` - NEW: permission that allows to add new comments for team members of the current customer diff --git a/assets/css/emails.css b/assets/css/emails.css new file mode 100644 index 00000000..706cbd9a --- /dev/null +++ b/assets/css/emails.css @@ -0,0 +1,1348 @@ +.wrapper { + width: 100%; } + +#outlook a { + padding: 0; } + +body { + width: 100% !important; + min-width: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + margin: 0; + Margin: 0; + padding: 0; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; } + +.ExternalClass { + width: 100%; } +.ExternalClass, +.ExternalClass p, +.ExternalClass span, +.ExternalClass font, +.ExternalClass td, +.ExternalClass div { + line-height: 100%; } + +#backgroundTable { + margin: 0; + Margin: 0; + padding: 0; + width: 100% !important; + line-height: 100% !important; } + +img { + outline: none; + text-decoration: none; + -ms-interpolation-mode: bicubic; + width: auto; + max-width: 100%; + clear: both; + display: block; } + +center { + width: 100%; + min-width: 580px; } + +a img { + border: none; } + +p { + margin: 0 0 0 10px; + Margin: 0 0 0 10px; } + +table { + border-spacing: 0; + border-collapse: collapse; } + +td { + word-wrap: break-word; + -webkit-hyphens: auto; + -moz-hyphens: auto; + hyphens: auto; + border-collapse: collapse !important; } + +table, tr, td { + padding: 0; + vertical-align: top; + text-align: left; } + +@media only screen { + html { + min-height: 100%; + background: #f3f3f3; } } + +table.body { + background: #f3f3f3; + height: 100%; + width: 100%; } + +table.container { + background: #fefefe; + width: 580px; + margin: 0 auto; + Margin: 0 auto; + text-align: inherit; } + +table.row { + padding: 0; + width: 100%; + position: relative; } + +table.spacer { + width: 100%; } +table.spacer td { + mso-line-height-rule: exactly; } + +table.container table.row { + display: table; } + +td.columns, +td.column, +th.columns, +th.column { + margin: 0 auto; + Margin: 0 auto; + padding-left: 16px; + padding-bottom: 16px; } +td.columns .column, +td.columns .columns, +td.column .column, +td.column .columns, +th.columns .column, +th.columns .columns, +th.column .column, +th.column .columns { + padding-left: 0 !important; + padding-right: 0 !important; } +td.columns .column center, +td.columns .columns center, +td.column .column center, +td.column .columns center, +th.columns .column center, +th.columns .columns center, +th.column .column center, +th.column .columns center { + min-width: none !important; } + +td.columns.last, +td.column.last, +th.columns.last, +th.column.last { + padding-right: 16px; } + +td.columns table:not(.button), +td.column table:not(.button), +th.columns table:not(.button), +th.column table:not(.button) { + width: 100%; } + +td.large-1, +th.large-1 { + width: 32.33333px; + padding-left: 8px; + padding-right: 8px; } + +td.large-1.first, +th.large-1.first { + padding-left: 16px; } + +td.large-1.last, +th.large-1.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-1, +.collapse > tbody > tr > th.large-1 { + padding-right: 0; + padding-left: 0; + width: 48.33333px; } + +.collapse td.large-1.first, +.collapse th.large-1.first, +.collapse td.large-1.last, +.collapse th.large-1.last { + width: 56.33333px; } + +td.large-1 center, +th.large-1 center { + min-width: 0.33333px; } + +.body .columns td.large-1, +.body .column td.large-1, +.body .columns th.large-1, +.body .column th.large-1 { + width: 8.33333%; } + +td.large-2, +th.large-2 { + width: 80.66667px; + padding-left: 8px; + padding-right: 8px; } + +td.large-2.first, +th.large-2.first { + padding-left: 16px; } + +td.large-2.last, +th.large-2.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-2, +.collapse > tbody > tr > th.large-2 { + padding-right: 0; + padding-left: 0; + width: 96.66667px; } + +.collapse td.large-2.first, +.collapse th.large-2.first, +.collapse td.large-2.last, +.collapse th.large-2.last { + width: 104.66667px; } + +td.large-2 center, +th.large-2 center { + min-width: 48.66667px; } + +.body .columns td.large-2, +.body .column td.large-2, +.body .columns th.large-2, +.body .column th.large-2 { + width: 16.66667%; } + +td.large-3, +th.large-3 { + width: 129px; + padding-left: 8px; + padding-right: 8px; } + +td.large-3.first, +th.large-3.first { + padding-left: 16px; } + +td.large-3.last, +th.large-3.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-3, +.collapse > tbody > tr > th.large-3 { + padding-right: 0; + padding-left: 0; + width: 145px; } + +.collapse td.large-3.first, +.collapse th.large-3.first, +.collapse td.large-3.last, +.collapse th.large-3.last { + width: 153px; } + +td.large-3 center, +th.large-3 center { + min-width: 97px; } + +.body .columns td.large-3, +.body .column td.large-3, +.body .columns th.large-3, +.body .column th.large-3 { + width: 25%; } + +td.large-4, +th.large-4 { + width: 177.33333px; + padding-left: 8px; + padding-right: 8px; } + +td.large-4.first, +th.large-4.first { + padding-left: 16px; } + +td.large-4.last, +th.large-4.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-4, +.collapse > tbody > tr > th.large-4 { + padding-right: 0; + padding-left: 0; + width: 193.33333px; } + +.collapse td.large-4.first, +.collapse th.large-4.first, +.collapse td.large-4.last, +.collapse th.large-4.last { + width: 201.33333px; } + +td.large-4 center, +th.large-4 center { + min-width: 145.33333px; } + +.body .columns td.large-4, +.body .column td.large-4, +.body .columns th.large-4, +.body .column th.large-4 { + width: 33.33333%; } + +td.large-5, +th.large-5 { + width: 225.66667px; + padding-left: 8px; + padding-right: 8px; } + +td.large-5.first, +th.large-5.first { + padding-left: 16px; } + +td.large-5.last, +th.large-5.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-5, +.collapse > tbody > tr > th.large-5 { + padding-right: 0; + padding-left: 0; + width: 241.66667px; } + +.collapse td.large-5.first, +.collapse th.large-5.first, +.collapse td.large-5.last, +.collapse th.large-5.last { + width: 249.66667px; } + +td.large-5 center, +th.large-5 center { + min-width: 193.66667px; } + +.body .columns td.large-5, +.body .column td.large-5, +.body .columns th.large-5, +.body .column th.large-5 { + width: 41.66667%; } + +td.large-6, +th.large-6 { + width: 274px; + padding-left: 8px; + padding-right: 8px; } + +td.large-6.first, +th.large-6.first { + padding-left: 16px; } + +td.large-6.last, +th.large-6.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-6, +.collapse > tbody > tr > th.large-6 { + padding-right: 0; + padding-left: 0; + width: 290px; } + +.collapse td.large-6.first, +.collapse th.large-6.first, +.collapse td.large-6.last, +.collapse th.large-6.last { + width: 298px; } + +td.large-6 center, +th.large-6 center { + min-width: 242px; } + +.body .columns td.large-6, +.body .column td.large-6, +.body .columns th.large-6, +.body .column th.large-6 { + width: 50%; } + +td.large-7, +th.large-7 { + width: 322.33333px; + padding-left: 8px; + padding-right: 8px; } + +td.large-7.first, +th.large-7.first { + padding-left: 16px; } + +td.large-7.last, +th.large-7.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-7, +.collapse > tbody > tr > th.large-7 { + padding-right: 0; + padding-left: 0; + width: 338.33333px; } + +.collapse td.large-7.first, +.collapse th.large-7.first, +.collapse td.large-7.last, +.collapse th.large-7.last { + width: 346.33333px; } + +td.large-7 center, +th.large-7 center { + min-width: 290.33333px; } + +.body .columns td.large-7, +.body .column td.large-7, +.body .columns th.large-7, +.body .column th.large-7 { + width: 58.33333%; } + +td.large-8, +th.large-8 { + width: 370.66667px; + padding-left: 8px; + padding-right: 8px; } + +td.large-8.first, +th.large-8.first { + padding-left: 16px; } + +td.large-8.last, +th.large-8.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-8, +.collapse > tbody > tr > th.large-8 { + padding-right: 0; + padding-left: 0; + width: 386.66667px; } + +.collapse td.large-8.first, +.collapse th.large-8.first, +.collapse td.large-8.last, +.collapse th.large-8.last { + width: 394.66667px; } + +td.large-8 center, +th.large-8 center { + min-width: 338.66667px; } + +.body .columns td.large-8, +.body .column td.large-8, +.body .columns th.large-8, +.body .column th.large-8 { + width: 66.66667%; } + +td.large-9, +th.large-9 { + width: 419px; + padding-left: 8px; + padding-right: 8px; } + +td.large-9.first, +th.large-9.first { + padding-left: 16px; } + +td.large-9.last, +th.large-9.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-9, +.collapse > tbody > tr > th.large-9 { + padding-right: 0; + padding-left: 0; + width: 435px; } + +.collapse td.large-9.first, +.collapse th.large-9.first, +.collapse td.large-9.last, +.collapse th.large-9.last { + width: 443px; } + +td.large-9 center, +th.large-9 center { + min-width: 387px; } + +.body .columns td.large-9, +.body .column td.large-9, +.body .columns th.large-9, +.body .column th.large-9 { + width: 75%; } + +td.large-10, +th.large-10 { + width: 467.33333px; + padding-left: 8px; + padding-right: 8px; } + +td.large-10.first, +th.large-10.first { + padding-left: 16px; } + +td.large-10.last, +th.large-10.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-10, +.collapse > tbody > tr > th.large-10 { + padding-right: 0; + padding-left: 0; + width: 483.33333px; } + +.collapse td.large-10.first, +.collapse th.large-10.first, +.collapse td.large-10.last, +.collapse th.large-10.last { + width: 491.33333px; } + +td.large-10 center, +th.large-10 center { + min-width: 435.33333px; } + +.body .columns td.large-10, +.body .column td.large-10, +.body .columns th.large-10, +.body .column th.large-10 { + width: 83.33333%; } + +td.large-11, +th.large-11 { + width: 515.66667px; + padding-left: 8px; + padding-right: 8px; } + +td.large-11.first, +th.large-11.first { + padding-left: 16px; } + +td.large-11.last, +th.large-11.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-11, +.collapse > tbody > tr > th.large-11 { + padding-right: 0; + padding-left: 0; + width: 531.66667px; } + +.collapse td.large-11.first, +.collapse th.large-11.first, +.collapse td.large-11.last, +.collapse th.large-11.last { + width: 539.66667px; } + +td.large-11 center, +th.large-11 center { + min-width: 483.66667px; } + +.body .columns td.large-11, +.body .column td.large-11, +.body .columns th.large-11, +.body .column th.large-11 { + width: 91.66667%; } + +td.large-12, +th.large-12 { + width: 564px; + padding-left: 8px; + padding-right: 8px; } + +td.large-12.first, +th.large-12.first { + padding-left: 16px; } + +td.large-12.last, +th.large-12.last { + padding-right: 16px; } + +.collapse > tbody > tr > td.large-12, +.collapse > tbody > tr > th.large-12 { + padding-right: 0; + padding-left: 0; + width: 580px; } + +.collapse td.large-12.first, +.collapse th.large-12.first, +.collapse td.large-12.last, +.collapse th.large-12.last { + width: 588px; } + +td.large-12 center, +th.large-12 center { + min-width: 532px; } + +.body .columns td.large-12, +.body .column td.large-12, +.body .columns th.large-12, +.body .column th.large-12 { + width: 100%; } + +td.large-offset-1, +td.large-offset-1.first, +td.large-offset-1.last, +th.large-offset-1, +th.large-offset-1.first, +th.large-offset-1.last { + padding-left: 64.33333px; } + +td.large-offset-2, +td.large-offset-2.first, +td.large-offset-2.last, +th.large-offset-2, +th.large-offset-2.first, +th.large-offset-2.last { + padding-left: 112.66667px; } + +td.large-offset-3, +td.large-offset-3.first, +td.large-offset-3.last, +th.large-offset-3, +th.large-offset-3.first, +th.large-offset-3.last { + padding-left: 161px; } + +td.large-offset-4, +td.large-offset-4.first, +td.large-offset-4.last, +th.large-offset-4, +th.large-offset-4.first, +th.large-offset-4.last { + padding-left: 209.33333px; } + +td.large-offset-5, +td.large-offset-5.first, +td.large-offset-5.last, +th.large-offset-5, +th.large-offset-5.first, +th.large-offset-5.last { + padding-left: 257.66667px; } + +td.large-offset-6, +td.large-offset-6.first, +td.large-offset-6.last, +th.large-offset-6, +th.large-offset-6.first, +th.large-offset-6.last { + padding-left: 306px; } + +td.large-offset-7, +td.large-offset-7.first, +td.large-offset-7.last, +th.large-offset-7, +th.large-offset-7.first, +th.large-offset-7.last { + padding-left: 354.33333px; } + +td.large-offset-8, +td.large-offset-8.first, +td.large-offset-8.last, +th.large-offset-8, +th.large-offset-8.first, +th.large-offset-8.last { + padding-left: 402.66667px; } + +td.large-offset-9, +td.large-offset-9.first, +td.large-offset-9.last, +th.large-offset-9, +th.large-offset-9.first, +th.large-offset-9.last { + padding-left: 451px; } + +td.large-offset-10, +td.large-offset-10.first, +td.large-offset-10.last, +th.large-offset-10, +th.large-offset-10.first, +th.large-offset-10.last { + padding-left: 499.33333px; } + +td.large-offset-11, +td.large-offset-11.first, +td.large-offset-11.last, +th.large-offset-11, +th.large-offset-11.first, +th.large-offset-11.last { + padding-left: 547.66667px; } + +td.expander, +th.expander { + visibility: hidden; + width: 0; + padding: 0 !important; } + +table.container.radius { + border-radius: 0; + border-collapse: separate; } + +.block-grid { + width: 100%; + max-width: 580px; } +.block-grid td { + display: inline-block; + padding: 8px; } + +.up-2 td { + width: 274px !important; } + +.up-3 td { + width: 177px !important; } + +.up-4 td { + width: 129px !important; } + +.up-5 td { + width: 100px !important; } + +.up-6 td { + width: 80px !important; } + +.up-7 td { + width: 66px !important; } + +.up-8 td { + width: 56px !important; } + +table.text-center, +th.text-center, +td.text-center, +h1.text-center, +h2.text-center, +h3.text-center, +h4.text-center, +h5.text-center, +h6.text-center, +p.text-center, +span.text-center { + text-align: center; } + +table.text-left, +th.text-left, +td.text-left, +h1.text-left, +h2.text-left, +h3.text-left, +h4.text-left, +h5.text-left, +h6.text-left, +p.text-left, +span.text-left { + text-align: left; } + +table.text-right, +th.text-right, +td.text-right, +h1.text-right, +h2.text-right, +h3.text-right, +h4.text-right, +h5.text-right, +h6.text-right, +p.text-right, +span.text-right { + text-align: right; } + +span.text-center { + display: block; + width: 100%; + text-align: center; } + +@media only screen and (max-width: 596px) { + .small-float-center { + margin: 0 auto !important; + float: none !important; + text-align: center !important; } + .small-text-center { + text-align: center !important; } + .small-text-left { + text-align: left !important; } + .small-text-right { + text-align: right !important; } } + +img.float-left { + float: left; + text-align: left; } + +img.float-right { + float: right; + text-align: right; } + +img.float-center, +img.text-center { + margin: 0 auto; + Margin: 0 auto; + float: none; + text-align: center; } + +table.float-center, +td.float-center, +th.float-center { + margin: 0 auto; + Margin: 0 auto; + float: none; + text-align: center; } + +.hide-for-large { + display: none !important; + mso-hide: all; + overflow: hidden; + max-height: 0; + font-size: 0; + width: 0; + line-height: 0; } +@media only screen and (max-width: 596px) { + .hide-for-large { + display: block !important; + width: auto !important; + overflow: visible !important; + max-height: none !important; + font-size: inherit !important; + line-height: inherit !important; } } + +table.body table.container .hide-for-large * { + mso-hide: all; } + +@media only screen and (max-width: 596px) { + table.body table.container .hide-for-large, + table.body table.container .row.hide-for-large { + display: table !important; + width: 100% !important; } } + +@media only screen and (max-width: 596px) { + table.body table.container .callout-inner.hide-for-large { + display: table-cell !important; + width: 100% !important; } } + +@media only screen and (max-width: 596px) { + table.body table.container .show-for-large { + display: none !important; + width: 0; + mso-hide: all; + overflow: hidden; } } + +body, +table.body, +h1, +h2, +h3, +h4, +h5, +h6, +p, +td, +th, +a { + color: #0a0a0a; + font-family: Helvetica, Arial, sans-serif; + font-weight: normal; + padding: 0; + margin: 0; + Margin: 0; + text-align: left; + line-height: 1.3; } + +h1, +h2, +h3, +h4, +h5, +h6 { + color: inherit; + word-wrap: normal; + font-family: Helvetica, Arial, sans-serif; + font-weight: normal; + margin-bottom: 10px; + Margin-bottom: 10px; } + +h1 { + font-size: 34px; } + +h2 { + font-size: 30px; } + +h3 { + font-size: 28px; } + +h4 { + font-size: 24px; } + +h5 { + font-size: 20px; } + +h6 { + font-size: 18px; } + +body, +table.body, +p, +td, +th { + font-size: 16px; + line-height: 1.3; } + +p { + margin-bottom: 10px; + Margin-bottom: 10px; } +p.lead { + font-size: 20px; + line-height: 1.6; } +p.subheader { + margin-top: 4px; + margin-bottom: 8px; + Margin-top: 4px; + Margin-bottom: 8px; + font-weight: normal; + line-height: 1.4; + color: #8a8a8a; } + +small { + font-size: 80%; + color: #cacaca; } + +a { + color: #2199e8; + text-decoration: none; } +a:hover { + color: #147dc2; } +a:active { + color: #147dc2; } +a:visited { + color: #2199e8; } + +h1 a, +h1 a:visited, +h2 a, +h2 a:visited, +h3 a, +h3 a:visited, +h4 a, +h4 a:visited, +h5 a, +h5 a:visited, +h6 a, +h6 a:visited { + color: #2199e8; } + +pre { + background: #f3f3f3; + margin: 30px 0; + Margin: 30px 0; } +pre code { + color: #cacaca; } +pre code span.callout { + color: #8a8a8a; + font-weight: bold; } +pre code span.callout-strong { + color: #ff6908; + font-weight: bold; } + +table.hr { + width: 100%; } +table.hr th { + height: 0; + max-width: 580px; + border-top: 0; + border-right: 0; + border-bottom: 1px solid #0a0a0a; + border-left: 0; + margin: 20px auto; + Margin: 20px auto; + clear: both; } + +.stat { + font-size: 40px; + line-height: 1; } +p + .stat { + margin-top: -16px; + Margin-top: -16px; } + +span.preheader { + display: none !important; + visibility: hidden; + mso-hide: all !important; + font-size: 1px; + color: #f3f3f3; + line-height: 1px; + max-height: 0px; + max-width: 0px; + opacity: 0; + overflow: hidden; } + +table.button { + width: auto; + margin: 0 0 16px 0; + Margin: 0 0 16px 0; } +table.button table td { + text-align: left; + color: #fefefe; + background: #2199e8; + border: 2px solid #2199e8; } +table.button table td a { + font-family: Helvetica, Arial, sans-serif; + font-size: 16px; + font-weight: bold; + color: #fefefe; + text-decoration: none; + display: inline-block; + padding: 8px 16px 8px 16px; + border: 0 solid #2199e8; + border-radius: 3px; } +table.button.radius table td { + border-radius: 3px; + border: none; } +table.button.rounded table td { + border-radius: 500px; + border: none; } + +table.button:hover table tr td a, +table.button:active table tr td a, +table.button table tr td a:visited, +table.button.tiny:hover table tr td a, +table.button.tiny:active table tr td a, +table.button.tiny table tr td a:visited, +table.button.small:hover table tr td a, +table.button.small:active table tr td a, +table.button.small table tr td a:visited, +table.button.large:hover table tr td a, +table.button.large:active table tr td a, +table.button.large table tr td a:visited { + color: #fefefe; } + +table.button.tiny table td, +table.button.tiny table a { + padding: 4px 8px 4px 8px; } + +table.button.tiny table a { + font-size: 10px; + font-weight: normal; } + +table.button.small table td, +table.button.small table a { + padding: 5px 10px 5px 10px; + font-size: 12px; } + +table.button.large table a { + padding: 10px 20px 10px 20px; + font-size: 20px; } + +table.button.expand, +table.button.expanded { + width: 100% !important; } +table.button.expand table, +table.button.expanded table { + width: 100%; } +table.button.expand table a, +table.button.expanded table a { + text-align: center; + width: 100%; + padding-left: 0; + padding-right: 0; } +table.button.expand center, +table.button.expanded center { + min-width: 0; } + +table.button:hover table td, +table.button:visited table td, +table.button:active table td { + background: #147dc2; + color: #fefefe; } + +table.button:hover table a, +table.button:visited table a, +table.button:active table a { + border: 0 solid #147dc2; } + +table.button.secondary table td { + background: #777777; + color: #fefefe; + border: 0px solid #777777; } + +table.button.secondary table a { + color: #fefefe; + border: 0 solid #777777; } + +table.button.secondary:hover table td { + background: #919191; + color: #fefefe; } + +table.button.secondary:hover table a { + border: 0 solid #919191; } + +table.button.secondary:hover table td a { + color: #fefefe; } + +table.button.secondary:active table td a { + color: #fefefe; } + +table.button.secondary table td a:visited { + color: #fefefe; } + +table.button.success table td { + background: #3adb76; + border: 0px solid #3adb76; } + +table.button.success table a { + border: 0 solid #3adb76; } + +table.button.success:hover table td { + background: #23bf5d; } + +table.button.success:hover table a { + border: 0 solid #23bf5d; } + +table.button.alert table td { + background: #ec5840; + border: 0px solid #ec5840; } + +table.button.alert table a { + border: 0 solid #ec5840; } + +table.button.alert:hover table td { + background: #e23317; } + +table.button.alert:hover table a { + border: 0 solid #e23317; } + +table.button.warning table td { + background: #ffae00; + border: 0px solid #ffae00; } + +table.button.warning table a { + border: 0px solid #ffae00; } + +table.button.warning:hover table td { + background: #cc8b00; } + +table.button.warning:hover table a { + border: 0px solid #cc8b00; } + +table.callout { + margin-bottom: 16px; + Margin-bottom: 16px; } + +th.callout-inner { + width: 100%; + border: 1px solid #cbcbcb; + padding: 10px; + background: #fefefe; } +th.callout-inner.primary { + background: #def0fc; + border: 1px solid #444444; + color: #0a0a0a; } +th.callout-inner.secondary { + background: #ebebeb; + border: 1px solid #444444; + color: #0a0a0a; } +th.callout-inner.success { + background: #e1faea; + border: 1px solid #1b9448; + color: #fefefe; } +th.callout-inner.warning { + background: #fff3d9; + border: 1px solid #996800; + color: #fefefe; } +th.callout-inner.alert { + background: #fce6e2; + border: 1px solid #b42912; + color: #fefefe; } + +.thumbnail { + border: solid 4px #fefefe; + box-shadow: 0 0 0 1px rgba(10, 10, 10, 0.2); + display: inline-block; + line-height: 0; + max-width: 100%; + transition: box-shadow 200ms ease-out; + border-radius: 3px; + margin-bottom: 16px; } +.thumbnail:hover, .thumbnail:focus { + box-shadow: 0 0 6px 1px rgba(33, 153, 232, 0.5); } + +table.menu { + width: 580px; } +table.menu td.menu-item, +table.menu th.menu-item { + padding: 10px; + padding-right: 10px; } +table.menu td.menu-item a, +table.menu th.menu-item a { + color: #2199e8; } + +table.menu.vertical td.menu-item, +table.menu.vertical th.menu-item { + padding: 10px; + padding-right: 0; + display: block; } +table.menu.vertical td.menu-item a, +table.menu.vertical th.menu-item a { + width: 100%; } + +table.menu.vertical td.menu-item table.menu.vertical td.menu-item, +table.menu.vertical td.menu-item table.menu.vertical th.menu-item, +table.menu.vertical th.menu-item table.menu.vertical td.menu-item, +table.menu.vertical th.menu-item table.menu.vertical th.menu-item { + padding-left: 10px; } + +table.menu.text-center a { + text-align: center; } + +.menu[align="center"] { + width: auto !important; } + +body.outlook p { + display: inline !important; } + +@media only screen and (max-width: 596px) { + table.body img { + width: auto; + height: auto; } + table.body center { + min-width: 0 !important; } + table.body .container { + width: 95% !important; } + table.body .columns, + table.body .column { + height: auto !important; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding-left: 16px !important; + padding-right: 16px !important; } + table.body .columns .column, + table.body .columns .columns, + table.body .column .column, + table.body .column .columns { + padding-left: 0 !important; + padding-right: 0 !important; } + table.body .collapse .columns, + table.body .collapse .column { + padding-left: 0 !important; + padding-right: 0 !important; } + td.small-1, + th.small-1 { + display: inline-block !important; + width: 8.33333% !important; } + td.small-2, + th.small-2 { + display: inline-block !important; + width: 16.66667% !important; } + td.small-3, + th.small-3 { + display: inline-block !important; + width: 25% !important; } + td.small-4, + th.small-4 { + display: inline-block !important; + width: 33.33333% !important; } + td.small-5, + th.small-5 { + display: inline-block !important; + width: 41.66667% !important; } + td.small-6, + th.small-6 { + display: inline-block !important; + width: 50% !important; } + td.small-7, + th.small-7 { + display: inline-block !important; + width: 58.33333% !important; } + td.small-8, + th.small-8 { + display: inline-block !important; + width: 66.66667% !important; } + td.small-9, + th.small-9 { + display: inline-block !important; + width: 75% !important; } + td.small-10, + th.small-10 { + display: inline-block !important; + width: 83.33333% !important; } + td.small-11, + th.small-11 { + display: inline-block !important; + width: 91.66667% !important; } + td.small-12, + th.small-12 { + display: inline-block !important; + width: 100% !important; } + .columns td.small-12, + .column td.small-12, + .columns th.small-12, + .column th.small-12 { + display: block !important; + width: 100% !important; } + table.body td.small-offset-1, + table.body th.small-offset-1 { + margin-left: 8.33333% !important; + Margin-left: 8.33333% !important; } + table.body td.small-offset-2, + table.body th.small-offset-2 { + margin-left: 16.66667% !important; + Margin-left: 16.66667% !important; } + table.body td.small-offset-3, + table.body th.small-offset-3 { + margin-left: 25% !important; + Margin-left: 25% !important; } + table.body td.small-offset-4, + table.body th.small-offset-4 { + margin-left: 33.33333% !important; + Margin-left: 33.33333% !important; } + table.body td.small-offset-5, + table.body th.small-offset-5 { + margin-left: 41.66667% !important; + Margin-left: 41.66667% !important; } + table.body td.small-offset-6, + table.body th.small-offset-6 { + margin-left: 50% !important; + Margin-left: 50% !important; } + table.body td.small-offset-7, + table.body th.small-offset-7 { + margin-left: 58.33333% !important; + Margin-left: 58.33333% !important; } + table.body td.small-offset-8, + table.body th.small-offset-8 { + margin-left: 66.66667% !important; + Margin-left: 66.66667% !important; } + table.body td.small-offset-9, + table.body th.small-offset-9 { + margin-left: 75% !important; + Margin-left: 75% !important; } + table.body td.small-offset-10, + table.body th.small-offset-10 { + margin-left: 83.33333% !important; + Margin-left: 83.33333% !important; } + table.body td.small-offset-11, + table.body th.small-offset-11 { + margin-left: 91.66667% !important; + Margin-left: 91.66667% !important; } + table.body table.columns td.expander, + table.body table.columns th.expander { + display: none !important; } + table.body .right-text-pad, + table.body .text-pad-right { + padding-left: 10px !important; } + table.body .left-text-pad, + table.body .text-pad-left { + padding-right: 10px !important; } + table.menu { + width: 100% !important; } + table.menu td, + table.menu th { + width: auto !important; + display: inline-block !important; } + table.menu.vertical td, + table.menu.vertical th, table.menu.small-vertical td, + table.menu.small-vertical th { + display: block !important; } + table.menu[align="center"] { + width: auto !important; } + table.button.small-expand, + table.button.small-expanded { + width: 100% !important; } + table.button.small-expand table, + table.button.small-expanded table { + width: 100%; } + table.button.small-expand table a, + table.button.small-expanded table a { + text-align: center !important; + width: 100% !important; + padding-left: 0 !important; + padding-right: 0 !important; } + table.button.small-expand center, + table.button.small-expanded center { + min-width: 0; } } \ No newline at end of file diff --git a/composer.json b/composer.json index 9cec64bd..c5e6f806 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,9 @@ "ext-pdo": "*", "ext-zip": "*", "beberlei/doctrineextensions": "^1.2", + "doctrine/doctrine-bundle": "^2.0", + "doctrine/doctrine-migrations-bundle": "^2.1", + "doctrine/orm": "^2.7", "erusev/parsedown": "^1.6", "friendsofsymfony/rest-bundle": "^2.5", "friendsofsymfony/user-bundle": "dev-master", @@ -29,6 +32,7 @@ "kimai/kimai2-composer": "^0.1", "laravolt/avatar": "^3.0", "league/csv": "^9.4", + "league/html-to-markdown": "^4.9", "mpdf/mpdf": "^8.0", "nelmio/api-doc-bundle": "^3.2", "nelmio/cors-bundle": "^1.5", @@ -39,26 +43,33 @@ "phpoffice/phpword": "^0.17", "psr/log": "^1.1", "sensio/framework-extra-bundle": "^5.2", - "symfony/asset": "^4.0", - "symfony/console": "^4.0", - "symfony/dotenv": "^4.0", - "symfony/expression-language": "^4.0", + "symfony/amazon-mailer": "^4.4", + "symfony/asset": "^4.4", + "symfony/console": "^4.4", + "symfony/dotenv": "^4.4", + "symfony/expression-language": "^4.4", "symfony/flex": "^1.0", - "symfony/form": "^4.0", - "symfony/framework-bundle": "^4.0", - "symfony/intl": "^4.0", + "symfony/form": "^4.4", + "symfony/framework-bundle": "^4.4", + "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/orm-pack": "^1.0", - "symfony/security-bundle": "^4.0", - "symfony/security-csrf": "^4.0", - "symfony/serializer": "^4.0", - "symfony/swiftmailer-bundle": "^3.2", - "symfony/translation": "^4.0", - "symfony/twig-bundle": "^4.0", - "symfony/validator": "^4.0", + "symfony/postmark-mailer": "^4.4", + "symfony/security-bundle": "^4.4", + "symfony/security-csrf": "^4.4", + "symfony/sendgrid-mailer": "^4.4", + "symfony/serializer": "^4.4", + "symfony/translation": "^4.4", + "symfony/twig-bundle": "^4.4", + "symfony/validator": "^4.4", "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/inky-extra": "^3.0", "twig/intl-extra": "^3.0", "twig/string-extra": "^3.0" }, @@ -72,13 +83,13 @@ "phpstan/phpstan-phpunit": "^0.12", "phpstan/phpstan-symfony": "^0.12", "phpunit/phpunit": "^8.0", - "symfony/browser-kit": "^4.0", - "symfony/css-selector": "^4.0", - "symfony/debug-bundle": "^4.0", + "symfony/browser-kit": "^4.4", + "symfony/css-selector": "^4.4", + "symfony/debug-bundle": "^4.4", "symfony/maker-bundle": "^1.12", - "symfony/phpunit-bridge": "^4.0", + "symfony/phpunit-bridge": "^4.4", "symfony/profiler-pack": "^1.0", - "symfony/var-dumper": "^4.0" + "symfony/var-dumper": "^4.4" }, "repositories": [ { diff --git a/composer.lock b/composer.lock index 48966314..78934b60 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b2981837e799c1f3dc53a03fb19ba617", + "content-hash": "71e1358f1434fdde57b390a2811c0315", "packages": [ { "name": "beberlei/doctrineextensions", @@ -2508,16 +2508,16 @@ }, { "name": "hslavich/oneloginsaml-bundle", - "version": "v1.4.1", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/hslavich/OneloginSamlBundle.git", - "reference": "7a8e7e7f0bbf30bc0fcb662d52a4549b0eba3828" + "reference": "7ffb4f93ea8d3bbc742eb15bf02c2618777ce2af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hslavich/OneloginSamlBundle/zipball/7a8e7e7f0bbf30bc0fcb662d52a4549b0eba3828", - "reference": "7a8e7e7f0bbf30bc0fcb662d52a4549b0eba3828", + "url": "https://api.github.com/repos/hslavich/OneloginSamlBundle/zipball/7ffb4f93ea8d3bbc742eb15bf02c2618777ce2af", + "reference": "7ffb4f93ea8d3bbc742eb15bf02c2618777ce2af", "shasum": "" }, "require": { @@ -2535,7 +2535,10 @@ "autoload": { "psr-4": { "Hslavich\\OneloginSamlBundle\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2553,20 +2556,20 @@ "onelogin", "saml" ], - "time": "2019-02-05T14:15:37+00:00" + "time": "2020-02-04T13:42:57+00:00" }, { "name": "illuminate/cache", - "version": "v6.13.1", + "version": "v6.15.0", "source": { "type": "git", "url": "https://github.com/illuminate/cache.git", - "reference": "807e85ee8af7013c7d2eefcc8e91d4d008f30d06" + "reference": "2669ff4aa3575401f3ae55fe4049f6d5efb221ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/cache/zipball/807e85ee8af7013c7d2eefcc8e91d4d008f30d06", - "reference": "807e85ee8af7013c7d2eefcc8e91d4d008f30d06", + "url": "https://api.github.com/repos/illuminate/cache/zipball/2669ff4aa3575401f3ae55fe4049f6d5efb221ff", + "reference": "2669ff4aa3575401f3ae55fe4049f6d5efb221ff", "shasum": "" }, "require": { @@ -2604,20 +2607,20 @@ ], "description": "The Illuminate Cache package.", "homepage": "https://laravel.com", - "time": "2020-01-23T14:27:36+00:00" + "time": "2020-02-04T13:59:03+00:00" }, { "name": "illuminate/contracts", - "version": "v6.13.1", + "version": "v6.15.0", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "f1326dfae72c646a8598138b446347954f5e991e" + "reference": "a6c7ef89684ce0e724b42c6bfe4b1107aee28a1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/f1326dfae72c646a8598138b446347954f5e991e", - "reference": "f1326dfae72c646a8598138b446347954f5e991e", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/a6c7ef89684ce0e724b42c6bfe4b1107aee28a1d", + "reference": "a6c7ef89684ce0e724b42c6bfe4b1107aee28a1d", "shasum": "" }, "require": { @@ -2648,20 +2651,20 @@ ], "description": "The Illuminate Contracts package.", "homepage": "https://laravel.com", - "time": "2020-01-07T13:47:03+00:00" + "time": "2020-02-08T09:26:21+00:00" }, { "name": "illuminate/support", - "version": "v6.13.1", + "version": "v6.15.0", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", - "reference": "c6bcd329fd8c7c46908f1af7f99dae164a4065a1" + "reference": "2601f1f2e9a1bf7724928c3c2892519667f9983f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/c6bcd329fd8c7c46908f1af7f99dae164a4065a1", - "reference": "c6bcd329fd8c7c46908f1af7f99dae164a4065a1", + "url": "https://api.github.com/repos/illuminate/support/zipball/2601f1f2e9a1bf7724928c3c2892519667f9983f", + "reference": "2601f1f2e9a1bf7724928c3c2892519667f9983f", "shasum": "" }, "require": { @@ -2709,7 +2712,7 @@ ], "description": "The Illuminate Support package.", "homepage": "https://laravel.com", - "time": "2020-01-28T14:36:33+00:00" + "time": "2020-02-10T23:12:52+00:00" }, { "name": "intervention/image", @@ -3298,6 +3301,117 @@ ], "time": "2019-12-15T19:51:41+00:00" }, + { + "name": "league/html-to-markdown", + "version": "4.9.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/html-to-markdown.git", + "reference": "1dcd0f85de786f46a7f224a27cc3d709ddd2a68c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/1dcd0f85de786f46a7f224a27cc3d709ddd2a68c", + "reference": "1dcd0f85de786f46a7f224a27cc3d709ddd2a68c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xml": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "mikehaertl/php-shellcommand": "~1.1.0", + "phpunit/phpunit": "^4.8|^5.7", + "scrutinizer/ocular": "~1.1" + }, + "bin": [ + "bin/html-to-markdown" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.10-dev" + } + }, + "autoload": { + "psr-4": { + "League\\HTMLToMarkdown\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + }, + { + "name": "Nick Cernis", + "email": "nick@cern.is", + "homepage": "http://modernnerd.net", + "role": "Original Author" + } + ], + "description": "An HTML-to-markdown conversion helper for PHP", + "homepage": "https://github.com/thephpleague/html-to-markdown", + "keywords": [ + "html", + "markdown" + ], + "time": "2019-12-28T01:32:28+00:00" + }, + { + "name": "lorenzo/pinky", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/lorenzo/pinky.git", + "reference": "2bc1a9d5696d6496df5d5682962929165a823e57" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lorenzo/pinky/zipball/2bc1a9d5696d6496df5d5682962929165a823e57", + "reference": "2bc1a9d5696d6496df5d5682962929165a823e57", + "shasum": "" + }, + "require": { + "ext-xsl": "*", + "php": ">=5.6.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "autoload": { + "files": [ + "src/pinky.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jose Lorenzo Rodriguez", + "email": "jose.zap@gmail.com" + } + ], + "description": "A Foundation for Emails (Inky) template transpiler", + "keywords": [ + "email", + "foundation", + "inky", + "template", + "zurb" + ], + "time": "2019-09-16T21:23:31+00:00" + }, { "name": "markbaker/complex", "version": "1.4.7", @@ -3542,16 +3656,16 @@ }, { "name": "mpdf/mpdf", - "version": "v8.0.4", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/mpdf/mpdf.git", - "reference": "d3147a0d790b6d11936fd9c73fa31a7ed45e3f6f" + "reference": "bad32aa9cd5958175aef185c02e032ddbadc56ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mpdf/mpdf/zipball/d3147a0d790b6d11936fd9c73fa31a7ed45e3f6f", - "reference": "d3147a0d790b6d11936fd9c73fa31a7ed45e3f6f", + "url": "https://api.github.com/repos/mpdf/mpdf/zipball/bad32aa9cd5958175aef185c02e032ddbadc56ea", + "reference": "bad32aa9cd5958175aef185c02e032ddbadc56ea", "shasum": "" }, "require": { @@ -3607,7 +3721,7 @@ "php", "utf-8" ], - "time": "2019-11-28T09:39:33+00:00" + "time": "2020-02-05T08:43:46+00:00" }, { "name": "myclabs/deep-copy", @@ -5060,43 +5174,38 @@ "time": "2019-01-30T14:11:19+00:00" }, { - "name": "swiftmailer/swiftmailer", - "version": "v6.2.3", + "name": "symfony/amazon-mailer", + "version": "v4.4.4", "source": { "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9" + "url": "https://github.com/symfony/amazon-mailer.git", + "reference": "ac5202e52d676a0a4a99a8dc67d03590692e5306" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/149cfdf118b169f7840bbe3ef0d4bc795d1780c9", - "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9", + "url": "https://api.github.com/repos/symfony/amazon-mailer/zipball/ac5202e52d676a0a4a99a8dc67d03590692e5306", + "reference": "ac5202e52d676a0a4a99a8dc67d03590692e5306", "shasum": "" }, "require": { - "egulias/email-validator": "~2.0", - "php": ">=7.0.0", - "symfony/polyfill-iconv": "^1.0", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" + "php": "^7.1.3", + "symfony/mailer": "^4.4|^5.0" }, "require-dev": { - "mockery/mockery": "~0.9.1", - "symfony/phpunit-bridge": "^3.4.19|^4.1.8" + "symfony/http-client": "^4.3|^5.0" }, - "suggest": { - "ext-intl": "Needed to support internationalized email addresses", - "true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed" - }, - "type": "library", + "type": "symfony-bridge", "extra": { "branch-alias": { - "dev-master": "6.2-dev" + "dev-master": "4.4-dev" } }, "autoload": { - "files": [ - "lib/swift_required.php" + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Amazon\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -5104,22 +5213,18 @@ "MIT" ], "authors": [ - { - "name": "Chris Corbyn" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "https://swiftmailer.symfony.com", - "keywords": [ - "email", - "mail", - "mailer" - ], - "time": "2019-11-12T09:31:26+00:00" + "description": "Symfony Amazon Mailer Bridge", + "homepage": "https://symfony.com", + "time": "2020-01-08T17:36:30+00:00" }, { "name": "symfony/asset", @@ -5258,20 +5363,20 @@ }, { "name": "symfony/cache-contracts", - "version": "v1.1.7", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "af50d14ada9e4e82cfabfabdc502d144f89be0a1" + "reference": "23ed8bfc1a4115feca942cb5f1aacdf3dcdf3c16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/af50d14ada9e4e82cfabfabdc502d144f89be0a1", - "reference": "af50d14ada9e4e82cfabfabdc502d144f89be0a1", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/23ed8bfc1a4115feca942cb5f1aacdf3dcdf3c16", + "reference": "23ed8bfc1a4115feca942cb5f1aacdf3dcdf3c16", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^7.2.5", "psr/cache": "^1.0" }, "suggest": { @@ -5280,7 +5385,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -5312,7 +5417,7 @@ "interoperability", "standards" ], - "time": "2019-10-04T21:43:27+00:00" + "time": "2019-11-18T17:27:11+00:00" }, { "name": "symfony/config", @@ -5454,6 +5559,59 @@ "homepage": "https://symfony.com", "time": "2020-01-25T12:44:29+00:00" }, + { + "name": "symfony/css-selector", + "version": "v4.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "a167b1860995b926d279f9bb538f873e3bfa3465" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/a167b1860995b926d279f9bb538f873e3bfa3465", + "reference": "a167b1860995b926d279f9bb538f873e3bfa3465", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2020-01-04T13:00:46+00:00" + }, { "name": "symfony/debug", "version": "v4.4.4", @@ -6332,6 +6490,59 @@ "homepage": "https://symfony.com", "time": "2020-01-30T16:24:07+00:00" }, + { + "name": "symfony/google-mailer", + "version": "v4.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/google-mailer.git", + "reference": "7dd2d4722ed7e9fe3be27b0afe1865dc81643815" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/google-mailer/zipball/7dd2d4722ed7e9fe3be27b0afe1865dc81643815", + "reference": "7dd2d4722ed7e9fe3be27b0afe1865dc81643815", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/mailer": "^4.4|^5.0" + }, + "require-dev": { + "symfony/http-client": "^4.3|^5.0" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Google\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Google Mailer Bridge", + "homepage": "https://symfony.com", + "time": "2020-01-04T13:00:46+00:00" + }, { "name": "symfony/http-foundation", "version": "v4.4.4", @@ -6610,6 +6821,180 @@ ], "time": "2020-01-30T15:14:06+00:00" }, + { + "name": "symfony/mailchimp-mailer", + "version": "v4.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailchimp-mailer.git", + "reference": "5f7a556ff893ac8b16ced444ca3b213b8ab42375" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailchimp-mailer/zipball/5f7a556ff893ac8b16ced444ca3b213b8ab42375", + "reference": "5f7a556ff893ac8b16ced444ca3b213b8ab42375", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/mailer": "^4.4|^5.0" + }, + "require-dev": { + "symfony/http-client": "^4.3|^5.0" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Mailchimp\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Mailchimp Mailer Bridge", + "homepage": "https://symfony.com", + "time": "2020-01-30T10:46:32+00:00" + }, + { + "name": "symfony/mailer", + "version": "v4.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "74c15502242b4f23dd28643a351db781ea2cd85a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/74c15502242b4f23dd28643a351db781ea2cd85a", + "reference": "74c15502242b4f23dd28643a351db781ea2cd85a", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10", + "php": "^7.1.3", + "psr/log": "~1.0", + "symfony/event-dispatcher": "^4.3", + "symfony/mime": "^4.4|^5.0", + "symfony/service-contracts": "^1.1|^2" + }, + "conflict": { + "symfony/http-kernel": "<4.4", + "symfony/sendgrid-mailer": "<4.4" + }, + "require-dev": { + "symfony/amazon-mailer": "^4.4|^5.0", + "symfony/google-mailer": "^4.4|^5.0", + "symfony/http-client-contracts": "^1.1|^2", + "symfony/mailchimp-mailer": "^4.4|^5.0", + "symfony/mailgun-mailer": "^4.4|^5.0", + "symfony/messenger": "^4.4|^5.0", + "symfony/postmark-mailer": "^4.4|^5.0", + "symfony/sendgrid-mailer": "^4.4|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Mailer Component", + "homepage": "https://symfony.com", + "time": "2020-01-04T13:00:46+00:00" + }, + { + "name": "symfony/mailgun-mailer", + "version": "v4.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailgun-mailer.git", + "reference": "de87b7fc1fd650fa5437eda8aedfa7cd63a76a03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/de87b7fc1fd650fa5437eda8aedfa7cd63a76a03", + "reference": "de87b7fc1fd650fa5437eda8aedfa7cd63a76a03", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/mailer": "^4.4|^5.0" + }, + "require-dev": { + "symfony/http-client": "^4.3|^5.0" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Mailgun\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Mailgun Mailer Bridge", + "homepage": "https://symfony.com", + "time": "2020-01-08T17:36:30+00:00" + }, { "name": "symfony/mime", "version": "v4.4.4", @@ -6856,34 +7241,6 @@ ], "time": "2020-01-04T13:00:46+00:00" }, - { - "name": "symfony/orm-pack", - "version": "v1.0.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/orm-pack.git", - "reference": "c57f5e05232ca40626eb9fa52a32bc8565e9231c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/orm-pack/zipball/c57f5e05232ca40626eb9fa52a32bc8565e9231c", - "reference": "c57f5e05232ca40626eb9fa52a32bc8565e9231c", - "shasum": "" - }, - "require": { - "doctrine/doctrine-bundle": "^1.6.10|^2.0", - "doctrine/doctrine-migrations-bundle": "^1.3|^2.0", - "doctrine/orm": "^2.5.11", - "php": "^7.0" - }, - "type": "symfony-pack", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A pack for the Doctrine ORM", - "time": "2019-10-18T05:41:09+00:00" - }, { "name": "symfony/polyfill-ctype", "version": "v1.13.1", @@ -7357,6 +7714,59 @@ ], "time": "2019-11-27T16:25:15+00:00" }, + { + "name": "symfony/postmark-mailer", + "version": "v4.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/postmark-mailer.git", + "reference": "afaaca6f9652ca82086392b9955ed94ca6fc0a9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/postmark-mailer/zipball/afaaca6f9652ca82086392b9955ed94ca6fc0a9e", + "reference": "afaaca6f9652ca82086392b9955ed94ca6fc0a9e", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/mailer": "^4.4|^5.0" + }, + "require-dev": { + "symfony/http-client": "^4.3|^5.0" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Postmark\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Postmark Mailer Bridge", + "homepage": "https://symfony.com", + "time": "2020-01-30T10:46:32+00:00" + }, { "name": "symfony/property-access", "version": "v4.4.4", @@ -7911,6 +8321,59 @@ "homepage": "https://symfony.com", "time": "2020-01-31T09:11:17+00:00" }, + { + "name": "symfony/sendgrid-mailer", + "version": "v4.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/sendgrid-mailer.git", + "reference": "96a6c3081c9fb47a6784320147370939626aa6cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/sendgrid-mailer/zipball/96a6c3081c9fb47a6784320147370939626aa6cd", + "reference": "96a6c3081c9fb47a6784320147370939626aa6cd", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/mailer": "^4.4|^5.0" + }, + "require-dev": { + "symfony/http-client": "^4.3|^5.0" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Sendgrid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Sendgrid Mailer Bridge", + "homepage": "https://symfony.com", + "time": "2020-01-04T13:10:16+00:00" + }, { "name": "symfony/serializer", "version": "v4.4.4", @@ -7994,20 +8457,20 @@ }, { "name": "symfony/service-contracts", - "version": "v1.1.8", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf" + "reference": "144c5e51266b281231e947b51223ba14acf1a749" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", + "reference": "144c5e51266b281231e947b51223ba14acf1a749", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^7.2.5", "psr/container": "^1.0" }, "suggest": { @@ -8016,7 +8479,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -8048,7 +8511,7 @@ "interoperability", "standards" ], - "time": "2019-10-14T12:27:06+00:00" + "time": "2019-11-18T17:27:11+00:00" }, { "name": "symfony/stopwatch", @@ -8164,71 +8627,6 @@ ], "time": "2020-01-14T17:54:59+00:00" }, - { - "name": "symfony/swiftmailer-bundle", - "version": "v3.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/swiftmailer-bundle.git", - "reference": "553d2474288349faed873da8ab7c1551a00d26ae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/553d2474288349faed873da8ab7c1551a00d26ae", - "reference": "553d2474288349faed873da8ab7c1551a00d26ae", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "swiftmailer/swiftmailer": "^6.1.3", - "symfony/config": "^4.3.8|^5.0", - "symfony/dependency-injection": "^4.3.8|^5.0", - "symfony/http-kernel": "^4.3.8|^5.0" - }, - "conflict": { - "twig/twig": "<1.41|<2.10" - }, - "require-dev": { - "symfony/console": "^4.3.8|^5.0", - "symfony/framework-bundle": "^4.3.8|^5.0", - "symfony/phpunit-bridge": "^4.3.8|^5.0", - "symfony/yaml": "^4.3.8|^5.0" - }, - "suggest": { - "psr/log": "Allows logging" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bundle\\SwiftmailerBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony SwiftmailerBundle", - "homepage": "http://symfony.com", - "time": "2019-11-14T16:18:31+00:00" - }, { "name": "symfony/templating", "version": "v4.4.4", @@ -8363,20 +8761,20 @@ }, { "name": "symfony/translation-contracts", - "version": "v1.1.7", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "364518c132c95642e530d9b2d217acbc2ccac3e6" + "reference": "8cc682ac458d75557203b2f2f14b0b92e1c744ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/364518c132c95642e530d9b2d217acbc2ccac3e6", - "reference": "364518c132c95642e530d9b2d217acbc2ccac3e6", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/8cc682ac458d75557203b2f2f14b0b92e1c744ed", + "reference": "8cc682ac458d75557203b2f2f14b0b92e1c744ed", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.2.5" }, "suggest": { "symfony/translation-implementation": "" @@ -8384,7 +8782,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -8416,7 +8814,7 @@ "interoperability", "standards" ], - "time": "2019-09-17T11:12:18+00:00" + "time": "2019-11-18T17:27:11+00:00" }, { "name": "symfony/twig-bridge", @@ -8937,17 +9335,120 @@ "time": "2020-01-21T11:12:16+00:00" }, { - "name": "twig/extra-bundle", - "version": "v3.0.1", + "name": "tijsverkoyen/css-to-inline-styles", + "version": "2.2.2", "source": { "type": "git", - "url": "https://github.com/twigphp/twig-extra-bundle.git", - "reference": "ce5c97dd566d9acd5d1fbd5eb76b6d264614725a" + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/ce5c97dd566d9acd5d1fbd5eb76b6d264614725a", - "reference": "ce5c97dd566d9acd5d1fbd5eb76b6d264614725a", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/dda2ee426acd6d801d5b7fd1001cde9b5f790e15", + "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^5.5 || ^7.0", + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "time": "2019-10-24T08:53:34+00:00" + }, + { + "name": "twig/cssinliner-extra", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/twigphp/cssinliner-extra.git", + "reference": "fe2f378b0b2987564fbef9c09a6119cd92e7764e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/cssinliner-extra/zipball/fe2f378b0b2987564fbef9c09a6119cd92e7764e", + "reference": "fe2f378b0b2987564fbef9c09a6119cd92e7764e", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "tijsverkoyen/css-to-inline-styles": "^2.0", + "twig/twig": "^2.4|^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.12-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\Extra\\CssInliner\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension to allow inlining CSS", + "homepage": "https://twig.symfony.com", + "keywords": [ + "css", + "inlining", + "twig" + ], + "time": "2020-01-01T17:10:47+00:00" + }, + { + "name": "twig/extra-bundle", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/twigphp/twig-extra-bundle.git", + "reference": "6eaf1637abe6b68518e7e0949ebb84e55770d5c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/6eaf1637abe6b68518e7e0949ebb84e55770d5c6", + "reference": "6eaf1637abe6b68518e7e0949ebb84e55770d5c6", "shasum": "" }, "require": { @@ -8993,20 +9494,75 @@ "extra", "twig" ], - "time": "2019-12-28T07:09:27+00:00" + "time": "2020-01-01T17:11:09+00:00" }, { - "name": "twig/intl-extra", - "version": "v3.0.1", + "name": "twig/inky-extra", + "version": "v3.0.2", "source": { "type": "git", - "url": "https://github.com/twigphp/intl-extra.git", - "reference": "291d79ef98891da3efe14f0771fbe03a25fe6bec" + "url": "https://github.com/twigphp/inky-extra.git", + "reference": "1723fbcf496bd53bdbd5fa3cc819dd4574a2f967" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/291d79ef98891da3efe14f0771fbe03a25fe6bec", - "reference": "291d79ef98891da3efe14f0771fbe03a25fe6bec", + "url": "https://api.github.com/repos/twigphp/inky-extra/zipball/1723fbcf496bd53bdbd5fa3cc819dd4574a2f967", + "reference": "1723fbcf496bd53bdbd5fa3cc819dd4574a2f967", + "shasum": "" + }, + "require": { + "lorenzo/pinky": "^1.0.5", + "php": "^7.1.3", + "twig/twig": "^2.4|^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.12-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\Extra\\Inky\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for the inky email templating engine", + "homepage": "https://twig.symfony.com", + "keywords": [ + "email", + "emails", + "inky", + "twig" + ], + "time": "2020-01-01T17:10:47+00:00" + }, + { + "name": "twig/intl-extra", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/twigphp/intl-extra.git", + "reference": "55d68a5836f40d62695488b4bdbc71fa71f52574" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/55d68a5836f40d62695488b4bdbc71fa71f52574", + "reference": "55d68a5836f40d62695488b4bdbc71fa71f52574", "shasum": "" }, "require": { @@ -9046,24 +9602,24 @@ "intl", "twig" ], - "time": "2019-12-28T07:09:27+00:00" + "time": "2020-02-11T05:45:44+00:00" }, { "name": "twig/string-extra", - "version": "v3.0.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/twigphp/string-extra.git", - "reference": "54fb5511c4a0189de1d48d0f7c98843c270084e8" + "reference": "b71ca9aa5ec6f78002f1d7aac572c0d25ae25dda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/string-extra/zipball/54fb5511c4a0189de1d48d0f7c98843c270084e8", - "reference": "54fb5511c4a0189de1d48d0f7c98843c270084e8", + "url": "https://api.github.com/repos/twigphp/string-extra/zipball/b71ca9aa5ec6f78002f1d7aac572c0d25ae25dda", + "reference": "b71ca9aa5ec6f78002f1d7aac572c0d25ae25dda", "shasum": "" }, "require": { - "php": "^7.2.9", + "php": "^7.2.5", "symfony/string": "^5.0", "twig/twig": "^2.4|^3.0" }, @@ -9101,20 +9657,20 @@ "twig", "unicode" ], - "time": "2019-12-27T07:33:44+00:00" + "time": "2020-01-02T12:43:40+00:00" }, { "name": "twig/twig", - "version": "v2.12.3", + "version": "v2.12.4", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "97b6311585cae66a26833b14b33785f5797f7d39" + "reference": "822d57eac92f966cd1e3232539b3dbbfed21f754" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/97b6311585cae66a26833b14b33785f5797f7d39", - "reference": "97b6311585cae66a26833b14b33785f5797f7d39", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/822d57eac92f966cd1e3232539b3dbbfed21f754", + "reference": "822d57eac92f966cd1e3232539b3dbbfed21f754", "shasum": "" }, "require": { @@ -9166,7 +9722,7 @@ "keywords": [ "templating" ], - "time": "2019-12-28T07:12:03+00:00" + "time": "2020-02-11T06:01:32+00:00" }, { "name": "webmozart/assert", @@ -10233,16 +10789,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.8", + "version": "0.12.9", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "62a552602b7586d82826231f2fd4cbfe39fe0b1d" + "reference": "297cb2458a96ea96d5e9d6ef38f1b7305c071f32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/62a552602b7586d82826231f2fd4cbfe39fe0b1d", - "reference": "62a552602b7586d82826231f2fd4cbfe39fe0b1d", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/297cb2458a96ea96d5e9d6ef38f1b7305c071f32", + "reference": "297cb2458a96ea96d5e9d6ef38f1b7305c071f32", "shasum": "" }, "require": { @@ -10268,7 +10824,7 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "time": "2020-01-26T23:36:48+00:00" + "time": "2020-02-04T22:30:27+00:00" }, { "name": "phpstan/phpstan-doctrine", @@ -11469,59 +12025,6 @@ "homepage": "https://symfony.com", "time": "2020-01-04T13:00:46+00:00" }, - { - "name": "symfony/css-selector", - "version": "v4.4.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "a167b1860995b926d279f9bb538f873e3bfa3465" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/a167b1860995b926d279f9bb538f873e3bfa3465", - "reference": "a167b1860995b926d279f9bb538f873e3bfa3465", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" - }, { "name": "symfony/debug-bundle", "version": "v4.4.4", diff --git a/config/bundles.php b/config/bundles.php index 331f3d08..283b68d8 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -10,7 +10,6 @@ return [ Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\TwigBundle\TwigBundle::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\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true], DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], diff --git a/config/packages/dev/mailer.yaml b/config/packages/dev/mailer.yaml new file mode 100644 index 00000000..59f449a9 --- /dev/null +++ b/config/packages/dev/mailer.yaml @@ -0,0 +1,3 @@ +framework: + mailer: + dsn: 'null://null' diff --git a/config/packages/dev/swiftmailer.yaml b/config/packages/dev/swiftmailer.yaml deleted file mode 100644 index e99c453a..00000000 --- a/config/packages/dev/swiftmailer.yaml +++ /dev/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'] \ No newline at end of file diff --git a/config/packages/fos_user.yaml b/config/packages/fos_user.yaml index ffd5340d..e03e148d 100644 --- a/config/packages/fos_user.yaml +++ b/config/packages/fos_user.yaml @@ -8,18 +8,8 @@ fos_user: registration: confirmation: enabled: false - template: '@FOSUser/Registration/email.txt.twig' resetting: retry_ttl: 7200 token_ttl: 86400 - email: - template: '@FOSUser/Resetting/email.txt.twig' service: - mailer: fos_user.mailer.twig_swift -# 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] + mailer: App\Mail\UserMails diff --git a/config/packages/mailer.yaml b/config/packages/mailer.yaml new file mode 100644 index 00000000..9a5dff77 --- /dev/null +++ b/config/packages/mailer.yaml @@ -0,0 +1,3 @@ +framework: + mailer: + dsn: '%env(MAILER_URL)%' diff --git a/config/packages/swiftmailer.yaml b/config/packages/swiftmailer.yaml deleted file mode 100644 index cae65084..00000000 --- a/config/packages/swiftmailer.yaml +++ /dev/null @@ -1,3 +0,0 @@ -swiftmailer: - url: '%env(MAILER_URL)%' - spool: { type: 'memory' } diff --git a/config/packages/test/mailer.yaml b/config/packages/test/mailer.yaml new file mode 100644 index 00000000..59f449a9 --- /dev/null +++ b/config/packages/test/mailer.yaml @@ -0,0 +1,3 @@ +framework: + mailer: + dsn: 'null://null' diff --git a/config/packages/test/swiftmailer.yaml b/config/packages/test/swiftmailer.yaml deleted file mode 100644 index f4380780..00000000 --- a/config/packages/test/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - disable_delivery: true diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index 31c0a5e4..3ac0dd32 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -4,4 +4,7 @@ twig: strict_variables: '%kernel.debug%' form_themes: - 'form/kimai-theme.html.twig' - exception_controller: null \ No newline at end of file + exception_controller: null + paths: + # used in templates/emails/layout.html.twig + '%kernel.project_dir%/assets/css': css diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 037a9bea..7acbf535 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,24 +12,13 @@ + - - - - - - - - - - - - - + diff --git a/src/Configuration/MailConfiguration.php b/src/Configuration/MailConfiguration.php new file mode 100644 index 00000000..c9512179 --- /dev/null +++ b/src/Configuration/MailConfiguration.php @@ -0,0 +1,24 @@ + getenv('APP_ENV'), - 'MAILER_FROM' => getenv('MAILER_FROM'), 'CORS_ALLOW_ORIGIN' => getenv('CORS_ALLOW_ORIGIN'), ]; } diff --git a/src/EventSubscriber/RegistrationSubscriber.php b/src/EventSubscriber/RegistrationSubscriber.php index db441e6b..636825dc 100644 --- a/src/EventSubscriber/RegistrationSubscriber.php +++ b/src/EventSubscriber/RegistrationSubscriber.php @@ -16,8 +16,10 @@ use FOS\UserBundle\Model\UserManagerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** - * This class intercepts the registration to make sure the first-ever - * registered user will get the SUPER_ADMIN role. + * This class intercepts the registration to make sure: + * + * - 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 { @@ -49,7 +51,7 @@ class RegistrationSubscriber implements EventSubscriberInterface */ public function onRegistrationSuccess(FormEvent $event) { - /** @var \FOS\UserBundle\Model\UserInterface $user */ + /** @var User $user */ $user = $event->getForm()->getData(); $roles = [User::ROLE_USER]; @@ -57,6 +59,7 @@ class RegistrationSubscriber implements EventSubscriberInterface $roles = [User::ROLE_SUPER_ADMIN]; } + $user->setLanguage($event->getRequest()->getLocale()); $user->setRoles($roles); } } diff --git a/src/Mail/KimaiMailer.php b/src/Mail/KimaiMailer.php new file mode 100644 index 00000000..74acdde1 --- /dev/null +++ b/src/Mail/KimaiMailer.php @@ -0,0 +1,43 @@ +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); + } +} diff --git a/src/Mail/UserMails.php b/src/Mail/UserMails.php new file mode 100644 index 00000000..0ec38d2e --- /dev/null +++ b/src/Mail/UserMails.php @@ -0,0 +1,97 @@ +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); + } +} diff --git a/symfony.lock b/symfony.lock index e103f948..2d1194f0 100644 --- a/symfony.lock +++ b/symfony.lock @@ -222,6 +222,12 @@ "league/csv": { "version": "9.4.1" }, + "league/html-to-markdown": { + "version": "4.9.1" + }, + "lorenzo/pinky": { + "version": "1.0.5" + }, "markbaker/complex": { "version": "1.4.6" }, @@ -417,8 +423,14 @@ "setasign/fpdi": { "version": "1.6.2" }, - "swiftmailer/swiftmailer": { - "version": "v6.0.2" + "symfony/amazon-mailer": { + "version": "4.4", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "4.4", + "ref": "9648db3ecae5c8a6b1a5f74715d3907124348815" + } }, "symfony/asset": { "version": "v4.0.3" @@ -510,6 +522,15 @@ "ref": "137a14eeb6b3f5370e7147af8aff6518504f50c7" } }, + "symfony/google-mailer": { + "version": "4.4", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "4.4", + "ref": "f8fd4ddb9b477510f8f4bce2b9c054ab428c0120" + } + }, "symfony/http-foundation": { "version": "v4.0.3" }, @@ -525,6 +546,36 @@ "symfony/ldap": { "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": { "version": "1.0", "recipe": { @@ -552,9 +603,6 @@ "symfony/options-resolver": { "version": "v4.0.3" }, - "symfony/orm-pack": { - "version": "v1.0.6" - }, "symfony/phpunit-bridge": { "version": "3.3", "recipe": { @@ -588,6 +636,15 @@ "symfony/polyfill-php73": { "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": { "version": "v4.0.3" }, @@ -630,6 +687,15 @@ "symfony/security-http": { "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": { "version": "v4.1.2" }, @@ -642,15 +708,6 @@ "symfony/string": { "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": { "version": "v4.1.1" }, @@ -720,9 +777,18 @@ "theseer/tokenizer": { "version": "1.1.0" }, + "tijsverkoyen/css-to-inline-styles": { + "version": "2.2.2" + }, + "twig/cssinliner-extra": { + "version": "v3.0.1" + }, "twig/extra-bundle": { "version": "v3.0.1" }, + "twig/inky-extra": { + "version": "v3.0.1" + }, "twig/intl-extra": { "version": "v3.0.1" }, diff --git a/templates/emails/confirmation.html.twig b/templates/emails/confirmation.html.twig new file mode 100644 index 00000000..1bccf2bc --- /dev/null +++ b/templates/emails/confirmation.html.twig @@ -0,0 +1,21 @@ +{% extends 'emails/layout.html.twig' %} + +{% block title %} + {{ 'registration.title'|trans({'%username%': user.displayName}, 'email') }} +{% endblock %} + +{% block body %} +

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

+ + + + + + + +

{{ 'button_copy_url_to_browser'|trans({'%url%': confirmationUrl}, 'email') }}

+ + + +

{{ 'automated_email_dont_answer'|trans({}, 'email') }}

+{% endblock %} diff --git a/templates/emails/layout.html.twig b/templates/emails/layout.html.twig new file mode 100644 index 00000000..045f3576 --- /dev/null +++ b/templates/emails/layout.html.twig @@ -0,0 +1,29 @@ +{% apply inky_to_html|inline_css(source('@css/emails.css')) %} + + + + + + + +

{% block title %}{% endblock %}

+
+
+
+ + + + + + {% block body %}{% endblock %} + + + +{% endapply %} diff --git a/templates/emails/password-reset.html.twig b/templates/emails/password-reset.html.twig new file mode 100644 index 00000000..745c20ee --- /dev/null +++ b/templates/emails/password-reset.html.twig @@ -0,0 +1,21 @@ +{% extends 'emails/layout.html.twig' %} + +{% block title %} + {{ 'reset.title'|trans({'%username%': user.displayName}, 'email') }} +{% endblock %} + +{% block body %} +

{{ 'reset.intro'|trans({'%username%': user.displayName, '%email%': user.email}, 'email') }}

+ + + + + + + +

{{ 'button_copy_url_to_browser'|trans({'%url%': confirmationUrl}, 'email') }}

+ + + +

{{ 'automated_email_dont_answer'|trans({}, 'email') }}

+{% endblock %} diff --git a/tests/Configuration/MailConfigurationTest.php b/tests/Configuration/MailConfigurationTest.php new file mode 100644 index 00000000..603bbf9e --- /dev/null +++ b/tests/Configuration/MailConfigurationTest.php @@ -0,0 +1,33 @@ +getFromAddress()); + + putenv('MAILER_FROM='); + self::assertNull($sut->getFromAddress()); + + putenv('MAILER_FROM=' . $previous); + } +} diff --git a/tests/EventSubscriber/RegistrationSubscriberTest.php b/tests/EventSubscriber/RegistrationSubscriberTest.php index 5f9ca429..984f5693 100644 --- a/tests/EventSubscriber/RegistrationSubscriberTest.php +++ b/tests/EventSubscriber/RegistrationSubscriberTest.php @@ -50,6 +50,7 @@ class RegistrationSubscriberTest extends TestCase $form->method('getData')->willReturn($user); $request = $this->createMock(Request::class); + $request->expects($this->any())->method('getLocale')->willReturn('ru'); $event = new FormEvent($form, $request); @@ -57,6 +58,7 @@ class RegistrationSubscriberTest extends TestCase $sut->onRegistrationSuccess($event); $this->assertEquals($expectedRoles, $user->getRoles()); + $this->assertEquals('ru', $user->getLanguage()); } public function getTestData() diff --git a/tests/Mail/KimaiMailerTest.php b/tests/Mail/KimaiMailerTest.php new file mode 100644 index 00000000..53541601 --- /dev/null +++ b/tests/Mail/KimaiMailerTest.php @@ -0,0 +1,51 @@ +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()); + } +} diff --git a/tests/Mail/UserMailsTest.php b/tests/Mail/UserMailsTest.php new file mode 100644 index 00000000..dfca2fdc --- /dev/null +++ b/tests/Mail/UserMailsTest.php @@ -0,0 +1,67 @@ +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); + } +} diff --git a/translations/email.de.xlf b/translations/email.de.xlf new file mode 100644 index 00000000..187ddbea --- /dev/null +++ b/translations/email.de.xlf @@ -0,0 +1,47 @@ + + + + + + button_copy_url_to_browser + Falls der Button nicht funktioniert, kopieren Sie bitte die folgende URL in ihren Browser: %url% + + + automated_email_dont_answer + Dies ist eine automatisch generierte E-Mail. Bitte antworten Sie nicht, die Adresse wird nicht gelesen. + + + registration.subject + Benutzerkonto aktivieren + + + registration.title + Willkommen %username% + + + registration.button + Aktivieren Sie Ihren Zugang + + + registration.intro + 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. + + + reset.subject + Passwort zurücksetzen + + + reset.title + Das kann passieren ... + + + reset.button + Passwort zurücksetzen + + + reset.intro + ... Du hast also Dein Passwort vergessen? Keine Sorge: Kimai wird Dir dabei helfen ein neues zu erstellen: + + + + diff --git a/translations/email.en.xlf b/translations/email.en.xlf new file mode 100644 index 00000000..e891f09d --- /dev/null +++ b/translations/email.en.xlf @@ -0,0 +1,47 @@ + + + + + + button_copy_url_to_browser + If the button doesn't work, please copy & paste the following URL to your browser: %url% + + + automated_email_dont_answer + This is an auto generated e-mail. Please do not reply, the address will not be read. + + + registration.subject + Activate your account + + + registration.title + Welcome %username% + + + registration.button + Activate your account + + + registration.intro + 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. + + + reset.subject + Reset your password + + + reset.title + That can happen ... + + + reset.button + Reset your password + + + reset.intro + ... so you forgot your password? Don't worry: Kimai will help you to create a new one: + + + +