Compare commits

..

506 commits

Author SHA1 Message Date
55f6e0f8dd
dockerize 2024-08-02 16:40:32 +02:00
dc4f6ca9e1
Merge remote-tracking branch 'upstream/master' 2024-08-02 16:40:08 +02:00
renovate[bot]
59cfbe16b6 Update dependency sass to v1.77.0 2024-05-10 17:09:16 +00:00
renovate[bot]
eb251062c2 Update dependency release-it to v17.2.1 2024-05-10 14:31:10 +00:00
renovate[bot]
1682dec357 Update dependency qunit-dom to v3.1.2 2024-05-10 10:50:14 +00:00
renovate[bot]
81b554587c Update dependency ember-source to v5.8.0 2024-05-10 07:43:00 +00:00
renovate[bot]
876237138a Update dependency ember-cli to v5.8.1 2024-05-10 03:20:30 +00:00
renovate[bot]
538b61a43b Update Node.js to v20.13.1 2024-05-10 01:44:20 +00:00
renovate[bot]
e15dc9a9c5 Update dependency typescript to v5.4.5 2024-05-09 21:54:08 +00:00
renovate[bot]
c1eeb8a87e Update dependency sinon to v17.0.2 2024-05-09 19:50:54 +00:00
renovate[bot]
87a786a0ca Update dependency ember-intl to v6.5.4 2024-05-09 15:10:26 +00:00
renovate[bot]
f02e7718b6 Update dependency ember-bootstrap to v6.3.1 2024-05-09 12:44:33 +00:00
renovate[bot]
8fb9f106ac Update dependency @babel/core to v7.24.5 2024-05-09 10:46:04 +00:00
Jeldrik Hanschke
6a1933ac9e
run tests with PHP 8.3 (#992) 2024-04-11 20:02:20 +02:00
renovate[bot]
cf209e2cc7 Update dependency ember-bootstrap to v6.3.0 2024-04-05 01:27:46 +00:00
renovate[bot]
520d06bb52 Update dependency typescript to v5.4.4 2024-04-04 23:43:34 +00:00
renovate[bot]
b25754edcd Update dependency sass to v1.74.1 2024-04-04 06:20:23 +00:00
renovate[bot]
83ec7b88aa Update dependency @babel/core to v7.24.4 2024-04-03 22:46:58 +00:00
renovate[bot]
679f565281 Update Node.js to v20.12.1 2024-04-03 18:09:57 +00:00
renovate[bot]
02d536221a Update dependency stylelint-config-standard-scss to v13.1.0 2024-04-03 04:06:36 +00:00
renovate[bot]
f09ca18be6 Update typescript-eslint monorepo to v7.5.0 2024-04-02 01:28:35 +00:00
renovate[bot]
3d623bd54e Update dependency @tsconfig/ember to v3.0.6 2024-03-27 10:30:45 +00:00
renovate[bot]
0278900b64 Update Node.js to v20.12.0 2024-03-26 21:55:27 +00:00
renovate[bot]
0a84146362 Update dependency stylelint to v16.3.1 2024-03-26 16:03:27 +00:00
renovate[bot]
308b067981 Update typescript-eslint monorepo to v7.4.0 2024-03-25 21:07:17 +00:00
renovate[bot]
a00ffa30d7 Update dependency @glint/environment-ember-loose to v1.4.0 2024-03-23 08:06:58 +00:00
renovate[bot]
729003c4e5 Update dependency @glint/template to v1.4.0 2024-03-23 05:06:06 +00:00
renovate[bot]
e91b5eeef3 Update dependency @glint/core to v1.4.0 2024-03-23 02:58:32 +00:00
renovate[bot]
2ff1efb846 Update dependency webpack to v5.91.0 2024-03-21 00:29:30 +00:00
renovate[bot]
088bfb45c2 Update dependency typescript to v5.4.3 2024-03-20 23:45:07 +00:00
renovate[bot]
8ce32e525f Update dependency @babel/core to v7.24.3 2024-03-20 20:02:17 +00:00
renovate[bot]
179f2ce4c5 Update dependency @babel/core to v7.24.1 2024-03-19 14:06:45 +00:00
renovate[bot]
c6350dfd2b Update typescript-eslint monorepo to v7.3.1 2024-03-19 00:51:08 +00:00
renovate[bot]
e99648ff53 Update dependency ember-cli-page-object to v2.3.0 2024-03-16 22:24:11 +00:00
renovate[bot]
36745e60fe Update dependency sass to v1.72.0 2024-03-14 02:00:47 +00:00
renovate[bot]
f593aa21bc Update dependency ember-page-title to v8.2.3 2024-03-12 08:16:55 +00:00
renovate[bot]
92ef17cdee Update typescript-eslint monorepo to v7.2.0 2024-03-11 22:04:57 +00:00
renovate[bot]
a1dc977a09 Update dependency ember-intl to v6.5.3 2024-03-11 18:12:55 +00:00
renovate[bot]
e81c89c8f7 Update dependency ember-intl to v6.5.2 2024-03-08 18:35:38 +00:00
renovate[bot]
d635c4b555 Update embroider monorepo to v3.4.6 2024-03-07 22:34:10 +00:00
renovate[bot]
b4db2389cb Update dependency typescript to v5.4.2 2024-03-06 23:33:08 +00:00
renovate[bot]
0f104875ef Update dependency @tsconfig/ember to v3.0.5 2024-03-06 08:31:41 +00:00
renovate[bot]
81fde80af8 Update dependency ember-cli to v5.7.0 2024-03-06 00:18:10 +00:00
renovate[bot]
c1e802672d Update dependency ember-source to v5.7.0 2024-03-05 00:29:56 +00:00
renovate[bot]
fc3d86859f Update typescript-eslint monorepo to v7.1.1 2024-03-04 22:16:26 +00:00
renovate[bot]
d30a6dfd0b Update typescript-eslint monorepo to v7.1.0 2024-03-02 13:26:37 +00:00
renovate[bot]
798b95a747 Update dependency @babel/core to v7.24.0 2024-03-02 09:28:25 +00:00
renovate[bot]
1da01c7a2b Update embroider monorepo to v3.4.5 2024-03-02 06:41:03 +00:00
renovate[bot]
8c576ce579 Update dependency ember-cli-mirage to v3.0.3 2024-03-02 03:36:15 +00:00
renovate[bot]
b04e797da9 Update dependency @tsconfig/ember to v3.0.4 2024-03-02 00:04:00 +00:00
renovate[bot]
7903c5115a Update dependency ember-intl to v6.5.1 2024-03-01 21:36:09 +00:00
renovate[bot]
01f1534b7e Update dependency eslint to v8.57.0 2024-02-24 00:25:31 +00:00
renovate[bot]
1263ec21fe Update dependency ember-cli-page-object to v2.2.3 2024-02-23 03:23:44 +00:00
renovate[bot]
c163b57855 Update dependency @ember/optional-features to v2.1.0 2024-02-21 21:06:13 +00:00
renovate[bot]
0ba7c4cdb8 Update dependency ember-intl to v6.4.1 2024-02-21 11:02:44 +00:00
renovate[bot]
80ce1dab9a Update dependency sass to v1.71.1 2024-02-21 07:52:26 +00:00
renovate[bot]
7f3874c302 Update dependency @types/ember__template to v4.0.7 2024-02-21 00:33:18 +00:00
renovate[bot]
39881a46e7 Update typescript-eslint monorepo to v7.0.2 2024-02-19 22:19:44 +00:00
renovate[bot]
f732447ee5 Update dependency webpack to v5.90.3 2024-02-19 19:42:05 +00:00
renovate[bot]
ad72196e04 Update dependency release-it to v17.1.1 2024-02-19 03:59:49 +00:00
renovate[bot]
6819e4b2c7 Update dependency release-it to v17.1.0 2024-02-18 21:29:02 +00:00
renovate[bot]
a1ef336e93
Update dependency webpack-subresource-integrity-embroider to v0.1.3 (#930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-18 12:12:16 +01:00
renovate[bot]
b22b3f511e
Update dependency webpack-subresource-integrity-embroider to v0.1.2 (#927)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-17 23:44:42 +01:00
Jeldrik Hanschke
fb61a07934
use existing shortcuts for date and time format (#929) 2024-02-17 23:44:26 +01:00
Jeldrik Hanschke
3f038097e0
delete empty controllers (#928)
* delete empty controllers

* delete boilerplate controller test
2024-02-17 23:44:13 +01:00
renovate[bot]
5a624f6b20
Pin dependencies (#923)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-17 14:09:09 +01:00
Jeldrik Hanschke
f1fc0a303b
remove deprecation warning for IE11 (#925) 2024-02-17 14:03:52 +01:00
Jeldrik Hanschke
d2d3c100d5
drop jsdom dependency introduced by mistake (#924) 2024-02-17 14:00:58 +01:00
renovate[bot]
1451ea9026 Update dependency ember-source to v5.6.0 2024-02-17 12:58:44 +00:00
Jeldrik Hanschke
f861b5ffea
Adopt Embroider (#845) 2024-02-17 13:52:09 +01:00
renovate[bot]
f8da2e787b Update dependency ember-bootstrap to v6.2.0 2024-02-17 12:28:46 +00:00
renovate[bot]
fb93e061e6
Update typescript-eslint monorepo to v7 (#914)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-17 12:32:09 +01:00
renovate[bot]
6547b0b5af Update dependency sass to v1.71.0 2024-02-16 06:57:57 +00:00
renovate[bot]
dc1fe01479 Update dependency qunit to v2.20.1 2024-02-16 04:03:23 +00:00
renovate[bot]
01c7fb266b Update dependency webpack to v5.90.2 2024-02-15 21:16:24 +00:00
renovate[bot]
aa4c2001ab Update dependency ember-page-title to v8.2.2 2024-02-15 18:08:16 +00:00
renovate[bot]
885f85f9f5 Update dependency release-it to v17.0.5 2024-02-15 01:44:05 +00:00
renovate[bot]
c955fd2fef Update Node.js to v20.11.1 2024-02-14 21:22:10 +00:00
renovate[bot]
825462e874 Update dependency @types/ember__routing to v4.0.22 2024-02-13 00:08:42 +00:00
renovate[bot]
05ace4028e Update dependency @ember/test-helpers to v3.3.0 2024-02-12 18:59:01 +00:00
renovate[bot]
1e79ea1b96 Update dependency eslint-plugin-qunit to v8.1.1 2024-02-12 09:50:46 +00:00
renovate[bot]
d1513b4aa6 Update dependency @types/ember__runloop to v4.0.10 2024-02-08 04:12:54 +00:00
renovate[bot]
dbc0db3f51 Update typescript-eslint monorepo to v6.21.0 2024-02-05 21:52:25 +00:00
renovate[bot]
67e6d03d6c Update dependency prettier to v3.2.5 2024-02-04 10:05:06 +00:00
renovate[bot]
ed66b7cfc3 Update dependency ember-cli-page-object to v2.2.2 2024-02-02 22:09:24 +00:00
renovate[bot]
37fc7786fd Update dependency webpack to v5.90.1 2024-02-01 21:17:56 +00:00
renovate[bot]
7e9e6a81b7 Update dependency stylelint to v16.2.1 2024-02-01 17:05:35 +00:00
renovate[bot]
0f305fb4d3
Update dependency stylelint-config-standard-scss to v13 (#839)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-31 23:55:36 +01:00
renovate[bot]
6fe5c205bc
Update dependency release-it to v17 (#759)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-31 23:55:20 +01:00
dependabot[bot]
d6e620a22a
Bump follow-redirects from 1.15.3 to 1.15.4 (#849)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-31 23:54:13 +01:00
renovate[bot]
cf165235bf
Update dependency ember-cli-browserstack to v3 (#898)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-31 23:54:02 +01:00
renovate[bot]
efde276440
Update dependency @glimmer/validator to v0.88.1 (#902)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-31 23:53:49 +01:00
renovate[bot]
4089403fd5 Update dependency ember-cli-page-object to v2.2.1 2024-01-31 04:55:45 +00:00
renovate[bot]
d8c90df496 Update dependency @types/ember__string to v3.0.15 2024-01-31 01:37:42 +00:00
renovate[bot]
e4d2c6b4e1 Update typescript-eslint monorepo to v6.20.0 2024-01-29 21:19:46 +00:00
renovate[bot]
c02aee03b0 Update dependency @babel/core to v7.23.9 2024-01-25 18:53:12 +00:00
renovate[bot]
4910823d22 Update dependency webpack to v5.90.0 2024-01-24 19:41:53 +00:00
renovate[bot]
ab77243cf0 Update dependency ember-page-title to v8.2.1 2024-01-24 04:51:17 +00:00
renovate[bot]
57ef093852 Update dependency ember-cli-browserstack to v2.1.0 2024-01-24 01:01:47 +00:00
renovate[bot]
2a5c04c78d Update dependency ember-cli to v5.6.0 2024-01-23 21:03:10 +00:00
renovate[bot]
4503a58726 Update dependency ember-cli-browserstack to v2.0.2 2024-01-23 18:15:21 +00:00
renovate[bot]
5dff714f6a Update typescript-eslint monorepo to v6.19.1 2024-01-22 21:08:27 +00:00
renovate[bot]
8011927a0e Update dependency ember-cli-page-object to v2.2.0 2024-01-21 05:09:08 +00:00
renovate[bot]
2e98ed7c7c Update dependency sass to v1.70.0 2024-01-20 12:17:22 +00:00
renovate[bot]
d6c735c546 Update dependency stylelint to v16.2.0 2024-01-20 09:33:36 +00:00
renovate[bot]
8d5720e332 Update dependency @glint/environment-ember-loose to v1.3.0 2024-01-20 06:59:22 +00:00
renovate[bot]
34ec449a90 Update dependency @types/luxon to v3.4.2 2024-01-20 05:09:06 +00:00
renovate[bot]
9e92e88d39 Update dependency @types/ember__routing to v4.0.21 2024-01-20 02:29:27 +00:00
renovate[bot]
b56a48c654 Update dependency @glint/template to v1.3.0 2024-01-19 23:15:27 +00:00
renovate[bot]
d125e8ddb5 Update dependency @glint/core to v1.3.0 2024-01-19 18:39:17 +00:00
renovate[bot]
3028293340 Update dependency prettier to v3.2.4 2024-01-19 17:42:01 +00:00
renovate[bot]
e4f8bc49ec Update dependency @types/rsvp to v4.0.9 2024-01-19 13:45:04 +00:00
renovate[bot]
07a80c6a8e Update dependency @types/qunit to v2.19.10 2024-01-19 10:10:57 +00:00
renovate[bot]
e4357c5db9 Update dependency @types/ember__utils to v4.0.7 2024-01-19 06:31:32 +00:00
renovate[bot]
0b73d813bf Update dependency @types/ember__test to v4.0.6 2024-01-19 04:02:56 +00:00
renovate[bot]
fada508758 Update dependency @types/ember__template to v4.0.6 2024-01-19 02:04:47 +00:00
renovate[bot]
8e3769a235 Update dependency @types/ember__string to v3.0.14 2024-01-18 23:30:36 +00:00
renovate[bot]
6613c400ab Update dependency @types/ember__service to v4.0.9 2024-01-18 20:24:08 +00:00
renovate[bot]
746b28b24f Update dependency @types/ember__runloop to v4.0.9 2024-01-18 17:40:18 +00:00
renovate[bot]
6a8e3584f6 Update dependency @types/ember__routing to v4.0.20 2024-01-18 12:46:19 +00:00
renovate[bot]
8cf6ee945a Update dependency @types/ember__polyfills to v4.0.6 2024-01-18 10:53:30 +00:00
renovate[bot]
93f27b91c2 Update dependency @types/ember__owner to v4.0.9 2024-01-18 08:22:46 +00:00
renovate[bot]
1a51a868a3 Update dependency @types/ember__object to v4.0.12 2024-01-18 03:01:13 +00:00
renovate[bot]
2207ca97d2 Update dependency @types/ember__modifier to v4.0.9 2024-01-18 01:26:37 +00:00
renovate[bot]
4cec7a3b4f Update dependency @types/ember__helper to v4.0.7 2024-01-17 21:37:02 +00:00
renovate[bot]
e742088211 Update dependency @types/ember__error to v4.0.6 2024-01-17 19:26:35 +00:00
renovate[bot]
7bcf3eb44e Update dependency @types/ember__engine to v4.0.11 2024-01-17 17:24:26 +00:00
renovate[bot]
ed1d28b0fd Update dependency @types/ember__destroyable to v4.0.5 2024-01-17 12:24:47 +00:00
renovate[bot]
40e0536295 Update dependency @types/ember__debug to v4.0.8 2024-01-17 09:05:09 +00:00
renovate[bot]
5094139ea8 Update dependency @types/ember__controller to v4.0.12 2024-01-17 08:10:43 +00:00
renovate[bot]
fe17341436 Update dependency @types/ember__component to v4.0.22 2024-01-17 06:41:17 +00:00
renovate[bot]
316c8c6124 Update dependency @types/ember__array to v4.0.10 2024-01-17 04:28:01 +00:00
renovate[bot]
35b7b008fd Update dependency @types/ember__application to v4.0.11 2024-01-17 00:12:21 +00:00
renovate[bot]
a7efdba91b Update dependency @types/ember to v4.0.11 2024-01-16 21:39:25 +00:00
renovate[bot]
b856d8f4b8 Update dependency @release-it-plugins/lerna-changelog to v6.1.0 2024-01-15 21:58:14 +00:00
renovate[bot]
e169b336a6 Update typescript-eslint monorepo to v6.19.0 2024-01-15 19:58:18 +00:00
renovate[bot]
203db780f9 Update dependency prettier to v3.2.2 2024-01-15 04:51:47 +00:00
renovate[bot]
fd4ce11941 Update dependency @types/luxon to v3.4.1 2024-01-15 00:54:27 +00:00
renovate[bot]
72de673d1b Update dependency prettier to v3.2.1 2024-01-13 03:12:37 +00:00
renovate[bot]
a18d2c897f Update dependency @types/ember__helper to v4.0.6 2024-01-13 00:19:20 +00:00
renovate[bot]
0bb678d38e Update Node.js to v20.11.0 2024-01-12 23:26:06 +00:00
renovate[bot]
68c65e0174 Update dependency prettier to v3.2.0 2024-01-12 18:23:54 +00:00
renovate[bot]
7a8ad1706e Update dependency eslint-plugin-prettier to v5.1.3 2024-01-10 07:11:13 +00:00
renovate[bot]
db9181f3b3 Update dependency eslint-plugin-n to v16.6.2 2024-01-09 06:54:39 +00:00
renovate[bot]
50a2a93577 Update typescript-eslint monorepo to v6.18.1 2024-01-09 05:12:22 +00:00
renovate[bot]
f197d77d8c Update dependency ember-page-title to v8.2.0 2024-01-08 18:08:28 +00:00
renovate[bot]
5917f36fa1 Update typescript-eslint monorepo to v6.18.0 2024-01-06 18:17:46 +00:00
Jeldrik Hanschke
bb936a422a
create/settings controller is not used anymore (#844) 2024-01-06 13:42:56 +01:00
renovate[bot]
8ab72e02a0
Update dependency stylelint-config-standard to v36 (#829)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-06 13:20:45 +01:00
renovate[bot]
ac6de1e44d
Update dependency @glimmer/validator to v0.87.1 (#824)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-06 13:20:31 +01:00
renovate[bot]
42897f6c17 Update dependency @glint/environment-ember-loose to v1.2.2 2024-01-06 11:22:04 +00:00
renovate[bot]
c57291b66d Update dependency @glint/template to v1.2.2 2024-01-06 07:55:22 +00:00
renovate[bot]
fff8885be9 Update dependency @glint/core to v1.2.2 2024-01-06 05:11:31 +00:00
renovate[bot]
e8cf1769f2 Update dependency @types/luxon to v3.4.0 2024-01-06 02:25:09 +00:00
renovate[bot]
e2a02f9358 Update dependency @types/luxon to v3.3.8 2024-01-04 00:50:30 +00:00
renovate[bot]
927b659bd4 Update dependency sass to v1.69.7 2024-01-03 04:29:42 +00:00
renovate[bot]
c17b3af574 Update dependency eslint-plugin-n to v16.6.1 2024-01-02 07:55:14 +00:00
renovate[bot]
62a9ab4f18 Update typescript-eslint monorepo to v6.17.0 2024-01-01 22:03:19 +00:00
renovate[bot]
27853cb0ca Update dependency @babel/core to v7.23.7 2023-12-30 00:32:53 +00:00
renovate[bot]
eaf133828a Update dependency eslint-plugin-n to v16.6.0 2023-12-29 15:26:23 +00:00
renovate[bot]
d06584fe17 Update dependency sass to v1.69.6 2023-12-29 04:43:57 +00:00
renovate[bot]
d77eacc1f3 Update typescript-eslint monorepo to v6.16.0 2023-12-25 21:07:02 +00:00
renovate[bot]
42bf684f3f Update dependency stylelint to v16.1.0 2023-12-25 15:44:45 +00:00
renovate[bot]
b788ce9f4c Update dependency eslint-plugin-prettier to v5.1.2 2023-12-24 09:10:44 +00:00
renovate[bot]
786ec48b28 Update dependency ember-auto-import to v2.7.2 2023-12-23 07:25:59 +00:00
renovate[bot]
f7246a5b99 Update dependency eslint-plugin-prettier to v5.1.1 2023-12-21 18:01:28 +00:00
renovate[bot]
e1693eedac Update dependency ember-bootstrap to v6.1.0 2023-12-20 19:03:23 +00:00
renovate[bot]
91680d6371 Update dependency eslint-plugin-n to v16.5.0 2023-12-20 12:04:24 +00:00
renovate[bot]
05c60efdcf Update dependency eslint-plugin-prettier to v5.1.0 2023-12-20 00:54:25 +00:00
Jeldrik Hanschke
27b36e2005
refactor create/settings controller to component (#820) 2023-12-19 19:48:54 +01:00
Jeldrik Hanschke
f25625f40d
rafactor create/options-datetime controller to component (#819) 2023-12-19 19:28:04 +01:00
renovate[bot]
3f365c2c95 Update dependency @tsconfig/ember to v3.0.3 2023-12-19 09:49:01 +00:00
renovate[bot]
f93c3ce7c6 Update typescript-eslint monorepo to v6.15.0 2023-12-18 21:02:51 +00:00
renovate[bot]
47d3770733
Lock file maintenance (#816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-18 06:54:02 +01:00
Jeldrik Hanschke
f9f0c365e8
refactor create/options controller to component (#815) 2023-12-17 23:10:57 +01:00
Jeldrik Hanschke
e3935e521f
refactor create/meta controller to component (#814) 2023-12-17 22:31:14 +01:00
renovate[bot]
26ab8495d5
Update dependency @glimmer/validator to v0.85.13 (#812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-16 23:30:43 +01:00
Jeldrik Hanschke
e4b6f72d18
fix badges in readme (#813) 2023-12-16 23:30:26 +01:00
Jeldrik Hanschke
4950e327ed
refactor /create/index controller to component (#760) 2023-12-16 23:22:43 +01:00
renovate[bot]
41d6b90099
Lock file maintenance (#715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-16 23:16:10 +01:00
renovate[bot]
8ff2706709
Pin dependencies (#811)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-16 23:01:39 +01:00
renovate[bot]
ee369bb0e8
Update dependency stylelint to v16 (#796)
* Update dependency stylelint-config-standard to v35

* Update dependency stylelint to v16

* Update dependency stylelint-config-standard-scss to v12

* Update dependency stylelint-prettier to v5

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-12-16 23:01:23 +01:00
Jeldrik Hanschke
f894eb72f4
fix TypeScript setup (#800) 2023-12-16 22:45:41 +01:00
renovate[bot]
621c0e1b7f Update dependency eslint to v8.56.0 2023-12-16 04:48:35 +00:00
renovate[bot]
3c15ea633b Update dependency ember-auto-import to v2.7.1 2023-12-13 22:37:05 +00:00
renovate[bot]
6fe86852a6 Update dependency eslint-plugin-ember to v11.12.0 2023-12-13 04:16:23 +00:00
renovate[bot]
2065696fe0 Update typescript-eslint monorepo to v6.14.0 2023-12-13 00:09:01 +00:00
renovate[bot]
be194331cd Update dependency eslint-plugin-n to v16.4.0 2023-12-12 21:31:05 +00:00
renovate[bot]
1699ee64f6 Update dependency ember-intl to v6.4.0 2023-12-12 15:11:24 +00:00
renovate[bot]
b9750ed763 Update dependency ember-source to v5.5.0 2023-12-12 12:33:45 +00:00
renovate[bot]
93d432866a Update dependency ember-cli to v5.5.0 2023-12-12 10:32:14 +00:00
renovate[bot]
3df9b4a887 Update dependency @babel/core to v7.23.6 2023-12-12 06:44:32 +00:00
renovate[bot]
6b5ded90f6 Update dependency prettier to v3.1.1 2023-12-10 12:57:21 +00:00
renovate[bot]
1c7ff7cd37 Update dependency typescript to v5.3.3 2023-12-07 01:19:51 +00:00
renovate[bot]
179de2d282 Update dependency @types/luxon to v3.3.7 2023-12-05 01:21:28 +00:00
renovate[bot]
7eb77e0eb9 Update typescript-eslint monorepo to v6.13.2 2023-12-04 21:54:03 +00:00
renovate[bot]
faaea66529 Update dependency ember-intl to v6.3.2 2023-12-04 18:31:01 +00:00
renovate[bot]
243140c84a Update dependency eslint-config-prettier to v9.1.0 2023-12-02 12:08:22 +00:00
renovate[bot]
5d4a648f08 Update dependency eslint to v8.55.0 2023-12-02 02:07:09 +00:00
renovate[bot]
e18de86fe7 Update dependency ember-intl to v6.3.1 2023-12-01 20:02:05 +00:00
renovate[bot]
6b56860a1f Update dependency ember-intl to v6.3.0 2023-12-01 03:14:57 +00:00
renovate[bot]
41900f4bb6 Update dependency @types/luxon to v3.3.6 2023-12-01 01:06:21 +00:00
Jeldrik Hanschke
ae9d1f1cb1
Update Ember Power Calendar together with addons (#785) 2023-11-30 17:42:41 +08:00
renovate[bot]
109ac46347 Update dependency release-it to v16.3.0 2023-11-29 18:58:35 +00:00
renovate[bot]
8c30bcc986 Update dependency @babel/core to v7.23.5 2023-11-29 16:38:11 +00:00
renovate[bot]
56b634e57a Update typescript-eslint monorepo to v6.13.1 2023-11-28 14:32:44 +00:00
renovate[bot]
137a195227 Update dependency fs-extra to v11.2.0 2023-11-28 04:18:01 +00:00
renovate[bot]
5546bfa682 Update typescript-eslint monorepo to v6.13.0 2023-11-27 21:35:31 +00:00
renovate[bot]
7f690bfd11 Update dependency stylelint-prettier to v4.1.0 2023-11-25 01:20:22 +00:00
renovate[bot]
6776b0989e Update dependency ember-auto-import to v2.7.0 2023-11-24 18:32:46 +00:00
renovate[bot]
509a58b66c Update Node.js to v20.10.0 2023-11-22 19:05:29 +00:00
renovate[bot]
a50e12571d Update dependency ember-cli to v5.4.1 2023-11-21 23:18:40 +00:00
renovate[bot]
c8842bacc4 Update dependency ember-template-lint to v5.13.0 2023-11-21 19:27:15 +00:00
renovate[bot]
ed575ecd3c Update dependency @types/sjcl to v1.0.34 2023-11-21 16:51:14 +00:00
renovate[bot]
d1da03ef0b Update dependency @types/rsvp to v4.0.8 2023-11-21 13:57:12 +00:00
renovate[bot]
5d443c03ea Update dependency @types/qunit to v2.19.9 2023-11-21 10:19:44 +00:00
renovate[bot]
f95fdefda4 Update dependency @types/luxon to v3.3.5 2023-11-21 07:17:54 +00:00
renovate[bot]
b62269d452 Update typescript-eslint monorepo to v6.12.0 2023-11-21 00:15:17 +00:00
renovate[bot]
fb151b04df Update dependency typescript to v5.3.2 2023-11-20 21:41:56 +00:00
renovate[bot]
acf8a1a607 Update dependency ember-intl to v6.2.2 2023-11-20 18:14:17 +00:00
renovate[bot]
646014867d Update dependency @ember/test-helpers to v3.2.1 2023-11-19 19:29:11 +00:00
renovate[bot]
e19a999f72 Update dependency eslint to v8.54.0 2023-11-18 00:55:11 +00:00
renovate[bot]
14b827a895 Update dependency ember-intl to v6.2.0 2023-11-14 21:52:06 +00:00
renovate[bot]
5d213562b9
Update dependency ember-bootstrap to v6.0.1 (#761)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-14 20:44:58 +01:00
renovate[bot]
122f2d75fc Update typescript-eslint monorepo to v6.11.0 2023-11-13 21:56:00 +00:00
renovate[bot]
1d964599c0 Update dependency prettier to v3.1.0 2023-11-13 08:08:54 +00:00
renovate[bot]
a235b5b0b8 Update dependency eslint-plugin-n to v16.3.1 2023-11-10 17:07:01 +00:00
renovate[bot]
cf69e47027 Update dependency @babel/core to v7.23.3 2023-11-09 12:05:41 +00:00
renovate[bot]
82cd422ebc
Update dependency ember-bootstrap to v6.0.0-6 (#742)
* Update dependency ember-bootstrap to v6.0.0-6

* whitelist has been renamed to include

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-11-08 22:34:40 +01:00
renovate[bot]
6ee4af67ae Update typescript-eslint monorepo to v6.10.0 2023-11-08 13:46:31 +00:00
renovate[bot]
dfb459496b
Update dependency stylelint-config-standard-scss to v11.1.0 (#754)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 09:24:21 +01:00
renovate[bot]
7bc6842502
Update dependency eslint-plugin-n to v16.3.0 (#753)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 09:24:14 +01:00
renovate[bot]
7f17f921e3
Update dependency eslint to v8.53.0 (#752)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 09:24:05 +01:00
renovate[bot]
73b825a89b
Update dependency ember-template-lint to v5.12.0 (#751)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 09:23:58 +01:00
renovate[bot]
cd704c76fb
Update dependency ember-page-title to v8.1.0 (#750)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 09:23:51 +01:00
renovate[bot]
3c333a6470
Update dependency ember-cli-deprecation-workflow to v2.2.0 (#748)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 08:20:18 +01:00
renovate[bot]
cde465c27b
Update dependency ember-cli-page-object to v2.1.3 (#747)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 08:20:13 +01:00
renovate[bot]
53156422c8
Update dependency @types/sjcl to v1.0.33 (#746)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 08:20:03 +01:00
renovate[bot]
3cc6874592
Update dependency @types/qunit to v2.19.8 (#745)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 08:19:56 +01:00
renovate[bot]
8fef7726b3
Update dependency miragejs to v0.1.48 (#743)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 08:19:48 +01:00
Jeldrik Hanschke
4ac7d87ace
run CI on RenovateBot features branch to enable automerging (#744) 2023-11-08 08:19:33 +01:00
renovate[bot]
81a3f7e322
Pin dependencies (#739)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-07 20:30:35 +01:00
Jeldrik Hanschke
09e5ab95ee
RenovateBot should not touch PHP version constraint (#741)
[skip ci]
2023-11-07 20:20:48 +01:00
renovate[bot]
d45237ac03
Pin dependencies (#740)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-07 20:08:38 +01:00
Jeldrik Hanschke
4bd7fdc4ed
RenovateBot should merge SemVer safe upgrades automatically (#738) 2023-11-07 20:00:34 +01:00
renovate[bot]
2783181bef
Update dependency @types/luxon to v3.3.4 (#736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-07 19:56:57 +01:00
renovate[bot]
71c58d8078
Update dependency @types/rsvp to v4.0.7 (#737)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-07 19:56:49 +01:00
Jeldrik Hanschke
9c701f013e
RenovateBot should pin dependencies (#735)
See https://docs.renovatebot.com/dependency-pinning/ for a great explanation.
2023-11-07 19:49:00 +01:00
Jeldrik Hanschke
fe75c3a98a
pin typescript to minor version (#734)
[skip ci]
2023-11-06 22:24:56 +01:00
Jeldrik Hanschke
fb3bfe9057
use Prettier for YAML files (#733) 2023-11-06 19:22:11 +01:00
Jeldrik Hanschke
de9d58b434
use Prettier for Glimmer templates (#731) 2023-11-05 20:57:52 +01:00
Jeldrik Hanschke
147f5dace4
Report copy success via tooltip at copy button (#730)
* refactor copy button to show success with tooltip

* remove ember-cli-flash

* update expected bundlesize
2023-11-05 17:06:27 +01:00
Jeldrik Hanschke
88a51964f1
report poll saving errors using a modal (#729) 2023-11-05 12:19:47 +01:00
Jeldrik Hanschke
0c4ef6fc5b
fix ESLint warnings for QUnit (#725) 2023-11-04 17:32:09 +01:00
Jeldrik Hanschke
bf87f6f305
converts poll/participation and poll/evaluation controllers to TypeScript (#724) 2023-11-04 17:21:35 +01:00
Jeldrik Hanschke
386910bdd3
remove remaining usage of action helper (#723) 2023-11-04 15:40:02 +01:00
Jeldrik Hanschke
76586f165d
Introduce typed templates with Glint (#714) 2023-11-04 14:54:30 +01:00
renovate[bot]
146f531a37
Update dependency ember-math-helpers to v4 (#721)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-04 13:18:22 +01:00
Jeldrik Hanschke
b087d865c2
Disable NPM dedupe RenovateBot option again (#720) 2023-11-04 12:36:22 +01:00
Jeldrik Hanschke
05364a307d
refactor logic to initalize locale (#719) 2023-11-03 20:16:25 +01:00
Jeldrik Hanschke
dc0a9a9aae
RebovateBot should dedupe deps (#718)
This hopefully works again reliable with the optimizations pushed to RebovateBot for deduplicating NPM dependencies.
2023-11-01 20:58:55 +01:00
renovate[bot]
df734b7d53
Update dependency ember-source to ~5.4.0 (#716)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-01 20:56:24 +01:00
renovate[bot]
6174cf0bc5
Update dependency ember-cli to ~5.4.0 (#717)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-01 20:55:50 +01:00
Jeldrik Hanschke
f0cff27e99
Convert to TypeScript (#713)
* setup typescript

* covert to TypeScript
2023-10-29 19:16:33 +01:00
renovate[bot]
a5d19c91f0
Update Node.js to v20 (#707)
* Update Node.js to v20

* use node 20 in CI and package.json constraint as well

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-29 19:10:04 +01:00
renovate[bot]
a59c2349b0
Update dependency ember-source to v5 (#689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-28 19:46:42 +02:00
Jeldrik Hanschke
2a76f98e99
drop ember-classic-decorator (#712) 2023-10-28 19:41:30 +02:00
Jeldrik Hanschke
5146bbdf36
Fix remaining deprecations (#711)
* fix: Use of assign has been deprecated. Please use Object.assign or the spread operator instead.

* fix @tagName argument of <LinkTo> is deprecated

* clean-up deprecation workflow
2023-10-28 19:21:45 +02:00
Jeldrik Hanschke
2d5914ff63
replace ember data with native classes (#708) 2023-10-28 19:15:06 +02:00
Jeldrik Hanschke
1072953cd4
refactor poll creation to not rely on Ember Data model (#710) 2023-10-28 17:50:17 +02:00
Jeldrik Hanschke
8a4954f4e8
drop support for polls created with Croodle <= 0.3.x (#709) 2023-10-27 20:40:18 +02:00
renovate[bot]
83c994c363
Update dependency ember-intl to v6 (#651)
* Update dependency ember-intl to v6

* workaround wrong assumption of ember-intl on default locale

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-23 21:26:23 +02:00
renovate[bot]
cdd40b52a9
Update actions/setup-node action to v4 (#706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-23 21:03:29 +02:00
Jeldrik Hanschke
748b17e0ea
answers could be derived from answerType (#704)
* answers could be derived from answerType

* improve readability of API test data

* update API tests for removed answers property
2023-10-23 17:33:25 +02:00
renovate[bot]
71c11e8dc5
Lock file maintenance (#705)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-23 07:31:20 +02:00
renovate[bot]
93901c98c5
Lock file maintenance (#699)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-19 13:46:31 +02:00
Jeldrik Hanschke
46cfa71083
try fixing RenovateBot by removing postUpdateOptions (#698) 2023-10-19 09:46:55 +02:00
Jeldrik Hanschke
20f427c074
stop relying on any prototype extension (#697) 2023-10-18 16:33:17 +02:00
Jeldrik Hanschke
3a23719127
do not rely on Array prototype extensions (#692)
* Array.any() relies on prototype extensions

* remove remaining usage of array prototype extensions
2023-10-18 14:52:57 +02:00
Jeldrik Hanschke
9d019bba2e
migrate to NPM (#696) 2023-10-17 14:37:46 +02:00
Jeldrik Hanschke
96893b2ee4
migrate to @release-it-plugins/lerna-changelog (#695) 2023-10-17 14:01:57 +02:00
renovate[bot]
8c33efbd3a
Update dependency fs-extra to v11 (#690)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-17 13:49:38 +02:00
Jeldrik Hanschke
6a5ab6167e
lember-cli-inject-live-reload breaks with Ember CLI > 5.0 and rootURL being an empty string (#694) 2023-10-17 13:20:10 +02:00
renovate[bot]
8118aabb1f
Update dependency ember-cli-clipboard to v1 (#637)
* Update dependency ember-cli-clipboard to v1

* update for changed public API

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-17 11:49:21 +02:00
Jeldrik Hanschke
8bb8af265f
upgrade blueprints to v5.3 (#693) 2023-10-17 11:38:49 +02:00
renovate[bot]
64c5de6e9f
Update dependency @ember/test-helpers to v3 (#660)
* Update dependency @ember/test-helpers to v3

* upgrade ember-qunit

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-17 11:02:41 +02:00
renovate[bot]
d2a24daf66
Update dependency ember-template-lint to v5 (#655)
* Update dependency ember-template-lint to v5

* fix new linting violations

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-17 10:46:08 +02:00
renovate[bot]
98ff62af80
Update dependency prettier to v3 (#668)
* Update dependency prettier to v3

* upgrade eslint-plugin-prettier and run prettier on all files

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-17 10:44:45 +02:00
renovate[bot]
337944e56c
Update dependency ember-cli to v5 (#633)
* Update dependency ember-cli to v5

* add ember-cli-clean-css required for CSS minification in Ember CLI v5

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-17 10:34:39 +02:00
renovate[bot]
482b5c44d1
Update dependency eslint-config-prettier to v9 (#664)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-17 00:15:04 +02:00
renovate[bot]
0814a4b13c
Update dependency eslint-plugin-qunit to v8 (#667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-17 00:14:47 +02:00
renovate[bot]
261435044d
Update dependency ember-cli to ~4.12.0 (#685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-17 00:14:16 +02:00
Jeldrik Hanschke
b08748c33c
upgrade Ember CLI and blueprints to v4.4 (#684)
* upgrade Ember CLI and blueprints to v4.4

* remove duplicated GitHub actions

* fix linting
2023-10-17 00:00:13 +02:00
renovate[bot]
ae79eb9f44
Update dependency lerna-changelog to v2 (#683)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-16 22:13:41 +02:00
renovate[bot]
79e585e196
Update dependency ember-source to ~4.12.0 (#682)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-16 22:13:22 +02:00
Jeldrik Hanschke
2e9d8581b1
WIP: Upgrade to Ember v4.4 (#679)
* first try

* upgrade ember-intl to v5

* simplify code and avoid deprecated / removed APIs

* fix RouterService.transitionTo not bound to this instance

* fix modified after consumption error

* fix lint
2023-10-16 14:26:00 +02:00
Jeldrik Hanschke
fa9f91288c
fix RouterService.transitionTo not bound to this instance (#680) 2023-10-16 14:10:29 +02:00
Jeldrik Hanschke
d5a245c4f2
upgrade ember-intl to v5 (#678) 2023-10-16 00:38:43 +02:00
Jeldrik Hanschke
d5957238e5
remove unused dependency ember-awesome-macros (#677) 2023-10-16 00:21:11 +02:00
renovate[bot]
831ed35136
Update dependency ember-cli-page-object to v2 (#673)
* Update dependency ember-cli-page-object to v2

* upgrade to new APIs

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-16 00:07:22 +02:00
Jeldrik Hanschke
86fd089d55
upgrade to eslint-plugin-prettier v4 (#676) 2023-10-15 23:40:17 +02:00
renovate[bot]
3fa4ccc82c
Update dependency ember-page-title to v8 (#662)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 23:40:03 +02:00
renovate[bot]
31e329c15a
Update dependency ember-cli-flash to v4 (#641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 23:39:42 +02:00
renovate[bot]
31577201d8
Update dependency ember-cli-mirage to v3 (#646)
* Update dependency ember-cli-mirage to v3

* follow upgrade guide

* fix test setup

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-15 23:26:27 +02:00
Jeldrik Hanschke
0f94d81ad4
fix ember-cli-page-object deprecations (#674)
* fix ember-cli-page-object deprecations

* make linter happy
2023-10-15 23:26:18 +02:00
Jeldrik Hanschke
871e970ba0
may not be the same day depending on current time and timezone if using UTC time (#675) 2023-10-15 23:20:53 +02:00
Jeldrik Hanschke
7630aab9c6
remove unused dep ember-cli-acceptance-test-helpers (#672) 2023-10-15 22:21:41 +02:00
Jeldrik Hanschke
4c734c0a2b
upgrade ember-qunit to v6 (#671) 2023-10-15 22:19:41 +02:00
Jeldrik Hanschke
3ff4e99bc6
import dasherize from @ember/string to fix Mirage deprecation (#670) 2023-10-15 22:15:46 +02:00
renovate[bot]
031eedc279
Update dependency qunit-dom to v3 (#669)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 22:12:56 +02:00
renovate[bot]
fa9c63a2e8
Update dependency ember-modifier to v4 (#661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 22:04:37 +02:00
renovate[bot]
545a4ba632
Update dependency eslint-plugin-ember to v11 (#665)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 22:04:09 +02:00
Jeldrik Hanschke
97605ec7d7
fix some deprecations (#659)
* get deprecation workflow up to date

* fix routing.transition-methods deprecation

* fix this-property-fallback deprecation

* fix implicit-injections deprecation

* argument-less-helper-paren-less-invocation

* remove unmaintained ember-transition-helper which triggers deprecated-run-loop-and-computed-dot-access deprecation

* reset to only log but not throw on errors to not block other development

* reset double quote to single quote changes in templates and fix Prettier config

* fix JS linting

* ugprade ember-template-lint to fix parser
2023-10-15 21:50:28 +02:00
renovate[bot]
e6ffb21cd5
Update dependency ember-maybe-import-regenerator to v1 (#653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:32:12 +02:00
renovate[bot]
5efb029a03
Update dependency ember-cli-htmlbars to v6 (#645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:16:53 +02:00
renovate[bot]
c070ec0b11
Update dependency ember-cli-babel to v8 (#635)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:16:22 +02:00
renovate[bot]
65f80a883f
Update dependency ember-cli-sass to v11 (#648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:16:09 +02:00
renovate[bot]
1f59cbd0d9
Update dependency ember-composable-helpers to v5 (#649)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:16:00 +02:00
renovate[bot]
f3ae67aa85
Update dependency ember-math-helpers to v3 (#652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:15:43 +02:00
renovate[bot]
dd7af9bed2
Update dependency ember-get-config to v2 (#650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:15:33 +02:00
renovate[bot]
aaef3c5473
Update dependency ember-resolver to v11 (#654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:15:00 +02:00
renovate[bot]
3f95b9a637
Update dependency ember-test-selectors to v6 (#656)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:14:35 +02:00
renovate[bot]
6ebaeec278
Update dependency ember-truth-helpers to v4 (#657)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 21:14:23 +02:00
Jeldrik Hanschke
4c0936af35
remove unused named outlet (#658) 2023-10-15 21:12:09 +02:00
Jeldrik Hanschke
02058ab756
upgrade to Ember 3.28 blueprints (#647) 2023-10-15 20:37:03 +02:00
renovate[bot]
c48d7d1441
Update dependency ember-source to ~3.28.0 (#590)
* Update dependency ember-source to ~3.28.0

* upgrade ember-data and ember-data-model-fragments to compatible versions

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-15 20:02:17 +02:00
Jeldrik Hanschke
b0a413aaae
upgrade Ember Data to v3.24 (#643) 2023-10-15 19:49:23 +02:00
Jeldrik Hanschke
8f775fcc56
remove leftover from ember-cp-validations clean-up (#642) 2023-10-15 19:30:10 +02:00
renovate[bot]
785829fa37
Update dependency ember-cli-deprecation-workflow to v2 (#640)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 19:12:00 +02:00
Jeldrik Hanschke
650910fcfc
refactor remaining components to @glimmer/component (#639) 2023-10-15 19:11:08 +02:00
renovate[bot]
c8f5a33af1
Lock file maintenance (#638)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 19:01:02 +02:00
renovate[bot]
0f494df216
Update dependency ember-power-calendar-luxon to ^0.5.0 (#617)
* Update dependency ember-power-calendar-luxon to ^0.5.0

* upgrade ember-power-calendar

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-15 18:53:58 +02:00
renovate[bot]
40cb4da694
Update dependency ember-cli-app-version to v6 (#634)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 17:41:45 +02:00
Jeldrik Hanschke
cc9e82fa06
RenovateBot should update dependencies within ranges once per week (#636) 2023-10-15 17:37:11 +02:00
renovate[bot]
408d06f9d4
Update dependency webpack to v5.89.0 (#632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 17:32:37 +02:00
renovate[bot]
68b1b7dfec
Update dependency ember-classic-decorator to v3 (#627)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 17:32:25 +02:00
Jeldrik Hanschke
82ec8b59f7
refactor to tracked properties (#621) 2023-10-15 17:32:11 +02:00
renovate[bot]
1497a36b94
Update Node.js to v18 (#602)
* Update Node.js to v18

* upgrade node at all places

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-15 15:35:42 +02:00
renovate[bot]
ca63cc995e
Update dependency sass to v1.69.3 (#616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-15 15:29:49 +02:00
Jeldrik Hanschke
857f2baa16
simplify form validation and drop ember-cp-validations (#631)
* replace ember-cp-validations with custom validation logic for poll creation

* refactor poll participation to not use ember-cp-validations

* drop ember-cp-validations from create.settings route

* remove unused leftovers from ember-cp-validations

* fix potential leak

* assertion thrown in CI tests (Firefox) indicate that listener is cleaned up automatically

* synchronize translations
2023-10-15 15:27:02 +02:00
Jeldrik Hanschke
77f8f5804f
drop computed properties in poll participation controller if possible (#630) 2023-10-15 12:06:27 +02:00
Jeldrik Hanschke
ec537c0eaf
refactor poll participation controller to native class (#629) 2023-10-15 11:38:02 +02:00
Jeldrik Hanschke
e984b9012d
fix displaying dates in participants table for polls having dates with times (#625)
* fix: participants table not listing times in header
* fix: participants table shows days in wrong timezone
2023-10-01 16:21:49 +02:00
renovate[bot]
77ce02d6bc
Update dependency release-it to v16.2.1 (#626)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-01 14:27:28 +02:00
renovate[bot]
0c64773b2d
Update dependency ember-bootstrap to v5 (#622)
* Update dependency ember-bootstrap to v5

* ensure same ember-modifier version

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-10-01 13:50:38 +02:00
Jeldrik Hanschke
dfd2704a04
upgrade ember-source to v3.24 (#624) 2023-09-30 17:15:24 +02:00
Jeldrik Hanschke
128cb37c1e
upgrade to Ember Bootstrap v4 (#623) 2023-09-30 11:15:50 +02:00
Jeldrik Hanschke
2094f0d91b
colocate component templates (#620) 2023-09-22 22:22:48 +02:00
renovate[bot]
6869b62ddb
Update actions/checkout action to v4 (#618)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-21 14:20:44 +02:00
renovate[bot]
9d685a24f9
Update dependency ember-cli to ~3.28.0 (#526)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-21 14:20:15 +02:00
renovate[bot]
b62fee843c
Update dependency ember-cli-content-security-policy to v2 (#592)
* Update dependency ember-cli-content-security-policy to v2

* update configuration

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2023-09-21 14:09:49 +02:00
Jeldrik Hanschke
2a4b1f8b73
Use native browser APIs and Luxon instead of Moment (#612) 2023-09-21 12:30:14 +02:00
renovate[bot]
9f47286899
Update dependency sass to v1.66.1 (#613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-04 17:44:03 +02:00
renovate[bot]
46be51fcea
Update dependency release-it to v16.1.5 (#608)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-20 19:42:31 +02:00
renovate[bot]
15bc12743f
Update Node.js to v16.20.2 (#611)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-20 19:42:03 +02:00
Jeldrik Hanschke
5143a6ae88
remove chart in poll evaluation (#610) 2023-08-19 20:57:57 +02:00
Jeldrik Hanschke
a226f2dd26 Release 0.7.0 2023-08-19 20:36:25 +02:00
dependabot[bot]
6ba0473dd3
Bump word-wrap from 1.2.3 to 1.2.4 (#609)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 14:26:53 +02:00
renovate[bot]
6c2441417a
Update actions/setup-node action to v3 (#606)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-15 16:17:25 +02:00
renovate[bot]
61e2534bc5
Update actions/checkout action to v3 (#605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-13 23:19:42 +02:00
renovate[bot]
a023e27763
Update dependency release-it-lerna-changelog to v5 (#604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-13 23:19:18 +02:00
renovate[bot]
9787e538dc
Update dependency release-it to v16 (#603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-13 21:46:45 +02:00
Jeldrik Hanschke
4dec5d2d6c
using docker for development is not needed anymore since supporting PHP 7.2 - 8.2 (#601) 2023-07-13 21:46:38 +02:00
renovate[bot]
821b700fb8
Update dependency ember-cli-clipboard to ^0.16.0 (#589)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-11 17:27:39 +02:00
renovate[bot]
79f30aa120
Update dependency ember-get-config to v0.5.0 (#596)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-11 17:26:59 +02:00
dependabot[bot]
81ff101a80
Bump semver from 5.7.1 to 5.7.2 (#599)
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-11 17:21:11 +02:00
renovate[bot]
690ead67be
Update dependency ember-power-calendar to ^0.19.0 (#597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-11 17:20:57 +02:00
Jeldrik Hanschke
a622cc9766
TravisCI usage has been dropped long time ago 2023-07-11 14:17:14 +02:00
Jeldrik Hanschke
f2332f7aca
fix build status badge in readme 2023-07-11 14:16:33 +02:00
Jeldrik Hanschke
81f8c41507
update documented PHP version 2023-07-11 14:09:59 +02:00
Jeldrik Hanschke
6382cbd908
test with PHP 8.x in CI (#595) 2023-07-11 14:08:56 +02:00
Jeldrik Hanschke
38cc827600
upgrade backend dependencies within configured ranges (#594) 2023-07-09 23:35:11 +02:00
Jeldrik Hanschke
26f08bde26
upgrade node to 16 (#593) 2023-07-09 23:17:31 +02:00
renovate[bot]
82e0e98c19
Update dependency ember-cli-browser-navigation-button-test-helper to ^0.3.0 (#588)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-09 23:07:39 +02:00
renovate[bot]
37bf1e090b
Update dependency bootstrap to v4.6.2 (#580)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-09 22:58:19 +02:00
Jeldrik Hanschke
bec7dd0228
upgrade all frontend deps within configured ranges (#586) 2023-07-09 22:57:55 +02:00
Jeldrik Hanschke
de299c6f80
downgrade to Firefox 102.0.1 used by ember-source repository to run tests (#579) 2023-07-09 22:35:16 +02:00
Weblate (bot)
142970d214
Translations update from Weblate (#461)
* Translated using Weblate (Italian)

Currently translated at 100.0% (141 of 141 strings)

Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Translate-URL: https://hosted.weblate.org/projects/croodle/client/it/
Translation: Croodle/client

* Translated using Weblate (Spanish)

Currently translated at 100.0% (141 of 141 strings)

Co-authored-by: badlop <badlop@process-one.net>
Translate-URL: https://hosted.weblate.org/projects/croodle/client/es/
Translation: Croodle/client

* Translated using Weblate (Norwegian Bokmål)

Currently translated at 79.4% (112 of 141 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 77.3% (109 of 141 strings)

Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Translate-URL: https://hosted.weblate.org/projects/croodle/client/nb_NO/
Translation: Croodle/client

* Translated using Weblate (Catalan)

Currently translated at 100.0% (141 of 141 strings)

Translated using Weblate (Catalan)

Currently translated at 97.1% (137 of 141 strings)

Co-authored-by: Maite Guix <maite.guix@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/croodle/client/ca/
Translation: Croodle/client

---------

Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: badlop <badlop@process-one.net>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Maite Guix <maite.guix@gmail.com>
2023-07-09 22:21:11 +02:00
renovate[bot]
86f659992a
Update dependency eslint to v7.32.0 (#520)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-09 22:06:32 +02:00
dependabot[bot]
394deb353f
Bump shell-quote from 1.7.2 to 1.8.1 (#577)
Bumps [shell-quote](https://github.com/ljharb/shell-quote) from 1.7.2 to 1.8.1.
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.7.2...v1.8.1)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-09 21:48:56 +02:00
dependabot[bot]
4415921ab2
Bump guzzlehttp/psr7 from 1.6.1 to 1.9.1 in /api (#575)
Bumps [guzzlehttp/psr7](https://github.com/guzzle/psr7) from 1.6.1 to 1.9.1.
- [Release notes](https://github.com/guzzle/psr7/releases)
- [Changelog](https://github.com/guzzle/psr7/blob/1.9.1/CHANGELOG.md)
- [Commits](https://github.com/guzzle/psr7/compare/1.6.1...1.9.1)

---
updated-dependencies:
- dependency-name: guzzlehttp/psr7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-09 21:48:45 +02:00
Jeldrik Hanschke
3468af6f93
upgrade node to 14 (#574) 2023-07-09 21:38:34 +02:00
renovate[bot]
d67bdbb33c
Update dependency ember-template-lint to v2.21.0 (#519)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-09 21:37:47 +02:00
Jeldrik Hanschke
3fec3508e5
add instructions running API using Docker for development purposes (#572) 2023-07-09 21:26:06 +02:00
Jeldrik Hanschke
94e126cd79
ensure moment-timezone has data for current and next year (#573) 2023-07-09 21:12:56 +02:00
Jeldrik Hanschke
346f5fe94e
pin node version using volta (#571) 2023-07-07 23:29:03 +02:00
renovate[bot]
fb5981e914
Update dependency ember-cli-page-object to v1.17.5 (#518)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-27 19:23:11 +01:00
renovate[bot]
00e35c6316
Update dependency ember-page-title to v6.2.0 (#513)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-27 19:23:04 +01:00
renovate[bot]
575d8054f6
Update dependency vlucas/phpdotenv to v3.6.8 (#514)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-27 19:04:17 +01:00
renovate[bot]
77ac3f9ca9
Update dependency ember-template-lint to v2.17.0 (#517)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-27 19:04:04 +01:00
renovate[bot]
85e34a3321
Update dependency ember-cli-babel to v7.23.1 (#511)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-27 19:02:13 +01:00
renovate[bot]
4da1e212ec
Update dependency bootstrap to v4.6.0 (#509)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-27 19:01:52 +01:00
Jeldrik Hanschke
a42e30ef2b
Merge pull request #515 from jelhan/dependabot/npm_and_yarn/socket.io-2.4.1
Bump socket.io from 2.3.0 to 2.4.1
2021-01-21 14:31:00 +01:00
Jeldrik Hanschke
d447bfed71
Merge pull request #512 from jelhan/renovate/sass-1.x-lockfile
Update dependency sass to v1.32.5
2021-01-21 14:30:50 +01:00
Jeldrik Hanschke
6fdfc1154a
Merge pull request #510 from jelhan/renovate/fs-extra-9.x-lockfile
Update dependency fs-extra to v9.1.0
2021-01-21 14:30:38 +01:00
Jeldrik Hanschke
7975efd382
Merge pull request #508 from jelhan/renovate/ember-fetch-8.x-lockfile
Update dependency ember-fetch to v8.0.4
2021-01-21 14:30:27 +01:00
Renovate Bot
1c2ee2aee5
Update dependency ember-fetch to v8.0.4 2021-01-21 09:06:42 +00:00
dependabot[bot]
0fc6331e8e
Bump socket.io from 2.3.0 to 2.4.1
Bumps [socket.io](https://github.com/socketio/socket.io) from 2.3.0 to 2.4.1.
- [Release notes](https://github.com/socketio/socket.io/releases)
- [Changelog](https://github.com/socketio/socket.io/blob/2.4.1/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io/compare/2.3.0...2.4.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-21 03:27:15 +00:00
Renovate Bot
9ebee16091
Update dependency sass to v1.32.5 2021-01-20 05:06:52 +00:00
Renovate Bot
d69779b48c
Update dependency fs-extra to v9.1.0 2021-01-19 18:56:42 +00:00
Jeldrik Hanschke
57d0c24cf1
Update dependency ember-power-calendar to v0.16.3 (#501)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-16 23:59:48 +01:00
Renovate Bot
6789c5ed3c
Update dependency ember-power-calendar to v0.16.3 2021-01-16 12:12:14 +00:00
renovate[bot]
e84fb40f49
Update dependency ember-page-title to v6 (#437)
* Update dependency ember-page-title to v6

* ember-page-title no longer requires ember-cli-head

ember-page-title no longer requires the usage of ember-cli-head.
Please remove {{head-layout}} from your application's application.hbs route template

Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
2021-01-16 13:11:19 +01:00
renovate[bot]
98341e88f3
Update dependency ember-auto-import to v1.10.1 (#489)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-16 12:55:17 +01:00
Jeldrik Hanschke
386a4c1f83
can not set type attribute using angle bracket invocation (#507) 2021-01-16 12:54:12 +01:00
renovate[bot]
0f27bad648
Update dependency ember-cli-mirage to v2 (#505)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-16 11:24:55 +01:00
renovate[bot]
7d3fcd77ee
Update dependency eslint to v7.18.0 (#506)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-16 11:24:48 +01:00
renovate[bot]
7582089a48
Update dependency release-it to v14.2.2 (#487)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-16 11:24:40 +01:00
renovate[bot]
5285a65dcb
Update dependency sass to v1.32.4 (#504)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-15 22:36:10 +01:00
renovate[bot]
5c3a740ee7
Update dependency eslint to v7.17.0 (#503)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-15 22:35:53 +01:00
renovate[bot]
58bc67aee2
Update dependency ember-template-lint to v2.16.0 (#502)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-15 22:35:33 +01:00
renovate[bot]
2bff2ae69b
Update dependency ember-cli-page-object to v1.17.4 (#486)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-15 22:34:46 +01:00
renovate[bot]
5f9257c78f
Update dependency qunit-dom to v1.6.0 (#483)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-15 22:34:21 +01:00
renovate[bot]
ea4ddc40ba
Update dependency ember-cli-flash to v2.1.0 (#500)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-14 23:54:36 +01:00
dependabot[bot]
b441b4c48a
Bump ini from 1.3.5 to 1.3.8 (#499)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-14 23:54:31 +01:00
renovate[bot]
e271d6a5f0
Update Glimmer.js packages to v1.0.3 (#493)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-14 23:54:16 +01:00
renovate[bot]
09f896f0be
Update dependency ember-source to v3.20.6 (#484)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-14 23:54:06 +01:00
renovate[bot]
353fa9a973
Update dependency ember-bootstrap-cp-validations to v2.1.0 (#492)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-14 21:05:45 +01:00
renovate[bot]
4beef54ee3
Update dependency ember-cli to ~3.24.0 (#496)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-14 21:05:18 +01:00
renovate[bot]
926476edc6
Update dependency eslint to v7.15.0 (#485)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-12-11 17:01:57 +01:00
renovate[bot]
3db1502b9b
Update dependency ember-template-lint to v2.15.0 (#495)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-12-11 17:01:33 +01:00
renovate[bot]
9a4667d58e
Update dependency sass to v1.30.0 (#490)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-12-11 16:21:38 +01:00
renovate[bot]
69f86ca90b
Update dependency ember-composable-helpers to v4.4.1 (#482)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-12-11 16:20:49 +01:00
renovate[bot]
fff213a830
Update dependency eslint to v7.13.0 (#480)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-14 15:50:36 +01:00
renovate[bot]
9890978323
Update dependency ember-load-initializers to v2.1.2 (#479)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-14 15:50:28 +01:00
renovate[bot]
89ce9978e3
Update dependency ember-moment to v8.0.1 (#481)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-09 09:29:10 +01:00
Jeldrik Hanschke
d35172c4f0
provide required credentials for BrowserStack (#478)
GitHub Secrets are not available in GitHub Action triggered by pull request
created from fork. But we want to run BrowserStack based tests for them
as well. Adding the credentials as clear text again as it was the case
before #467.
2020-11-05 18:58:03 +01:00
renovate[bot]
0eca654bb2
Update dependency vlucas/phpdotenv to v3.6.7 (#470)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-31 08:18:10 +01:00
Jeldrik Hanschke
58144bbf62
address deprecated arguments in Ember Bootstrap (#468) 2020-10-31 08:17:32 +01:00
Jeldrik Hanschke
4fd4333c3c
simplify autofocus implementation using a modifier (#465) 2020-10-27 22:34:08 +01:00
Jeldrik Hanschke
7da65be276
refactor deprecated {{title}} to {{page-title}} (#464) 2020-10-27 22:20:35 +01:00
Jeldrik Hanschke
524451d717
migrate BrowserStack tests from TravisCI to GitHub Actions (#467) 2020-10-27 22:20:20 +01:00
renovate[bot]
334a36d572
Update dependency release-it-lerna-changelog to v3 (#460)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-27 19:24:16 +01:00
renovate[bot]
99390415a2
Update dependency ember-power-calendar to v0.16.2 (#463)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-27 13:49:25 +01:00
renovate[bot]
3551d77766
Update dependency eslint to v7.12.0 (#462)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-27 13:49:14 +01:00
renovate[bot]
6ef1a66337
Update shivammathur/setup-php action to v2 (#459)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-22 14:53:37 +02:00
renovate[bot]
e83e8d651d
Update actions/checkout action to v2 (#458)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-22 14:52:59 +02:00
renovate[bot]
4927220a3d
Update dependency ember-power-calendar to v0.16.1 (#457)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-22 14:52:49 +02:00
renovate[bot]
0723b8a7a4
Update dependency ember-data to v3.12.6 (#451)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-20 10:58:05 +02:00
renovate[bot]
1f296dfe6b
Update dependency @ember/optional-features to v2 (#454)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-20 10:57:11 +02:00
renovate[bot]
dfb3620028
Update dependency ember-cli-app-version to v4 (#455)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-20 10:48:29 +02:00
renovate[bot]
aec406a13e
Update dependency ember-cli to ~3.22.0 (#450)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-18 23:18:27 +02:00
dependabot[bot]
fba86e2ec7
Bump http-proxy from 1.18.0 to 1.18.1 (#419)
Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/http-party/node-http-proxy/releases)
- [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/http-party/node-http-proxy/compare/1.18.0...1.18.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-18 22:55:12 +02:00
renovate[bot]
e3fb734069
Update dependency ember-cli-mirage to v1.1.8 (#427)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-18 22:54:38 +02:00
Jeldrik Hanschke
a58d81dc69
let RenovateBot manage all depdendencies (#449) 2020-10-18 22:54:00 +02:00
Jeldrik Hanschke
fee222e995
upgrade to Ember 3.20 (#447) 2020-10-18 22:38:37 +02:00
Jeldrik Hanschke
87d7b5d36d
Add Norwegian language and other updates from Weblate (#446)
* Translated using Weblate (Catalan)

Currently translated at 94.3% (133 of 141 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (141 of 141 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (141 of 141 strings)

Translated using Weblate (German)

Currently translated at 100.0% (141 of 141 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (141 of 141 strings)

Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
Translate-URL: https://hosted.weblate.org/projects/croodle/client/ca/
Translate-URL: https://hosted.weblate.org/projects/croodle/client/de/
Translate-URL: https://hosted.weblate.org/projects/croodle/client/es/
Translate-URL: https://hosted.weblate.org/projects/croodle/client/it/
Translation: Croodle/client

* Translated using Weblate (Italian)

Currently translated at 100.0% (141 of 141 strings)

Translated using Weblate (French)

Currently translated at 100.0% (141 of 141 strings)

Translated using Weblate (Spanish)

Currently translated at 95.7% (135 of 141 strings)

Translated using Weblate (German)

Currently translated at 100.0% (141 of 141 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (141 of 141 strings)

Translated using Weblate (French)

Currently translated at 100.0% (141 of 141 strings)

Translated using Weblate (German)

Currently translated at 100.0% (141 of 141 strings)

Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Translate-URL: https://hosted.weblate.org/projects/croodle/client/de/
Translate-URL: https://hosted.weblate.org/projects/croodle/client/es/
Translate-URL: https://hosted.weblate.org/projects/croodle/client/fr/
Translate-URL: https://hosted.weblate.org/projects/croodle/client/it/
Translation: Croodle/client

* Translated using Weblate (Norwegian Bokmål)

Currently translated at 75.8% (107 of 141 strings)

Translated using Weblate (English)

Currently translated at 100.0% (141 of 141 strings)

Added translation using Weblate (Norwegian Bokmål)

Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Translate-URL: https://hosted.weblate.org/projects/croodle/client/en/
Translate-URL: https://hosted.weblate.org/projects/croodle/client/nb_NO/
Translation: Croodle/client

* Translated using Weblate (Spanish)

Currently translated at 95.7% (135 of 141 strings)

Translated using Weblate (German)

Currently translated at 100.0% (141 of 141 strings)

Co-authored-by: Jeldrik Hanschke <admin@jhanschke.de>
Translate-URL: https://hosted.weblate.org/projects/croodle/client/de/
Translate-URL: https://hosted.weblate.org/projects/croodle/client/es/
Translation: Croodle/client

* Translated using Weblate (English)

Currently translated at 100.0% (141 of 141 strings)

Translation: Croodle/client
Translate-URL: https://hosted.weblate.org/projects/croodle/client/en/

* Translated using Weblate (Catalan)

Currently translated at 94.3% (133 of 141 strings)

Translation: Croodle/client
Translate-URL: https://hosted.weblate.org/projects/croodle/client/ca/

* Translated using Weblate (German)

Currently translated at 100.0% (141 of 141 strings)

Translation: Croodle/client
Translate-URL: https://hosted.weblate.org/projects/croodle/client/de/

* Translated using Weblate (Spanish)

Currently translated at 95.7% (135 of 141 strings)

Translation: Croodle/client
Translate-URL: https://hosted.weblate.org/projects/croodle/client/es/

* Translated using Weblate (French)

Currently translated at 100.0% (141 of 141 strings)

Translation: Croodle/client
Translate-URL: https://hosted.weblate.org/projects/croodle/client/fr/

* Translated using Weblate (Italian)

Currently translated at 100.0% (141 of 141 strings)

Translation: Croodle/client
Translate-URL: https://hosted.weblate.org/projects/croodle/client/it/

* Translated using Weblate (Norwegian Bokmål)

Currently translated at 75.8% (107 of 141 strings)

Translation: Croodle/client
Translate-URL: https://hosted.weblate.org/projects/croodle/client/nb_NO/

* integrate norwegian locale

Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
2020-10-18 22:11:19 +02:00
renovate[bot]
f7102469f6
Update dependency ember-cli-flash to v2 (#444)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-18 22:08:15 +02:00
Jeldrik Hanschke
9640a41f84
Merge pull request #406 from jelhan/renovate/ember-power-calendar-0.x
Update dependency ember-power-calendar to ^0.16.0
2020-10-18 17:53:11 +02:00
Jeldrik Hanschke
5411743c1a
Merge pull request #342 from jelhan/use-latest-safari
run tests against latest safari in BrowserStack
2020-10-18 17:51:40 +02:00
Renovate Bot
653dcdac1c
Update dependency ember-power-calendar to ^0.16.0 2020-10-17 22:40:54 +00:00
Jeldrik Hanschke
b7dba1c845
Merge pull request #443 from jelhan/renovate/ember-cli-flash-1.x-lockfile
Update dependency ember-cli-flash to v1.9.1
2020-10-18 00:38:11 +02:00
Jeldrik Hanschke
fe28f04d82
Merge pull request #440 from jelhan/renovate/release-it-14.x
Update dependency release-it to v14
2020-10-18 00:37:59 +02:00
Jeldrik Hanschke
05e3b0062a
Merge pull request #445 from jelhan/remove-duplicated-tests-on-travis-ci
do not run tests on TravisCI which are also run as GitHub Actions
2020-10-18 00:11:08 +02:00
Jeldrik Hanschke
4f3d3405c3 use latest safari in BrowserStack 2020-10-17 23:26:23 +02:00
Renovate Bot
73cf997705
Update dependency release-it to v14 2020-10-17 21:24:32 +00:00
Renovate Bot
d5355cb873
Update dependency ember-cli-flash to v1.9.1 2020-10-17 21:22:58 +00:00
Jeldrik Hanschke
a940a18bad
Merge pull request #438 from jelhan/renovate/ember-test-selectors-5.x
Update dependency ember-test-selectors to v5
2020-10-17 23:21:34 +02:00
Jeldrik Hanschke
e4698a3a74
Merge pull request #426 from jelhan/update-spanish-catalan-and-italian-translations
updated translations spanish, catalan, italian.

Original translations were provided by @j0rgeSD. @vmaffione provided some improvements for the Italian once.
2020-10-17 23:20:03 +02:00
Jeldrik Hanschke
d1677c67b6 do not run tests on TravisCI which are run as GitHub Actions
All tests except for BrowserStack tests were executed both on TravisCI
and as GitHub Actions. As GitHub Actions has been prooven to be stable
we can remove duplicated tests now and only run BrowserStack tests on
TravisCI. We should migrate them to GitHub Actions as well in a
follow-up merge request to reduce complexity.
2020-10-17 23:14:32 +02:00
Renovate Bot
cb9a6d9550
Update dependency ember-test-selectors to v5 2020-10-17 20:14:03 +00:00
Jeldrik Hanschke
3bc210a193
Apply suggestions from code review
The suggestions were provided by @vmaffione in a review of the merge request.
2020-10-17 22:13:53 +02:00
Jeldrik Hanschke
5b2aa02527
Update translations/it.yml 2020-10-17 22:12:46 +02:00
Jeldrik Hanschke
639790b33b
Merge pull request #439 from jelhan/renovate/ember-truth-helpers-3.x
Update dependency ember-truth-helpers to v3
2020-10-17 22:09:45 +02:00
Jeldrik Hanschke
39fed5e95b
Merge pull request #415 from jelhan/renovate/ember-cli-clipboard-0.x
Update dependency ember-cli-clipboard to ^0.15.0
2020-10-17 22:09:11 +02:00
Jeldrik Hanschke
8c531cde16
Merge pull request #435 from jelhan/renovate/ember-classic-decorator-2.x
Update dependency ember-classic-decorator to v2
2020-10-17 22:08:38 +02:00
Jeldrik Hanschke
4b0973996b
Merge pull request #432 from jelhan/renovate/release-it-lerna-changelog-2.x-lockfile
Update dependency release-it-lerna-changelog to v2.4.0
2020-10-17 22:08:04 +02:00
Renovate Bot
be6b35024a
Update dependency release-it-lerna-changelog to v2.4.0 2020-10-16 19:56:40 +00:00
Renovate Bot
50a0668a9a
Update dependency ember-cli-clipboard to ^0.15.0 2020-10-14 22:18:48 +00:00
Renovate Bot
3e12c07375
Update dependency ember-truth-helpers to v3 2020-10-14 20:48:21 +00:00
Jeldrik Hanschke
59328ab43b
Merge pull request #399 from jelhan/renovate/ember-intl-4.x-lockfile
Update dependency ember-intl to v4.4.1
2020-10-14 22:45:40 +02:00
Jeldrik Hanschke
e254db71eb
Merge pull request #429 from jelhan/renovate/ember-math-helpers-2.x-lockfile
Update dependency ember-math-helpers to v2.15.0
2020-10-14 22:44:47 +02:00
Jeldrik Hanschke
8d4acac882
Merge pull request #430 from jelhan/renovate/ember-page-title-5.x-lockfile
Update dependency ember-page-title to v5.2.3
2020-10-14 22:44:12 +02:00
Jeldrik Hanschke
b7779c4edb
Merge pull request #431 from jelhan/renovate/fs-extra-9.x-lockfile
Update dependency fs-extra to v9.0.1
2020-10-14 22:43:53 +02:00
Renovate Bot
6d2c701dca
Update dependency ember-classic-decorator to v2 2020-10-14 19:09:39 +00:00
Renovate Bot
c3a606c81b
Update dependency fs-extra to v9.0.1 2020-10-14 19:08:29 +00:00
Renovate Bot
9964ab0bee
Update dependency ember-page-title to v5.2.3 2020-10-14 19:07:40 +00:00
Renovate Bot
852cdde29c
Update dependency ember-math-helpers to v2.15.0 2020-10-14 19:07:14 +00:00
Renovate Bot
ff14e0a6ae
Update dependency ember-intl to v4.4.1 2020-10-14 19:06:48 +00:00
Jeldrik Hanschke
b872d5c51d
Merge pull request #434 from jelhan/renovate/ember-bootstrap-cp-validations-2.x
Update dependency ember-bootstrap-cp-validations to v2
2020-10-14 21:05:03 +02:00
Renovate Bot
ee5507905f
Update dependency ember-bootstrap-cp-validations to v2 2020-10-14 16:33:56 +00:00
Jeldrik Hanschke
17872949af
Merge pull request #401 from jelhan/renovate/ember-awesome-macros-6.x
Update dependency ember-awesome-macros to v6
2020-10-14 18:29:34 +02:00
Jeldrik Hanschke
ebc84f2769
Merge pull request #403 from jelhan/renovate/ember-composable-helpers-4.x
Update dependency ember-composable-helpers to v4
2020-10-14 18:29:12 +02:00
Jeldrik Hanschke
9223f5b8ad
Merge pull request #428 from jelhan/renovate/ember-cli-moment-shim-3.x-lockfile
Update dependency ember-cli-moment-shim to v3.8.0
2020-10-14 18:29:00 +02:00
Renovate Bot
eb8c8fddb7
Update dependency ember-composable-helpers to v4 2020-10-14 13:47:20 +00:00
Renovate Bot
82530e3a34
Update dependency ember-awesome-macros to v6 2020-10-14 13:47:01 +00:00
Renovate Bot
9d20a9a39a
Update dependency ember-cli-moment-shim to v3.8.0 2020-10-14 13:41:24 +00:00
Jeldrik Hanschke
534333b03c
Merge pull request #369 from jelhan/renovate/ember-composable-helpers-3.x-lockfile
Update dependency ember-composable-helpers to v3.2.0
2020-10-14 15:39:46 +02:00
Jeldrik Hanschke
9e88edc82a
Update dependency ember-classic-decorator to v1.0.8 (#373)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-14 15:39:27 +02:00
renovate[bot]
317b25677c
Update dependency eslint-plugin-node to v11.1.0 (#393)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-14 15:39:16 +02:00
renovate[bot]
c63bb2b225
Update dependency @glimmer/component to v1.0.2 (#422)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-14 15:38:59 +02:00
renovate[bot]
8877a622f7
Update dependency bootstrap to v4.5.3 (#423)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-14 15:38:52 +02:00
renovate[bot]
7412fc500e
Update dependency ember-cli-flash to v1.9.0 (#424)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-14 15:38:43 +02:00
Renovate Bot
91ce2c54e8
Update dependency ember-composable-helpers to v3.2.0 2020-10-13 19:04:43 +00:00
Renovate Bot
94b8188d0a
Update dependency ember-classic-decorator to v1.0.8 2020-10-13 19:02:51 +00:00
renovate[bot]
30f8693d76
Update dependency ember-bootstrap to v3.1.4 (#383)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-13 21:01:06 +02:00
jorge_sD
c70386612b updated translations spanish, catalan, italian. 2020-10-13 20:54:57 +02:00
Jeldrik Hanschke
646c4c5a64
add french translations provided by sharkyiwc (#425) 2020-10-13 20:53:06 +02:00
renovate[bot]
168a90d787
Update dependency ember-cli-page-object to v1.17.3 (#397)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-13 20:01:38 +02:00
renovate[bot]
38d8b265ff
Update dependency sass to v1.26.10 (#405)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-13 18:00:56 +02:00
renovate[bot]
60b0131a4a
Update dependency ember-cli-browserstack to v1.1.0 (#414)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-13 18:00:47 +02:00
renovate[bot]
f9e4143899
Update dependency ember-auto-import to v1.6.0 (#413)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-10-13 18:00:15 +02:00
renovate[bot]
b819124fb1
Update dependency vlucas/phpdotenv to ^3.6.6 (#386)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-07-10 23:34:31 +02:00
renovate[bot]
99198e9f0f
Update dependency release-it-lerna-changelog to v2.3.0 (#404)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-07-10 23:33:33 +02:00
renovate[bot]
d351a1012f
Update dependency bootstrap to v4.5.0 (#411)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-07-10 23:33:13 +02:00
Jeldrik Hanschke
03983fa294
drop support for PHP 7.1 (#410)
Also upgrades dependencies with composer upgrade.
2020-05-22 10:03:46 +02:00
314 changed files with 62409 additions and 23704 deletions

View file

@ -4,7 +4,6 @@
root = true
[*]
end_of_line = lf
charset = utf-8

View file

@ -5,5 +5,11 @@
Setting `disableAnalytics` to true will prevent any data from being sent.
*/
"disableAnalytics": false
"disableAnalytics": false,
/**
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
*/
"isTypeScriptProject": true
}

View file

@ -1,21 +1,14 @@
# unconventional js
/blueprints/*/files/
/vendor/
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/api
/coverage/
!.*
.*/
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try

View file

@ -1,55 +1,64 @@
'use strict';
module.exports = {
root: true,
parser: 'babel-eslint',
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
legacyDecorators: true
}
ecmaVersion: 'latest',
},
plugins: [
'ember'
],
plugins: ['ember', '@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:ember/recommended'
'plugin:ember/recommended',
'plugin:prettier/recommended',
],
env: {
browser: true,
},
rules: {
// Croodle is not compliant with some of the recommended rules yet.
// We should refactor the code step by step and enable them as soon
// as the code is compliant.
'ember/classic-decorator-no-classic-methods': 'warn',
'ember/no-controller-access-in-routes': 'warn',
'ember/no-observers': 'warn',
'ember/no-jquery': 'error',
'no-prototype-builtins': 'warn',
},
overrides: [
// ts files
{
files: ['**/*.ts'],
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
],
rules: {},
},
// node files
{
files: [
'.eslintrc.js',
'.template-lintrc.js',
'ember-cli-build.js',
'testem.js',
'blueprints/*/index.js',
'config/**/*.js',
'lib/*/index.js',
'server/**/*.js'
'./.eslintrc.js',
'./.prettierrc.js',
'./.stylelintrc.js',
'./.template-lintrc.js',
'./ember-cli-build.js',
'./testem.js',
'./blueprints/*/index.js',
'./config/**/*.js',
'./lib/*/index.js',
'./server/**/*.js',
],
parserOptions: {
sourceType: 'script'
},
env: {
browser: false,
node: true
node: true,
},
plugins: ['node'],
rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
// add your custom rules and overrides for node files here
// this can be removed once the following is fixed
// https://github.com/mysticatea/eslint-plugin-node/issues/77
'node/no-unpublished-require': 'off'
})
}
]
extends: ['plugin:n/recommended'],
},
{
// test files
files: ['tests/**/*-test.{js,ts}'],
extends: ['plugin:qunit/recommended'],
rules: {},
},
],
};

156
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,156 @@
name: Test Frontend and backend
# This workflow is triggered on pushes to the repository.
on:
push:
branches:
- master
- renovate/*
pull_request:
jobs:
lint:
name: lint
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install Dependencies
run: npm ci
- name: Lint
run: npm run lint
test-bundlesize:
name: test bundlesize
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install php
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
extensions: mbstring, zip
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install node modules
run: npm ci
- name: Run tests
run: npm run test:bundlesize
test-csp-header:
name: test CSP in .htaccess
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install node modules
run: npm ci
- name: Run tests
run: npm run test:csp-header
test-chrome:
name: test against Chrome
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install node modules
run: npm ci
- name: Install chrome browser
run: |
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
- name: Build with test environment
env:
CI: true
run: npm run build --environment test
- name: run tests in chrome
run: npm run test:ember --launch Chrome --path dist
test-firefox:
name: test against Firefox
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install node modules
run: npm ci
- name: Setup firefox
uses: browser-actions/setup-firefox@latest
with:
firefox-version: 102.0.1
- name: Build with test environment
env:
CI: true
run: npm run build --environment test
- name: run tests in firefox
run: npm run test:ember --launch Firefox --path dist
test-browserstack:
name: test against additional browser in BrowserStack
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install node modules
run: npm ci
- name: Build with test environment
env:
CI: true
run: npm run build --environment test
- name: 'BrowserStack Env Setup'
uses: 'browserstack/github-actions/setup-env@master'
with:
username: 'jeldrikhanschke1'
access-key: 'xaM9Uxurv2GyxFLKQXgj'
- name: 'Start BrowserStackLocal Tunnel'
uses: 'browserstack/github-actions/setup-local@master'
with:
local-testing: 'start'
local-logging-level: 'all-logs'
local-identifier: 'random'
- name: 'Running test on BrowserStack'
run: npm run test:ember --config-file testem.browserstack.js --path dist
- name: 'BrowserStackLocal Stop'
uses: browserstack/github-actions/setup-local@master
with:
local-testing: stop
test-backend:
name: Test php backend
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
steps:
- name: Checkout repository files
uses: actions/checkout@v4
- name: Install php
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, zip
- name: Install php dependencies
run: composer install
working-directory: ./api
- name: Run backend tests
run: ./vendor/bin/codecept run
working-directory: ./api

View file

@ -1,120 +0,0 @@
name: Test Frontend and backend
# This workflow is triggered on pushes to the repository.
on:
push:
branches:
- master
pull_request:
jobs:
lint-javascript:
name: lint javascript
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install node modules
run: yarn install
- name: Run lint
run: yarn lint:js
lint-templates:
name: lint ember templates
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install node modules
run: yarn install
- name: Run lint
run: yarn lint:hbs
test-bundlesize:
name: test bundlesize
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install php
uses: shivammathur/setup-php@v1
with:
php-version: '7.4'
extensions: mbstring, zip
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install node modules
run: yarn install
- name: Run tests
run: yarn test:bundlesize
test-csp-header:
name: test CSP in .htaccess
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install node modules
run: yarn install
- name: Run tests
run: yarn test:csp-header
test-chrome:
name: test in chromium
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install node modules
run: yarn install
- name: Install chrome browser
run: |
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
- name: Build with test environment
env:
CI: true
run: yarn build --environment test
- name: run tests in chrome
run: yarn test --launch Chrome --path dist
test-firefox:
name: test in firefox
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install node modules
run: yarn install
- name: Install Firefox
run: sudo apt-get install firefox
- name: Build with test environment
env:
CI: true
run: yarn build --environment test
- name: run tests in firefox
run: yarn test --launch Firefox --path dist
test-backend:
name: Test php backend
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.2', '7.3', '7.4']
steps:
- name: Checkout repository files
uses: actions/checkout@v2
- name: Install php
uses: shivammathur/setup-php@v1
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, zip
- name: Install php dependencies
run: composer install
working-directory: ./api
- name: Run backend tests
run: ./vendor/bin/codecept run
working-directory: ./api

17
.gitignore vendored
View file

@ -1,33 +1,34 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist/
/tmp/
/declarations/
/api/tests/_output/
/api/tests/_tmp/
/api/tests/_support/_generated/
# dependencies
/bower_components/
/node_modules/
/api/vendor/
# misc
/.env*
/.pnp*
/.sass-cache
/connect.lock
/.eslintcache
/coverage/
/libpeerconnection.log
/npm-debug.log*
/testem.log
/yarn-error.log
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/npm-shrinkwrap.json.ember-try
/package.json.ember-try
/package-lock.json.ember-try
/yarn.lock.ember-try
# broccoli-debug
/DEBUG/
# BrowserStack
/*.pid
/*.log

13
.prettierignore Normal file
View file

@ -0,0 +1,13 @@
# unconventional js
/blueprints/*/files/
# compiled output
/dist/
# misc
/coverage/
!.*
.*/
# ember-try
/.node_modules.ember-try/

12
.prettierrc.js Normal file
View file

@ -0,0 +1,12 @@
'use strict';
module.exports = {
overrides: [
{
files: '*.{js,ts}',
options: {
singleQuote: true,
},
},
],
};

View file

@ -14,5 +14,8 @@
},
"npm": {
"publish": false
},
"plugins": {
"@release-it-plugins/lerna-changelog": {}
}
}

View file

@ -1,37 +0,0 @@
{
"extends": [
"config:base"
],
"ignoreDeps": [
"@ember/jquery",
"@ember/optional-features",
"broccoli-asset-rev",
"ember-ajax",
"ember-cli",
"ember-cli-app-version",
"ember-cli-babel",
"ember-cli-dependency-checker",
"ember-cli-eslint",
"ember-cli-htmlbars",
"ember-cli-htmlbars-inline-precompile",
"ember-cli-inject-live-reload",
"ember-cli-sri",
"ember-cli-template-lint",
"ember-cli-uglify",
"ember-data",
"ember-export-application-global",
"ember-load-initializers",
"ember-maybe-import-regenerator",
"ember-qunit",
"ember-resolver",
"ember-source",
"ember-welcome-page",
"eslint-plugin-ember",
"loader.js",
"qunit-dom"
],
"ignorePaths": [
"lib/**"
],
"rangeStrategy": "update-lockfile"
}

8
.stylelintignore Normal file
View file

@ -0,0 +1,8 @@
# unconventional files
/blueprints/*/files/
# compiled output
/dist/
# addons
/.node_modules.ember-try/

15
.stylelintrc.js Normal file
View file

@ -0,0 +1,15 @@
'use strict';
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-standard-scss',
'stylelint-prettier/recommended',
],
rules: {
'declaration-block-no-duplicate-properties': null,
'no-descending-specificity': null,
'scss/no-global-function-names': null,
'selector-class-pattern': null,
},
};

View file

@ -1,10 +1,19 @@
'use strict';
module.exports = {
extends: 'recommended',
plugins: ['ember-template-lint-plugin-prettier'],
extends: ['recommended', 'ember-template-lint-plugin-prettier:recommended'],
rules: {
'no-implicit-this': {
allow: ['scroll-first-invalid-element-into-view-port'],
},
},
overrides: [
{
files: ['tests/integration/modifiers/*.js'],
rules: {
'require-input-label': false,
},
},
],
};

View file

@ -1,70 +0,0 @@
---
language: php
matrix:
include:
- env: TEST="API"
php: 7.3
- env: TEST="API"
php: 7.2
- env: TEST="API"
php: 7.1
- env: TEST="EMBER"
- env: TEST="BROWSER"
- env: TEST="BUNDLESIZE"
dist: trusty
sudo: false
addons:
chrome: stable
firefox: latest-esr
cache:
yarn: true
env:
global:
- "BROWSERSTACK_USERNAME=jeldrikhanschke1"
- "BROWSERSTACK_ACCESS_KEY=xaM9Uxurv2GyxFLKQXgj"
before_install:
# use a recent node version if ember build is tested
- if [ $TEST = "EMBER" ] || [ $TEST = "BROWSER" ] || [ $TEST = "BUNDLESIZE" ]; then nvm install --lts; fi
# provide yarn if ember build is tested
- if [ $TEST = "EMBER" ] || [ $TEST = "BROWSER" ] || [ $TEST = "BUNDLESIZE" ]; then curl -o- -L https://yarnpkg.com/install.sh | bash; fi
- if [ $TEST = "EMBER" ] || [ $TEST = "BROWSER" ] || [ $TEST = "BUNDLESIZE" ]; then export PATH=$HOME/.yarn/bin:$PATH; fi
install:
# install dependencies for client
- if [ $TEST = "EMBER" ] || [ $TEST = "BROWSER" ] || [ $TEST = "BUNDLESIZE" ]; then yarn install --no-interactive; fi
# install dependencies for api
- if [ $TEST = "API" ]; then cd api/ && composer install && cd ..; fi
before_script:
# http://php.net/manual/de/ini.core.php#ini.always-populate-raw-post-data
- if [ $TEST = "API" ]; then echo 'always_populate_raw_post_data = -1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi
# create a browser stack tunnel for cross-browser testing
- if [ $TEST = "BROWSER" ]; then node_modules/ember-cli/bin/ember browserstack:connect; fi
branches:
only:
- master
script:
# run frontend and integration tests
- if [ $TEST = "EMBER" ]; then yarn run lint:hbs; fi
- if [ $TEST = "EMBER" ]; then yarn run lint:js; fi
- if [ $TEST = "EMBER" ]; then yarn test; fi
# test that CSP headers in public/.htaccess are matching the ones configured in config/environment.js
- if [ $TEST = "EMBER" ]; then grep "`node_modules/ember-cli/bin/ember csp-headers --environment production --silent 2>&1 | sed 's/ $//'`" public/.htaccess || (echo "CSP headers in public/.htaccess does not match configuration" && exit 1); fi
# test against different browsers using sauce lab
- if [ $TEST = "BROWSER" ]; then yarn test --config-file testem.browserstack.js; fi
# test bundle size
- if [ $TEST = "BUNDLESIZE" ]; then yarn test:bundlesize; fi
# run api tests with composer
- if [ $TEST = "API" ]; then cd api/ && ./vendor/bin/codecept run && cd ..; fi
after_script:
# destroy the sauce tunnel
- if [ $TEST = "BROWSER" ]; then node_modules/ember-cli/bin/ember browserstack:disconnect; fi

View file

@ -1,3 +1,3 @@
{
"ignore_dirs": ["tmp", "dist"]
"ignore_dirs": ["dist"]
}

32
Dockerfile Normal file
View file

@ -0,0 +1,32 @@
# Croodle Dockerfile.
#
# https://github.com/fuerst/croodle-docker
#
# Version 1.0
FROM php:8.0-apache
MAINTAINER Bernhard Fürst, bernhard.fuerst@fuerstnet.de
# You may overwrite the version.
# Use a release tag from https://github.com/jelhan/croodle/releases.
ENV CROODLE_VERSION v0.7.0
WORKDIR /var/www/html
# Run Apache on unprivileged Port
RUN sed -i 's/80/8080/g' /etc/apache2/ports.conf
# Install Cron
RUN apt-get update && apt-get -y install -qq cron
ADD crontab /etc/cron.d/croodle
RUN chmod 0644 /etc/cron.d/croodle
RUN crontab /etc/cron.d/croodle
# Grab and expand release files.
# RUN rm -rf * \
# && curl -SL -o croodle.tgz https://github.com/jelhan/croodle/releases/download/${CROODLE_VERSION}/croodle-${CROODLE_VERSION}.tar.gz \
# && tar zxf croodle.tgz \
# && rm croodle.tgz \
# && chmod 777 data
RUN rm -rf *
COPY dist/ /var/www/html

View file

@ -1,8 +1,7 @@
# Croodle
[![Build Status](https://travis-ci.org/jelhan/croodle.svg?branch=master)](https://travis-ci.org/jelhan/croodle)
[![Build Status](https://github.com/jelhan/croodle/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/jelhan/croodle/actions/workflows/ci.yml?query=branch%3Amaster)
[![Code Climate](https://codeclimate.com/github/jelhan/croodle/badges/gpa.svg)](https://codeclimate.com/github/jelhan/croodle)
[![devDependency Status](https://david-dm.org/jelhan/croodle/dev-status.svg)](https://david-dm.org/jelhan/croodle?type=dev)
Croodle is an end-to-end encrypted web application to schedule a date or to do a poll on a any topic. All data like title, description, number and labels of options, available answers and names of users and their selections are encrypted/decrypted in the browser using strong 256-bit AES encryption.
@ -23,7 +22,7 @@ Theoretically you could also check for an attack like this by analysing the sour
## Requirements
Croodle is designed to have as few as possible requirements on the server it is running on. Croodle runs on almost every web space with PHP >= 5.6. Croodle stores the data in textfiles, so there is no need for a database server like MySQL.
Croodle is designed to have as few as possible requirements on the server it is running on. Croodle runs on almost every web space with PHP >= 7.2. Croodle stores the data in textfiles, so there is no need for a database server like MySQL.
Due to security reasons you should have TLS encryption enabled and provide a valid certificate. (see the [security notice](#security-notice))
@ -31,14 +30,14 @@ Due to security reasons you should have TLS encryption enabled and provide a val
Production builds are provided as github [release assets](https://github.com/jelhan/croodle/releases).
If you like to build yourself you have to install [yarn](https://yarnpkg.com/), [ember-cli](http://www.ember-cli.com/) and [composer](https://getcomposer.org/) before.
If you like to build yourself you have to install [node](https://nodejs.org/), [ember-cli](http://www.ember-cli.com/) and [composer](https://getcomposer.org/) before. It's recommended using [volta](https://volta.sh/) to ensure a compatible and tested node version is used.
```shell
git clone git@github.com:jelhan/croodle.git
cd croodle
yarn install
npm install
cd api/ && composer install --no-dev && cd ..
yarn build --prod
npm run build
```
Afterwards copy all files in `/dist` folder to your werbserver.
@ -65,15 +64,26 @@ If source files are changing, a rebuild and reload is triggered.
By default Croodle uses an api mock in development. Since that one
does not persist records all polls are gone after a reload.
If you like to test against the real API, run api via php built-in web
server: `php -S 127.0.0.1:8080 -t dist/`
If you like to test against the real API, you should run the API
using php built-in web server locally:
```sh
php -S 127.0.0.1:8080 -t dist/
```
Afterwards start ember-cli development server using `--proxy` option:
`ember server --proxy http://127.0.0.1:8080`.
```sh
ember server --proxy http://127.0.0.1:8080
```
Ember-cli clears dist folder on each rebuild. If you like to keep
created polls over rebuild, configure api to use a non default folder
to save your polls:
`CROODLE__DATA_DIR=/tmp/croodle_data php -S 127.0.0.1:8080 -t dist/`
```sh
CROODLE__DATA_DIR=/tmp/croodle_data php -S 127.0.0.1:8080 -t dist/
```
## Running tests
@ -96,9 +106,6 @@ without `--no-dev` option).
## Credits
Continous Integration powered by<br>
<a href="https://travis-ci.com/"><img src="https://travis-ci.com/images/logos/TravisCI-Full-Color.png" height="50"></a>
Cross-browser testing provided by<br>
<a href="https://www.browserstack.com"><img src="docs/Browserstack-logo.svg" height="50"></a>

View file

@ -32,14 +32,14 @@ Once the prep work is completed, the actual release is straight forward:
* First ensure that you have installed the project dependencies:
```sh
yarn install
npm ci
```
* Second, do your release:
```sh
export GITHUB_AUTH="github-personal-access-token"
yarn release
npm run release
```
[release-it](https://github.com/release-it/release-it/) manages the actual

View file

@ -177,11 +177,6 @@ class Model {
}
$data = self::convertFromStorage($storageObject);
if(method_exists($model, 'restoreLegacySupportHook')) {
$model->restoreLegacySupportHook($data);
}
$properties = array_merge(
static::ENCRYPTED_PROPERTIES,
static::PLAIN_PROPERTIES,

View file

@ -6,7 +6,6 @@ require_once 'user.php';
class Poll extends model {
const ENCRYPTED_PROPERTIES = [
'anonymousUser',
'answers',
'answerType',
'creationDate',
'description',
@ -126,20 +125,4 @@ class Poll extends model {
return false;
}
}
protected function restoreLegacySupportHook(&$data) {
if (!isset($data->version) || $data->version === 'v0.3-0') {
if (isset($data->poll) && is_object($data->poll)) {
$data = $data->poll;
}
foreach($data as $key => $value) {
if (strpos($key, 'encrypted') === 0) {
$newKey = lcfirst(substr($key, 9));
$data->$newKey = $data->$key;
unset($data->$key);
}
}
}
}
}

View file

@ -9,7 +9,7 @@ class User extends Model {
'name',
'selections'
];
const PLAIN_PROPERTIES = [
'poll',
'version'
@ -17,15 +17,15 @@ class User extends Model {
protected function generateNewId() {
$userDir = $this->getDir();
// check if user folder exists
if (!file_exists($userDir)) {
return $this->get('poll') . '_0';
}
// get all files in user folder
$files = scandir($userDir);
// get highest existing id
$highestId = 0;
foreach ($files as $f) {
@ -50,9 +50,9 @@ class User extends Model {
}
if (!Poll::isValidId($pollId)) {
throw new Exception('cound not get a valid id when getPollDir was called');
throw new Exception('Could not get a valid id when getPollDir was called');
}
return DATA_FOLDER . $pollId . '/';
}
@ -69,25 +69,9 @@ class User extends Model {
public static function isValidId($id) {
$parts = explode('_', $id);
return count($parts) === 2 &&
Poll::isValidId($parts[0]) &&
intval($parts[1]) == $parts[1];
}
protected function restoreLegacySupportHook(&$data) {
if (!isset($data->version) || $data->version === 'v0.3-0') {
if (isset($data->user) && is_object($data->user)) {
$data = $data->user;
}
foreach($data as $key => $value) {
if (strpos($key, 'encrypted') === 0) {
$newKey = lcfirst(substr($key, 9));
$data->$newKey = $data->$key;
unset($data->$key);
}
}
}
}
}

View file

@ -1,25 +1,25 @@
actor: Tester
paths:
tests: tests
log: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
tests: tests
log: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
settings:
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
error_level: E_ALL & ~E_DEPRECATED
extensions:
enabled:
- Codeception\Extension\RunFailed
- Codeception\Extension\PhpBuiltinServer
- CleanUpExtension
config:
Codeception\Extension\PhpBuiltinServer:
hostname: localhost
port: 8000
documentRoot: .
startDelay: 1
phpIni: /etc/php5/apache2/php.ini
enabled:
- Codeception\Extension\RunFailed
- Codeception\Extension\PhpBuiltinServer
- CleanUpExtension
config:
Codeception\Extension\PhpBuiltinServer:
hostname: localhost
port: 8000
documentRoot: .
startDelay: 1
params:
- .env.testing
- .env.testing

View file

@ -1,17 +1,24 @@
{
"require": {
"php": "^7.1",
"slim/slim": "^3.5"
"php": "^7.2",
"slim/slim": "3.12.4"
},
"require-dev": {
"flow/jsonpath": "^0.5.0",
"vlucas/phpdotenv": "^3.6.0",
"codeception/codeception": "^3.1",
"codeception/phpbuiltinserver": "^1.5"
"flow/jsonpath": "0.5.0",
"vlucas/phpdotenv": "3.6.10",
"codeception/codeception": "3.1.3",
"codeception/phpbuiltinserver": "@dev"
},
"config": {
"platform": {
"php": "7.1"
"php": "7.2"
}
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/jelhan/PhpBuiltinServer.git",
"reference":"support-codeception-error-level-and-memory-limit-settings"
}
]
}

2601
api/composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,34 @@
<?php
$pollId = substr(md5(__FILE__), 0, 10);
$pollJson = '{"anonymousUser":"{\"iv\":\"gVHZSXyMm10Fn+kDooa7uw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"GJsQQYA7TdAa+v3Rvg==\"}","answers":"{\"iv\":\"aK1JcI3viLPIlOO45K+ePA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"Bx4SRcww+hJ46NIiVcWBUZHADADX\/XPsxXMx4XzMQZWqu6M0690D4oTflSRJoqxe0egxdfMOUxuWhmACG\/UYXSYJQjcSg+QTq6KJbaXG+SvsCMZ7iz12a\/uf9lXyiag4IbLldgL4vE3LfZO6oih\/o\/yG4hechjNdSkqUa2IvsRbXWB2aHen6a5Ch5WjqWrr4xRRrukPvf7aumilT2Cf0LswHJ2fwYNilylV0h9oegKYp+qWphm4SL8x2ogRemSCt7u7ByEOwZV0w6D9bz9RvGLTRRLJaLIm\/VlE3k7R6Hz1vyps=\"}","answerType":"{\"iv\":\"ILkAzgUfAGNUtLr7CbEJEQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"nMOp+QApQGgP9dwefNpi\"}","creationDate":"{\"iv\":\"6tWbieK03uXUR+E0AMbs0A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"YkkLVBkFyx4xFldZ7qnDESG0teHJmXaPMUB05p9L0xUIMg==\"}","description":"{\"iv\":\"fWvHh47So4WBNfEHXrwLiA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"5W7nauOakSoFD52V\"}","expirationDate":"{\"iv\":\"HRsMvEQaoCp8QdqBGHevnA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"LXYamNRDyhIY5xY+CLqI4GHbocc9NoHQtePKU9fHpJn9zg==\"}","forceAnswer":"{\"iv\":\"bh4iZ4pKe0GnXcM764702g==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"q5VBynWGotXRrc2P\"}","isDateTime":"{\"iv\":\"mlDCtvsJZaDlZD9kqfJHuA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"53g42C6Am+0s25\/DsA==\"}","options":"{\"iv\":\"ZneP\/x45NGh\/DC26GI4kvg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"4MvV9SNQq2dB6b\/MdX47R0KaRSfyZOZMEVUFDv7G3\/EcDBv7Z0pgSU9JXoF8BoSOz40rYrRtTw==\"}","pollType":"{\"iv\":\"j3P6eN0ZmNMMxLTAVD6gjQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"opwiZHAQi+I8R5HDxLfLK59DcQ==\"}","timezone":"{\"iv\":\"HKkSqcJONggGT9QQ+jZdUg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"BANN8sJlk8JK9A==\"}","title":"{\"iv\":\"4DX7dAJt7JIBHaR1V0Ct8A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"f1VUZf69nB94TF3\/HA==\"}","version":"v0.3.0+0ae62f31","serverExpirationDate":"2015-11-22T20:35:03.764Z"}';
$userJson = '{"user":{"name":"{\"iv\":\"kizIqK7FPNmRuQB7VHsMOw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"UsYMzrww3HKR8vl2TKVE\"}","selections":"{\"iv\":\"hRmiZagEhQVhw2cg6UJNrg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"2zIPGpiSC6wJHRoAMYBFPXx3qmlZg0Z/Jt/15mY+sHPLCqoAn97TKGN6KIvl/5gmgCFqLQFNo6uppCTUhljoV5y2kMtGvm0g3+NdpcejWGOeMACDPcp1mpXII87ZTfC6WrtxcWCB6UGYN8EynOdndFTGp+WVZnXCCya7YPThk/QRwoHoPWS6+TJFT9WeHV4i4kUIg2K3kdz3Op7S/c7l7KbOc8GsyjZzv0bRDnAm68/+FlJyZnvfMfU8vTxExsIsd0pBy4JBV4hg9SlCPectb5BAvBCULLDPA08prf262RUmVKJ+M3P1+5KkBQcnQwnUW/fzAQ7lqA==\"}","creationDate":"{\"iv\":\"xqdDY/A7MHLeAsoU9S/j+A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"TQOhbjveZbvdiyYpxfwNyu5pi1PLia9FApJJRmr3QoyrWA==\"}","version":"v0.3.0+0ae62f31","poll":"' . $pollId . '"}}';
$pollJson = <<<EOD
{
"anonymousUser": "{\"iv\":\"gVHZSXyMm10Fn+kDooa7uw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"GJsQQYA7TdAa+v3Rvg==\"}",
"answerType": "{\"iv\":\"ILkAzgUfAGNUtLr7CbEJEQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"nMOp+QApQGgP9dwefNpi\"}",
"creationDate": "{\"iv\":\"6tWbieK03uXUR+E0AMbs0A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"YkkLVBkFyx4xFldZ7qnDESG0teHJmXaPMUB05p9L0xUIMg==\"}",
"description": "{\"iv\":\"fWvHh47So4WBNfEHXrwLiA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"5W7nauOakSoFD52V\"}",
"expirationDate": "{\"iv\":\"HRsMvEQaoCp8QdqBGHevnA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"LXYamNRDyhIY5xY+CLqI4GHbocc9NoHQtePKU9fHpJn9zg==\"}",
"forceAnswer": "{\"iv\":\"bh4iZ4pKe0GnXcM764702g==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"q5VBynWGotXRrc2P\"}",
"isDateTime": "{\"iv\":\"mlDCtvsJZaDlZD9kqfJHuA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"53g42C6Am+0s25/DsA==\"}",
"options": "{\"iv\":\"ZneP/x45NGh/DC26GI4kvg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"4MvV9SNQq2dB6b/MdX47R0KaRSfyZOZMEVUFDv7G3/EcDBv7Z0pgSU9JXoF8BoSOz40rYrRtTw==\"}",
"pollType": "{\"iv\":\"j3P6eN0ZmNMMxLTAVD6gjQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"opwiZHAQi+I8R5HDxLfLK59DcQ==\"}",
"timezone": "{\"iv\":\"HKkSqcJONggGT9QQ+jZdUg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"BANN8sJlk8JK9A==\"}",
"title": "{\"iv\":\"4DX7dAJt7JIBHaR1V0Ct8A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"f1VUZf69nB94TF3/HA==\"}",
"version": "v0.3.0+0ae62f31",
"serverExpirationDate": "2015-11-22T20:35:03.764Z"
}
EOD;
$userJson = <<<EOD
{
"user": {
"name": "{\"iv\":\"kizIqK7FPNmRuQB7VHsMOw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"UsYMzrww3HKR8vl2TKVE\"}",
"selections": "{\"iv\":\"hRmiZagEhQVhw2cg6UJNrg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"2zIPGpiSC6wJHRoAMYBFPXx3qmlZg0Z/Jt/15mY+sHPLCqoAn97TKGN6KIvl/5gmgCFqLQFNo6uppCTUhljoV5y2kMtGvm0g3+NdpcejWGOeMACDPcp1mpXII87ZTfC6WrtxcWCB6UGYN8EynOdndFTGp+WVZnXCCya7YPThk/QRwoHoPWS6+TJFT9WeHV4i4kUIg2K3kdz3Op7S/c7l7KbOc8GsyjZzv0bRDnAm68/+FlJyZnvfMfU8vTxExsIsd0pBy4JBV4hg9SlCPectb5BAvBCULLDPA08prf262RUmVKJ+M3P1+5KkBQcnQwnUW/fzAQ7lqA==\"}",
"creationDate": "{\"iv\":\"xqdDY/A7MHLeAsoU9S/j+A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"TQOhbjveZbvdiyYpxfwNyu5pi1PLia9FApJJRmr3QoyrWA==\"}",
"version": "v0.3.0+0ae62f31",
"poll": "$pollId"
}
}
EOD;
$pollDir = 'tests/_tmp/data/' . $pollId . '/';
$userDir = $pollDir . 'user/';

View file

@ -1,6 +1,23 @@
<?php
$pollJson = '{"poll":{"title":"{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}","description":"{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}","pollType":"{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}","answerType":"{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO/yFmk\"}","answers":"{\"iv\":\"WRdAwEa0DF+E83ginLYtPw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"Oaer31ct2PXkmXkzJ1EXRPM3LMf6vGfzMZqjODwey4f7EhqSCUhYov+N7AZKCAAXYVS4WR84kKizxXBK2PQBSFrlB3Bll74ED9ZzRJSJD00otMG9BbgUR90aFws+1jMBP5vpti9+POsii85zLbDPkNg/Th/C4Ufv5YWwg/4ZV0bFMyOgfdjtOWaG5YAMTGUIkz9U9+VCesYJQaTb497qTD/Wmtz8J/2pUxdL5/b5xkdh2DJ4/N5q0Kz/CEbaoKwbexnQDlSr3ldlIhs7UmBjC9gkpgG2l9fu6a0VZFBE8hvzYrw=\"}","options":"{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs/Jm5XK/thQW0phxKd0OxKt9NZ3FE/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}","creationDate":"{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p/ANinfanE/51DbcDNw==\"}","forceAnswer":"{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}","anonymousUser":"{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}","timezone":"{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}","expirationDate":"{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}","serverExpirationDate":"2015-11-22T22:05:15.065Z","version":"v0.3.0+0ae62f31"}}';
$pollJson = <<<EOD
{
"poll": {
"title": "{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}",
"description": "{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}",
"pollType": "{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}",
"answerType": "{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO/yFmk\"}",
"options": "{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs/Jm5XK/thQW0phxKd0OxKt9NZ3FE/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}",
"creationDate": "{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p/ANinfanE/51DbcDNw==\"}",
"forceAnswer": "{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}",
"anonymousUser": "{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}",
"timezone": "{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}",
"expirationDate": "{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}",
"serverExpirationDate": "2015-11-22T22:05:15.065Z",
"version": "v0.3.0+0ae62f31"
}
}
EOD;
$I = new ApiTester($scenario);
$I->wantTo('create a poll');

View file

@ -1,8 +1,34 @@
<?php
$pollId = substr(md5(__FILE__), 0, 10);
$pollJson = '{"anonymousUser":"{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}","answers":"{\"iv\":\"WRdAwEa0DF+E83ginLYtPw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"Oaer31ct2PXkmXkzJ1EXRPM3LMf6vGfzMZqjODwey4f7EhqSCUhYov+N7AZKCAAXYVS4WR84kKizxXBK2PQBSFrlB3Bll74ED9ZzRJSJD00otMG9BbgUR90aFws+1jMBP5vpti9+POsii85zLbDPkNg\/Th\/C4Ufv5YWwg\/4ZV0bFMyOgfdjtOWaG5YAMTGUIkz9U9+VCesYJQaTb497qTD\/Wmtz8J\/2pUxdL5\/b5xkdh2DJ4\/N5q0Kz\/CEbaoKwbexnQDlSr3ldlIhs7UmBjC9gkpgG2l9fu6a0VZFBE8hvzYrw=\"}","answerType":"{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO\/yFmk\"}","creationDate":"{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p\/ANinfanE\/51DbcDNw==\"}","description":"{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}","expirationDate":"{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}","forceAnswer":"{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}","isDateTime":"{\"iv\":\"3y9OmTJDG0mLqU5zLoZwgQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"yyGaGitGrunDSpsRpw==\"}","options":"{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs\/Jm5XK\/thQW0phxKd0OxKt9NZ3FE\/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}","pollType":"{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}","timezone":"{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}","title":"{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}","version":"v0.3.0+0ae62f31","serverExpirationDate":"2015-11-22T22:05:15.065Z"}';
$userJson = '{"user":{"name":"{\"iv\":\"kizIqK7FPNmRuQB7VHsMOw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"UsYMzrww3HKR8vl2TKVE\"}","selections":"{\"iv\":\"hRmiZagEhQVhw2cg6UJNrg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"2zIPGpiSC6wJHRoAMYBFPXx3qmlZg0Z/Jt/15mY+sHPLCqoAn97TKGN6KIvl/5gmgCFqLQFNo6uppCTUhljoV5y2kMtGvm0g3+NdpcejWGOeMACDPcp1mpXII87ZTfC6WrtxcWCB6UGYN8EynOdndFTGp+WVZnXCCya7YPThk/QRwoHoPWS6+TJFT9WeHV4i4kUIg2K3kdz3Op7S/c7l7KbOc8GsyjZzv0bRDnAm68/+FlJyZnvfMfU8vTxExsIsd0pBy4JBV4hg9SlCPectb5BAvBCULLDPA08prf262RUmVKJ+M3P1+5KkBQcnQwnUW/fzAQ7lqA==\"}","creationDate":"{\"iv\":\"xqdDY/A7MHLeAsoU9S/j+A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"TQOhbjveZbvdiyYpxfwNyu5pi1PLia9FApJJRmr3QoyrWA==\"}","version":"v0.3.0+0ae62f31","poll":"' . $pollId . '"}}';
$pollJson = <<<EOD
{
"anonymousUser": "{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}",
"answerType": "{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO/yFmk\"}",
"creationDate": "{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p/ANinfanE/51DbcDNw==\"}",
"description": "{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}",
"expirationDate": "{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}",
"forceAnswer": "{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}",
"isDateTime": "{\"iv\":\"3y9OmTJDG0mLqU5zLoZwgQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"yyGaGitGrunDSpsRpw==\"}",
"options": "{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs/Jm5XK/thQW0phxKd0OxKt9NZ3FE/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}",
"pollType": "{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}",
"timezone": "{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}",
"title": "{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}",
"version": "v0.3.0+0ae62f31",
"serverExpirationDate": "2015-11-22T22:05:15.065Z"
}
EOD;
$userJson = <<<EOD
{
"user": {
"name": "{\"iv\":\"kizIqK7FPNmRuQB7VHsMOw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"UsYMzrww3HKR8vl2TKVE\"}",
"selections": "{\"iv\":\"hRmiZagEhQVhw2cg6UJNrg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"2zIPGpiSC6wJHRoAMYBFPXx3qmlZg0Z/Jt/15mY+sHPLCqoAn97TKGN6KIvl/5gmgCFqLQFNo6uppCTUhljoV5y2kMtGvm0g3+NdpcejWGOeMACDPcp1mpXII87ZTfC6WrtxcWCB6UGYN8EynOdndFTGp+WVZnXCCya7YPThk/QRwoHoPWS6+TJFT9WeHV4i4kUIg2K3kdz3Op7S/c7l7KbOc8GsyjZzv0bRDnAm68/+FlJyZnvfMfU8vTxExsIsd0pBy4JBV4hg9SlCPectb5BAvBCULLDPA08prf262RUmVKJ+M3P1+5KkBQcnQwnUW/fzAQ7lqA==\"}",
"creationDate": "{\"iv\":\"xqdDY/A7MHLeAsoU9S/j+A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"TQOhbjveZbvdiyYpxfwNyu5pi1PLia9FApJJRmr3QoyrWA==\"}",
"version": "v0.3.0+0ae62f31",
"poll": "$pollId"
}
}
EOD;
$pollDir = TEST_DATA_DIR . $pollId . '/';
$usersDir = $pollDir . 'users/';

View file

@ -1,7 +1,23 @@
<?php
$pollId = substr(md5(__FILE__), 0, 10);
$pollJson = '{"anonymousUser":"{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}","answers":"{\"iv\":\"WRdAwEa0DF+E83ginLYtPw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"Oaer31ct2PXkmXkzJ1EXRPM3LMf6vGfzMZqjODwey4f7EhqSCUhYov+N7AZKCAAXYVS4WR84kKizxXBK2PQBSFrlB3Bll74ED9ZzRJSJD00otMG9BbgUR90aFws+1jMBP5vpti9+POsii85zLbDPkNg\/Th\/C4Ufv5YWwg\/4ZV0bFMyOgfdjtOWaG5YAMTGUIkz9U9+VCesYJQaTb497qTD\/Wmtz8J\/2pUxdL5\/b5xkdh2DJ4\/N5q0Kz\/CEbaoKwbexnQDlSr3ldlIhs7UmBjC9gkpgG2l9fu6a0VZFBE8hvzYrw=\"}","answerType":"{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO\/yFmk\"}","creationDate":"{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p\/ANinfanE\/51DbcDNw==\"}","description":"{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}","expirationDate":"{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}","forceAnswer":"{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}","isDateTime":"{\"iv\":\"3y9OmTJDG0mLqU5zLoZwgQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"yyGaGitGrunDSpsRpw==\"}","options":"{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs\/Jm5XK\/thQW0phxKd0OxKt9NZ3FE\/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}","pollType":"{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}","timezone":"{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}","title":"{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}","version":"v0.3.0+0ae62f31","serverExpirationDate":"2015-01-01T00:00:00.000Z"}';
$pollJson = <<<EOD
{
"anonymousUser": "{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}",
"answerType": "{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO/yFmk\"}",
"creationDate": "{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p/ANinfanE/51DbcDNw==\"}",
"description": "{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}",
"expirationDate": "{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}",
"forceAnswer": "{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}",
"isDateTime": "{\"iv\":\"3y9OmTJDG0mLqU5zLoZwgQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"yyGaGitGrunDSpsRpw==\"}",
"options": "{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs/Jm5XK/thQW0phxKd0OxKt9NZ3FE/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}",
"pollType": "{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}",
"timezone": "{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}",
"title": "{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}",
"version": "v0.3.0+0ae62f31",
"serverExpirationDate": "2015-01-01T00:00:00.000Z"
}
EOD;
$pollDir = TEST_DATA_DIR . $pollId . '/';
$usersDir = $pollDir . 'users/';

View file

@ -1,88 +0,0 @@
<?php
$pollId = substr(md5(__FILE__), 0, 10);
$pollJson = '{"poll":{"encryptedTitle":"{\"iv\":\"G1QGS+OHz5Z6Y4Og/3UFRQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"rgMRyJep4e0+Jj+K0ZTqbJS1j/gaouoTCoSHgXFdccn5L9gHBo1JO7Sl\"}","encryptedDescription":"{\"iv\":\"StcBqdGghIip/N3gLFmTMQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"5fgh7XABR7OifXoqHxE+c89mnVwkKUAG+x7D+BOGzoZK8dGT\"}","encryptedPollType":"{\"iv\":\"JYFdfUTb6xLWja302/bAKQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"wW+J3cGGF1tQUNfxt4gENDcZXQ==\"}","encryptedAnswerType":"{\"iv\":\"i0lDlvIVg2Le8pBSb47CIA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"+LNTLmILvxsN1X6E+vWa\"}","encryptedAnswers":"{\"iv\":\"xQV29b/F+gvlLl0zDCB7yw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"n5CwLLhSE5d28NU2/rOB7o6tXXWdDE7/uPr951Rr2ZQsmhsadmVwYE0K3Cxt+Hif4Am1jliS+PFjgVralrsSB00vlIH53wvDqQmNdk1Q/2zIebsVhHueamL4REyXn+18uVrjRarioojwOPYJLxNJHh0kPHATd0TgJxTb87RXgqUvAr1xc6DL7hY+fIbGoa6Otzt+OqIPhRTpaL+My1TYFXWQSlJxpPVSOILe1G/y6wg3Cp1lx4aFdHmGOGmrW+EF5pW9XrIz4A+3kNapSyUsDyuMk8wejrJpRHNcFpIlyRkxgUU=\"}","encryptedOptions":"{\"iv\":\"ruAw1xvAVLh9D19ngrEDgw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"pl1JeMRamWBuScBb1QOT9eqheJG2KD3y8RjtoPhNVid90wmoOQDm6WGtwt+gz6QXQEWUmIIXt8lyAJTH7updSnceW1SihfDi7xMmPTOf/338uSt3RdA2q+F+skiT14gheXHMtSFQaeVGvS8QfDXQfJBY9zJYp+On\"}","encryptedCreationDate":"{\"iv\":\"H/eApLF+Ja7ebX/1tPg7+w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"8BhpHOrgM3L7XftckMdkXSFzXi2evkfheanKfcjMFxzYsg==\"}","encryptedForceAnswer":"{\"iv\":\"DapF8f4GhKPORoIrDPiRXg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"WfPqwu5yBkhrHJWR\"}","encryptedAnonymousUser":"{\"iv\":\"sIclbapCBCxkHi0QrlCqOA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"SXyhfk7DVgfVKfp7Kw==\"}","encryptedIsDateTime":"{\"iv\":\"CbB/QEzDlENL3qRK2ZjWxA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"TD0rnzzHaawAWyUP\"}","encryptedTimezone":"{\"iv\":\"wQQXNefWW5QC9VZ1KkQQmA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"5Zkx8f0WQAgBBG1s0DcxoHeA5Dc/fEI=\"}","version":"v0.3-0"}}';
$user1Json = '{"user":{"encryptedName":"{\"iv\":\"wibexCADUTTMP8vjmegTwA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"r6Vu5fzAgvXctXaCx70/Pr1ldaOE\"}","encryptedSelections":"{\"iv\":\"gCMgC5Rie++L3s42RGzQJg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"jTmkMEkFTUiy8vGJinXAUmi7u8RNQ/FnLgnd+CMKL2XtqjeZuXgfMMDAdTEZnzNQTK7p/IhHBZrcrksUTnWkv68+f76msMs3rOqnYi7jhVL7O7NZMVNXysAgzalrQ+78Zz8TqoJ1qIARksTTCOi7Md07XKkYptCr3QUu0r8kfgk3KbGDuIE3tS4gGuB5CLKuPfFcbE0DjWAcr9IIEXpSPgjzJyEAx3bDd89ZfbRE0RaMoAR7Vqx+L3Hs6pXoUSbtnBJOQypNQYqUYycWA/kxCcuQEBlHwIR5qq7c9VsXNBG9SfGSA62scmbg6pxVXd3jEyTaxw3+B5r705mpMgAEY6NiJykob34x8LThdJP7XZKfe/tyczcKAlcQtJ7ocQsac0l1gRLK6eKHcNs8I3Zzi5iBzyqZtg0OVHzI8NiYpjwvg7piTHawsujAZIYkw/S4Pt29wkbb/heWpUsdJOF2xlfYYkTHnrPbX5jwZbNIgA==\"}","encryptedCreationDate":"{\"iv\":\"NGuUKkuLbabetQG5co01ZA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"ratk7+pQ14nax+slSf7ttOk2IIbQx7W2iu3I5VuUIR8RGg==\"}","version":"v0.3-0","poll":"gpwW7uZhbP"}}';
$user2Json = '{"user":{"encryptedName":"{\"iv\":\"GsRvWloC3GYi+MoOCUQ1vg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"0upVYhihQhWZcWpXcx5xKGHOTKTcVptz\"}","encryptedSelections":"{\"iv\":\"1FQ0Bf91k3JQbr23AhTszg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"u6qAugU7o/gTUvjaBrSjlNsU1AJ5oIPiSqrXs7iC7117ss2iX0aENcwsGG09XUk+K1llyrGAI7Fp2uBqn6fyujpacJrJG5oO7SR7F8xwc5TpMlWp/CHN2C9VPdOnm8KhdDtt6IUbNV+McjBxa3FtNVttkF4FAtUGYSurrrEscRad7bvSVbYzYkMs+83xS/ui+pJ3NLuNPntfErRIJw3EKacaUfm2eHCftBVvPHTy3AQbJ9mSKy3tMch+qu1nLnyFSMKjRieCFOgkT3LkQcvfpSteV3V/UNfm82ERy7AYOB8KZ0hW1R/vDp2R+EjFS3/0cw+a8luW6HGcyY0fs18uIbsSUaLOiThKTjp9pYhupXEa9gz1DeZMC51M79Ha4YC9uy3AyG5hH29DYF5yhBPD1Z0iYcgosJ8TweiYN0AvlCYsy939VRSzFGeiI/ZFN76DF0YP1LAOK9bTXHN9n8oyDoQbBMKcY48/uWZZpCAvBw==\"}","encryptedCreationDate":"{\"iv\":\"gLvf5OObVV10vRrQbMcrDw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"sWHHRuInJDY=\",\"ct\":\"2S5gjZmZ8QQRPfovPpLTbvQLsurgNIHXhkG0Ze8OpTScCw==\"}","version":"v0.3-0","poll":"gpwW7uZhbP"}}';
$pollDir = 'tests/_tmp/data/' . $pollId . '/';
$userDir = $pollDir . 'user/';
mkdir($pollDir);
file_put_contents($pollDir . 'poll_data', $pollJson);
mkdir($userDir);
file_put_contents($userDir . '0', $user1Json);
file_put_contents($userDir . '1', $user2Json);
$I = new ApiTester($scenario);
$I->wantTo('get an existing legacy (v0.3.0) poll with users');
$I->sendGET('/polls/' . $pollId);
$I->seeResponseCodeIs(200);
$I->seeHttpHeader('Content-Type', 'application/json');
$I->seeHttpHeader('Expires', '-1');
$I->seeResponseIsJson();
$pollData = json_decode($pollJson, true)["poll"];
unset($pollData["serverExpirationDate"]);
unset($pollData["encryptedIsDateTime"]);
foreach($pollData as $key => $value) {
if (strpos($key, 'encrypted') === 0) {
$key = lcfirst(substr($key, 9));
}
else {
$key = $key;
}
$I->seeResponseContainsJson(
array(
'poll' => array(
$key => $value
)
)
);
}
$I->seeResponseContainsJson(["poll" => ["id" => $pollId]]);
$I->dontSeeResponseJsonMatchesJsonPath('poll.serverExpirationDate');
$I->seeResponseJsonMatchesJsonPath('poll.users');
$users = $I->grabDataFromResponseByJsonPath('poll.users')[0];
\PHPUnit_Framework_Assert::assertTrue(
is_array($users),
'user should be an array'
);
\PHPUnit_Framework_Assert::assertEquals(
count($users),
2,
'user array should contain 2 users'
);
function wellformUser($user) {
$return = $user["user"];
foreach ($return as $key => $value) {
if(strpos($key, 'encrypted') === 0) {
$return[lcfirst(substr($key, 9))] = $value;
unset($return[$key]);
}
}
return $return;
}
$I->seeResponseContainsJson([
"poll" => [
"users" => [
wellformUser(json_decode($user1Json, true)),
wellformUser(json_decode($user2Json, true))
]
]
]);
$I->seeResponseJsonMatchesJsonPath('poll.users.0.id');
$I->seeResponseJsonMatchesJsonPath('poll.users.1.id');
$user1Id = $I->grabDataFromResponseByJsonPath('poll.users.0.id')[0];
$user2Id = $I->grabDataFromResponseByJsonPath('poll.users.1.id')[0];
\PHPUnit_Framework_Assert::assertTrue(
$user1Id !== $user2Id,
'user ids are unique'
);
\PHPUnit_Framework_Assert::assertEquals(
explode('_', $user1Id)[0],
$pollId,
'user id starts by poll id'
);

View file

@ -1,7 +1,24 @@
<?php
$pollId = substr(md5(__FILE__), 0, 10);
$pollJson = '{"anonymousUser":"{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}","answers":"{\"iv\":\"WRdAwEa0DF+E83ginLYtPw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"Oaer31ct2PXkmXkzJ1EXRPM3LMf6vGfzMZqjODwey4f7EhqSCUhYov+N7AZKCAAXYVS4WR84kKizxXBK2PQBSFrlB3Bll74ED9ZzRJSJD00otMG9BbgUR90aFws+1jMBP5vpti9+POsii85zLbDPkNg\/Th\/C4Ufv5YWwg\/4ZV0bFMyOgfdjtOWaG5YAMTGUIkz9U9+VCesYJQaTb497qTD\/Wmtz8J\/2pUxdL5\/b5xkdh2DJ4\/N5q0Kz\/CEbaoKwbexnQDlSr3ldlIhs7UmBjC9gkpgG2l9fu6a0VZFBE8hvzYrw=\"}","answerType":"{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO\/yFmk\"}","creationDate":"{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p\/ANinfanE\/51DbcDNw==\"}","description":"{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}","expirationDate":"{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}","forceAnswer":"{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}","options":"{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs\/Jm5XK\/thQW0phxKd0OxKt9NZ3FE\/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}","pollType":"{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}","timezone":"{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}","title":"{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}","version":"v0.3.0+0ae62f31","serverExpirationDate":"' . date("Y-m-dTH:i:s.000Z", strtotime("+3 month")) . '"}';
$expirationDate = date("Y-m-dTH:i:s.000Z", strtotime("+3 month"));
$pollJson = <<<EOD
{
"anonymousUser": "{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}",
"answerType": "{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO/yFmk\"}",
"creationDate": "{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p/ANinfanE/51DbcDNw==\"}",
"description": "{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}",
"expirationDate": "{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}",
"forceAnswer": "{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}",
"options": "{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs/Jm5XK/thQW0phxKd0OxKt9NZ3FE/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}",
"pollType": "{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}",
"timezone": "{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}",
"title": "{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}",
"version": "v0.3.0+0ae62f31",
"serverExpirationDate": "$expirationDate"
}
EOD;
mkdir('tests/_tmp/data/' . $pollId);
file_put_contents('tests/_tmp/data/' . $pollId . '/poll_data', $pollJson);

View file

@ -1,9 +1,41 @@
<?php
$pollId = substr(md5(__FILE__), 0, 10);
$pollJson = '{"anonymousUser":"{\"iv\":\"gVHZSXyMm10Fn+kDooa7uw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"GJsQQYA7TdAa+v3Rvg==\"}","answers":"{\"iv\":\"aK1JcI3viLPIlOO45K+ePA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"Bx4SRcww+hJ46NIiVcWBUZHADADX\/XPsxXMx4XzMQZWqu6M0690D4oTflSRJoqxe0egxdfMOUxuWhmACG\/UYXSYJQjcSg+QTq6KJbaXG+SvsCMZ7iz12a\/uf9lXyiag4IbLldgL4vE3LfZO6oih\/o\/yG4hechjNdSkqUa2IvsRbXWB2aHen6a5Ch5WjqWrr4xRRrukPvf7aumilT2Cf0LswHJ2fwYNilylV0h9oegKYp+qWphm4SL8x2ogRemSCt7u7ByEOwZV0w6D9bz9RvGLTRRLJaLIm\/VlE3k7R6Hz1vyps=\"}","answerType":"{\"iv\":\"ILkAzgUfAGNUtLr7CbEJEQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"nMOp+QApQGgP9dwefNpi\"}","creationDate":"{\"iv\":\"6tWbieK03uXUR+E0AMbs0A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"YkkLVBkFyx4xFldZ7qnDESG0teHJmXaPMUB05p9L0xUIMg==\"}","description":"{\"iv\":\"fWvHh47So4WBNfEHXrwLiA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"5W7nauOakSoFD52V\"}","expirationDate":"{\"iv\":\"HRsMvEQaoCp8QdqBGHevnA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"LXYamNRDyhIY5xY+CLqI4GHbocc9NoHQtePKU9fHpJn9zg==\"}","forceAnswer":"{\"iv\":\"bh4iZ4pKe0GnXcM764702g==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"q5VBynWGotXRrc2P\"}","options":"{\"iv\":\"ZneP\/x45NGh\/DC26GI4kvg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"4MvV9SNQq2dB6b\/MdX47R0KaRSfyZOZMEVUFDv7G3\/EcDBv7Z0pgSU9JXoF8BoSOz40rYrRtTw==\"}","pollType":"{\"iv\":\"j3P6eN0ZmNMMxLTAVD6gjQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"opwiZHAQi+I8R5HDxLfLK59DcQ==\"}","timezone":"{\"iv\":\"HKkSqcJONggGT9QQ+jZdUg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"BANN8sJlk8JK9A==\"}","title":"{\"iv\":\"4DX7dAJt7JIBHaR1V0Ct8A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"f1VUZf69nB94TF3\/HA==\"}","version":"v0.3.0+0ae62f31","serverExpirationDate":"' . date("Y-m-dTH:i:s.000Z", strtotime("+3 month")) . '"}';
$user1Json = '{"name":"{\"iv\":\"GJXPSYYmTVfEsst31BD92w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"smbuxujRLF/xNS1syTWFguE=\"}","selections":"{\"iv\":\"WXlkCM3pGyD+SyIhccmHYg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"UHs2ArTVTHfS04J3HNvwrV68xFqra0Q0qtVpLZRLPUbqgdXmn960FjEFiLa9Cnk5OcKEQtbOgWJXehHlAJFAWFpdyDE/gcCOKG62c2/hVauroeycQE16wDCjrEwor/FV9HxNjTbYxoJASjCy9ROLdOUhSlFfQfHLcvVpsTgPpnKPr7aYBgODu5XIdRI8Pf5nYF0K96KE9xn+mkg3ZjyXWSk1LBaBDpIOCrcj+8zl7tLtkgPNfh8aNVgQHC5hRrIbL9kZwD4XXEUPImRFITEy2rUWKp8Q0/jAgHCnqSzOLOFS8KrJOktDX++DjK3cB4oT5ttLvcRxRQ==\"}","creationDate":"{\"iv\":\"3/KUsITWzJNWx5fDzYC9Xg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"llgQE2GpZDg1ZKRRlXliGlF09VsrVZ1R57EIQ21+dej5yg==\"}","version":"v0.3.0+0ae62f31","poll":"l3zyFJUWcQ"}';
$user2Json = '{"name":"{\"iv\":\"DVNTCOFfACEOrgtVNVMyww==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"eug7bstOm7T\/CCFs32o=\"}","selections":"{\"iv\":\"ubEuXoXzw4QFuzjAyvXC6w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"PfQ4v4hkBf+S0GX7JmnIp2LO5sh\/jg9nEIPn8NeU2Gn9Rb7cqsjCLgKOQ2xkiIzCyimVBOYg0fjGCyzM\/b6ZPQnY+86teNGogEteD4fjqGHhO832FNOy7Oci0YC8VAM1x9SlQNBI9V+vFc706JbZgwA8JY46UMiGK3HU49pgbYMpdnWEmt4dGzGrLMnNbh4J1Or5JydKmrp4dXaMiiggSXhmUTgBJSRhF7dxQm16oaA1lJpCWoQBvu+WTJv34LnBXHbgg6JcAEEONaQRw1jmMeqo36tQJxSdjiVfcDWzMifWiz\/nhQMqDHkc19iOAmDBo2Rf+yrGWA==\"}","creationDate":"{\"iv\":\"Sj4pVW\/maHa8DUNFHhyUrw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"HaY9MtEzVmEg3dxtI\/pfaIrsivBJSNeC5l5iJHQrvyYQGA==\"}","version":"v0.3.0+0ae62f31","poll":"l3zyFJUWcQ"}';
$expirationDate = date("Y-m-dTH:i:s.000Z", strtotime("+3 month"));
$pollJson = <<<EOD
{
"anonymousUser": "{\"iv\":\"gVHZSXyMm10Fn+kDooa7uw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"GJsQQYA7TdAa+v3Rvg==\"}",
"answerType": "{\"iv\":\"ILkAzgUfAGNUtLr7CbEJEQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"nMOp+QApQGgP9dwefNpi\"}",
"creationDate": "{\"iv\":\"6tWbieK03uXUR+E0AMbs0A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"YkkLVBkFyx4xFldZ7qnDESG0teHJmXaPMUB05p9L0xUIMg==\"}",
"description": "{\"iv\":\"fWvHh47So4WBNfEHXrwLiA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"5W7nauOakSoFD52V\"}",
"expirationDate": "{\"iv\":\"HRsMvEQaoCp8QdqBGHevnA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"LXYamNRDyhIY5xY+CLqI4GHbocc9NoHQtePKU9fHpJn9zg==\"}",
"forceAnswer": "{\"iv\":\"bh4iZ4pKe0GnXcM764702g==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"q5VBynWGotXRrc2P\"}",
"options": "{\"iv\":\"ZneP/x45NGh/DC26GI4kvg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"4MvV9SNQq2dB6b/MdX47R0KaRSfyZOZMEVUFDv7G3/EcDBv7Z0pgSU9JXoF8BoSOz40rYrRtTw==\"}",
"pollType": "{\"iv\":\"j3P6eN0ZmNMMxLTAVD6gjQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"opwiZHAQi+I8R5HDxLfLK59DcQ==\"}",
"timezone": "{\"iv\":\"HKkSqcJONggGT9QQ+jZdUg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"BANN8sJlk8JK9A==\"}",
"title": "{\"iv\":\"4DX7dAJt7JIBHaR1V0Ct8A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"f1VUZf69nB94TF3/HA==\"}",
"version": "v0.3.0+0ae62f31",
"serverExpirationDate": "$expirationDate"
}
EOD;
$user1Json = <<<EOD
{
"name": "{\"iv\":\"GJXPSYYmTVfEsst31BD92w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"smbuxujRLF/xNS1syTWFguE=\"}",
"selections": "{\"iv\":\"WXlkCM3pGyD+SyIhccmHYg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"UHs2ArTVTHfS04J3HNvwrV68xFqra0Q0qtVpLZRLPUbqgdXmn960FjEFiLa9Cnk5OcKEQtbOgWJXehHlAJFAWFpdyDE/gcCOKG62c2/hVauroeycQE16wDCjrEwor/FV9HxNjTbYxoJASjCy9ROLdOUhSlFfQfHLcvVpsTgPpnKPr7aYBgODu5XIdRI8Pf5nYF0K96KE9xn+mkg3ZjyXWSk1LBaBDpIOCrcj+8zl7tLtkgPNfh8aNVgQHC5hRrIbL9kZwD4XXEUPImRFITEy2rUWKp8Q0/jAgHCnqSzOLOFS8KrJOktDX++DjK3cB4oT5ttLvcRxRQ==\"}",
"creationDate": "{\"iv\":\"3/KUsITWzJNWx5fDzYC9Xg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"llgQE2GpZDg1ZKRRlXliGlF09VsrVZ1R57EIQ21+dej5yg==\"}",
"version": "v0.3.0+0ae62f31",
"poll": "l3zyFJUWcQ"
}
EOD;
$user2Json = <<<EOD
{
"name": "{\"iv\":\"DVNTCOFfACEOrgtVNVMyww==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"eug7bstOm7T/CCFs32o=\"}",
"selections": "{\"iv\":\"ubEuXoXzw4QFuzjAyvXC6w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"PfQ4v4hkBf+S0GX7JmnIp2LO5sh/jg9nEIPn8NeU2Gn9Rb7cqsjCLgKOQ2xkiIzCyimVBOYg0fjGCyzM/b6ZPQnY+86teNGogEteD4fjqGHhO832FNOy7Oci0YC8VAM1x9SlQNBI9V+vFc706JbZgwA8JY46UMiGK3HU49pgbYMpdnWEmt4dGzGrLMnNbh4J1Or5JydKmrp4dXaMiiggSXhmUTgBJSRhF7dxQm16oaA1lJpCWoQBvu+WTJv34LnBXHbgg6JcAEEONaQRw1jmMeqo36tQJxSdjiVfcDWzMifWiz/nhQMqDHkc19iOAmDBo2Rf+yrGWA==\"}",
"creationDate": "{\"iv\":\"Sj4pVW/maHa8DUNFHhyUrw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"HaY9MtEzVmEg3dxtI/pfaIrsivBJSNeC5l5iJHQrvyYQGA==\"}",
"version": "v0.3.0+0ae62f31",
"poll": "l3zyFJUWcQ"
}
EOD;
$pollDir = 'tests/_tmp/data/' . $pollId . '/';
$userDir = $pollDir . 'user/';

View file

@ -1,7 +1,6 @@
<?php
$pollTemplate = array(
"anonymousUser" => "{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}",
"answers" => "{\"iv\":\"WRdAwEa0DF+E83ginLYtPw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"Oaer31ct2PXkmXkzJ1EXRPM3LMf6vGfzMZqjODwey4f7EhqSCUhYov+N7AZKCAAXYVS4WR84kKizxXBK2PQBSFrlB3Bll74ED9ZzRJSJD00otMG9BbgUR90aFws+1jMBP5vpti9+POsii85zLbDPkNg\/Th\/C4Ufv5YWwg\/4ZV0bFMyOgfdjtOWaG5YAMTGUIkz9U9+VCesYJQaTb497qTD\/Wmtz8J\/2pUxdL5\/b5xkdh2DJ4\/N5q0Kz\/CEbaoKwbexnQDlSr3ldlIhs7UmBjC9gkpgG2l9fu6a0VZFBE8hvzYrw=\"}",
"answerType" => "{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO\/yFmk\"}",
"creationDate" => "{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p\/ANinfanE\/51DbcDNw==\"}",
"description" => "{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}",

View file

@ -1,24 +0,0 @@
import RESTAdapter from '@ember-data/adapter/rest';
import { inject as service } from '@ember/service';
import AdapterFetch from 'ember-fetch/mixins/adapter-fetch';
export default class ApplicationAdapter extends RESTAdapter.extend(AdapterFetch) {
@service
encryption;
// set namespace to api.php in same subdirectory
namespace =
window.location.pathname
// remove index.html if it's there
.replace(/index.html$/, '')
// remove tests prefix which is added by testem (starting with a number)
.replace(/\/\d+\/tests/, '')
// remove tests prefix which is added by tests run in browser
.replace(/tests/, '')
// remove leading and trailing slash
.replace(/\/$/, '')
// add api.php
.concat('/api/index.php')
// remove leading slash
.replace(/^\//g, '')
}

View file

@ -1,7 +1,7 @@
import Application from '@ember/application';
import Resolver from 'ember-resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';
import config from 'croodle/config/environment';
export default class App extends Application {
modulePrefix = config.modulePrefix;

View file

@ -1,15 +0,0 @@
import classic from 'ember-classic-decorator';
import Component from '@ember/component';
@classic
export default class AutofocusableElement extends Component {
autofocus = true;
didInsertElement() {
super.didInsertElement(...arguments);
if (this.autofocus) {
this.element.focus();
}
}
}

View file

@ -1,9 +1,14 @@
<BsButton
@disabled={{@disabled}}
@onClick={{@onClick}}
class="cr-steps-bottom-nav__button cr-steps-bottom-nav__prev-button prev"
data-test-action="back"
...attributes
>
<span class="cr-steps-bottom-nav__icon oi oi-caret-left" title={{t "action.back"}} aria-hidden="true"></span>
<span
class="cr-steps-bottom-nav__icon oi oi-caret-left"
title={{t "action.back"}}
aria-hidden="true"
></span>
<span class="cr-steps-bottom-nav__label">
{{t "action.back"}}
</span>

View file

@ -0,0 +1,16 @@
import templateOnlyComponent from '@ember/component/template-only';
interface BackButtonSignature {
Args: { onClick?: () => void };
Element: HTMLButtonElement;
}
const BackButton = templateOnlyComponent<BackButtonSignature>();
export default BackButton;
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
BackButton: typeof BackButton;
}
}

28
app/components/bs-form.js Normal file
View file

@ -0,0 +1,28 @@
import BaseBsForm from 'ember-bootstrap/components/bs-form';
import IntlMessage from '../utils/intl-message';
export default class BsForm extends BaseBsForm {
'__ember-bootstrap_subclass' = true;
get hasValidator() {
return true;
}
async validate(model) {
const isInvalid = Object.getOwnPropertyNames(
Object.getPrototypeOf(model),
).some((potentialValidationKey) => {
// Validation getters must be named `propertyValidation` by our convention
if (!potentialValidationKey.endsWith('Validation')) {
return false;
}
// Validation errors must be an instance of IntlMessage by convention
return model[potentialValidationKey] instanceof IntlMessage;
});
if (isInvalid) {
throw new Error();
}
}
}

View file

@ -0,0 +1,26 @@
import BaseBsFormElement from 'ember-bootstrap/components/bs-form/element';
import { inject as service } from '@ember/service';
export default class BsFormElement extends BaseBsFormElement {
'__ember-bootstrap_subclass' = true;
@service intl;
get errors() {
// native validation state doesn't integrate with Ember's autotracking, so we need to invalidate our `errors` getter explicitly when
// `this.value` changes by consuming it here.
// eslint-disable-next-line no-unused-vars
const { model, property } = this.args;
const validation = model[`${property}Validation`];
if (validation === undefined || validation === null) {
return [];
}
return [this.intl.t(validation.key, validation.options)];
}
get hasValidator() {
return true;
}
}

View file

@ -1,13 +0,0 @@
import classic from 'ember-classic-decorator';
import BaseBsInput from 'ember-bootstrap/components/bs-form/element/control/input';
@classic
export default class CustomizedBsInput extends BaseBsInput {
didInsertElement() {
super.didInsertElement(...arguments);
if (this.autofocus) {
this.element.focus();
}
}
}

View file

@ -0,0 +1,42 @@
<div class="cr-form-wrapper box">
<BsForm
@formLayout="horizontal"
@model={{@formData}}
@onSubmit={{this.submit}}
as |form|
>
<form.element
@label={{t "create.index.input.pollType.label"}}
@property="pollType"
@showValidationOn={{array "change" "focusOut"}}
@useIcons={{false}}
class="poll-type"
data-test-form-element="poll-type"
as |el|
>
<select
id={{el.id}}
class="form-control"
required
{{on "change" (pick "target.value" el.setValue)}}
{{autofocus}}
>
<option value="FindADate" selected={{eq el.value "FindADate"}}>
{{t "pollTypes.findADate.label"}}
</option>
<option value="MakeAPoll" selected={{eq el.value "MakeAPoll"}}>
{{t "pollTypes.makeAPoll.label"}}
</option>
</select>
</form.element>
<div class="row cr-steps-bottom-nav">
<div class="col-6 col-md-8 order-12">
<NextButton />
</div>
<div class="col-6 col-md-4 order-1 text-right">
<BackButton disabled />
</div>
</div>
</BsForm>
</div>

View file

@ -0,0 +1,38 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { registerDestructor } from '@ember/destroyable';
import type RouterService from '@ember/routing/router-service';
import type { CreateIndexRouteModel } from '../routes/create/index';
export interface CreateIndexSignature {
Args: {
formData: CreateIndexRouteModel['formData'];
poll: CreateIndexRouteModel['poll'];
};
}
export default class CreateIndexComponent extends Component<CreateIndexSignature> {
@service declare router: RouterService;
@action
submit() {
this.router.transitionTo('create.meta');
}
constructor(owner: unknown, args: CreateIndexSignature['Args']) {
super(owner, args);
registerDestructor(this, () => {
const { poll, formData } = this.args;
poll.pollType = formData.pollType;
});
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
CreateIndex: typeof CreateIndexComponent;
}
}

View file

@ -0,0 +1,45 @@
<div class="cr-form-wrapper box">
<BsForm
@formLayout="horizontal"
@model={{@formData}}
@onInvalid={{(scroll-first-invalid-element-into-view-port)}}
@onSubmit={{this.submit}}
novalidate
as |form|
>
<form.element
@controlType="text"
@label={{t "create.meta.input.title.label"}}
@property="title"
class="title"
data-test-form-element="title"
as |el|
>
<el.control
placeholder={{t "create.meta.input.title.placeholder"}}
{{autofocus}}
/>
</form.element>
<form.element
@controlType="textarea"
@label={{t "create.meta.input.description.label"}}
@property="description"
class="description"
data-test-form-element="description"
as |el|
>
<el.control
placeholder={{t "create.meta.input.description.placeholder"}}
/>
</form.element>
<div class="row cr-steps-bottom-nav">
<div class="col-6 col-md-8 order-12">
<NextButton />
</div>
<div class="col-6 col-md-4 order-1 text-right">
<BackButton @onClick={{this.previousPage}} />
</div>
</div>
</BsForm>
</div>

View file

@ -0,0 +1,44 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { registerDestructor } from '@ember/destroyable';
import type RouterService from '@ember/routing/router-service';
import type { CreateMetaRouteModel } from '../routes/create/meta';
export interface CreateMetaSignature {
Args: {
formData: CreateMetaRouteModel['formData'];
poll: CreateMetaRouteModel['poll'];
};
}
export default class CreateMetaComponent extends Component<CreateMetaSignature> {
@service declare router: RouterService;
@action
previousPage() {
this.router.transitionTo('create.index');
}
@action
submit() {
this.router.transitionTo('create.options');
}
constructor(owner: unknown, args: CreateMetaSignature['Args']) {
super(owner, args);
registerDestructor(this, () => {
const { poll, formData } = this.args;
poll.title = formData.title;
poll.description = formData.description;
});
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
CreateMeta: typeof CreateMetaComponent;
}
}

View file

@ -1,37 +1,35 @@
{{#let @form as |form|}}
<form.element
{{#let @formElement as |FormElement|}}
<FormElement
@label={{t "create.options.dates.label"}}
@property="options"
data-test-form-element-for="days"
as |el|
>
<div
class="
form-control
cr-h-auto
cr-pr-validation
{{if (eq el.validation "error") "is-invalid"}}
{{if (eq el.validation "success") "is-valid"}}
"
class="form-control cr-h-auto cr-pr-validation
{{if (eq el.validation 'error') 'is-invalid'}}
{{if (eq el.validation 'success') 'is-valid'}}
"
id={{el.id}}
>
<div class="row">
<div class="col-12 col-md-6">
<InlineDatepicker
@center={{this.calendarCenter}}
@selectedDays={{this.selectedDays}}
@onCenterChange={{action (mut this.calendarCenter) value="moment"}}
@onSelect={{action "daysSelected"}}
@onCenterChange={{fn this.handleCalenderCenterChange 0}}
@onSelect={{this.handleSelectedDaysChange}}
/>
</div>
<div class="col-md-6 cr-hide-on-mobile">
<InlineDatepicker
@center={{this.calendarCenterNext}}
@selectedDays={{this.selectedDays}}
@onCenterChange={{action (mut this.calendarCenter) value="moment"}}
@onSelect={{action "daysSelected"}}
@onCenterChange={{fn this.handleCalenderCenterChange -1}}
@onSelect={{this.handleSelectedDaysChange}}
/>
</div>
</div>
</div>
</form.element>
</FormElement>
{{/let}}

View file

@ -1,87 +0,0 @@
import classic from 'ember-classic-decorator';
import { action, computed } from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { isArray } from '@ember/array';
import { isPresent } from '@ember/utils';
import moment from 'moment';
@classic
export default class CreateOptionsDates extends Component {
@service('store')
store;
@computed('options.[]')
get selectedDays() {
return this.options
// should be unique
.uniqBy('day')
// raw dates
.map(({ date }) => date)
// filter out invalid
.filter(moment.isMoment)
.toArray();
}
@computed('calendarCenter')
get calendarCenterNext() {
return moment(this.calendarCenter).add(1, 'months');
}
@action
daysSelected({ moment: newMoments }) {
let { options } = this;
if (!isArray(newMoments)) {
// special case: all options are unselected
options.clear();
return;
}
// array of options that represent days missing in updated selection
let removedOptions = options.filter((option) => {
return !newMoments.find((newMoment) => newMoment.format('YYYY-MM-DD') === option.day);
});
// array of moments that aren't represented yet by an option
let addedMoments = newMoments.filter((moment) => {
return !options.find((option) => moment.format('YYYY-MM-DD') === option.day);
});
// remove options that represent deselected days
options.removeObjects(removedOptions);
// add options for newly selected days
let newOptions = addedMoments.map((moment) => {
return this.store.createFragment('option', {
title: moment.format('YYYY-MM-DD'),
})
});
newOptions.forEach((newOption) => {
// options must be insert into options array at correct position
let insertBefore = options.find(({ date }) => {
if (!moment.isMoment(date)) {
// ignore options that do not represent a valid date
return false;
}
return date.isAfter(newOption.date);
});
let position = isPresent(insertBefore) ? options.indexOf(insertBefore) : options.length;
options.insertAt(position, newOption);
});
}
@action
updateCalenderCenter(diff) {
this.calendarCenter.add(diff, 'months');
this.notifyPropertyChange('calenderCenter');
}
init() {
super.init(arguments);
let { selectedDays } = this;
this.set('calendarCenter', selectedDays.length >= 1 ? selectedDays[0] : moment());
}
}

View file

@ -0,0 +1,63 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { isArray } from '@ember/array';
import { DateTime } from 'luxon';
import { tracked } from '@glimmer/tracking';
import type { FormDataOption } from './create-options';
import type BsFormElementComponent from 'ember-bootstrap/components/bs-form/element';
export interface CreateOptionsDatesSignature {
Args: {
formElement: BsFormElementComponent;
options: Array<FormDataOption>;
updateOptions: (options: string[]) => void;
};
}
export default class CreateOptionsDates extends Component<CreateOptionsDatesSignature> {
@tracked calendarCenter =
this.selectedDays.length >= 1
? (this.selectedDays[0] as DateTime)
: DateTime.local();
get selectedDays(): DateTime[] {
return this.args.options.map(
({ value }) => DateTime.fromISO(value) as DateTime,
);
}
get calendarCenterNext() {
return this.calendarCenter.plus({ months: 1 });
}
@action
handleSelectedDaysChange({
datetime: newDatesAsLuxonDateTime,
}: {
datetime: DateTime[];
}) {
if (!isArray(newDatesAsLuxonDateTime)) {
// special case: all options are unselected
this.args.updateOptions([]);
return;
}
this.args.updateOptions(
newDatesAsLuxonDateTime.map((datetime) => datetime.toISODate() as string),
);
}
@action
handleCalenderCenterChange(
offset: number,
{ datetime }: { datetime: DateTime },
) {
this.calendarCenter = datetime.plus({ months: offset });
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
CreateOptionsDates: typeof CreateOptionsDates;
}
}

View file

@ -0,0 +1,127 @@
<div class="cr-form-wrapper box">
{{#if this.errorMessage}}
<BsAlert @type="warning">
{{t this.errorMessage}}
</BsAlert>
{{/if}}
<BsForm
@onInvalid={{(scroll-first-invalid-element-into-view-port)}}
@onSubmit={{this.submit}}
@formLayout="horizontal"
@model={{this.formData}}
novalidate
as |form|
>
<div class="days">
{{#each-in this.formData.datetimes as |date timeOptions|}}
{{!--
@glint-ignore
Types for value returned by `{{#each-in}}` are broken if used
with a `Map`. https://github.com/typed-ember/glint/issues/645
--}}
{{#each timeOptions as |timeOption indexInTimeOptions|}}
<div data-test-day={{date}}>
<form.element
@label={{format-date timeOption.jsDate dateStyle="full"}}
{{!
show label only for the first time of this date
}}
@invisibleLabel={{gt indexInTimeOptions 0}}
@model={{timeOption}}
@property="time"
class="option"
as |el|
>
<div class="input-group">
<el.control
@placeholder="00:00"
@type="time"
@value={{el.value}}
{{! focus input if it's the first one }}
{{autofocus enabled=timeOption.isFirstTimeOnFirstDate}}
{{! run validation for partially filled input on focusout event }}
{{on "focusout" (fn this.validateInput timeOption)}}
{{on "change" (fn this.validateInput timeOption)}}
{{!
Validation for partially input field must be reset if input is cleared.
But `@onChange` is not called and `focusout` event not triggered in that
scenario. Need to listen to additional events to ensure that partially
input validation is updated as soon as user fixed a partially input.
The `keyup` events captures all scenarios in which the input is cleared
using keyboard. `focusin` event is triggered if user clicks the clears
button provided by native input. As a fallback validation is rerun on
`focusout`.
As the time of implementation this was only affecting Chrome cause
Firefox does not consider partially time input as invalid, Edge prevents
partially filling in first place and Desktop Safari as well as IE 11
do not support `<input type="time">`.
}}
{{on "focusin" (fn this.updateInputValidation timeOption)}}
{{on "keyup" (fn this.updateInputValidation timeOption)}}
id={{el.id}}
/>
<div class="input-group-append">
<BsButton
@onClick={{fn this.formData.deleteOption timeOption}}
@type="link"
class="delete"
data-test-action="delete"
>
<span
class="oi oi-trash"
title={{t "create.options.button.delete.label"}}
aria-hidden="true"
></span>
<span class="sr-only">
{{t "create.options.button.delete.label"}}
</span>
</BsButton>
</div>
</div>
<BsButton
@onClick={{fn this.formData.addOption date}}
@type="link"
@size="sm"
class="add cr-option-menu__button cr-option-menu__add-button float-left"
data-test-action="add"
>
<span
class="oi oi-plus"
title={{t "create.options.button.add.label"}}
aria-hidden="true"
></span>
<span class="sr-only">{{t
"create.options.button.add.label"
}}</span>
</BsButton>
</form.element>
</div>
{{/each}}
{{/each-in}}
</div>
{{#if this.formData.hasMultipleDays}}
<form.element>
<BsButton
@onClick={{this.adoptTimesOfFirstDay}}
@size="sm"
class="adopt-times-of-first-day"
data-test-action="adopt-times-of-first-day"
>
{{t "create.options-datetime.copy-first-line"}}
</BsButton>
</form.element>
{{/if}}
<div class="row cr-steps-bottom-nav">
<div class="col-6 col-md-8 order-12">
<NextButton />
</div>
<div class="col-6 col-md-4 order-1 text-right">
<BackButton @onClick={{this.previousPage}} />
</div>
</div>
</BsForm>
</div>

View file

@ -1,190 +0,0 @@
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { isPresent, isEmpty } from '@ember/utils';
import { action, get } from '@ember/object';
import {
validator, buildValidations
}
from 'ember-cp-validations';
import { raw } from 'ember-awesome-macros';
import { groupBy } from 'ember-awesome-macros/array';
import { next } from '@ember/runloop';
let modelValidations = buildValidations({
dates: [
validator('collection', true),
validator('length', {
dependentKeys: ['model.datetimes.[]'],
min: 1
}),
validator('valid-collection', {
dependentKeys: ['model.datetimes.[]', 'model.datetimes.@each.time']
})
]
});
export default class CreateOptionsDatetime extends Component.extend(modelValidations) {
@service
store;
errorMesage = null;
// group dates by day
@groupBy('dates', raw('day'))
groupedDates;
get datesForFirstDay() {
// dates are sorted
let firstDay = this.groupedDates[0];
return firstDay.items;
}
get timesForFirstDay() {
return this.datesForFirstDay.map((date) => date.time).filter((time) => isPresent(time));
}
@action
addOption(afterOption) {
let options = this.dates;
let dayString = afterOption.get('day');
let fragment = this.store.createFragment('option', {
title: dayString
});
let position = options.indexOf(afterOption) + 1;
options.insertAt(
position,
fragment
);
next(() => {
this.notifyPropertyChange('_nestedChildViews');
});
}
@action
adoptTimesOfFirstDay() {
const dates = this.dates;
const datesForFirstDay = this.datesForFirstDay;
const timesForFirstDay = this.timesForFirstDay;
const datesWithoutFirstDay = this.groupedDates.slice(1);
/* validate if times on firstDay are valid */
const datesForFirstDayAreValid = datesForFirstDay.every((date) => {
// ignore dates where time is null
return isEmpty(date.get('time')) || date.get('validations.isValid');
});
if (!datesForFirstDayAreValid) {
this.set('errorMessage', 'create.options-datetime.fix-validation-errors-first-day');
return;
}
datesWithoutFirstDay.forEach(({ items }) => {
if (isEmpty(timesForFirstDay)) {
// there aren't any times on first day
const remainingOption = items[0];
// remove all times but the first one
dates.removeObjects(
items.slice(1)
);
// set title as date without time
remainingOption.set('title', remainingOption.get('date').format('YYYY-MM-DD'));
} else {
// adopt times of first day
if (timesForFirstDay.get('length') < items.length) {
// remove excess options
dates.removeObjects(
items.slice(timesForFirstDay.get('length'))
);
}
// set times according to first day
let targetPosition;
timesForFirstDay.forEach((timeOfFirstDate, index) => {
const target = items[index];
if (target === undefined) {
const basisDate = get(items[0], 'date').clone();
let [hour, minute] = timeOfFirstDate.split(':');
let dateString = basisDate.hour(hour).minute(minute).toISOString();
let fragment = this.store.createFragment('option', {
title: dateString
});
dates.insertAt(
targetPosition,
fragment
);
targetPosition++;
} else {
target.set('time', timeOfFirstDate);
targetPosition = dates.indexOf(target) + 1;
}
});
}
});
}
/*
* removes target option if it's not the only date for this day
* otherwise it deletes time for this date
*/
@action
deleteOption(target) {
let position = this.dates.indexOf(target);
let datesForThisDay = this.groupedDates.find((group) => {
return group.value === target.get('day');
}).items;
if (datesForThisDay.length > 1) {
this.dates.removeAt(position);
} else {
target.set('time', null);
}
}
@action
previousPage() {
this.onPrevPage();
}
@action
submit() {
if (this.get('validations.isValid')) {
this.onNextPage();
} else {
this.set('shouldShowErrors', true);
}
}
@action
inputChanged(date, value) {
// update property, which is normally done by default
date.set('time', value);
// reset partially filled state
date.set('isPartiallyFilled', false);
// reset error message
this.set('errorMessage', null);
}
// validate input field for being partially filled
@action
validateInput(date, event) {
let element = event.target;
// update partially filled time validation error
if (!element.checkValidity()) {
date.set('isPartiallyFilled', true);
} else {
date.set('isPartiallyFilled', false);
}
}
// remove partially filled validation error if user fixed it
@action
updateInputValidation(date, event) {
let element = event.target;
if (element.checkValidity() && date.isPartiallyFilled) {
date.set('isPartiallyFilled', false);
}
}
}

View file

@ -0,0 +1,285 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { TrackedMap, TrackedSet } from 'tracked-built-ins';
import { DateTime } from 'luxon';
import IntlMessage from '../utils/intl-message';
import type RouterService from '@ember/routing/router-service';
import type Transition from '@ember/routing/transition';
import type { CreateOptionsDatetimeRouteModel } from 'croodle/routes/create/options-datetime';
class FormDataTimeOption {
formData;
// ISO 8601 date string: YYYY-MM-DD
date: string;
// ISO 8601 time string without seconds: HH:mm
@tracked time: string | null;
// helper property set by modifiers to track if input element is invalid
// because user only entered the time partly (e.g. "10:--").
@tracked isPartiallyFilled = false;
get timeValidation() {
const { isPartiallyFilled } = this;
if (isPartiallyFilled) {
return new IntlMessage(
'create.options-datetime.error.partiallyFilledTime',
);
}
// The same time must not be entered twice for a day.
// It should show a validation error if the same time has been entered for
// the same day already before. Only the second input field containing the
// duplicated time should show the validation error.
const { formData, date } = this;
const timesForThisDate = Array.from(formData.datetimes.get(date)!);
const isDuplicate = timesForThisDate
.slice(0, timesForThisDate.indexOf(this))
.some((timeOption) => timeOption.time == this.time);
if (isDuplicate) {
return new IntlMessage('create.options-datetime.error.duplicatedDate');
}
return null;
}
get datetime() {
const { date, time } = this;
const isoString = time === null ? date : `${date}T${time}`;
return DateTime.fromISO(isoString);
}
get jsDate() {
const { datetime } = this;
return datetime.toJSDate();
}
get isValid() {
const { timeValidation } = this;
return timeValidation === null;
}
get isFirstTimeOnFirstDate() {
const { formData, date } = this;
const { datetimes } = formData;
return (
Array.from(datetimes.keys())[0] === date &&
Array.from(datetimes.get(date)!)[0] === this
);
}
constructor(
formData: FormData,
{ date, time }: { date: string; time: string | null },
) {
this.formData = formData;
this.date = date;
this.time = time;
}
}
class FormData {
@tracked datetimes: Map<string, Set<FormDataTimeOption>>;
get optionsValidation() {
const { datetimes } = this;
const allTimeOptionsAreValid = Array.from(datetimes.values()).every(
(timeOptionsForDate) =>
Array.from(timeOptionsForDate).every(
(timeOption) => timeOption.isValid,
),
);
if (!allTimeOptionsAreValid) {
return new IntlMessage('create.options-datetime.error.invalidTime');
}
return null;
}
get hasMultipleDays() {
return this.datetimes.size > 1;
}
get validationStatePerDate() {
const validationState: Map<string, boolean> = new Map();
for (const [date, timeOptions] of this.datetimes.entries()) {
validationState.set(
date,
Array.from(timeOptions).every((time) => time.isValid),
);
}
return validationState;
}
@action
addOption(date: string) {
this.datetimes
.get(date)!
.add(new FormDataTimeOption(this, { date, time: null }));
}
/*
* removes target option if it's not the only time for this date
* otherwise it deletes time for this date
*/
@action
deleteOption(option: FormDataTimeOption) {
const timeOptionsForDate = this.datetimes.get(option.date)!;
if (timeOptionsForDate.size > 1) {
timeOptionsForDate.delete(option);
} else {
option.time = null;
}
}
@action
adoptTimesOfFirstDay() {
const timeOptionsForFirstDay = Array.from(
Array.from(this.datetimes.values())[0]!,
) as FormDataTimeOption[];
const timesForFirstDayAreValid = timeOptionsForFirstDay.every(
(timeOption) => timeOption.isValid,
);
if (!timesForFirstDayAreValid) {
return false;
}
for (const date of Array.from(this.datetimes.keys()).slice(1)) {
this.datetimes.set(
date,
new TrackedSet(
timeOptionsForFirstDay.map(
({ time }) => new FormDataTimeOption(this, { date, time }),
),
),
);
}
}
constructor({
dates,
times,
}: {
dates: Set<string>;
times: Map<string, Set<string>>;
}) {
const datetimes = new Map();
for (const date of dates) {
const timesForDate = times.has(date)
? Array.from(times.get(date) as Set<string>)
: [null];
datetimes.set(
date,
new TrackedSet(
timesForDate.map(
(time) => new FormDataTimeOption(this, { date, time }),
),
),
);
}
this.datetimes = new TrackedMap(datetimes);
}
}
export interface CreateOptoinsDatetimeSignature {
Args: {
poll: CreateOptionsDatetimeRouteModel;
};
}
export default class CreateOptionsDatetime extends Component<CreateOptoinsDatetimeSignature> {
@service declare router: RouterService;
formData = new FormData({
dates: this.args.poll.dateOptions,
times: this.args.poll.timesForDateOptions,
});
@tracked errorMessage: string | null = null;
@action
adoptTimesOfFirstDay() {
const { formData } = this;
const successful = formData.adoptTimesOfFirstDay();
if (!successful) {
this.errorMessage =
'create.options-datetime.fix-validation-errors-first-day';
}
}
@action
previousPage() {
this.router.transitionTo('create.options');
}
@action
submit() {
this.router.transitionTo('create.settings');
}
// validate input field for being partially filled
@action
validateInput(option: FormDataTimeOption, event: Event) {
const element = event.target as HTMLInputElement;
// update partially filled time validation error
option.isPartiallyFilled = !element.checkValidity();
}
// remove partially filled validation error if user fixed it
@action
updateInputValidation(option: FormDataTimeOption, event: Event) {
const element = event.target as HTMLInputElement;
if (element.checkValidity() && option.isPartiallyFilled) {
option.isPartiallyFilled = false;
}
}
@action
handleTransition(transition: Transition) {
if (transition.from?.name === 'create.options-datetime') {
this.args.poll.timesForDateOptions = new Map(
// FormData.datetimes Map has a Set of FormDataTime object as values
// We need to transform it to a Set of plain time strings
Array.from(this.formData.datetimes.entries())
.map(([key, timeOptions]): [string, Set<string>] => {
return [
key,
new Set(
Array.from(timeOptions)
.map(({ time }: FormDataTimeOption) => time)
// There might be FormDataTime objects without a time, which
// we need to filter out
.filter((time) => time !== null),
) as Set<string>,
];
})
// There might be dates without any time, which we need to filter out
.filter(([, times]) => times.size > 0),
);
this.router.off('routeWillChange', this.handleTransition);
}
}
constructor(owner: unknown, args: CreateOptoinsDatetimeSignature['Args']) {
super(owner, args);
this.router.on('routeWillChange', this.handleTransition);
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
CreateOptionsDatetime: typeof CreateOptionsDatetime;
}
}

View file

@ -0,0 +1,53 @@
{{#let @formElement as |FormElement|}}
{{#each @options as |option index|}}
<FormElement
{{! show label only on first item }}
@label={{unless index (t "create.options.options.label")}}
@model={{option}}
@property="value"
class="option"
data-test-form-element="option"
data-test-option={{index}}
as |el|
>
<div class="input-group">
<el.control
{{! first control should be focused automatically }}
{{autofocus enabled=(eq index 0)}}
/>
<div class="input-group-append">
<BsButton
@onClick={{fn @deleteOption option}}
@type="link"
class="delete"
{{! disable delete button if there is only one option }}
disabled={{lte @options.length 1}}
>
<span
class="oi oi-trash"
title={{t "create.options.button.delete.label"}}
aria-hidden="true"
></span>
<span class="sr-only">{{t
"create.options.button.delete.label"
}}</span>
</BsButton>
</div>
</div>
<BsButton
@onClick={{fn @addOption "" index}}
@type="link"
@size="sm"
class="add float-left"
>
<span
class="oi oi-plus"
title={{t "create.options.button.add.label"}}
aria-hidden="true"
></span>
<span class="sr-only">{{t "create.options.button.add.label"}}</span>
</BsButton>
</FormElement>
{{/each}}
{{/let}}

View file

@ -1,49 +0,0 @@
import classic from 'ember-classic-decorator';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { next } from '@ember/runloop';
@classic
export default class CreateOptionsText extends Component {
@action
addOption(element) {
let fragment = this.store.createFragment('option');
let options = this.options;
let position = this.options.indexOf(element) + 1;
options.insertAt(
position,
fragment
);
}
@action
deleteOption(element) {
let position = this.options.indexOf(element);
this.options.removeAt(position);
}
enforceMinimalOptionsAmount() {
let options = this.options;
while (options.length < 2) {
options.pushObject(
this.store.createFragment('option')
);
}
}
@service('store')
store;
init() {
super.init(...arguments);
// need to delay pushing fragments into options array to prevent
// > You modified "disabled" twice on <(unknown):ember330> in a single render.
// error.
next(() => {
this.enforceMinimalOptionsAmount();
});
}
}

View file

@ -0,0 +1,24 @@
import templateOnlyComponent from '@ember/component/template-only';
import type { FormDataOption } from './create-options';
import type BsFormElementComponent from 'ember-bootstrap/components/bs-form/element';
interface CreateOptionsTextSignature {
Args: {
Named: {
addOption: (value: string, afterPosition: number) => void;
deleteOption: (option: FormDataOption) => void;
formElement: BsFormElementComponent;
options: FormDataOption[];
};
};
}
const CreateOptionsText = templateOnlyComponent<CreateOptionsTextSignature>();
export default CreateOptionsText;
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
CreateOptionsText: typeof CreateOptionsText;
}
}

View file

@ -0,0 +1,34 @@
<div class="cr-form-wrapper box">
<BsForm
@formLayout="horizontal"
@model={{this.formData}}
@onInvalid={{(scroll-first-invalid-element-into-view-port)}}
@onSubmit={{this.submit}}
novalidate
as |form|
>
{{#if (eq @poll.pollType "MakeAPoll")}}
<CreateOptionsText
@options={{this.formData.options}}
@addOption={{this.formData.addOption}}
@deleteOption={{this.formData.deleteOption}}
@formElement={{form.element}}
/>
{{else}}
<CreateOptionsDates
@options={{this.formData.options}}
@updateOptions={{this.formData.updateOptions}}
@formElement={{form.element}}
/>
{{/if}}
<div class="row cr-steps-bottom-nav">
<div class="col-6 col-md-8 order-12">
<NextButton />
</div>
<div class="col-6 col-md-4 order-1 text-right">
<BackButton @onClick={{this.previousPage}} />
</div>
</div>
</BsForm>
</div>

View file

@ -1,50 +0,0 @@
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import Component from '@ember/component';
import {
validator, buildValidations
}
from 'ember-cp-validations';
let Validations = buildValidations({
options: [
validator('collection', true),
validator('length', {
dependentKeys: ['model.options.[]', 'model.intl.locale'],
min: 1,
// it's impossible to delete all text options so this case could be ignored
// for validation error message
descriptionKey: 'create.options.error.notEnoughDates'
}),
validator('valid-collection', {
dependentKeys: ['model.options.[]', 'model.options.@each.title']
})
]
});
export default class CreateOptionsComponent extends Component.extend(Validations) {
shouldShowErrors = false;
// consumed by validator
@service intl;
@action
previousPage() {
this.onPrevPage();
}
@action
submit() {
if (this.get('validations.isValid')) {
this.onNextPage();
} else {
this.set('shouldShowErrors', true);
}
}
init() {
super.init(...arguments);
this.intl.locale;
}
}

View file

@ -0,0 +1,168 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { TrackedArray, TrackedSet } from 'tracked-built-ins';
import IntlMessage from '../utils/intl-message';
import { tracked } from '@glimmer/tracking';
import type RouterService from '@ember/routing/router-service';
import type { CreateOptionsRouteModel } from '../routes/create/options';
import type Transition from '@ember/routing/transition';
export class FormDataOption {
@tracked value;
formData;
get valueValidation() {
const { formData, value } = this;
// Every option must have a label
if (!value) {
return new IntlMessage('create.options.error.valueMissing');
}
// Options must be unique. There must not be another option having the
// same value before
const isUnique = !formData.options
.slice(0, this.formData.options.indexOf(this))
.some((option) => option.value === this.value);
if (!isUnique) {
return new IntlMessage('create.options.error.duplicatedOption');
}
return null;
}
get isValid() {
return this.valueValidation === null;
}
constructor(formData: FormData, value: string) {
this.formData = formData;
this.value = value;
}
}
class FormData {
@tracked options;
get optionsValidation() {
const { options } = this;
if (options.length < 1) {
// UI enforces that there is at least one option if poll type is `MakeAPoll`.
// This validation error can only happen if poll type is `FindADate`.
return new IntlMessage('create.options.error.notEnoughDates');
}
if (options.some((option) => !option.isValid)) {
return new IntlMessage('create.options.error.invalidOption');
}
return null;
}
@action
updateOptions(values: string[]) {
this.options = new TrackedArray(
values.map((value) => new FormDataOption(this, value)),
);
}
@action
addOption(value: string, afterPosition = this.options.length - 1) {
const option = new FormDataOption(this, value);
this.options.splice(afterPosition + 1, 0, option);
}
@action
deleteOption(option: FormDataOption) {
this.options.splice(this.options.indexOf(option), 1);
}
constructor(
{ options }: { options: Set<string> },
{ defaultOptionCount }: { defaultOptionCount: number },
) {
const normalizedOptions =
options.size === 0 && defaultOptionCount > 0
? ['', '']
: Array.from(options);
this.options = new TrackedArray(
normalizedOptions.map((value) => new FormDataOption(this, value)),
);
}
}
export interface CreateOptionsSignature {
Args: {
poll: CreateOptionsRouteModel;
};
}
export default class CreateOptions extends Component<CreateOptionsSignature> {
@service declare router: RouterService;
formData = new FormData(
{ options: this.options },
{ defaultOptionCount: this.args.poll.pollType === 'MakeAPoll' ? 2 : 0 },
);
get options() {
const { poll } = this.args;
const { dateOptions, freetextOptions, pollType } = poll;
return pollType === 'FindADate' ? dateOptions : freetextOptions;
}
@action
previousPage() {
this.router.transitionTo('create.meta');
}
@action
submit() {
const { pollType } = this.args.poll;
if (pollType === 'FindADate') {
this.router.transitionTo('create.options-datetime');
} else {
this.router.transitionTo('create.settings');
}
}
@action handleTransition(transition: Transition) {
if (transition.from?.name === 'create.options') {
this.updatePoll();
this.router.off('routeWillChange', this.handleTransition);
}
}
updatePoll() {
const { poll } = this.args;
const { pollType } = poll;
const { options } = this.formData;
const pollOptions = options.map(({ value }) => value);
if (pollType === 'FindADate') {
poll.dateOptions = new TrackedSet(pollOptions.sort());
} else {
poll.freetextOptions = new TrackedSet(pollOptions);
}
}
constructor(owner: unknown, args: CreateOptionsSignature['Args']) {
super(owner, args);
// Cannot use a destructor because that one runs _after_ the other component
// rendered by the next route is initialized.
this.router.on('routeWillChange', this.handleTransition);
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
CreateOptions: typeof CreateOptions;
}
}

View file

@ -0,0 +1,113 @@
<div class="cr-form-wrapper box">
<BsForm
@formLayout="horizontal"
@model={{this}}
@onInvalid={{(scroll-first-invalid-element-into-view-port)}}
@onSubmit={{this.createPoll}}
novalidate
as |form|
>
<form.element
@label={{t "create.settings.answerType.label"}}
@property="answerType"
@showValidationOn={{array "change" "focusOut"}}
@useIcons={{false}}
class="answer-type"
as |el|
>
<select
id={{el.id}}
class="custom-select"
{{on "change" (pick "target.value" el.setValue)}}
{{autofocus}}
>
{{#each this.answerTypes as |answerType|}}
<option
value={{answerType.id}}
selected={{eq el.value answerType.id}}
>
{{t answerType.labelTranslation}}
</option>
{{/each}}
</select>
</form.element>
<form.element
@controlType="select"
@label={{t "create.settings.expirationDate.label"}}
@property="expirationDuration"
@showValidationOn={{array "change" "focusOut"}}
@useIcons={{false}}
class="expiration-duration"
as |el|
>
<select
id={{el.id}}
{{on "change" (pick "target.value" el.setValue)}}
class="custom-select"
>
{{#each this.expirationDurations as |duration|}}
<option value={{duration.id}} selected={{eq el.value duration.id}}>
{{t duration.labelTranslation}}
</option>
{{/each}}
</select>
</form.element>
<form.element
@controlType="checkbox"
@label={{t "create.settings.anonymousUser.label"}}
@showValidationOn="change"
@property="anonymousUser"
class="anonymous-user"
/>
<form.element
@controlType="checkbox"
@label={{t "create.settings.forceAnswer.label"}}
@showValidationOn="change"
@property="forceAnswer"
class="force-answer"
/>
<div class="row cr-steps-bottom-nav">
<div class="col-6 col-md-8 order-12">
<SaveButton
@isPending={{form.isSubmitting}}
data-test-button="submit"
/>
</div>
<div class="col-6 col-md-4 order-1 text-right">
<BackButton @onClick={{this.previousPage}} />
</div>
</div>
<BsModal
@onHidden={{this.resetSavingPollFailedState}}
@onSubmit={{form.submit}}
@open={{this.savingPollFailed}}
data-test-modal="saving-failed"
as |modal|
>
<modal.header
@closeButton={{false}}
@title={{t "error.poll.savingFailed.title"}}
/>
<modal.body>
<p>
{{t "error.poll.savingFailed.description"}}
</p>
</modal.body>
<modal.footer>
<BsButton @onClick={{modal.close}} data-test-button="abort">
{{t "action.abort"}}
</BsButton>
<SaveButton
@isPending={{form.isSubmitting}}
@onClick={{modal.submit}}
data-test-button="retry"
type="button"
>
{{t "modal.save-retry.button-retry"}}
</SaveButton>
</modal.footer>
</BsModal>
</BsForm>
</div>

View file

@ -0,0 +1,188 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { service } from '@ember/service';
import { isPresent } from '@ember/utils';
import { DateTime, Duration } from 'luxon';
import { generatePassphrase } from '../utils/encryption';
import Poll from '../models/poll';
import type IntlService from 'ember-intl/services/intl';
import type RouterService from '@ember/routing/router-service';
import type { CreateSettingsRouteModel } from 'croodle/routes/create/settings';
export interface CreateSettingsSignature {
Args: {
poll: CreateSettingsRouteModel;
};
}
export default class CreateSettingsComponent extends Component<CreateSettingsSignature> {
@service declare intl: IntlService;
@service declare router: RouterService;
@tracked savingPollFailed = false;
get anonymousUser() {
return this.args.poll.anonymousUser;
}
set anonymousUser(value) {
this.args.poll.anonymousUser = value;
}
get answerType() {
return this.args.poll.answerType;
}
set answerType(value) {
this.args.poll.answerType = value;
}
get answerTypes() {
return [
{ id: 'YesNo', labelTranslation: 'answerTypes.yesNo.label' },
{ id: 'YesNoMaybe', labelTranslation: 'answerTypes.yesNoMaybe.label' },
{ id: 'FreeText', labelTranslation: 'answerTypes.freeText.label' },
];
}
get expirationDuration() {
// TODO: must be calculated based on model.expirationDate
return 'P3M';
}
set expirationDuration(value) {
this.args.poll.expirationDate = isPresent(value)
? (DateTime.local().plus(Duration.fromISO(value)).toISO() as string)
: '';
}
get expirationDurations() {
return [
{
id: 'P7D',
labelTranslation: 'create.settings.expirationDurations.P7D',
},
{
id: 'P1M',
labelTranslation: 'create.settings.expirationDurations.P1M',
},
{
id: 'P3M',
labelTranslation: 'create.settings.expirationDurations.P3M',
},
{
id: 'P6M',
labelTranslation: 'create.settings.expirationDurations.P6M',
},
{
id: 'P1Y',
labelTranslation: 'create.settings.expirationDurations.P1Y',
},
{ id: '', labelTranslation: 'create.settings.expirationDurations.never' },
];
}
get forceAnswer() {
return this.args.poll.forceAnswer;
}
set forceAnswer(value) {
this.args.poll.forceAnswer = value;
}
@action
previousPage() {
const { pollType } = this.args.poll;
if (pollType === 'FindADate') {
this.router.transitionTo('create.options-datetime');
} else {
this.router.transitionTo('create.options');
}
}
@action
async createPoll() {
const { poll } = this.args;
const {
anonymousUser,
answerType,
description,
expirationDate,
forceAnswer,
freetextOptions,
dateOptions,
timesForDateOptions,
pollType,
title,
} = poll;
// calculate options
const options: string[] = [];
if (pollType === 'FindADate') {
// merge date with times
for (const date of dateOptions) {
if (timesForDateOptions.has(date)) {
for (const time of timesForDateOptions.get(date)!) {
const [hour, minute] = time.split(':') as [string, string];
options.push(
DateTime.fromISO(date)
.set({
hour: parseInt(hour),
minute: parseInt(minute),
second: 0,
millisecond: 0,
})
.toISO() as string,
);
}
} else {
options.push(date);
}
}
} else {
options.push(...freetextOptions);
}
// save poll
try {
const encryptionKey = generatePassphrase();
// save poll
const poll = await Poll.create(
{
anonymousUser,
answerType,
description,
expirationDate,
forceAnswer,
options: options.map((option) => {
return { title: option };
}),
pollType,
title,
},
encryptionKey,
);
// redirect to new poll
await this.router.transitionTo('poll.participation', poll.id, {
queryParams: {
encryptionKey,
},
});
} catch (err) {
this.savingPollFailed = true;
reportError(err);
}
}
@action
resetSavingPollFailedState() {
this.savingPollFailed = false;
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
CreateSettings: typeof CreateSettingsComponent;
}
}

View file

@ -2,23 +2,24 @@
@center={{@center}}
@selected={{@selectedDays}}
@onCenterChange={{@onCenterChange}}
@onSelect={{@onSelect}} as |calendar|
@onSelect={{@onSelect}}
as |calendar|
>
<nav class="ember-power-calendar-nav">
<button
type="button"
class="ember-power-calendar-nav-control"
onclick={{action calendar.actions.moveCenter -1 "month"}}
{{on "click" (fn calendar.actions.moveCenter -1 "month")}}
>
«
</button>
<div class="ember-power-calendar-nav-title">
{{moment-format calendar.center "MMMM YYYY"}}
{{format-date calendar.center month="long" year="numeric"}}
</div>
<button
type="button"
class="ember-power-calendar-nav-control"
onclick={{action calendar.actions.moveCenter 1 "month"}}
{{on "click" (fn calendar.actions.moveCenter 1 "month")}}
>
»
</button>

View file

@ -1,7 +0,0 @@
import classic from 'ember-classic-decorator';
import { tagName } from '@ember-decorators/component';
import Component from '@ember/component';
@classic
@tagName('')
export default class InlineDatepicker extends Component {}

View file

@ -0,0 +1,23 @@
import templateOnlyComponent from '@ember/component/template-only';
import type { DateTime } from 'luxon';
interface InlineDatepickerSignature {
Args: {
Named: {
center: DateTime;
onCenterChange: (day: { datetime: DateTime }) => void;
onSelect: (days: { datetime: DateTime[] }) => void;
selectedDays: DateTime[];
};
};
}
const InlineDatepicker = templateOnlyComponent<InlineDatepickerSignature>();
export default InlineDatepicker;
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
InlineDatepicker: typeof InlineDatepicker;
}
}

View file

@ -0,0 +1,8 @@
{{! template-lint-disable require-input-label }}
<select class="language-select" {{on "change" this.handleChange}}>
{{#each-in this.locales as |localeKey localeName|}}
<option value={{localeKey}} selected={{eq localeKey this.currentLocale}}>
{{localeName}}
</option>
{{/each-in}}
</select>

View file

@ -1,49 +0,0 @@
import classic from 'ember-classic-decorator';
import { classNames, tagName } from '@ember-decorators/component';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { readOnly } from '@ember/object/computed';
import Component from '@ember/component';
import localesMeta from 'croodle/locales/meta';
@classic
@tagName('select')
@classNames('language-select')
export default class LanguageSelect extends Component {
@service
intl;
@service
moment;
@service
powerCalendar;
@readOnly('intl.primaryLocale')
current;
@computed('intl.locales')
get locales() {
let currentLocale = this.intl.primaryLocale;
return Object.keys(localesMeta).map(function(locale) {
return {
id: locale,
selected: locale === currentLocale,
text: localesMeta[locale]
};
});
}
change() {
let locale = this.element.options[this.element.selectedIndex].value;
this.intl.set('locale', locale.includes('-') ? [locale, locale.split('-')[0]] : [locale]);
this.moment.changeLocale(locale);
this.powerCalendar.set('locale', locale);
if (window.localStorage) {
window.localStorage.setItem('locale', locale);
}
}
}

View file

@ -0,0 +1,38 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import localesMeta from 'croodle/locales/meta';
import { action } from '@ember/object';
import type IntlService from 'ember-intl/services/intl';
import type PowerCalendarService from 'ember-power-calendar/services/power-calendar';
export default class LanguageSelect extends Component {
@service declare intl: IntlService;
@service declare powerCalendar: PowerCalendarService;
get currentLocale() {
return this.intl.primaryLocale;
}
locales = localesMeta;
@action
handleChange(event: Event) {
const selectElement = event.target as HTMLSelectElement;
const locale = selectElement.value as keyof typeof this.locales;
this.intl.locale = locale.includes('-')
? [locale, locale.split('-')[0] as string]
: [locale];
this.powerCalendar.locale = locale;
if (window.localStorage) {
window.localStorage.setItem('locale', locale);
}
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
LanguageSelect: typeof LanguageSelect;
}
}

View file

@ -0,0 +1,5 @@
<span
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
></span>

View file

@ -0,0 +1,13 @@
import templateOnlyComponent from '@ember/component/template-only';
interface LoadingSpinnerSignature {}
const LoadingSpinner = templateOnlyComponent<LoadingSpinnerSignature>();
export default LoadingSpinner;
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
LoadingSpinner: typeof LoadingSpinner;
}
}

View file

@ -0,0 +1,20 @@
import templateOnlyComponent from '@ember/component/template-only';
interface NextButtonSignature {
Args: {
Named: {
isPending?: boolean;
};
};
Element: HTMLButtonElement;
}
const NextButton = templateOnlyComponent<NextButtonSignature>();
export default NextButton;
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
NextButton: typeof NextButton;
}
}

View file

@ -1,134 +0,0 @@
import classic from 'ember-classic-decorator';
import { inject as service } from '@ember/service';
import { readOnly } from '@ember/object/computed';
import Component from '@ember/component';
import { get, computed } from '@ember/object';
import { isArray } from '@ember/array';
import { isPresent } from '@ember/utils';
import moment from 'moment';
const addArrays = function() {
let args = Array.prototype.slice.call(arguments);
let basis = args.shift();
if (!isArray(basis)) {
return [];
}
args.forEach(function(array) {
array.forEach(function(value, index) {
if (isPresent(value)) {
basis[index] = basis[index] + value;
}
});
});
return basis;
};
@classic
export default class PollEvaluationChart extends Component {
@service
intl;
@computed
get chartOptions() {
return {
legend: {
display: false
},
scales: {
xAxes: [{
stacked: true
}],
yAxes: [{
stacked: true,
ticks: {
callback(value) {
return `${value} %`;
},
max: 100,
min: 0
}
}]
},
tooltips: {
mode: 'label',
callbacks: {
label(tooltipItem, data) {
let { datasets } = data;
let { datasetIndex } = tooltipItem;
let { label } = datasets[datasetIndex];
let value = tooltipItem.yLabel;
return `${label}: ${value} %`;
}
}
}
}
}
@computed('users.[]', 'options.{[],each.title}', 'currentLocale')
get data() {
let labels = this.options.map((option) => {
let value = get(option, 'title');
if (!this.isFindADate) {
return value;
}
let hasTime = value.length > 10; // 'YYYY-MM-DD'.length === 10
let momentFormat = hasTime ? 'LLLL' : this.momentLongDayFormat;
let timezone = this.timezone;
let date = hasTime && isPresent(timezone) ? moment.tz(value, timezone) : moment(value);
date.locale(this.currentLocale);
return date.format(momentFormat);
});
let datasets = [];
let participants = this.users.length;
let yes = this.users.map(({ selections }) => {
return selections.map(({ type }) => type === 'yes' ? 1 : 0);
});
datasets.push({
label: this.intl.t('answerTypes.yes.label').toString(),
backgroundColor: 'rgba(151,187,205,0.5)',
borderColor: 'rgba(151,187,205,0.8)',
hoverBackgroundColor: 'rgba(151,187,205,0.75)',
hoverBorderColor: 'rgba(151,187,205,1)',
data: addArrays.apply(this, yes).map((value) => Math.round(value / participants * 100))
});
if (this.answerType === 'YesNoMaybe') {
let maybe = this.users.map(({ selections }) => {
return selections.map(({ type }) => type === 'maybe' ? 1 : 0);
});
datasets.push({
label: this.intl.t('answerTypes.maybe.label').toString(),
backgroundColor: 'rgba(220,220,220,0.5)',
borderColor: 'rgba(220,220,220,0.8)',
hoverBackgroundColor: 'rgba(220,220,220,0.75)',
hoverBorderColor: 'rgba(220,220,220,1)',
data: addArrays.apply(this, maybe).map((value) => Math.round(value / participants * 100))
});
}
return {
datasets,
labels
};
}
@readOnly('poll.answerType')
answerType;
@readOnly('intl.primaryLocale')
currentLocale;
@readOnly('poll.isFindADate')
isFindADate;
@readOnly('poll.options')
options;
@readOnly('poll.users')
users;
}

View file

@ -0,0 +1,86 @@
<div class="participants-table">
<table class="table" data-test-table-of="participants">
<thead>
{{#if @poll.hasTimes}}
<tr>
<th>
{{! column for name }}
</th>
{{#each-in this.optionsPerDay as |jsDate count|}}
{{!
@glint-ignore
We can be sure that count is a number because it is destructed from a
Map, which values are only numbers. But somehow Glint / TypeScript
is not sure about it.
}}
<th colspan={{count}}>
{{format-date jsDate dateStyle="full" timeZone=@timeZone}}
</th>
{{/each-in}}
</tr>
{{/if}}
<tr>
<th>
{{! column for name }}
</th>
{{#each @poll.options as |option|}}
<th>
{{#if (and @poll.isFindADate @poll.hasTimes)}}
{{#if option.hasTime}}
{{!
@glint-ignore
Narrowring is not working here correctly. Due to the only executing if
`option.hasTime` is `true`, we know that `option.jsDate` cannot be `null`.
But TypeScript does not support narrowing through a chain of getters
currently.
}}
{{! @glint-ignore }}{{! prettier-ignore }}
{{format-date option.jsDate
timeStyle="short"
timeZone=@timeZone
}}
{{/if}}
{{else if @poll.isFindADate}}
{{!
@glint-ignore
Narrowring is not working here correctly. Due to the only executing if
`option.hasTime` is `true`, we know that `option.jsDate` cannot be `null`.
But TypeScript does not support narrowing through a chain of getters
currently.
}}
{{format-date option.jsDate dateStyle="full" timeZone=@timeZone}}
{{else}}
{{option.title}}
{{/if}}
</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each this.usersSorted as |user|}}
<tr data-test-participant={{user.id}}>
<td data-test-value-for="name">
{{user.name}}
</td>
{{#each @poll.options as |option index|}}
{{#let (get user.selections index) as |selection|}}
<td
class={{selection.type}}
data-test-is-selection-cell
data-test-value-for={{option.title}}
>
{{#if selection.labelTranslation}}
{{t selection.labelTranslation}}
{{else}}
{{selection.label}}
{{/if}}
</td>
{{/let}}
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
</div>

View file

@ -1,29 +0,0 @@
import classic from 'ember-classic-decorator';
import { readOnly } from '@ember/object/computed';
import Component from '@ember/component';
import { raw } from 'ember-awesome-macros';
import { groupBy, sort } from 'ember-awesome-macros/array';
@classic
export default class PollEvaluationParticipantsTable extends Component {
@readOnly('poll.hasTimes')
hasTimes;
@readOnly('poll.isFindADate')
isFindADate;
@readOnly('poll.isFreeText')
isFreeText;
@readOnly('poll.options')
options;
@groupBy('options', raw('day'))
optionsGroupedByDays;
@readOnly('poll.users')
users;
@sort('users', ['creationDate'])
usersSorted;
}

View file

@ -0,0 +1,52 @@
import Component from '@glimmer/component';
import type Poll from 'croodle/models/poll';
import { DateTime } from 'luxon';
export interface PollEvaluationParticipantsTableSignature {
Args: {
poll: Poll;
timeZone: string | undefined;
};
}
export default class PollEvaluationParticipantsTable extends Component<PollEvaluationParticipantsTableSignature> {
get optionsPerDay() {
const { poll } = this.args;
const optionsPerDay: Map<string, number> = new Map();
for (const option of poll.options) {
if (!option.day) {
throw new Error(
`Excepts all options to have a valid ISO8601 date string when using optionsPerDay getter`,
);
}
optionsPerDay.set(
option.day,
optionsPerDay.has(option.day)
? (optionsPerDay.get(option.day) as number) + 1
: 0,
);
}
return new Map(
Array.from(optionsPerDay.entries()).map(([dayString, count]) => [
DateTime.fromISO(dayString).toJSDate(),
count,
]),
);
}
get usersSorted() {
const { poll } = this.args;
return Array.from(poll.users).sort((a, b) =>
a.creationDate > b.creationDate ? 1 : -1,
);
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
PollEvaluationParticipantsTable: typeof PollEvaluationParticipantsTable;
}
}

View file

@ -1,23 +1,28 @@
{{!--
{{!
There must not be a line break between option text and "</strong>." cause otherwise
we will see a space between option string and following dot.
--}}
{{#if @isFindADate}}
{{! Need to disable block indentation rule cause there shouldn't be a space between date and dot }}
{{! template-lint-disable block-indentation }}
<strong class="best-option-value">
{{moment-format
@evaluationBestOption.option.title
(if @evaluationBestOption.option.hasTime "LLLL" @momentLongDayFormat)
locale=@currentLocale
timeZone=@timezone
}}
{{!
Checking `@evaluationBestOption.option.jsDate` is the same as checking `@isFindADate`.
If poll type is `FindADate` we can be sure that every option is a valid ISO861
string. Therefore `Option.jsDate` must be `true` by design. But Glint / TypeScript
does not understand that. Therefore we need to use the less readable form.
}}
{{#if @evaluationBestOption.option.jsDate}}
<strong data-test-best-option={{@evaluationBestOption.option.title}}>
{{format-date
@evaluationBestOption.option.jsDate
dateStyle="full"
timeStyle=(if @evaluationBestOption.option.hasTime "short" undefined)
timeZone=(if @timeZone @timeZone undefined)
}}</strong>.
{{! template-lint-enable block-indentation }}
{{else}}
<strong class="best-option-value">{{@evaluationBestOption.option.title}}</strong>.
<strong
data-test-best-option={{@evaluationBestOption.option.title}}
>{{@evaluationBestOption.option.title}}</strong>.
{{/if}}
<br>
<br />
{{#if @isFindADate}}
{{#if @evaluationBestOption.answers.yes}}

View file

@ -0,0 +1,24 @@
import templateOnlyComponent from '@ember/component/template-only';
import type { BestOption } from './poll-evaluation-summary';
interface PollEvaluationSummaryOptionSignature {
Args: {
Named: {
evaluationBestOption: BestOption;
isFindADate: boolean;
timeZone: string | undefined;
};
};
Element: HTMLButtonElement;
}
const PollEvaluationSummaryOption =
templateOnlyComponent<PollEvaluationSummaryOptionSignature>();
export default PollEvaluationSummaryOption;
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
PollEvaluationSummaryOption: typeof PollEvaluationSummaryOption;
}
}

View file

@ -0,0 +1,62 @@
<div class="evaluation-summary">
<h2>
{{t "poll.evaluation.label"}}
</h2>
<p class="participants">
{{t "poll.evaluation.participants" count=@poll.users.length}}
</p>
<p class="best-options">
{{#if @poll.isFindADate}}
{{t
"poll.evaluation.bestOption.label.findADate"
count=this.bestOptions.length
}}
{{else}}
{{t
"poll.evaluation.bestOption.label.makeAPoll"
count=this.bestOptions.length
}}
{{/if}}
{{#if (gt this.bestOptions.length 1)}}
<ul>
{{#each this.bestOptions as |evaluationBestOption|}}
<li>
<PollEvaluationSummaryOption
@evaluationBestOption={{evaluationBestOption}}
@isFindADate={{@poll.isFindADate}}
@timeZone={{@timeZone}}
/>
</li>
{{/each}}
</ul>
{{else}}
<PollEvaluationSummaryOption
{{!
@glint-ignore
We can be sure that `this.bestOptions` contains at least one item
as a poll must always have at least one option.
}}
@evaluationBestOption={{get this.bestOptions 0}}
@isFindADate={{@poll.isFindADate}}
@timeZone={{@timeZone}}
/>
{{/if}}
</p>
<p class="last-participation">
{{#if this.lastParticipationAt}}
{{t
"poll.evaluation.lastParticipation"
ago=(format-date-relative this.lastParticipationAt)
}}
{{else}}
{{!
No need for the else block as user cannot enter evaluation page if
no one participated in the poll yet.
}}
{{/if}}
</p>
</div>

View file

@ -1,96 +0,0 @@
import classic from 'ember-classic-decorator';
import { classNames } from '@ember-decorators/component';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { readOnly, max, mapBy, gt } from '@ember/object/computed';
import Component from '@ember/component';
import { copy } from '@ember/object/internals';
import { isEmpty } from '@ember/utils';
@classic
@classNames('evaluation-summary')
export default class PollEvaluationSummary extends Component {
@service
intl;
@computed('users.[]')
get bestOptions() {
// can not evaluate answer type free text
if (this.get('poll.isFreeText')) {
return undefined;
}
// can not evaluate a poll without users
if (isEmpty(this.users)) {
return undefined;
}
let answers = this.poll.answers.reduce((answers, answer) => {
answers[answer.get('type')] = 0;
return answers;
}, {});
let evaluation = this.poll.options.map((option) => {
return {
answers: copy(answers),
option,
score: 0
};
});
let bestOptions = [];
this.users.forEach((user) => {
user.selections.forEach(({ type }, i) => {
evaluation[i].answers[type]++;
switch (type) {
case 'yes':
evaluation[i].score += 2;
break;
case 'maybe':
evaluation[i].score += 1;
break;
case 'no':
evaluation[i].score -= 2;
break;
}
});
});
evaluation.sort((a, b) => b.score - a.score);
let bestScore = evaluation[0].score;
for (let i = 0; i < evaluation.length; i++) {
if (
bestScore === evaluation[i].score
) {
bestOptions.push(
evaluation[i]
);
} else {
break;
}
}
return bestOptions;
}
@readOnly('intl.primaryLocale')
currentLocale;
@gt('bestOptions.length', 1)
multipleBestOptions;
@max('participationDates')
lastParticipationAt;
@mapBy('users', 'creationDate')
participationDates;
@readOnly('users.length')
participantsCount;
@readOnly('poll.users')
users;
}

View file

@ -0,0 +1,124 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import type IntlService from 'ember-intl/services/intl';
import type Option from 'croodle/models/option';
import type User from 'croodle/models/user';
import type { Answer } from 'croodle/utils/answers-for-answer-type';
import type Poll from 'croodle/models/poll';
export interface PollEvaluationSummarySignature {
Args: {
poll: Poll;
timeZone: string | undefined;
};
}
export interface BestOption {
answers: Record<'yes' | 'no' | 'maybe', number>;
option: Option;
score: number;
}
export default class PollEvaluationSummary extends Component<PollEvaluationSummarySignature> {
@service declare intl: IntlService;
get bestOptions(): BestOption[] | null {
const { poll } = this.args;
const { isFreeText, options, users } = poll;
// can not evaluate answer type free text
if (isFreeText) {
return null;
}
// can not evaluate a poll without users
if (users.length < 1) {
return null;
}
const answers = poll.answers.reduce(
(answers, answer: Answer) => {
answers[answer.type] = 0;
return answers;
},
{} as Record<'yes' | 'no' | 'maybe', number>,
);
const evaluation: BestOption[] = options.map((option: Option) => {
return {
answers: { ...answers },
option,
score: 0,
};
});
users.forEach((user: User) => {
user.selections.forEach(({ type }, i) => {
if (!type) {
// type may be undefined if poll does not force an answer to all options
return;
}
const evaluationForOption = evaluation[i];
if (evaluationForOption === undefined) {
throw new Error(
'Mismatch between number of options in poll and selections for user',
);
}
if (type !== 'yes' && type !== 'no' && type !== 'maybe') {
throw new Error(
`Encountered not supported type of user selection: ${type}`,
);
}
evaluationForOption.answers[type]++;
switch (type) {
case 'yes':
evaluation[i]!.score += 2;
break;
case 'maybe':
evaluation[i]!.score += 1;
break;
case 'no':
evaluation[i]!.score -= 2;
break;
}
});
});
evaluation.sort((a, b) => b.score - a.score);
const bestOptions = [];
const bestScore = evaluation[0]!.score;
for (const evaluationForOption of evaluation) {
if (evaluationForOption.score === bestScore) {
bestOptions.push(evaluationForOption);
} else {
break;
}
}
return bestOptions;
}
get lastParticipationAt() {
const { users } = this.args.poll;
let lastParticipationAt = null;
for (const { creationDate } of users) {
if (lastParticipationAt === null || creationDate >= lastParticipationAt) {
lastParticipationAt = creationDate;
}
}
return lastParticipationAt;
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
PollEvaluationSummary: typeof PollEvaluationSummary;
}
}

View file

@ -0,0 +1,27 @@
<BsButton
@type="primary"
{{!
Due to a bug in Ember, conditional modifiers cannot be used with the "on"
modifier. Need to always apply the modifier and fallback to a noop function
(returned by noop helper) instead of only applying the modifier when needed.
See https://github.com/emberjs/ember.js/issues/19869.
}}
{{on "click" (if @onClick @onClick (noop))}}
class="cr-steps-bottom-nav__button cr-steps-bottom-nav__next-button next"
type="submit"
...attributes
>
<span class="cr-steps-bottom-nav__label">
{{#if (has-block)}}
{{yield}}
{{else}}
{{t "action.save"}}
{{/if}}
</span>
{{#if @isPending}}
<LoadingSpinner />
{{else}}
<span class="cr-steps-bottom-nav__icon oi oi-circle-check"></span>
{{/if}}
</BsButton>

View file

@ -0,0 +1,24 @@
import templateOnlyComponent from '@ember/component/template-only';
interface SaveButtonSignature {
Args: {
Named: {
isPending: boolean;
onClick?: () => void;
};
};
Blocks: {
default: [];
};
Element: HTMLButtonElement;
}
const SaveButton = templateOnlyComponent<SaveButtonSignature>();
export default SaveButton;
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
SaveButton: typeof SaveButton;
}
}

View file

@ -0,0 +1,32 @@
<div class="box poll-link cr-poll-link">
<p>{{t "poll.share.title"}}</p>
<p class="link cr-poll-link__link">
<small>
<code class="cr-poll-link__url" data-test-poll-url>{{this.pollUrl}}</code>
</small>
<CopyButton
@text={{this.pollUrl}}
@onSuccess={{this.showCopySuccessMessage}}
class="btn btn-secondary cr-poll-link__copy-btn btn-sm"
data-test-button="copy-link"
>
{{t "poll.link.copy-label"}}&nbsp;
<span
class="oi oi-clipboard"
title={{t "poll.link.copy-label"}}
aria-hidden="true"
></span>
<BsTooltip
@placement="bottom"
@triggerEvents="click"
data-test-tooltip="copied"
>
{{t "poll.link.copied"}}
</BsTooltip>
</CopyButton>
</p>
<small class="text-muted">
{{t "poll.share.notice"}}
</small>
</div>

View file

@ -0,0 +1,40 @@
import Component from '@glimmer/component';
import { service } from '@ember/service';
import type RouterService from '@ember/routing/router-service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SharePollUrlComponent extends Component {
@service declare router: RouterService;
@tracked shouldShowCopySuccessMessage = false;
get pollUrl() {
// RouterService.currentURL is relative to the webserver's
// directory from which Croodle is served.
const relativeUrl = this.router.currentURL;
if (!relativeUrl) {
throw new Error('Relative URL is `null`. Cannot calculate poll URL.');
}
// The URL under which Croodle is served, is not known at
// build-time. But we can construct it ourself, by parsing
// window.location.href and replacing the hash part.
const absoluteUrl = new URL(window.location.href);
absoluteUrl.hash = relativeUrl;
return absoluteUrl.toString();
}
@action
showCopySuccessMessage() {
this.shouldShowCopySuccessMessage = true;
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
SharePollUrl: typeof SharePollUrlComponent;
}
}

14
app/config/environment.d.ts vendored Normal file
View file

@ -0,0 +1,14 @@
/**
* Type declarations for
* import config from 'croodle/config/environment'
*/
declare const config: {
environment: string;
modulePrefix: string;
podModulePrefix: string;
locationType: 'history' | 'hash' | 'none';
rootURL: string;
APP: Record<string, unknown>;
};
export default config;

View file

@ -1,61 +0,0 @@
import { inject as service } from '@ember/service';
import { action, computed } from '@ember/object';
import Controller from '@ember/controller';
export default class CreateController extends Controller {
@service
router;
@computed('model.pollType', 'visitedSteps')
get canEnterMetaStep() {
return this.visitedSteps.has('meta') && this.model.pollType;
}
@computed('model.title', 'visitedSteps')
get canEnterOptionsStep() {
let { title } = this.model;
return this.visitedSteps.has('options') &&
typeof title === 'string' && title.length >= 2;
}
@computed('model.options.[]', 'visitedSteps')
get canEnterOptionsDatetimeStep() {
return this.visitedSteps.has('options-datetime') && this.model.options.length >= 1;
}
@computed('model.options.[]', 'visitedSteps')
get canEnterSettingsStep() {
return this.visitedSteps.has('settings') && this.model.options.length >= 1;
}
@computed('model.pollType')
get isFindADate() {
return this.model.pollType === 'FindADate';
}
@action
updateVisitedSteps() {
let { currentRouteName } = this.router;
// currentRouteName might not be defined in some edge cases
if (!currentRouteName) {
return;
}
let step = currentRouteName.split('.').pop();
this.visitedSteps.add(step);
// as visitedSteps is a Set must notify about changes manually
this.notifyPropertyChange('visitedSteps');
}
listenForStepChanges() {
this.set('visitedSteps', new Set());
this.router.on('routeDidChange', this.updateVisitedSteps);
}
clearListenerForStepChanges() {
this.router.off('routeDidChange', this.updateVisitedSteps);
}
}

73
app/controllers/create.ts Normal file
View file

@ -0,0 +1,73 @@
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import Controller from '@ember/controller';
import { TrackedSet } from 'tracked-built-ins';
import type RouterService from '@ember/routing/router-service';
import type { CreateRouteModel } from 'croodle/routes/create';
export default class CreateController extends Controller {
@service declare router: RouterService;
declare model: CreateRouteModel;
visitedSteps = new TrackedSet();
get canEnterMetaStep() {
return this.visitedSteps.has('meta') && this.model.pollType;
}
get canEnterOptionsStep() {
const { title } = this.model;
return (
this.visitedSteps.has('options') &&
typeof title === 'string' &&
title.length >= 2
);
}
get canEnterOptionsDatetimeStep() {
return (
this.visitedSteps.has('options-datetime') &&
this.model.dateOptions.size >= 1
);
}
get canEnterSettingsStep() {
const { model, visitedSteps } = this;
const { dateOptions, freetextOptions, pollType } = model;
const options = pollType === 'FindADate' ? dateOptions : freetextOptions;
return visitedSteps.has('settings') && options.size >= 1;
}
get isFindADate() {
return this.model.pollType === 'FindADate';
}
@action
updateVisitedSteps() {
const { currentRouteName } = this.router;
// currentRouteName might not be defined in some edge cases
if (!currentRouteName) {
return;
}
const step = currentRouteName.split('.').pop();
this.visitedSteps.add(step);
}
@action transitionTo(route: string) {
this.router.transitionTo(route);
}
listenForStepChanges() {
this.set('visitedSteps', new TrackedSet());
this.router.on('routeDidChange', this.updateVisitedSteps);
}
clearListenerForStepChanges() {
this.router.off('routeDidChange', this.updateVisitedSteps);
}
}

View file

@ -1,42 +0,0 @@
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { alias } from '@ember/object/computed';
import Controller from '@ember/controller';
import {
validator, buildValidations
}
from 'ember-cp-validations';
const Validations = buildValidations({
pollType: [
validator('presence', {
presence: true,
dependentKeys: ['model.intl.locale']
}),
validator('inclusion', {
in: ['FindADate', 'MakeAPoll'],
dependentKeys: ['model.intl.locale']
})
]
});
export default class CreateIndex extends Controller.extend(Validations) {
@service
intl;
@alias('model.pollType')
pollType;
@action
submit() {
if (this.get('validations.isValid')) {
this.transitionToRoute('create.meta');
}
}
init() {
super.init(...arguments);
this.intl.locale;
}
}

View file

@ -1,50 +0,0 @@
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { alias } from '@ember/object/computed';
import Controller from '@ember/controller';
import {
validator, buildValidations
}
from 'ember-cp-validations';
const Validations = buildValidations({
title: [
validator('presence', {
presence: true,
dependentKeys: ['model.intl.locale']
}),
validator('length', {
min: 2,
dependentKeys: ['model.intl.locale']
})
]
});
export default class CreateMetaController extends Controller.extend(Validations) {
@service
intl;
@alias('model.description')
description;
@alias('model.title')
title;
init() {
super.init(...arguments);
this.get('intl.locale');
}
@action
previousPage() {
this.transitionToRoute('create.index');
}
@action
submit() {
if (this.get('validations.isValid')) {
this.transitionToRoute('create.options');
}
}
}

View file

@ -1,47 +0,0 @@
import classic from 'ember-classic-decorator';
import { action } from '@ember/object';
import { alias } from '@ember/object/computed';
import Controller from '@ember/controller';
import moment from 'moment';
@classic
export default class CreateOptionsDatetimeController extends Controller {
@action
nextPage() {
this.normalizeOptions();
this.transitionToRoute('create.settings');
}
@action
previousPage() {
this.transitionToRoute('create.options');
}
normalizeOptions() {
const options = this.options;
// remove all days from options which haven't a time but there is atleast
// one option with time for that day
const daysWithTime = options.map((option) => {
if (moment(option.get('title'), 'YYYY-MM-DD', true).isValid()) {
return null;
} else {
return moment(option.get('title')).format('YYYY-MM-DD');
}
}).uniq().filter((option) => option !== null);
const removeObjects = options.filter((option) => {
return daysWithTime.indexOf(option.get('title')) !== -1;
});
options.removeObjects(
removeObjects
);
// sort options
// ToDo: Find a better way without reseting the options
this.set('options', options.sortBy('title'));
}
@alias('model.options')
options;
}

View file

@ -1,24 +0,0 @@
import classic from 'ember-classic-decorator';
import { action } from '@ember/object';
import { alias } from '@ember/object/computed';
import Controller from '@ember/controller';
@classic
export default class CreateOptionsController extends Controller {
@action
nextPage() {
if (this.isFindADate) {
this.transitionToRoute('create.options-datetime');
} else {
this.transitionToRoute('create.settings');
}
}
@action
previousPage() {
this.transitionToRoute('create.meta');
}
@alias('model.isFindADate')
isFindADate;
}

View file

@ -1,146 +0,0 @@
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
import Controller from '@ember/controller';
import { isPresent } from '@ember/utils';
import { action, computed } from '@ember/object';
import answersForAnswerType from 'croodle/utils/answers-for-answer-type';
import {
validator, buildValidations
}
from 'ember-cp-validations';
import moment from 'moment';
const Validations = buildValidations({
anonymousUser: validator('presence', {
presence: true,
dependentKeys: ['model.intl.locale']
}),
answerType: [
validator('presence', {
presence: true,
dependentKeys: ['model.intl.locale']
}),
validator('inclusion', {
in: ['YesNo', 'YesNoMaybe', 'FreeText'],
dependentKeys: ['model.intl.locale']
})
],
forceAnswer: validator('presence', true)
});
export default class CreateSettings extends Controller.extend(Validations) {
@service
encryption;
@service
intl;
@alias('model.anonymousUser')
anonymousUser;
@alias('model.answerType')
answerType;
@computed
get answerTypes() {
return [
{ id: 'YesNo', labelTranslation: 'answerTypes.yesNo.label' },
{ id: 'YesNoMaybe', labelTranslation: 'answerTypes.yesNoMaybe.label' },
{ id: 'FreeText', labelTranslation: 'answerTypes.freeText.label' },
];
}
@computed('model.expirationDate')
get expirationDuration() {
// TODO: must be calculated based on model.expirationDate
return 'P3M';
}
set expirationDuration(value) {
this.set(
'model.expirationDate',
isPresent(value) ? moment().add(moment.duration(value)).toISOString(): ''
);
return value;
}
@computed
get expirationDurations() {
return [
{ id: 'P7D', labelTranslation: 'create.settings.expirationDurations.P7D' },
{ id: 'P1M', labelTranslation: 'create.settings.expirationDurations.P1M' },
{ id: 'P3M', labelTranslation: 'create.settings.expirationDurations.P3M' },
{ id: 'P6M', labelTranslation: 'create.settings.expirationDurations.P6M' },
{ id: 'P1Y', labelTranslation: 'create.settings.expirationDurations.P1Y' },
{ id: '', labelTranslation: 'create.settings.expirationDurations.never' },
];
}
@alias('model.forceAnswer')
forceAnswer;
@action
previousPage() {
let { isFindADate } = this.model;
if (isFindADate) {
this.transitionToRoute('create.options-datetime');
} else {
this.transitionToRoute('create.options');
}
}
@action
async submit() {
if (!this.validations.isValid) {
return;
}
let poll = this.model;
// set timezone if there is atleast one option with time
if (
poll.isFindADate &&
poll.options.any(({ title }) => {
return !moment(title, 'YYYY-MM-DD', true).isValid();
})
) {
this.set('model.timezone', moment.tz.guess());
}
// save poll
try {
await poll.save();
} catch(err) {
this.flashMessages.danger('error.poll.savingFailed');
throw err;
}
try {
// reload as workaround for bug: duplicated records after save
await poll.reload();
// redirect to new poll
await this.transitionToRoute('poll', poll, {
queryParams: {
encryptionKey: this.encryption.key,
},
});
} catch(err) {
// TODO: show feedback to user
throw err;
}
}
@action
updateAnswerType(answerType) {
this.set('model.answerType', answerType);
this.set('model.answers', answersForAnswerType(answerType));
}
init() {
super.init(...arguments);
this.intl.locale;
}
}

View file

@ -1,5 +0,0 @@
import classic from 'ember-classic-decorator';
import Controller from '@ember/controller';
@classic
export default class ErrorController extends Controller {}

View file

@ -1,16 +0,0 @@
import classic from 'ember-classic-decorator';
import { computed } from '@ember/object';
import { equal } from '@ember/object/computed';
import Controller from '@ember/controller';
import sjcl from 'sjcl';
@classic
export default class PollErrorController extends Controller {
@computed('model')
get decryptionFailed() {
return this.model instanceof sjcl.exception.corrupt;
}
@equal('model.errors.firstObject.status', '404')
notFound;
}

View file

@ -0,0 +1,13 @@
import Controller from '@ember/controller';
import sjcl from 'sjcl';
import { NotFoundError } from '../utils/api';
export default class PollErrorController extends Controller {
get decryptionFailed() {
return this.model instanceof sjcl.exception.corrupt;
}
get notFound() {
return this.model instanceof NotFoundError;
}
}

View file

@ -1,110 +0,0 @@
import { inject as service } from '@ember/service';
import { readOnly } from '@ember/object/computed';
import Controller from '@ember/controller';
import { isPresent, isEmpty } from '@ember/utils';
import { action, computed } from '@ember/object';
import { observes } from '@ember-decorators/object';
import moment from 'moment';
export default class PollController extends Controller {
@service
encryption;
@service
flashMessages;
@service
intl;
@service
router;
queryParams = ['encryptionKey'];
encryptionKey = '';
timezoneChoosen = false;
useLocalTimezone = false;
@readOnly('intl.primaryLocale')
currentLocale;
@computed('currentLocale')
get momentLongDayFormat() {
let currentLocale = this.currentLocale;
return moment.localeData(currentLocale)
.longDateFormat('LLLL')
.replace(
moment.localeData(currentLocale).longDateFormat('LT'), '')
.trim();
}
@readOnly('model')
poll;
@computed('router.currentURL', 'encryptionKey')
get pollUrl() {
return window.location.href;
}
@computed('poll.expirationDate')
get showExpirationWarning() {
let expirationDate = this.poll.expirationDate;
if (isEmpty(expirationDate)) {
return false;
}
return moment().add(2, 'weeks').isAfter(moment(expirationDate));
}
/*
* return true if current timezone differs from timezone poll got created with
*/
@computed('poll.timezone')
get timezoneDiffers() {
let modelTimezone = this.poll.timezone;
return isPresent(modelTimezone) && moment.tz.guess() !== modelTimezone;
}
@computed('timezoneDiffers', 'timezoneChoosen')
get mustChooseTimezone() {
return this.timezoneDiffers && !this.timezoneChoosen;
}
@computed('useLocalTimezone')
get timezone() {
return this.useLocalTimezone ? undefined : this.poll.timezone;
}
@action
linkAction(type) {
let flashMessages = this.flashMessages;
switch (type) {
case 'copied':
flashMessages.success(`poll.link.copied`);
break;
case 'selected':
flashMessages.info(`poll.link.selected`);
break;
}
}
@action
useLocalTimezone() {
this.set('useLocalTimezone', true);
this.set('timezoneChoosen', true);
}
// TODO: Remove this code. It's spooky.
@observes('encryptionKey')
preventEncryptionKeyChanges() {
if (
!isEmpty(this.encryption.key) &&
this.encryptionKey !== this.encryption.key
) {
// work-a-round for url not being updated
window.location.hash = window.location.hash.replace(this.encryptionKey, this.encryption.key);
this.set('encryptionKey', this.encryption.key);
}
}
}

68
app/controllers/poll.ts Normal file
View file

@ -0,0 +1,68 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { isPresent, isEmpty } from '@ember/utils';
import { action } from '@ember/object';
import { DateTime } from 'luxon';
import { tracked } from '@glimmer/tracking';
import type IntlService from 'ember-intl/services/intl';
import type RouterService from '@ember/routing/router-service';
import type { PollRouteModel } from 'croodle/routes/poll';
export default class PollController extends Controller {
@service declare intl: IntlService;
@service declare router: RouterService;
declare model: PollRouteModel;
queryParams = ['encryptionKey'];
encryptionKey = '';
@tracked timezoneChoosen = false;
@tracked shouldUseLocalTimezone = false;
get showExpirationWarning() {
const { model: poll } = this;
const { expirationDate } = poll;
if (isEmpty(expirationDate)) {
return false;
}
return (
DateTime.local().plus({ weeks: 2 }) >= DateTime.fromISO(expirationDate)
);
}
/*
* return true if current timezone differs from timezone poll got created with
*/
get timezoneDiffers() {
const { model: poll } = this;
const { timezone: pollTimezone } = poll;
return (
isPresent(pollTimezone) &&
Intl.DateTimeFormat().resolvedOptions().timeZone !== pollTimezone
);
}
get mustChooseTimezone() {
return this.timezoneDiffers && !this.timezoneChoosen;
}
get timezone() {
const { model: poll, shouldUseLocalTimezone } = this;
return shouldUseLocalTimezone || !poll.timezone ? undefined : poll.timezone;
}
@action
useLocalTimezone() {
this.shouldUseLocalTimezone = true;
this.timezoneChoosen = true;
}
@action
usePollTimezone() {
this.timezoneChoosen = true;
}
}

View file

@ -1,108 +0,0 @@
import classic from 'ember-classic-decorator';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { readOnly, not, gt, and } from '@ember/object/computed';
import Controller, { inject as controller } from '@ember/controller';
@classic
export default class PollEvaluationController extends Controller {
@readOnly('intl.primaryLocale')
currentLocale;
@readOnly('poll.hasTimes')
hasTimes;
@service
intl;
@readOnly('pollController.momentLongDayFormat')
momentLongDayFormat;
@readOnly('model')
poll;
@controller('poll')
pollController;
@readOnly('pollController.timezone')
timezone;
@readOnly('poll.users')
users;
/*
* evaluates poll data
* if free text answers are allowed evaluation is disabled
*/
@computed('users.[]')
get evaluation() {
if (!this.isEvaluable) {
return [];
}
let evaluation = [];
let options = [];
let lookup = [];
// init options array
this.poll.options.forEach((option, index) => {
options[index] = 0;
});
// init array of evalutation objects
// create object for every possible answer
this.poll.answers.forEach((answer) => {
evaluation.push({
id: answer.label,
label: answer.label,
options: [...options],
});
});
// create object for no answer if answers are not forced
if (!this.poll.forceAnswer) {
evaluation.push({
id: null,
label: 'no answer',
options: [...options],
});
}
// create lookup array
evaluation.forEach(function(value, index) {
lookup[value.id] = index;
});
// loop over all users
this.poll.users.forEach((user) => {
// loop over all selections of the user
user.selections.forEach(function(selection, optionIndex) {
let answerIndex;
// get answer index by lookup array
if (typeof lookup[selection.value.label] === 'undefined') {
answerIndex = lookup[null];
} else {
answerIndex = lookup[selection.get('value.label')];
}
// increment counter
try {
evaluation[answerIndex].options[optionIndex]++;
} catch (e) {
// ToDo: Throw an error
}
});
});
return evaluation;
}
@gt('poll.users.length', 0)
hasUsers;
@not('poll.isFreeText')
isNotFreeText;
@and('hasUsers', 'isNotFreeText')
isEvaluable;
}

View file

@ -0,0 +1,21 @@
import Controller, { inject as controller } from '@ember/controller';
import { inject as service } from '@ember/service';
import type IntlService from 'ember-intl/services/intl';
import type PollController from '../poll';
import type { PollEvaluationRouteModel } from 'croodle/routes/poll/evaluation';
export default class PollEvaluationController extends Controller {
@service declare intl: IntlService;
@controller('poll') declare pollController: PollController;
declare model: PollEvaluationRouteModel;
get isEvaluable() {
const { model: poll } = this;
const { isFreeText, users } = poll;
const hasUsers = users.length > 0;
return hasUsers && !isFreeText;
}
}

View file

@ -1,240 +0,0 @@
import classic from 'ember-classic-decorator';
import { inject as service } from '@ember/service';
import { not, readOnly } from '@ember/object/computed';
import Controller, { inject as controller } from '@ember/controller';
import { getOwner } from '@ember/application';
import { isPresent, isEmpty } from '@ember/utils';
import EmberObject, { computed } from '@ember/object';
import {
validator, buildValidations
}
from 'ember-cp-validations';
import moment from 'moment';
import config from 'croodle/config/environment';
const validCollection = function(collection) {
// return false if any object in collection is inValid
return !collection.any((object) => {
return object.get('validations.isInvalid');
});
};
const Validations = buildValidations({
name: [
validator('presence', {
presence: true,
disabled: readOnly('model.anonymousUser'),
dependentKeys: ['model.intl.locale']
}),
validator('unique', {
parent: 'poll',
attributeInParent: 'users',
dependentKeys: ['model.poll.users.[]', 'model.poll.users.@each.name', 'model.intl.locale'],
disable: readOnly('model.anonymousUser'),
messageKey: 'errors.uniqueName',
ignoreNewRecords: true,
})
],
selections: [
validator('collection', true),
// all selection objects must be valid
// if forceAnswer is true in poll settings
validator(validCollection, {
dependentKeys: ['model.forceAnswer', 'model.selections.[]', 'model.selections.@each.value', 'model.intl.locale']
})
]
});
const SelectionValidations = buildValidations({
value: validator('presence', {
presence: true,
disabled: not('model.forceAnswer'),
messageKey: computed('model.isFreeText', function() {
return this.get('model.isFreeText') ? 'errors.present' : 'errors.answerRequired';
}),
dependentKeys: ['model.intl.locale']
})
});
@classic
class SelectionObject extends EmberObject.extend(SelectionValidations) {
@service intl;
value = null;
init() {
super.init(...arguments);
// current locale needs to be consumed in order to be observeable
// for localization of validation messages
this.intl.locale;
}
}
export default Controller.extend(Validations, {
actions: {
async submit() {
if (!this.get('validations.isValid')) {
return;
}
let poll = this.poll;
let selections = this.selections.map(({ value }) => {
if (value === null) {
return {};
}
if (this.isFreeText) {
return {
label: value,
};
}
// map selection to answer if it's not freetext
let answer = poll.answers.findBy('type', value);
let { icon, label, labelTranslation, type } = answer;
return {
icon,
label,
labelTranslation,
type,
};
});
let user = this.store.createRecord('user', {
creationDate: new Date(),
name: this.name,
poll,
selections,
version: config.APP.version,
});
this.set('newUserRecord', user);
await this.actions.save.bind(this)();
},
async save() {
let user = this.newUserRecord;
try {
await user.save();
this.set('savingFailed', false);
} catch (error) {
// couldn't save user model
this.set('savingFailed', true);
return;
}
// reset form
this.set('name', '');
this.selections.forEach((selection) => {
selection.set('value', null);
});
this.transitionToRoute('poll.evaluation', this.model, {
queryParams: { encryptionKey: this.encryption.key }
});
}
},
anonymousUser: readOnly('poll.anonymousUser'),
currentLocale: readOnly('intl.locale'),
encryption: service(),
forceAnswer: readOnly('poll.forceAnswer'),
intl: service(),
init() {
this._super(...arguments);
// current locale needs to be consumed in order to be observeable
// for localization of validation messages
this.intl.locale;
},
isFreeText: readOnly('poll.isFreeText'),
isFindADate: readOnly('poll.isFindADate'),
momentLongDayFormat: readOnly('pollController.momentLongDayFormat'),
name: '',
options: readOnly('poll.options'),
poll: readOnly('model'),
pollController: controller('poll'),
possibleAnswers: computed('poll.answers', function() {
return this.get('poll.answers').map((answer) => {
const owner = getOwner(this);
const AnswerObject = EmberObject.extend({
icon: answer.get('icon'),
type: answer.get('type')
});
if (!isEmpty(answer.get('labelTranslation'))) {
return AnswerObject.extend({
intl: service(),
label: computed('intl.locale', function() {
return this.intl.t(this.labelTranslation);
}),
labelTranslation: answer.get('labelTranslation'),
}).create(owner.ownerInjection());
} else {
return AnswerObject.extend({
label: answer.get('label')
});
}
});
}),
savingFailed: false,
selections: computed('options', 'pollController.dates', function() {
let options = this.options;
let isFindADate = this.isFindADate;
let lastDate;
return options.map((option) => {
let labelValue;
let momentFormat;
let value = option.get('title');
// format label
if (isFindADate) {
let hasTime = value.length > 10; // 'YYYY-MM-DD'.length === 10
let timezone = this.timezone;
let date = isPresent(timezone) ? moment.tz(value, timezone) : moment(value);
if (hasTime && lastDate && date.format('YYYY-MM-DD') === lastDate.format('YYYY-MM-DD')) {
labelValue = value;
// do not repeat dates for different times
momentFormat = 'LT';
} else {
labelValue = value;
momentFormat = hasTime ? 'LLLL' : 'day';
lastDate = date;
}
} else {
labelValue = value;
}
// https://github.com/offirgolan/ember-cp-validations#basic-usage---objects
// To lookup validators, container access is required which can cause an issue with Object creation
// if the object is statically imported. The current fix for this is as follows.
let owner = getOwner(this);
return SelectionObject.create(owner.ownerInjection(), {
labelValue,
momentFormat,
// forceAnswer and isFreeText must be included in model
// cause otherwise validations can't depend on it
forceAnswer: this.forceAnswer,
isFreeText: this.isFreeText,
});
});
}),
timezone: readOnly('pollController.timezone')
});

View file

@ -0,0 +1,107 @@
import Controller, { inject as controller } from '@ember/controller';
import User from '../../models/user';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import type RouterService from '@ember/routing/router-service';
import type PollController from '../poll';
import type { PollParticipationRouteModel } from 'croodle/routes/poll/participation';
import type Poll from 'croodle/models/poll';
import type { SelectionInput } from 'croodle/models/selection';
export default class PollParticipationController extends Controller {
@service declare router: RouterService;
@controller('poll') declare pollController: PollController;
declare model: PollParticipationRouteModel;
@tracked name = '';
@tracked savingFailed = false;
newUserData: {
name: string | null;
poll: Poll;
selections: SelectionInput[];
} | null = null;
@action
async submit() {
const { formData, poll } = this.model;
const { name } = formData;
const { answers, isFreeText } = poll;
const selections = formData.selections.map(({ value }) => {
if (value === null) {
return {};
}
if (isFreeText) {
return {
label: value,
};
}
// map selection to answer if it's not freetext
const answer = answers.find(({ type }) => type === value);
if (!answer) {
throw new Error('Mapping selection to answer failed');
}
const { icon, labelTranslation, type } = answer;
return {
icon,
labelTranslation,
type,
};
});
this.newUserData = {
name,
poll,
selections,
};
await this.save();
}
@action
async save() {
const { model, newUserData: userData } = this;
const { poll } = model;
// As know that the route is `poll.participation`, which means that there
// is a parent `poll` for sure.
const { encryptionKey } = this.router.currentRoute?.parent?.queryParams as {
encryptionKey: string;
};
if (!userData) {
throw new Error(
'save method called before submit method has set the user data',
);
}
if (!encryptionKey) {
throw new Error('Can not lookup encryption key');
}
try {
await User.create(userData, encryptionKey);
this.savingFailed = false;
} catch (error) {
// couldn't save user model
this.savingFailed = true;
return;
}
this.router.transitionTo('poll.evaluation', poll.id, {
queryParams: { encryptionKey },
});
}
@action
resetSavingStatus() {
this.savingFailed = false;
}
}

View file

@ -1,2 +1 @@
export default {
};
export default {};

Some files were not shown because too many files have changed in this diff Show more