Compare commits

...

47 Commits
v0.5.0 ... main

Author SHA1 Message Date
Nicolai Ort 57a84c256a Merge pull request 'Fixed decimal separator' (#47) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #47
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-07-19 16:15:35 +00:00
Nicolai Ort b78534e1b0 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 16:14:59 +00:00
Nicolai Ort 9b85b54da5 Merge branch 'dev' of git.odit.services:lfk/document-server into dev
continuous-integration/drone/push Build is passing Details
2021-07-19 18:14:03 +02:00
Nicolai Ort 4b5a86282d 🚀Bumped version to v0.5.4 2021-07-19 18:13:53 +02:00
Nicolai Ort c9cb03ea95
Fixed decimal separator in docker 2021-07-19 18:13:37 +02:00
Philipp Dormann c7f57548f3 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 15:53:52 +00:00
Philipp Dormann 8d00307170 Merge pull request 'Hotfixes' (#46) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #46
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-07-19 15:53:40 +00:00
Nicolai Ort 5e92b9a48f 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 15:50:28 +00:00
Nicolai Ort 01e1323555 🚀Bumped version to v0.5.3
continuous-integration/drone/push Build is passing Details
2021-07-19 17:49:35 +02:00
Nicolai Ort f8465721cd 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 15:41:59 +00:00
Nicolai Ort 4cea7cb32f Merge branch 'dev' of git.odit.services:lfk/document-server into dev
continuous-integration/drone/push Build is passing Details
2021-07-19 17:41:13 +02:00
Nicolai Ort 72303b1105
Fix for runner donation array 2021-07-19 17:39:52 +02:00
Nicolai Ort 451b7fbe05 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 15:23:19 +00:00
Nicolai Ort 2a3322612d
Fixed Locale comma format
continuous-integration/drone/push Build is passing Details
2021-07-19 17:22:03 +02:00
Philipp Dormann 4b4d66ae78 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-04 11:42:27 +00:00
Philipp Dormann c935950eb0 Merge pull request 'v0.5.2: hotfix TypeError in Runner Certificate generation' (#45) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #45
2021-07-04 11:41:55 +00:00
Philipp Dormann 573b921197 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-04 11:40:08 +00:00
Philipp Dormann 274c13e358
🚀Bumped version to v0.5.2
continuous-integration/drone/push Build is passing Details
2021-07-04 13:39:06 +02:00
Philipp Dormann ff0421da2f 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-04 11:34:41 +00:00
Philipp Dormann 915baa6efa Merge branch 'bugfix/44-runner-certificates-result-in-a-status-500' into dev
continuous-integration/drone/push Build is passing Details
close #44
2021-07-04 13:33:50 +02:00
Philipp Dormann bac004d74e wrap distanceDonations.reduce in array length check
ref #44
2021-07-04 13:32:53 +02:00
Nicolai Ort b7b7f6a0ae 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 16:16:50 +00:00
Nicolai Ort 11efdebacf Merge pull request 'Release 0.5.1' (#43) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #43
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-04-22 16:16:12 +00:00
Nicolai Ort 0f2d6f58d6 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 16:14:54 +00:00
Nicolai Ort df8bd1133b Merge branch 'dev' of git.odit.services:lfk/document-server into dev
continuous-integration/drone/push Build is passing Details
2021-04-22 18:14:02 +02:00
Nicolai Ort 22fb3edd78 🚀Bumped version to v0.5.1 2021-04-22 18:13:50 +02:00
Nicolai Ort ded610f114 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 16:13:22 +00:00
Nicolai Ort a4c8dade23 Updated docker-compose example🐳
continuous-integration/drone/push Build is passing Details
2021-04-22 18:12:38 +02:00
Nicolai Ort b6fc069042 Emoji+Chinese fixes🌍 2021-04-22 18:10:32 +02:00
Nicolai Ort 60cc343adf 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 15:55:26 +00:00
Nicolai Ort 010f2046ad Added new config options to reamde
continuous-integration/drone/push Build is passing Details
2021-04-22 17:54:43 +02:00
Nicolai Ort c18cb7f135 Merge branch 'dev' of git.odit.services:lfk/document-server into dev 2021-04-22 17:50:46 +02:00
Philipp Dormann 2e7c3e8a5b 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 15:50:43 +00:00
Nicolai Ort ac9be793bd You can now configure the card's code format distinct from the others 2021-04-22 17:50:42 +02:00
Philipp Dormann c18fc4ec93 Merge branch 'dev' of https://git.odit.services/lfk/document-server into dev
continuous-integration/drone/push Build is passing Details
2021-04-22 17:49:40 +02:00
Philipp Dormann 981bae4786 more typo fixes 2021-04-22 17:49:31 +02:00
Philipp Dormann 754d0ca58c 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 15:47:59 +00:00
Philipp Dormann fa26ed6012 typo fixes
continuous-integration/drone/push Build is passing Details
2021-04-22 17:47:19 +02:00
Nicolai Ort cc4a2b4ab4 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 15:44:08 +00:00
Nicolai Ort e97e209746 Now laoding card subtitle from env
continuous-integration/drone/push Build is passing Details
2021-04-22 17:41:36 +02:00
Nicolai Ort 8f30d8933f Fixed typo in translation 2021-04-22 17:35:02 +02:00
Nicolai Ort f78037c0f1 Fixed barcode generation for runenrcard pdfs🐞 2021-04-22 17:32:36 +02:00
Nicolai Ort 3c02e13997 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-16 19:10:33 +00:00
Nicolai Ort d8f3a6ed06 Dependenc bump 🔝
continuous-integration/drone/push Build is passing Details
2021-04-16 21:09:03 +02:00
Nicolai Ort 2ee4c06055 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-11 17:49:45 +00:00
Nicolai Ort 76418f65e1 Quick callstack fix🛠
continuous-integration/drone/push Build is passing Details
2021-04-11 19:49:05 +02:00
Nicolai Ort a57e0909b9 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-31 18:16:18 +00:00
11 changed files with 411 additions and 339 deletions

View File

@ -2,8 +2,59 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v0.5.4](https://git.odit.services/lfk/document-server/compare/v0.5.1...v0.5.4)
- Merge branch 'bugfix/44-runner-certificates-result-in-a-status-500' into dev [`#44`](https://git.odit.services/lfk/document-server/issues/44)
- Fixed Locale comma format [`2a33226`](https://git.odit.services/lfk/document-server/commit/2a3322612d473bd9002cf8d6f9807f9dc7d687da)
- wrap distanceDonations.reduce in array length check [`bac004d`](https://git.odit.services/lfk/document-server/commit/bac004d74eb954d1753d4efcdb927822b89fa757)
- 🧾New changelog file version [CI SKIP] [skip ci] [`ff0421d`](https://git.odit.services/lfk/document-server/commit/ff0421da2f16a8f79f9987dabea7bdcb4ef88c05)
- 🧾New changelog file version [CI SKIP] [skip ci] [`5e92b9a`](https://git.odit.services/lfk/document-server/commit/5e92b9a48fcbb8ab6a179c53f180a7a6bd743ae7)
- Fixed decimal separator in docker [`c9cb03e`](https://git.odit.services/lfk/document-server/commit/c9cb03ea95acccd6cdc8b86c747c787938840d07)
- 🧾New changelog file version [CI SKIP] [skip ci] [`f846572`](https://git.odit.services/lfk/document-server/commit/f8465721cddfb55d51eb30d29d74ef63d825b5ac)
- Fix for runner donation array [`72303b1`](https://git.odit.services/lfk/document-server/commit/72303b11052276ad15373887f9e04183841f56f4)
- 🧾New changelog file version [CI SKIP] [skip ci] [`451b7fb`](https://git.odit.services/lfk/document-server/commit/451b7fbe0543991e8a203e38daa350a954ae0e11)
- 🧾New changelog file version [CI SKIP] [skip ci] [`573b921`](https://git.odit.services/lfk/document-server/commit/573b9211972a55df0a38742cb6eb789d6fd3717b)
- 🚀Bumped version to v0.5.4 [`4b5a862`](https://git.odit.services/lfk/document-server/commit/4b5a86282d0618a0c5b00fc796efe77db2103356)
- 🧾New changelog file version [CI SKIP] [skip ci] [`c7f5754`](https://git.odit.services/lfk/document-server/commit/c7f57548f316e1bb6635bd56bd269d80ac1e220f)
- Merge pull request 'Hotfixes' (#46) from dev into main [`8d00307`](https://git.odit.services/lfk/document-server/commit/8d003071704b5a6d7b0d70aff2f9cb05c3660b78)
- 🚀Bumped version to v0.5.3 [`01e1323`](https://git.odit.services/lfk/document-server/commit/01e1323555fe67f6f0ce3c18163e475035bd1cdd)
- 🧾New changelog file version [CI SKIP] [skip ci] [`4b4d66a`](https://git.odit.services/lfk/document-server/commit/4b4d66ae784150f7e1cc491a3fc5d84c93273aee)
- Merge pull request 'v0.5.2: hotfix TypeError in Runner Certificate generation' (#45) from dev into main [`c935950`](https://git.odit.services/lfk/document-server/commit/c935950eb052bce71185fc74c750ec77f081e7df)
- 🚀Bumped version to v0.5.2 [`274c13e`](https://git.odit.services/lfk/document-server/commit/274c13e358f16207fe8bb5cdc1b9ede0582ecb46)
- 🧾New changelog file version [CI SKIP] [skip ci] [`b7b7f6a`](https://git.odit.services/lfk/document-server/commit/b7b7f6a0ae304d24f90a3de3931f53cf08770060)
#### [v0.5.1](https://git.odit.services/lfk/document-server/compare/v0.5.0...v0.5.1)
> 22 April 2021
- Merge pull request 'Release 0.5.1' (#43) from dev into main [`11efdeb`](https://git.odit.services/lfk/document-server/commit/11efdebacf076ecfe0e10cdcda37ac07464901ce)
- Quick callstack fix🛠 [`76418f6`](https://git.odit.services/lfk/document-server/commit/76418f65e1e111e83838f0d42c541ae6a8063a09)
- Fixed barcode generation for runenrcard pdfs🐞 [`f78037c`](https://git.odit.services/lfk/document-server/commit/f78037c0f15162d5b98986edf20d263961f43e69)
- Updated docker-compose example🐳 [`a4c8dad`](https://git.odit.services/lfk/document-server/commit/a4c8dade23e448d4d4caefe304a6cd9195c873a4)
- Now laoding card subtitle from env [`e97e209`](https://git.odit.services/lfk/document-server/commit/e97e2097463f4c88947e865a38ea1e5aa2be1f5d)
- typo fixes [`fa26ed6`](https://git.odit.services/lfk/document-server/commit/fa26ed6012ded759d3702587dba67c2090324801)
- 🧾New changelog file version [CI SKIP] [skip ci] [`2ee4c06`](https://git.odit.services/lfk/document-server/commit/2ee4c060557a44db1974a015412288f7942ebe72)
- more typo fixes [`981bae4`](https://git.odit.services/lfk/document-server/commit/981bae4786a2fa12a1355122e8c5a1e95e29cf32)
- You can now configure the card's code format distinct from the others [`ac9be79`](https://git.odit.services/lfk/document-server/commit/ac9be793bd598771174f5313ef8288240306ba5c)
- 🧾New changelog file version [CI SKIP] [skip ci] [`0f2d6f5`](https://git.odit.services/lfk/document-server/commit/0f2d6f58d6a8a8888263778cf1a14c73b28e774e)
- 🚀Bumped version to v0.5.1 [`22fb3ed`](https://git.odit.services/lfk/document-server/commit/22fb3edd7836ba4ca35e6b208ab6f6620da60f4a)
- Emoji+Chinese fixes🌍 [`b6fc069`](https://git.odit.services/lfk/document-server/commit/b6fc069042dc9c5d7ec97f2660568e8e105780b9)
- 🧾New changelog file version [CI SKIP] [skip ci] [`cc4a2b4`](https://git.odit.services/lfk/document-server/commit/cc4a2b4ab4c2cb9976797f93e8348607fb88ea7d)
- Dependenc bump 🔝 [`d8f3a6e`](https://git.odit.services/lfk/document-server/commit/d8f3a6ed063a9cdf6189e85ae01a5516b4295892)
- 🧾New changelog file version [CI SKIP] [skip ci] [`a57e090`](https://git.odit.services/lfk/document-server/commit/a57e0909b919a1c720c9994b6baa8910b78dc569)
- 🧾New changelog file version [CI SKIP] [skip ci] [`ded610f`](https://git.odit.services/lfk/document-server/commit/ded610f11464a27429b8184a32554e99aed63f72)
- 🧾New changelog file version [CI SKIP] [skip ci] [`60cc343`](https://git.odit.services/lfk/document-server/commit/60cc343adf71ed3b849d1d93af3d60cbc2820fed)
- Added new config options to reamde [`010f204`](https://git.odit.services/lfk/document-server/commit/010f2046ad326898c75b6546e4d70a6f78346d8b)
- 🧾New changelog file version [CI SKIP] [skip ci] [`2e7c3e8`](https://git.odit.services/lfk/document-server/commit/2e7c3e8a5b7f6a0461254b33c6f412929719c966)
- 🧾New changelog file version [CI SKIP] [skip ci] [`754d0ca`](https://git.odit.services/lfk/document-server/commit/754d0ca58ccf8f77570ff6218f2dec61cfb4f808)
- Fixed typo in translation [`8f30d89`](https://git.odit.services/lfk/document-server/commit/8f30d8933f105b4bf112c81222a72ca1931145d7)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3c02e13`](https://git.odit.services/lfk/document-server/commit/3c02e13997b1626fb0e6496da4c58eac2cc6fcf8)
#### [v0.5.0](https://git.odit.services/lfk/document-server/compare/v0.4.3...v0.5.0) #### [v0.5.0](https://git.odit.services/lfk/document-server/compare/v0.4.3...v0.5.0)
> 31 March 2021
- Merge pull request 'Release 0.5.0' (#42) from dev into main [`a81db03`](https://git.odit.services/lfk/document-server/commit/a81db03ba3b274c44be4b4c0c318083bdeb07987)
- Added translations [`ac572f1`](https://git.odit.services/lfk/document-server/commit/ac572f1ea31cb66985e04cb5d56cc67f521e990d) - Added translations [`ac572f1`](https://git.odit.services/lfk/document-server/commit/ac572f1ea31cb66985e04cb5d56cc67f521e990d)
- Added translations [`7fea1ca`](https://git.odit.services/lfk/document-server/commit/7fea1ca78ff6fdbb38dee0edd9918eaeb1264d18) - Added translations [`7fea1ca`](https://git.odit.services/lfk/document-server/commit/7fea1ca78ff6fdbb38dee0edd9918eaeb1264d18)
- Sorted translations 🌍 [`2278e4a`](https://git.odit.services/lfk/document-server/commit/2278e4ad06947b540323856ea1e71022562ea719) - Sorted translations 🌍 [`2278e4a`](https://git.odit.services/lfk/document-server/commit/2278e4ad06947b540323856ea1e71022562ea719)
@ -23,11 +74,12 @@ All notable changes to this project will be documented in this file. Dates are d
- Added template strings [`2b21957`](https://git.odit.services/lfk/document-server/commit/2b2195727b15b8666edf0d925f2e68a98030153d) - Added template strings [`2b21957`](https://git.odit.services/lfk/document-server/commit/2b2195727b15b8666edf0d925f2e68a98030153d)
- Fixed background opacity [`2a4cfdb`](https://git.odit.services/lfk/document-server/commit/2a4cfdb2f88ad3ac1ebc925199a440756e9e9d3a) - Fixed background opacity [`2a4cfdb`](https://git.odit.services/lfk/document-server/commit/2a4cfdb2f88ad3ac1ebc925199a440756e9e9d3a)
- Now with embedded background [`64fce5b`](https://git.odit.services/lfk/document-server/commit/64fce5bd019a00bf34c1ebd133c1904bb577b67b) - Now with embedded background [`64fce5b`](https://git.odit.services/lfk/document-server/commit/64fce5bd019a00bf34c1ebd133c1904bb577b67b)
- Made footer text configureable [`63c7beb`](https://git.odit.services/lfk/document-server/commit/63c7beb8b9cdc564186c5b86a4f305c8575f5b9f) - 🧾New changelog file version [CI SKIP] [skip ci] [`7ae4750`](https://git.odit.services/lfk/document-server/commit/7ae47503076f6721d1cfd82fbf8218b9febfa580)
- 🚀Bumped version to v0.5.0 [`f623c0a`](https://git.odit.services/lfk/document-server/commit/f623c0a7cd06f707ac488456c9e8a051d3ceae46) - 🚀Bumped version to v0.5.0 [`f623c0a`](https://git.odit.services/lfk/document-server/commit/f623c0a7cd06f707ac488456c9e8a051d3ceae46)
- Merge pull request 'Generate runner certificates feature/36-runner_certificates' (#41) from feature/36-runner_certificates into dev [`d3f7d1a`](https://git.odit.services/lfk/document-server/commit/d3f7d1a6c9858d7fdf09c696622962e6f8471e78) - Merge pull request 'Generate runner certificates feature/36-runner_certificates' (#41) from feature/36-runner_certificates into dev [`d3f7d1a`](https://git.odit.services/lfk/document-server/commit/d3f7d1a6c9858d7fdf09c696622962e6f8471e78)
- disabled testing for now [`cec8930`](https://git.odit.services/lfk/document-server/commit/cec893032dea9f312e37841232a9434e19b79003) - disabled testing for now [`cec8930`](https://git.odit.services/lfk/document-server/commit/cec893032dea9f312e37841232a9434e19b79003)
- Added missing interpolations [`b43aeec`](https://git.odit.services/lfk/document-server/commit/b43aeec0cf40a9c37a10072062ab5d93102f6c81) - Added missing interpolations [`b43aeec`](https://git.odit.services/lfk/document-server/commit/b43aeec0cf40a9c37a10072062ab5d93102f6c81)
- Made footer text configureable [`63c7beb`](https://git.odit.services/lfk/document-server/commit/63c7beb8b9cdc564186c5b86a4f305c8575f5b9f)
- 🧾New changelog file version [CI SKIP] [skip ci] [`f1084b5`](https://git.odit.services/lfk/document-server/commit/f1084b59a74dcc5981fd314721c36726706f386c) - 🧾New changelog file version [CI SKIP] [skip ci] [`f1084b5`](https://git.odit.services/lfk/document-server/commit/f1084b59a74dcc5981fd314721c36726706f386c)
- disabled testing for now [`e75f151`](https://git.odit.services/lfk/document-server/commit/e75f15142e293349a071a7cdcc53cc10780304f6) - disabled testing for now [`e75f151`](https://git.odit.services/lfk/document-server/commit/e75f15142e293349a071a7cdcc53cc10780304f6)
- Removed temporary background-image fix [`5ba26c4`](https://git.odit.services/lfk/document-server/commit/5ba26c4cbfae7d3f31d3709aaeb372c14de78fa9) - Removed temporary background-image fix [`5ba26c4`](https://git.odit.services/lfk/document-server/commit/5ba26c4cbfae7d3f31d3709aaeb372c14de78fa9)

View File

@ -19,7 +19,9 @@ RUN apk add --no-cache \
ca-certificates \ ca-certificates \
ttf-freefont \ ttf-freefont \
nodejs \ nodejs \
yarn yarn \
font-noto-emoji \
&& apk add wqy-zenhei --update-cache --repository https://nl.alpinelinux.org/alpine/edge/testing
# Tell Puppeteer to skip installing Chrome. We'll be using the installed package. # Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \

View File

@ -39,6 +39,9 @@ The basic generation mechanism makes the templates and routes interchangeable (i
| DISCLAIMER_TEXT | String | N/A | A disclaimer that will get displayed on the bottom of each sponsoring contract. R/N You can only provide the disclaimer for one language. | DISCLAIMER_TEXT | String | N/A | A disclaimer that will get displayed on the bottom of each sponsoring contract. R/N You can only provide the disclaimer for one language.
| DONATIONS_FOOTER_TEXT | String | N/A | A text that will get displayed on the bottom of each runner certificate's second page. R/N You can only provide the text for one language. | DONATIONS_FOOTER_TEXT | String | N/A | A text that will get displayed on the bottom of each runner certificate's second page. R/N You can only provide the text for one language.
| CONTRACTS_PER_RUNNER | Number | 1 | The amount of contracts that get created per runner (per request). | CONTRACTS_PER_RUNNER | Number | 1 | The amount of contracts that get created per runner (per request).
| CODEFORMAT | String | code39 | The barcode format for everything except.
| CODEFORMAT_CARDS | String | code39 | The barcode format for runnercards (overwrites CODEFORMAT).
| CARD_SUBTITLE | String | Empty | A subtitle that get's displayed on the cards under the eventname.
## Templates ## Templates
> The document server uses html templates to generate various pdf documents. > The document server uses html templates to generate various pdf documents.

View File

@ -7,9 +7,12 @@ services:
environment: environment:
APP_PORT: 4010 APP_PORT: 4010
NODE_ENV: production NODE_ENV: production
EVENT_NAME: "Lauf für Kaya! 2021" EVENT_NAME: "Testen für Kaya!"
CURRENCY_SYMBOL: "€" CURRENCY_SYMBOL: "€"
API_KEY: RYRccAJ4SKZnZaEci6Nyk9Z6mw3sD94fyKJ74WNzi6hLkxGNyJDrKPkxBmPwvR4f API_KEY: NqZSYTy5AFQ7MppbLW5moqpTk7u7YrNUHKYhKYuThnnya2WpCOIU694hIZT1FzYe
CONTRACTS_PER_RUNNER: 2 CONTRACTS_PER_RUNNER: 2
SPONSORING_RECEIPT_MINIMUM_AMOUNT: 50 SPONSORING_RECEIPT_MINIMUM_AMOUNT: 42
DISCLAIMER_TEXT: "Rechtsgrundlage unserer Datenverarbeitung aufgrund freiwilliger Einwilligung ist Art. 6 Abs. 1 e), Abs. 3 DSGVO i.V.m. Art. 85 BayEUG. Mit Ihrer Unterschrift willigen Sie in unsere Datennutzung zum Zwecke des Lauf für Kaya! ein. Die Daten für Spendenquittungen" DISCLAIMER_TEXT: "Hier könnte ihre Werbung stehen"
CODEFORMAT: "code39"
CODEFORMAT_CARDS: "ean13"
CARD_SUBTITLE: "Hier könnte mehr Werbung stehen"

View File

@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-document-server", "name": "@odit/lfk-document-server",
"version": "0.5.0", "version": "0.5.4",
"description": "The document generation server for the LfK! runner system. This generates certificates, sponsoring aggreements and more", "description": "The document generation server for the LfK! runner system. This generates certificates, sponsoring aggreements and more",
"main": "src/app.ts", "main": "src/app.ts",
"scripts": { "scripts": {
@ -53,7 +53,7 @@
"handlebars": "4.7.7", "handlebars": "4.7.7",
"i18next": "20.1.0", "i18next": "20.1.0",
"i18next-fs-backend": "1.1.1", "i18next-fs-backend": "1.1.1",
"mime-types": "2.1.29", "mime-types": "2.1.30",
"pdf-lib": "1.16.0", "pdf-lib": "1.16.0",
"puppeteer": "8.0.0", "puppeteer": "8.0.0",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",

View File

@ -1,309 +1,308 @@
import axios from 'axios'; import axios from 'axios';
import cheerio from "cheerio"; import cheerio from "cheerio";
import fs from "fs"; import fs from "fs";
import Handlebars from 'handlebars'; import Handlebars from 'handlebars';
import i18next from "i18next"; import i18next from "i18next";
import Backend from 'i18next-fs-backend'; import Backend from 'i18next-fs-backend';
import mime from "mime-types"; import mime from "mime-types";
import path from 'path'; import path from 'path';
import { PDFDocument } from 'pdf-lib'; import { PDFDocument } from 'pdf-lib';
import puppeteer from "puppeteer"; import puppeteer from "puppeteer";
import { awaitAsyncHandlebarHelpers, helpers } from './asyncHelpers'; import { awaitAsyncHandlebarHelpers, helpers } from './asyncHelpers';
import { config } from './config'; import { config } from './config';
import { CertificateRunner } from './models/CertificateRunner'; import { CertificateRunner } from './models/CertificateRunner';
import { Runner } from './models/Runner'; import { Runner } from './models/Runner';
import { RunnerCard } from './models/RunnerCard'; import { RunnerCard } from './models/RunnerCard';
import { RunnerGroup } from './models/RunnerGroup'; import { RunnerGroup } from './models/RunnerGroup';
/** /**
* This class is responsible for all things pdf creation. * This class is responsible for all things pdf creation.
* This uses the html templates from src/templates. * This uses the html templates from src/templates.
*/ */
export class PdfCreator { export class PdfCreator {
private templateDir = path.join(__dirname, '/templates'); private templateDir = path.join(__dirname, '/templates');
private browser; private browser;
private static interpolations = { eventname: config.eventname, sponsoring_receipt_minimum_amount: config.sponsoring_receipt_minimum_amount, currency_symbol: config.currency_symbol } private static interpolations = { eventname: config.eventname, sponsoring_receipt_minimum_amount: config.sponsoring_receipt_minimum_amount, currency_symbol: config.currency_symbol }
private static contractsPerRunner = config.contracts_per_runner; private static contractsPerRunner = config.contracts_per_runner;
/** /**
* Main constructor. * Main constructor.
* Initializes i18n(ext), Handlebars and puppeteer. * Initializes i18n(ext), Handlebars and puppeteer.
*/ */
constructor() { constructor() {
this.init(); this.init();
} }
/** /**
* Main constructor. * Main constructor.
* Initializes i18n(ext), Handlebars and puppeteer. * Initializes i18n(ext), Handlebars and puppeteer.
*/ */
public async init() { public async init() {
const minimal_args = [ const minimal_args = [
'--autoplay-policy=user-gesture-required', '--autoplay-policy=user-gesture-required',
'--disable-background-networking', '--disable-background-networking',
'--disable-background-timer-throttling', '--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows', '--disable-backgrounding-occluded-windows',
'--disable-breakpad', '--disable-breakpad',
'--disable-client-side-phishing-detection', '--disable-client-side-phishing-detection',
'--disable-component-update', '--disable-component-update',
'--disable-default-apps', '--disable-default-apps',
'--disable-dev-shm-usage', '--disable-dev-shm-usage',
'--disable-domain-reliability', '--disable-domain-reliability',
'--disable-extensions', '--disable-extensions',
'--disable-features=AudioServiceOutOfProcess', '--disable-features=AudioServiceOutOfProcess',
'--disable-hang-monitor', '--disable-hang-monitor',
'--disable-ipc-flooding-protection', '--disable-ipc-flooding-protection',
'--disable-notifications', '--disable-notifications',
'--disable-offer-store-unmasked-wallet-cards', '--disable-offer-store-unmasked-wallet-cards',
'--disable-popup-blocking', '--disable-popup-blocking',
'--disable-print-preview', '--disable-print-preview',
'--disable-prompt-on-repost', '--disable-prompt-on-repost',
'--disable-renderer-backgrounding', '--disable-renderer-backgrounding',
'--disable-speech-api', '--disable-speech-api',
'--disable-sync', '--disable-sync',
'--hide-scrollbars', '--hide-scrollbars',
'--ignore-gpu-blacklist', '--ignore-gpu-blacklist',
'--metrics-recording-only', '--metrics-recording-only',
'--mute-audio', '--mute-audio',
'--no-default-browser-check', '--no-default-browser-check',
'--no-first-run', '--no-first-run',
'--no-pings', '--no-pings',
'--no-zygote', '--no-zygote',
'--password-store=basic', '--password-store=basic',
'--use-gl=swiftshader', '--use-gl=swiftshader',
'--no-sandbox' '--no-sandbox'
]; ];
await i18next await i18next
.use(Backend) .use(Backend)
.init({ .init({
fallbackLng: 'en', fallbackLng: 'en',
lng: 'en', lng: 'en',
backend: { backend: {
loadPath: path.join(__dirname, '/locales/{{lng}}.json') loadPath: path.join(__dirname, '/locales/{{lng}}.json')
} }
}); });
await Handlebars.registerHelper(helpers); await Handlebars.registerHelper(helpers);
await Handlebars.registerHelper('__', await Handlebars.registerHelper('__',
function (str) { function (str) {
return i18next.t(str, PdfCreator.interpolations).toString(); return i18next.t(str, PdfCreator.interpolations).toString();
} }
); );
await Handlebars.registerHelper('--sponsor', await Handlebars.registerHelper('--sponsor',
function (str) { function (str) {
const index = (parseInt(str) % config.sponor_logos.length); const index = (parseInt(str) % config.sponor_logos.length);
if (isNaN(index)) { if (isNaN(index)) {
return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==" return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="
} }
return config.sponor_logos[index]; return config.sponor_logos[index];
} }
); );
await Handlebars.registerHelper('--format_kilometers', await Handlebars.registerHelper('--format_kilometers',
function (str) { function (str) {
let meters = parseInt(str); let meters = parseInt(str);
return ((meters / 1000).toFixed(3).toString()) return ((meters / 1000).toLocaleString("en-EN", { minimumFractionDigits: 1, maximumFractionDigits: 3 }).replace(".", ","));
} }
); );
await Handlebars.registerHelper('--format_currency', await Handlebars.registerHelper('--format_currency',
function (str) { function (str) {
let meters = parseInt(str); let meters = parseInt(str);
return ((meters / 100).toFixed(2).toString()) return ((meters / 100).toLocaleString("en-EN", { minimumFractionDigits: 2, maximumFractionDigits: 2 }).replace(".", ","));
} }
); );
this.browser = await puppeteer.launch({ headless: true, args: minimal_args }); this.browser = await puppeteer.launch({ headless: true, args: minimal_args });
} }
/** /**
* Generate sponsoring contract pdfs. * Generate sponsoring contract pdfs.
* @param runner The runner you want to generate the contracts for. * @param runner The runner you want to generate the contracts for.
* @param locale The locale used for the contracts (default:en) * @param locale The locale used for the contracts (default:en)
*/ */
public async generateSponsoringContract(runners: Runner[], locale: string = "en", codeformat: string = config.codeformat): Promise<Buffer> { public async generateSponsoringContract(runners: Runner[], locale: string = "en", codeformat: string = config.codeformat): Promise<Buffer> {
if (runners.length == 1 && Object.keys(runners[0]).length == 0) { if (runners.length == 1 && Object.keys(runners[0]).length == 0) {
runners[0] = this.generateEmptyRunner(); runners[0] = this.generateEmptyRunner();
} }
for (var i = 1; i < PdfCreator.contractsPerRunner; i++) { if (runners.length > 50) {
runners = runners.reduce(function (res, current, index, array) { let pdf_promises = new Array<Promise<Buffer>>();
return res.concat([current, current]); let i, j;
}, []); for (i = 0, j = runners.length; i < j; i += 50) {
} let chunk = runners.slice(i, i + 50);
if (runners.length > 50) { pdf_promises.push(this.generateSponsoringContract(chunk, locale));
let pdf_promises = new Array<Promise<Buffer>>(); }
let i, j; const pdfs = await Promise.all(pdf_promises);
for (i = 0, j = runners.length; i < j; i += 50) { return await this.mergePdfs(pdfs);
let chunk = runners.slice(i, i + 50); }
pdf_promises.push(this.generateSponsoringContract(chunk, locale)); for (var i = 1; i < PdfCreator.contractsPerRunner; i++) {
} runners = runners.reduce(function (res, current, index, array) {
const pdfs = await Promise.all(pdf_promises); return res.concat([current, current]);
return await this.mergePdfs(pdfs); }, []);
} }
await i18next.changeLanguage(locale); await i18next.changeLanguage(locale);
const template_source = fs.readFileSync(`${this.templateDir}/sponsoring_contract.html`, 'utf8'); const template_source = fs.readFileSync(`${this.templateDir}/sponsoring_contract.html`, 'utf8');
const template = Handlebars.compile(template_source); const template = Handlebars.compile(template_source);
let result = template({ runners, codeformat, disclaimer: config.disclaimer_text }); let result = template({ runners, codeformat, disclaimer: config.disclaimer_text });
result = await awaitAsyncHandlebarHelpers(result); result = await awaitAsyncHandlebarHelpers(result);
const pdf = await this.renderPdf(result, { format: "A5", landscape: true }); const pdf = await this.renderPdf(result, { format: "A5", landscape: true });
return pdf return pdf
} }
/** /**
* Generate runner card pdfs. * Generate runner card pdfs.
* @param cards The runner cars you want to generate the cards for. * @param cards The runner cars you want to generate the cards for.
* @param locale The locale used for the cards (default:en) * @param locale The locale used for the cards (default:en)
*/ */
public async generateRunnerCards(cards: RunnerCard[], locale: string = "en", codeformat: string = config.codeformat): Promise<Buffer> { public async generateRunnerCards(cards: RunnerCard[], locale: string = "en", codeformat: string = config.codeformat_cards): Promise<Buffer> {
if (cards.length > 10) { if (cards.length > 10) {
let pdf_promises = new Array<Promise<Buffer>>(); let pdf_promises = new Array<Promise<Buffer>>();
let i, j; let i, j;
for (i = 0, j = cards.length; i < j; i += 10) { for (i = 0, j = cards.length; i < j; i += 10) {
let chunk = cards.slice(i, i + 10); let chunk = cards.slice(i, i + 10);
pdf_promises.push(this.generateRunnerCards(chunk, locale)); pdf_promises.push(this.generateRunnerCards(chunk, locale, codeformat));
} }
const pdfs = await Promise.all(pdf_promises); const pdfs = await Promise.all(pdf_promises);
return await this.mergePdfs(pdfs); return await this.mergePdfs(pdfs);
} }
const cards_swapped = this.swapArrayPairs(cards); const cards_swapped = this.swapArrayPairs(cards);
await i18next.changeLanguage(locale); await i18next.changeLanguage(locale);
const template_source = fs.readFileSync(`${this.templateDir}/runner_card.html`, 'utf8'); const template_source = fs.readFileSync(`${this.templateDir}/runner_card.html`, 'utf8');
const template = Handlebars.compile(template_source); const template = Handlebars.compile(template_source);
let result = template({ cards, cards_swapped, eventname: config.eventname, codeformat: codeformat }) let result = template({ cards, cards_swapped, eventname: config.eventname, codeformat: codeformat, card_subtitle: config.card_subtitle })
result = await awaitAsyncHandlebarHelpers(result); result = await awaitAsyncHandlebarHelpers(result);
const pdf = await this.renderPdf(result, { format: "A4", landscape: false }); const pdf = await this.renderPdf(result, { format: "A4", landscape: false });
return pdf return pdf
} }
/** /**
* Generate sponsoring contract pdfs. * Generate sponsoring contract pdfs.
* @param runner The runner you want to generate the contracts for. * @param runner The runner you want to generate the contracts for.
* @param locale The locale used for the contracts (default:en) * @param locale The locale used for the contracts (default:en)
*/ */
public async generateRunnerCertficates(runners: CertificateRunner[], locale: string = "en"): Promise<Buffer> { public async generateRunnerCertficates(runners: CertificateRunner[], locale: string = "en"): Promise<Buffer> {
if (runners.length > 50) { if (runners.length > 50) {
let pdf_promises = new Array<Promise<Buffer>>(); let pdf_promises = new Array<Buffer>();
let i, j; let i, j;
for (i = 0, j = runners.length; i < j; i += 50) { for (i = 0, j = runners.length; i < j; i += 50) {
let chunk = runners.slice(i, i + 50); let chunk = runners.slice(i, i + 50);
pdf_promises.push(this.generateRunnerCertficates(chunk, locale)); pdf_promises.push(await this.generateRunnerCertficates(chunk, locale));
} }
const pdfs = await Promise.all(pdf_promises); return await this.mergePdfs(pdf_promises);
return await this.mergePdfs(pdfs); }
} await i18next.changeLanguage(locale);
await i18next.changeLanguage(locale); const template_source = fs.readFileSync(`${this.templateDir}/runner_certificate.html`, 'utf8');
const template_source = fs.readFileSync(`${this.templateDir}/runner_certificate.html`, 'utf8'); const template = Handlebars.compile(template_source);
const template = Handlebars.compile(template_source); let result = template({ runners, eventname: config.eventname, currency_symbol: config.currency_symbol, donations_footer_text: config.donations_footer_text });
let result = template({ runners, eventname: config.eventname, currency_symbol: config.currency_symbol, donations_footer_text: config.donations_footer_text }); result = await awaitAsyncHandlebarHelpers(result);
result = await awaitAsyncHandlebarHelpers(result); const pdf = await this.renderPdf(result, { format: "A4", landscape: false, printBackground: true });
const pdf = await this.renderPdf(result, { format: "A4", landscape: false, printBackground: true }); return pdf;
return pdf; }
}
/**
/** * Converts all images in html to base64.
* Converts all images in html to base64. * Works with image files in the template directory or images from urls.
* Works with image files in the template directory or images from urls. * @param html The html string whoms images shall get replaced.
* @param html The html string whoms images shall get replaced. */
*/ public async imgToBase64(html): Promise<string> {
public async imgToBase64(html): Promise<string> { const $ = cheerio.load(html)
const $ = cheerio.load(html)
$('img').each(async (index, element) => {
$('img').each(async (index, element) => { let imgsrc = $(element).attr("src");
let imgsrc = $(element).attr("src"); if (imgsrc.startsWith("data:image")) {
if (imgsrc.startsWith("data:image")) { return;
return; }
} const img_type = mime.lookup(imgsrc);
const img_type = mime.lookup(imgsrc);
if (!(img_type.includes("image"))) {
if (!(img_type.includes("image"))) { throw new Error("File is not image mime type");
throw new Error("File is not image mime type"); }
}
let image;
let image; if (imgsrc.startsWith("http")) {
if (imgsrc.startsWith("http")) { image = (await axios.get(imgsrc)).data;
image = (await axios.get(imgsrc)).data; image = Buffer.from(image).toString('base64');
image = Buffer.from(image).toString('base64'); }
} else {
else { if (imgsrc.startsWith("./")) {
if (imgsrc.startsWith("./")) { imgsrc = imgsrc.replace("./", "");
imgsrc = imgsrc.replace("./", ""); }
} image = fs.readFileSync(`${this.templateDir}/${imgsrc}`, { encoding: "base64" });
image = fs.readFileSync(`${this.templateDir}/${imgsrc}`, { encoding: "base64" }); }
}
image = `data:${img_type};base64,${image}`
image = `data:${img_type};base64,${image}` $(element).attr("src", image)
$(element).attr("src", image) });
});
return $.html();
return $.html(); }
}
/**
/** * This method manages the creation of pdfs via puppeteer.
* This method manages the creation of pdfs via puppeteer. * @param html The HTML that should get rendered.
* @param html The HTML that should get rendered. * @param options Puppeteer PDF option (eg: {format: "A4"})
* @param options Puppeteer PDF option (eg: {format: "A4"}) */
*/ public async renderPdf(html: string, options): Promise<any> {
public async renderPdf(html: string, options): Promise<any> { html = await this.imgToBase64(html);
html = await this.imgToBase64(html); let page = await this.browser.newPage();
let page = await this.browser.newPage(); await page.setContent(html);
await page.setContent(html); const pdf = await page.pdf(options);
const pdf = await page.pdf(options); await page.close();
await page.close(); return pdf;
return pdf; }
}
/**
/** * Merges multiple pdfs into one.
* Merges multiple pdfs into one. * @param pdfs The pdfs you want to merge as an buffer array.
* @param pdfs The pdfs you want to merge as an buffer array. * @returns The merged pdf as a buffer.
* @returns The merged pdf as a buffer. */
*/ private async mergePdfs(pdfs: Buffer[]): Promise<Buffer> {
private async mergePdfs(pdfs: Buffer[]): Promise<Buffer> { const mergedPdf = await PDFDocument.create();
const mergedPdf = await PDFDocument.create();
for (const pdfBuffer of pdfs) {
for (const pdfBuffer of pdfs) { const pdf = await PDFDocument.load(pdfBuffer);
const pdf = await PDFDocument.load(pdfBuffer); const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices()); copiedPages.forEach((page) => {
copiedPages.forEach((page) => { mergedPdf.addPage(page);
mergedPdf.addPage(page); });
}); }
}
return <Buffer>(await mergedPdf.save());
return <Buffer>(await mergedPdf.save()); }
}
/**
/** * Generates a new dummy runner with halfspaces for all strings.
* Generates a new dummy runner with halfspaces for all strings. * Can be used to generate empty sponsoring contracts.
* Can be used to generate empty sponsoring contracts. * @returns A new runner object that apears to be empty.
* @returns A new runner object that apears to be empty. */
*/ private generateEmptyRunner(): Runner {
private generateEmptyRunner(): Runner { let group = new RunnerGroup();
let group = new RunnerGroup(); group.id = 0;
group.id = 0; group.name = "";
group.name = ""; let runner = new Runner();
let runner = new Runner(); runner.id = 0;
runner.id = 0; runner.firstname = "";
runner.firstname = ""; runner.lastname = "";
runner.lastname = ""; runner.group = group;
runner.group = group; return runner;
return runner; }
}
/**
/** * Swaps pairs (0/1, 2/3, ...) of elements in an array recursively.
* Swaps pairs (0/1, 2/3, ...) of elements in an array recursively. * If the last element has no partner it inserts an empty element at the end and swaps the two
* If the last element has no partner it inserts an empty element at the end and swaps the two * This is needed to generate pdfs with front- and backside that get printet on one paper.
* This is needed to generate pdfs with front- and backside that get printet on one paper. * @param array The array which's pairs shall get switched.
* @param array The array which's pairs shall get switched. * @returns Array with swapped pairs,
* @returns Array with swapped pairs, */
*/ private swapArrayPairs(array): Array<any> {
private swapArrayPairs(array): Array<any> { if (array.length == 1) {
if (array.length == 1) { return [null, array[0]];
return [null, array[0]]; }
} if (array.length == 0) {
if (array.length == 0) { return null;
return null; }
}
const rest = this.swapArrayPairs(array.slice(2))
const rest = this.swapArrayPairs(array.slice(2)) if (!rest) {
if (!rest) { return [array[1], array[0]]
return [array[1], array[0]] }
} return [array[1], array[0]].concat(rest);
return [array[1], array[0]].concat(rest); }
}
} }

View File

@ -9,12 +9,14 @@ export const config = {
eventname: process.env.EVENT_NAME || "Please set the event name", eventname: process.env.EVENT_NAME || "Please set the event name",
currency_symbol: process.env.CURRENCY_SYMBOL || "€", currency_symbol: process.env.CURRENCY_SYMBOL || "€",
sponsoring_receipt_minimum_amount: process.env.SPONSORING_RECEIPT_MINIMUM_AMOUNT || "10", sponsoring_receipt_minimum_amount: process.env.SPONSORING_RECEIPT_MINIMUM_AMOUNT || "10",
codeformat: process.env.CODEFORMAT || "qrcode", codeformat: process.env.CODEFORMAT || "code39",
codeformat_cards: process.env.CODEFORMAT_CARDS || process.env.CODEFORMAT || "code39",
sponor_logos: getSponsorLogos(), sponor_logos: getSponsorLogos(),
api_key: getApiKey(), api_key: getApiKey(),
disclaimer_text: process.env.DISCLAIMER_TEXT || "", disclaimer_text: process.env.DISCLAIMER_TEXT || "",
donations_footer_text: process.env.DONATIONS_FOOTER_TEXT || "", donations_footer_text: process.env.DONATIONS_FOOTER_TEXT || "",
contracts_per_runner: parseInt(process.env.CONTRACTS_PER_RUNNER) || 1, contracts_per_runner: parseInt(process.env.CONTRACTS_PER_RUNNER) || 1,
card_subtitle: process.env.CARD_SUBTITLE || ""
} }
let errors = 0 let errors = 0
if (typeof config.internal_port !== "number") { if (typeof config.internal_port !== "number") {

View File

@ -38,7 +38,7 @@ export class PdfController {
@Post('/cards') @Post('/cards')
@OpenAPI({ description: "Generate runner card pdfs from runner card objects.<br>You can choose your prefered locale by passing the 'locale' query-param." }) @OpenAPI({ description: "Generate runner card pdfs from runner card objects.<br>You can choose your prefered locale by passing the 'locale' query-param." })
async generateCards(@Body({ validate: true, options: { limit: "500mb" } }) cards: RunnerCard | RunnerCard[], @Res() res: any, @QueryParam("locale") locale: string, @QueryParam("download") download: boolean) { async generateCards(@Body({ validate: true, options: { limit: "500mb" } }) cards: RunnerCard | RunnerCard[], @Res() res: any, @QueryParam("locale") locale: string, @QueryParam("codeformat") codeformat: string, @QueryParam("download") download: boolean) {
if (!this.initialized) { if (!this.initialized) {
await this.pdf.init(); await this.pdf.init();
this.initialized = true; this.initialized = true;
@ -47,7 +47,7 @@ export class PdfController {
cards = [cards]; cards = [cards];
} }
cards = this.mapCardGroupNames(cards); cards = this.mapCardGroupNames(cards);
const contracts = await this.pdf.generateRunnerCards(cards, locale); const contracts = await this.pdf.generateRunnerCards(cards, locale, codeformat);
res.setHeader('content-type', 'application/pdf'); res.setHeader('content-type', 'application/pdf');
if (download) { if (download) {
res.setHeader('Content-Disposition', 'attachment; filename="cards.pdf"') res.setHeader('Content-Disposition', 'attachment; filename="cards.pdf"')
@ -97,12 +97,21 @@ export class PdfController {
else { else {
runner.group.fullName = `${runner.group.parentGroup.name}/${runner.group.name}`; runner.group.fullName = `${runner.group.parentGroup.name}/${runner.group.name}`;
} }
runner.donationPerDistanceTotal = runner.distanceDonations.reduce(function (sum, current) { runner.donationPerDistanceTotal = 0;
return sum + current.amountPerDistance; if (!Array.isArray(runner.distanceDonations)){
}, 0); runner.distanceDonations = [].concat(runner.distanceDonations)
runner.donationTotal = runner.distanceDonations.reduce(function (sum, current) { }
return sum + current.amount; if (runner.distanceDonations.length > 0) {
}, 0); runner.donationPerDistanceTotal += runner.distanceDonations.reduce(function (sum, current) {
return sum + current.amountPerDistance;
}, 0);
}
runner.donationTotal = 0;
if (runner.distanceDonations.length > 0) {
runner.donationTotal += runner.distanceDonations.reduce(function (sum, current) {
return sum + current.amount;
}, 0);
}
response.push(runner) response.push(runner)
} }
return response; return response;

View File

@ -1,18 +1,19 @@
{ {
"address": "Adresse", "address": "Adresse",
"betrag-km": "Betrag/KM", "betrag-km": "Betrag/ km",
"city": "Stadt", "city": "Stadt",
"date": "Datum", "date": "Datum",
"firstname": "Vorname", "firstname": "Vorname",
"fuer-den-guten-zweck-zurueckgelegt": "für den guten Zweck zurückgelegt", "fuer-den-guten-zweck-zurueckgelegt": "für den guten Zweck zurückgelegt",
"gesamt": "Gesamt", "gesamt": "Gesamt",
"gesamtbetrag": "Gesamtbetrag", "gesamtbetrag": "Gesamtbetrag",
"group": "Team/Klasse", "group": "Team/ Klasse",
"hat-beim-eventname": "Hat beim {{eventname}}", "hat-beim-eventname": "Hat beim {{eventname}}",
"house_number": "Hausnummer", "house_number": "Hausnummer",
"id": "ID", "id": "ID",
"lastname": "Nachname", "lastname": "Nachname",
"location": "Ort", "location": "Ort",
"mit_unterstuetzung_von": "Mit Unterstützung von:",
"please_use_blockletters": "Bitte in DRUCKBUCHSTABEN schreiben", "please_use_blockletters": "Bitte in DRUCKBUCHSTABEN schreiben",
"postalcode": "Postleitzahl", "postalcode": "Postleitzahl",
"signature": "Unterschrift", "signature": "Unterschrift",
@ -20,7 +21,7 @@
"sponsor-in": "Sponsor:in", "sponsor-in": "Sponsor:in",
"sponsoring_address_condition": "Muss ausgefüllt werden, wenn Sie eine Spendenquittung benötigen - Spendenquittungen können erst ab einem Gesamtbetrag von {{sponsoring_receipt_minimum_amount}}{{currency_symbol}} ausgestellt werden", "sponsoring_address_condition": "Muss ausgefüllt werden, wenn Sie eine Spendenquittung benötigen - Spendenquittungen können erst ab einem Gesamtbetrag von {{sponsoring_receipt_minimum_amount}}{{currency_symbol}} ausgestellt werden",
"sponsoring_amount_per_distance": "mit einem Betrag von _____{{currency_symbol}} pro gelaufenem Kilometer zu unterstützen.", "sponsoring_amount_per_distance": "mit einem Betrag von _____{{currency_symbol}} pro gelaufenem Kilometer zu unterstützen.",
"sponsoring_subtitle": "Ich/Wir sind bereit anlässlich des {{eventname}}", "sponsoring_subtitle": "Ich bin/ Wir sind bereit anlässlich des {{eventname}}",
"sponsoring_title": "Sponsoringerklärung", "sponsoring_title": "Sponsoringerklärung",
"sponsorings": "Sponsorings", "sponsorings": "Sponsorings",
"street": "Straße", "street": "Straße",

View File

@ -1,18 +1,19 @@
{ {
"address": "Address", "address": "Address",
"betrag-km": "Amount/KM", "betrag-km": "Amount/ km",
"city": "City", "city": "City",
"date": "date", "date": "date",
"firstname": "First name", "firstname": "First name",
"fuer-den-guten-zweck-zurueckgelegt": "for our good cuse at the {{eventname}}", "fuer-den-guten-zweck-zurueckgelegt": "for our good cause at the {{eventname}}",
"gesamt": "Combined", "gesamt": "Combined",
"gesamtbetrag": "Total", "gesamtbetrag": "Total",
"group": "Team/class", "group": "Team/ class",
"hat-beim-eventname": "Ran", "hat-beim-eventname": "Ran",
"house_number": "House number", "house_number": "House number",
"id": "ID", "id": "ID",
"lastname": "Last name", "lastname": "Last name",
"location": "Location", "location": "Location",
"mit_unterstuetzung_von": "Supported by:",
"please_use_blockletters": "Please write in BLOCK LETTERS.", "please_use_blockletters": "Please write in BLOCK LETTERS.",
"postalcode": "Postal code", "postalcode": "Postal code",
"signature": "Signature", "signature": "Signature",
@ -20,9 +21,9 @@
"sponsor-in": "Donor", "sponsor-in": "Donor",
"sponsoring_address_condition": "You have to provide an address if you want a donation receipt - Donation receipts can't be issued for total donation amounts under {{sponsoring_receipt_minimum_amount}}{{currency_symbol}}", "sponsoring_address_condition": "You have to provide an address if you want a donation receipt - Donation receipts can't be issued for total donation amounts under {{sponsoring_receipt_minimum_amount}}{{currency_symbol}}",
"sponsoring_amount_per_distance": "with the amount of _____{{currency_symbol}} per kilometer run.", "sponsoring_amount_per_distance": "with the amount of _____{{currency_symbol}} per kilometer run.",
"sponsoring_subtitle": "On the ocation of the {{eventname}} I/We want to support", "sponsoring_subtitle": "On the occasion of the {{eventname}} I/We want to support",
"sponsoring_title": "Sponsoring contract", "sponsoring_title": "Sponsoring contract",
"sponsorings": "Donations", "sponsorings": "Donations",
"street": "Street", "street": "Street",
"urkunde": "Certifcate" "urkunde": "Certificate"
} }

View File

@ -33,18 +33,18 @@
{{#each cards}} {{#each cards}}
<div class="column is-half runnercard"> <div class="column is-half runnercard">
<p class="title is-5" style="text-align: center; padding-bottom: 0; margin-top: -0.75rem;">{{../eventname}}</p> <p class="title is-5" style="text-align: center; padding-bottom: 0; margin-top: -0.75rem;">{{../eventname}}</p>
<p style="text-align: center; margin-top: -1.5rem; font-size: small;">lauf-fuer-kaya.de - am 01.01.2021</p> <p style="text-align: center; margin-top: -1.5rem; font-size: small;">{{../card_subtitle}}</p>
<p style="font-size: small;">Mit unterstützung von:</p> <p style="font-size: small;">{{__ "mit_unterstuetzung_von"}}</p>
<div class="columns" style="height: 6rem; overflow: hidden;"> <div class="columns" style="height: 6rem; overflow: hidden;">
<div class="column is-two-thirds"> <div class="column is-half">
<!--SPONSOR LOGO HERE--> <!--SPONSOR LOGO HERE-->
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;" <img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
src="{{--sponsor this.id}}" /> src="{{--sponsor this.id}}" />
</div> </div>
<div class="column is-one-third"> <div class="column is-half">
<!--BARCODE HERE--> <!--BARCODE HERE-->
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;" <img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
src="{{--bc this.id ../codeformat}}" /> src="{{--bc this.code ../codeformat}}" />
</div> </div>
</div> </div>
<p>{{this.runner.lastname}}, {{this.runner.firstname}} {{this.runner.middlename}}</p> <p>{{this.runner.lastname}}, {{this.runner.firstname}} {{this.runner.middlename}}</p>
@ -61,7 +61,7 @@
<div style="height: 2cm; padding: 0 0 2.25cm 0"> <div style="height: 2cm; padding: 0 0 2.25cm 0">
<img style="object-fit: cover; max-height: 2cm;" src="{{--sponsor this.id}}" /> <img style="object-fit: cover; max-height: 2cm;" src="{{--sponsor this.id}}" />
</div> </div>
<img style="object-fit: cover; max-height: 2.5cm; position: relative;" src="{{--bc this.id ../codeformat}}" /> <img style="object-fit: cover; max-height: 2.5cm; position: relative;" src="{{--bc this.code ../codeformat}}" />
</div> </div>
{{/each}} {{/each}}
</div> </div>