Compare commits

...

716 Commits
3.2 ... main

Author SHA1 Message Date
Emanuel Schütze 94263da223
Updated autoupdate service (#6446) 2022-06-20 16:01:22 +02:00
Emanuel Schütze 0e6f223cf0
Updated services (#6445) 2022-06-17 18:51:59 +02:00
GabrielInTheWorld b053baa5a6
Updating auth service (#6444) 2022-06-16 19:28:15 +02:00
Emanuel Schütze 52af4ac0da
Updated client (#6442) 2022-06-14 12:49:55 +00:00
Emanuel Schütze 37f8d76c24
Updated services (#6441) 2022-06-14 08:55:09 +00:00
Emanuel Schütze b251b942c3
Updated client (#6439) 2022-06-10 16:19:48 +00:00
Emanuel Schütze 986f55382d
Updated AU and client (#6438) 2022-06-10 18:07:48 +02:00
peb-adr 11d70bec88
Updated client,AU,icc,vote (#6437)
Co-authored-by: Adrian Richter <adrian@intevation.de>
2022-06-10 12:13:16 +00:00
Emanuel Schütze 47d7c48425
Updated services (#6436) 2022-06-09 16:45:52 +02:00
peb-adr 4cc5c55b13
Updated client (#6435)
Co-authored-by: Adrian Richter <adrian@intevation.de>
2022-06-08 17:10:00 +00:00
Emanuel Schütze e343bb7e6b
Updated services + Add OpenTelemetry (#6434)
OpenTelemetry now available in dev mode.
 -> run `make run-dev-otel`
As of now backend and datastore report spans.
2022-06-08 16:46:14 +00:00
Emanuel Schütze 676be7f1fa
Updated services (#6432) 2022-06-02 14:38:44 +00:00
Emanuel Schütze aeffbb6622
Updated services (#6431) 2022-05-31 22:32:52 +02:00
Emanuel Schütze 05a1712131
Updated services (#6430) 2022-05-30 17:03:45 +02:00
peb-adr f41c117cd0
Updated services (#6429)
Co-authored-by: Adrian Richter <adrian@intevation.de>
2022-05-25 18:39:28 +02:00
Emanuel Schütze b5ddb57b06
Updated services (#6426) 2022-05-20 18:34:21 +02:00
Emanuel Schütze 5fd8e184e0
Updated client (#6425) 2022-05-19 12:33:05 +00:00
Emanuel Schütze 88a45044e8
Updated services (#6424) 2022-05-19 09:43:32 +02:00
peb-adr 8d9d114fca
Updated services (#6423)
Co-authored-by: Adrian Richter <adrian@intevation.de>
2022-05-18 11:36:18 +02:00
Emanuel Schütze 110f4d769d
Updated client (#6422) 2022-05-17 08:55:14 +00:00
peb-adr 1467f2b529
Updated services (#6421)
Co-authored-by: Adrian Richter <adrian@intevation.de>
2022-05-16 17:36:36 +00:00
peb-adr 43a218f58a
Updated services (#6420)
Co-authored-by: Adrian Richter <adrian@intevation.de>
2022-05-16 14:43:45 +02:00
peb-adr 73ddad152d
Updated services (#6419)
Co-authored-by: Adrian Richter <adrian@intevation.de>
2022-05-13 17:05:07 +02:00
Ralf Peschke 6ac9bde47d
Issue6417 last modules set to main (#6418) 2022-05-11 11:33:52 +00:00
Emanuel Schütze c5b95594d8
Updated services (#6416) 2022-05-10 05:32:26 +00:00
Emanuel Schütze 464a0e59e8
Updated client (#6415) 2022-05-02 09:09:14 +00:00
Emanuel Schütze b1f2574205
Updated services (#6414) 2022-04-29 12:08:53 +02:00
Emanuel Schütze f900b0dbcf
Updated services (#6411) 2022-04-27 10:51:04 +00:00
Emanuel Schütze 96ad0652d5
Updated services (#6410) 2022-04-25 13:44:35 +00:00
Emanuel Schütze e34be730d8
Updated services (#6406) 2022-04-23 16:06:52 +00:00
Emanuel Schütze ce1c30661a
Updated services (#6405) 2022-04-20 17:59:45 +00:00
Emanuel Schütze 17bbbc9962
Updated services and AUTHORS (#6395) 2022-03-30 13:31:23 +02:00
dependabot[bot] 5bf57687b2
Bump minimist from 1.2.5 to 1.2.6 in /integration (#6390)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-26 19:06:30 +01:00
Emanuel Schütze cfdcb80874
Use version 4.0.0-beta (#6389) 2022-03-26 06:33:33 +01:00
Emanuel Schütze 1d38d096e8
Updated services (#6388) 2022-03-25 11:25:46 +00:00
Emanuel Schütze c2019bcb07
Updated services (#6387) 2022-03-24 08:15:57 +00:00
Emanuel Schütze 871a42532a
Updated services (#6386) 2022-03-22 12:51:29 +00:00
Emanuel Schütze abe1597bd1
Updated services (#6384) 2022-03-21 16:55:11 +01:00
dependabot[bot] 023e445930
Bump follow-redirects from 1.14.7 to 1.14.8 in /integration (#6360)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
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>
2022-03-17 12:59:36 +00:00
Emanuel Schütze f8347972fa
Updated services (#6383) 2022-03-11 14:25:43 +00:00
Emanuel Schütze a54555dc62
Updated services (#6382) 2022-03-09 14:23:05 +00:00
Emanuel Schütze f5425644e5
Updated services (#6381) 2022-03-04 16:14:23 +01:00
peb-adr 0b7f0c4073
Updated services (#6379) 2022-03-03 17:33:09 +00:00
Emanuel Schütze 3090779e7a
Updated services (#6377) 2022-03-01 18:18:30 +01:00
Emanuel Schütze 35d0d9d365
Updated services (#6375) 2022-02-25 14:31:18 +00:00
peb-adr a8f19e9901
cleanup services-to-main.sh (#6372)
- remove -B from checkout, didn't work as expected
- all submodules are now using main, fallback to master no longer needed
- make using git pull optional (--pull)

Co-authored-by: Adrian Richter <adrian@intevation.de>
2022-02-25 15:16:36 +01:00
Emanuel Schütze acfb4554d2
Updated AUTHORS (#6374) 2022-02-25 11:27:33 +01:00
Emanuel Schütze 92e658ab50
Updated services (#6373) 2022-02-24 10:24:43 +01:00
Emanuel Schütze 283383bb26
Updated services (#6368) 2022-02-17 18:10:29 +01:00
Emanuel Schütze 81e75aabe2
Updated services (#6363) 2022-02-17 14:50:55 +01:00
GabrielInTheWorld 473c742681
Speed up the 'make run-dev' command (#6362) 2022-02-16 16:19:58 +01:00
bug0001 b9ca06af25
Documentation improvements (#6350)
Documentation improvements OS4
2022-02-16 11:24:50 +00:00
Emanuel Schütze 8f2fb37f79
Updated services (#6358) 2022-02-14 15:05:15 +00:00
Emanuel Schütze a98349020a
Updated services (#6357) 2022-02-11 12:20:40 +00:00
Emanuel Schütze a1d0d2cf44
Updated services (#6349) 2022-02-03 23:03:50 +00:00
Emanuel Schütze 18a8449b5c
Updated package-lock.json (#6348) 2022-02-03 17:42:18 +01:00
GabrielInTheWorld 13b3204dab
Update DEVELOPMENT.md (#6347)
Changes some wording to prevent mixing up "git checkout" with "After checking out".
2022-02-03 16:09:15 +00:00
Emanuel Schütze 714a03c154
Updated services (#6345) 2022-01-28 18:04:11 +00:00
Emanuel Schütze 04d2676b0b
Updated services (#6344) 2022-01-25 17:26:19 +00:00
Emanuel Schütze 9edf0487f1
Updated services (#6341) 2022-01-21 17:28:53 +01:00
Ralf Peschke df8c36030d
Open port 5678 for debugpy in dev (#6340)
Co-authored-by: Ralf Peschke <rpeschke@peschke-it.de>
2022-01-20 09:24:53 +01:00
Emanuel Schütze 532be0bac7
Updated services (#6339) 2022-01-19 16:18:25 +00:00
peb-adr d54499f633
make services-to-main.sh more compatible (#6332) 2022-01-18 19:46:13 +00:00
Emanuel Schütze 7b24b3db36
Updated services (#6333) 2022-01-14 17:07:19 +00:00
Emanuel Schütze 97a83af8c8
Updated services (#6331) 2022-01-14 00:32:03 +00:00
Emanuel Schütze e3ad7d2e1f
Use 'main' as default branch in services-to-main.sh. (#6330) 2022-01-13 19:10:34 +01:00
peb-adr ff13c99818
proxy: check ALLOWED_HOSTS only for client requests (#6328)
This feature is only intended to identify misdirected browser requests.
The other routes are called by services/tools which will not necessarily
set the 'Host' header and thus break.

Co-authored-by: Adrian Richter <adrian@intevation.de>
2022-01-12 17:11:21 +01:00
Emanuel Schütze 670bbdba5a
Updated services (#6326) 2022-01-10 17:09:19 +00:00
Sean 88dad9fc3e
Set various information to "main" (#6325)
Changes various usages from master to main
2022-01-10 17:46:40 +01:00
Norman Jäckel fa0fa370d6
Added LABELS to proxy's Dockerfile. (#6323) 2022-01-07 20:12:51 +00:00
Emanuel Schütze b8e01143ba
Merge pull request #6324 from emanuelschuetze/update-20220107
Updated services
2022-01-07 20:49:18 +01:00
Emanuel Schütze 504b620a40 Updated services 2022-01-07 20:21:11 +01:00
Emanuel Schütze b4bee3483f
Merge pull request #6320 from emanuelschuetze/update20220106
updated services
2022-01-06 23:59:58 +01:00
Emanuel Schütze 51bfd49aa5 updated services 2022-01-06 23:38:44 +01:00
Emanuel Schütze d75d11a511
Merge pull request #6319 from emanuelschuetze/update-20220106
Updated services
2022-01-06 16:09:20 +01:00
Emanuel Schütze 0e18053510 Updated services 2022-01-06 15:50:56 +01:00
Emanuel Schütze f0bff0a754
Merge pull request #6316 from gsiv/update-buildsh
docker/build.sh: Add ICC and voting service
2022-01-06 15:45:33 +01:00
peb-adr 3ecbd356dc
Merge pull request #6315 from peb-adr/proxy-ahosts
proxy: add ALLOWED_HOSTS env var
2022-01-06 12:41:09 +01:00
Gernot Schulz c841f817b2 docker/build.sh: Add ICC and voting service 2021-12-28 13:45:37 +01:00
root ba74f33e68 proxy: add ALLOWED_HOSTS env var
will setup caddy to check if the Host-header matches one of the space
delimited fields in ALLOWED_HOSTS. Or do no such thing if ALLOWED_HOSTS
is not set.
2021-12-22 12:42:46 +01:00
Emanuel Schütze b2741c782e
Merge pull request #6313 from emanuelschuetze/update-20211210
Updated services
2021-12-10 18:50:36 +01:00
Emanuel Schütze a3455c3981 Updated services 2021-12-10 18:18:52 +01:00
Norman Jäckel 51c72c543c
Merge pull request #6309 from peb-adr/local-prod
add local production setup
2021-12-10 16:34:08 +01:00
Emanuel Schütze 8a325e1569
Merge pull request #6312 from emanuelschuetze/services-to-master
Updated services-to-master script
2021-12-10 15:54:11 +01:00
Adrian Richter 26ef35f72e add local production setup 2021-12-10 12:45:06 +01:00
Emanuel Schütze 1241a651c6 Updated services-to-master script 2021-12-10 12:44:13 +01:00
GabrielInTheWorld 95504ce8c0
Merge pull request #6310 from GabrielInTheWorld/dev
Improves the 'services-to-master' function
2021-12-10 10:20:16 +01:00
GabrielInTheWorld 29edcc18f0 Improves the 'services-to-master' function 2021-12-09 16:23:36 +01:00
Emanuel Schütze 74f4f2dfbd
Merge pull request #6307 from emanuelschuetze/update-20211208
Updated services
2021-12-08 17:12:58 +01:00
Emanuel Schütze 119576f4a5 Updated services 2021-12-08 16:23:33 +01:00
Emanuel Schütze eb79dd9aca
Merge pull request #6305 from tsiegleauq/fix-icc-integration
Fix ICC integration
2021-12-07 20:14:58 +01:00
Sean 44e9c99eb3 Fix ICC integration
Updates the ICC Service
Changes the ICC port to 9007
Adjust the proxy
Adjust services.env
2021-12-07 17:47:09 +01:00
Emanuel Schütze 396dc75961
Merge pull request #6303 from GabrielInTheWorld/new-service-to-master
Automatically checkout the latest commit for every submodule
2021-12-07 16:43:30 +01:00
GabrielInTheWorld 9809eb029c Automatically checkout the latest commit for every submodule
Adds a new script "services-to-master.sh" and updates the Makefile
2021-12-02 15:17:14 +01:00
Emanuel Schütze fd3371dd0b
Merge pull request #6302 from emanuelschuetze/update20211201
Updated services
2021-12-02 12:09:16 +01:00
Emanuel Schütze 5762c4a9a0 Updated services 2021-12-02 11:49:02 +01:00
Emanuel Schütze 3d56027ae4
Update README.md 2021-11-30 22:03:19 +01:00
Emanuel Schütze 845f8c5676
Merge pull request #6297 from emanuelschuetze/update-serices20211125
Updated services
2021-11-25 23:00:12 +01:00
Emanuel Schütze 003acc1b78 Updated services 2021-11-25 20:29:43 +01:00
Sean f4e3257d88
Merge pull request #6293 from tsiegleauq/cypress-integration-no-docker
Run cypress workflow without docker
2021-11-24 11:30:11 +01:00
Sean fe163c809d
Merge pull request #6288 from ostcar/vote-volumes
Add volumes to vote-dev-environment
2021-11-24 10:57:24 +01:00
Sean 693e116222 Run cypress workflow without docker
Runs the cypress workflow directly without docker
2021-11-23 22:47:49 +01:00
Sean 3adea03883
Merge pull request #6291 from tsiegleauq/remove-announce-workflow
Remove GHActions to announce file changes
2021-11-23 10:58:57 +01:00
Sean 12308fbe3c Remove GHActions to announce file changes
Removes the GHActions that would create issues when files change.
The files in question were moved to the backend-repo
2021-11-23 10:31:32 +01:00
Sean 597da01430
Merge pull request #6290 from peb-adr/actions-build-vote
add vote-service to build-images workflow
2021-11-22 17:14:21 +01:00
Adrian Richter d77a14d233 add vote-service to build-images workflow 2021-11-22 15:54:09 +01:00
GabrielInTheWorld 0ab17c3597
Merge pull request #6284 from peb-adr/fix-proxy-dev
fix missing caddy_base when rebuilding
2021-11-22 11:58:31 +01:00
peb-adr 004ff453bc
Merge branch 'master' into fix-proxy-dev 2021-11-22 11:00:06 +01:00
Oskar Hahn 77dac78d88 Add volumes to vote-dev-environment
needed for auto reload
2021-11-19 19:53:15 +01:00
Emanuel Schütze 77d672544c
Merge pull request #6286 from emanuelschuetze/update-20211118
Updated services
2021-11-18 20:23:46 +01:00
Emanuel Schütze df08720d0e Updated services 2021-11-18 17:53:59 +01:00
GabrielInTheWorld e0b2679941
Merge pull request #6274 from tsiegleauq/add-vote-service
Add vote service to OS4 setup
2021-11-18 14:56:36 +01:00
Sean Engelhardt 6846b2ab91 Add vote service to OS4 setup
Integrate vote service, makes according changes to configuration
Updates the client to integrate the vote service
Updates the backend to integrate the vote service
2021-11-17 23:32:40 +01:00
Adrian Richter 3dabed8667 fix missing caddy_base when rebuilding 2021-11-17 17:57:34 +01:00
Sean 67a82de70c
Merge pull request #6281 from GabrielInTheWorld/new-dev-setup
Change the initial data
2021-11-15 15:04:47 +01:00
GabrielInTheWorld bed69cf9d2 Change the initial data 2021-11-15 14:40:03 +01:00
Sean 3b798b8ce8
Merge pull request #6275 from peb-adr/https
make local cert file location adjustable
2021-11-11 15:46:36 +01:00
Adrian Richter 84d38dc553 make local cert file location adjustable 2021-11-11 15:17:38 +01:00
Ralf Peschke a100ff22b1
Issue1008 Backend: models.yml etc. moved to backend, remove omitted workflows 2021-11-11 14:37:39 +01:00
Emanuel Schütze e9d0c07eb4
Merge pull request #6271 from emanuelschuetze/updateServices
Updated services
2021-11-04 16:33:47 +01:00
Emanuel Schütze 8a0fd4473c Updated services 2021-11-04 10:59:15 +01:00
Ralf Peschke 7a679dc530 Remove omitted workflows 2021-11-03 13:30:24 +01:00
Ralf Peschke 34eb57a31d
Merge pull request #6265 from r-peschke/BAIssue678_smtp-server
ClientIssue553: Add Testmailserver to openslides-dev-setup
2021-11-02 17:23:08 +01:00
Ralf Peschke 60e4e537f8 Remove caddy volume 2021-11-02 17:03:05 +01:00
Ralf Peschke 8c6edaf4b0 BAIssue1008: models.yml etc. deleted from repo 2021-11-01 18:51:58 +01:00
Ralf Peschke 912fafcca3 ClientIssue553: Add Testmailserver to openslides-dev-setup 2021-10-29 16:46:15 +02:00
reiterl b7263f0c3c
Merge pull request #6269 from reiterl/rename_in_progress
Rename one motion_state in initial/example data.
2021-10-29 10:18:04 +02:00
Ludwig Reiter 60d266789e Use newest backend. 2021-10-28 15:17:23 +02:00
Ludwig Reiter 95e48b05c2 Rename one motion_state in initial/example data. 2021-10-28 15:10:47 +02:00
reiterl 4430f0560a
Merge pull request #6268 from reiterl/limit_of_users
Add field limit_of_users to organization.
2021-10-28 14:29:31 +02:00
Ludwig Reiter 2ec3ad19a2 Change md5sum of initial data. 2021-10-28 14:11:15 +02:00
Ludwig Reiter b97b0de412 Add field limit_of_users to organization. 2021-10-28 14:08:36 +02:00
GabrielInTheWorld cd3dfb4229
Merge pull request #6267 from GabrielInTheWorld/of-initial-themes
Adds the openslides-red and -green theme to the initial data
2021-10-28 08:34:07 +02:00
GabrielInTheWorld 89b8422361 Adds the openslides-red and -green theme to the initial data 2021-10-27 12:18:07 +02:00
peb-adr bad1e131ee
Merge pull request #6261 from peb-adr/proxy-rework
Proxy rework
2021-10-25 14:59:31 +02:00
Adrian Richter 4d02165f1f add auto https support + manage router by header
switch from caddy file to native json
2021-10-25 12:58:32 +02:00
Ralf Peschke 4fc44929f8
Merge pull request #6255 from r-peschke/pull_req6246_req_true_fields
PR6246 mediafile.list_of_speakers not required, because of directories
2021-10-19 10:38:01 +02:00
Ralf Peschke e622822a3f - set mediafle.list_of_speakers required=false
- use submodule backend with generated models.py
- fix bug initial-data.json MI5 -> Mi7, because file data represents MI7
2021-10-19 10:18:35 +02:00
Ralf Peschke 30e978d288 Revert "PR6246 mediafile.list_of_speakers not required, because of directories"
This reverts commit 851e54077f.
2021-10-19 09:57:02 +02:00
Ralf Peschke 851e54077f PR6246 mediafile.list_of_speakers not required, because of directories 2021-10-18 19:34:01 +02:00
Emanuel Schütze 772e3a391f
Merge pull request #6253 from emanuelschuetze/update-20211014
Updated services
2021-10-14 22:38:11 +02:00
Emanuel Schütze 29f1a3b86d Updated services 2021-10-14 22:18:08 +02:00
reiterl d97e97186b
Merge pull request #6242 from reiterl/themes_collection
Add theme collection to models.
2021-10-12 11:02:50 +02:00
Ludwig Reiter dc2998b0ea Update initial-data md5sum. 2021-10-12 10:39:41 +02:00
Emanuel Schütze b5eece6052
Merge pull request #6248 from emanuelschuetze/update20211009
Updated services
2021-10-09 21:21:23 +02:00
Emanuel Schütze 53db1caa39 Updated services 2021-10-09 12:07:22 +02:00
Emanuel Schütze b21d76c882
Merge pull request #6246 from ostcar/requied_relations
Set required attribute on relation and generic-relation fields
2021-10-08 20:32:04 +02:00
Oskar Hahn 94cb1a47d2 Set required attribute on relation and
generic-relation fields
2021-10-08 17:14:14 +02:00
Ludwig Reiter 97056e9f2a Add missing options to theme. 2021-10-08 09:15:10 +02:00
Ludwig Reiter 285b900b5a Rename default_theme_id into theme_id
And update initial and example data.
2021-10-08 09:15:09 +02:00
Ludwig Reiter fdfe3bd66a Add required to default_theme_id and organization_id. 2021-10-08 09:15:09 +02:00
Ludwig Reiter ee3b84abde Add one theme to initial/example data. 2021-10-08 09:15:09 +02:00
Ludwig Reiter e2382a7c86 Fix indentation of initial-data.json 2021-10-08 09:15:09 +02:00
Ludwig Reiter cc9df51cc3 Update models.yml, initial-data.json and example-data.json 2021-10-08 09:15:09 +02:00
Ludwig Reiter 5aa9bc082d Update initial and example data. 2021-10-08 09:15:09 +02:00
Ludwig Reiter 1078a7101a Change theme/organization_ids
It should be a relation and not a relation-list, as there is only one
organization and it is renamed to avoid name conflict.
2021-10-08 09:15:09 +02:00
Ludwig Reiter 9c0329b2ad Change theme attribute names, because large letters are not allowed. 2021-10-08 09:15:09 +02:00
Ludwig Reiter 703d61a3b7 Update models.yml, initial-data.json and example-data.json 2021-10-08 09:15:09 +02:00
Ludwig Reiter d5d41a532a Add organization/theme_ids to models.yml 2021-10-08 09:14:29 +02:00
Ludwig Reiter c293544f24 Remove user/theme_id. 2021-10-08 09:14:29 +02:00
Ludwig Reiter 225a6eb5de Update initial and example data. 2021-10-08 09:14:29 +02:00
Ludwig Reiter 494b8d54f4 Rename organization/theme_id into default_theme_id. 2021-10-08 09:14:29 +02:00
Ludwig Reiter 1da3c6dced Changes to the models.yml. 2021-10-08 09:14:29 +02:00
Ludwig Reiter 23e7bc4df2 Fix minus problem in var names. 2021-10-08 09:14:29 +02:00
Ludwig Reiter 38ac9c2838 Fix type. 2021-10-08 09:14:29 +02:00
Ludwig Reiter 90d705666d Add theme collection to models. 2021-10-08 09:14:29 +02:00
Emanuel Schütze 855f5f2e86
Merge pull request #6247 from GabrielInTheWorld/updates
Updates services
2021-10-07 19:27:57 +02:00
GabrielMeyer 42b30d1ea9 Updates services 2021-10-07 16:33:38 +02:00
jsangmeister 4021c7620b
Merge pull request #6244 from jsangmeister/add-state-weight
Add motion state weight
2021-10-07 14:04:02 +02:00
Joshua Sangmeister cb85892447 Fix checksum 2021-10-06 15:43:00 +02:00
Joshua Sangmeister 3f4a020808 Update Backend 2021-10-06 15:25:47 +02:00
Joshua Sangmeister 615736eda2 Fix example & initial data 2021-10-06 12:54:52 +02:00
Joshua Sangmeister 9483414fb3 Add motion state weight 2021-10-06 10:45:15 +02:00
reiterl 973184237a
Merge pull request #6243 from jsangmeister/fix-gh-actions
Fix initial data workflow
2021-10-05 13:12:35 +02:00
Joshua Sangmeister 72d5ee06d3 Fix initial data workflow 2021-10-04 15:18:29 +02:00
Norman Jäckel 0e95e7f7e0
Merge pull request #6236 from normanjaeckel/Typo
Fixed typo and changed some format.
2021-10-02 20:07:05 +02:00
GabrielInTheWorld 34b6fcd0b3
Merge pull request #6239 from GabrielInTheWorld/updates
Updates the client, backend and media-service
2021-10-01 15:14:04 +02:00
GabrielMeyer 788afca754 Updates the client, backend and media-service 2021-10-01 14:29:57 +02:00
Emanuel Schütze c4dcedcb17
Merge pull request #6238 from emanuelschuetze/update-20210930
Updated services
2021-09-30 09:37:05 +02:00
Emanuel Schütze 8859892e10 Updated services 2021-09-30 09:18:01 +02:00
GabrielInTheWorld 5415f68723
Merge pull request #6237 from GabrielInTheWorld/updating-client
New version of the client
2021-09-29 17:40:31 +02:00
GabrielMeyer 64908007c7 New version 2021-09-29 16:54:41 +02:00
Norman Jäckel 44a3193406 Fixed typo and changed some format. 2021-09-28 22:13:13 +02:00
Emanuel Schütze c628fe3244
Merge pull request #6235 from emanuelschuetze/update-20210924
Updated services
2021-09-24 11:51:58 +02:00
Emanuel Schütze ddc8cd2716 Updated services 2021-09-24 11:34:17 +02:00
Emanuel Schütze 5aa0b84123
Merge pull request #6233 from peb-adr/fix-tagging
fix client/version.txt path
2021-09-24 11:31:02 +02:00
Adrian Richter cc4666646d shouldve double checked the path 2021-09-24 11:08:14 +02:00
Emanuel Schütze cda2c0c21c
Merge pull request #6224 from peb-adr/tagging
unique version tag for docker images
2021-09-24 10:36:18 +02:00
GabrielInTheWorld 058ee57658
Merge pull request #6232 from GabrielInTheWorld/of-perm-can-forward
Updates the wording according to #6231
2021-09-24 08:22:16 +02:00
Adrian Richter 4e00e13038 tag images uniquely + write to client/version.txt
* adjust path to client/src/assets/version.txt

      * also tag as latest

      * add timestamp to image tag + client/version.txt
2021-09-23 18:54:23 +02:00
GabrielMeyer 6629cf9ee7 Updates the wording 2021-09-23 16:57:50 +02:00
GabrielInTheWorld 7ffe5922a0
Merge pull request #6226 from r-peschke/BA939_New_field_motion_state_dont_set_identifier
New field motion_state/dont_set_identifier (Backend #939)
2021-09-23 14:45:46 +02:00
Ralf Peschke 0e830e677b Initia-dat.json plus workflow and new backend submodule 2021-09-23 14:10:44 +02:00
Ralf Peschke 761c248e19 Changed hash for initial_data.json 2021-09-23 14:10:44 +02:00
Ralf Peschke 1fe6e4e58e New field motion_state/dont_set_identifier (Backend #939) 2021-09-23 14:10:44 +02:00
GabrielInTheWorld b81b7e9980
Merge pull request #6231 from GabrielInTheWorld/of-perm-can-forward
Adjusts the models.yml to the new perm (#6230)
2021-09-23 13:54:03 +02:00
GabrielMeyer 1fb1d834e1 Adjusts the models.yml to the new perm (#6230) 2021-09-23 13:31:28 +02:00
GabrielInTheWorld 3e553a2714
Merge pull request #6230 from GabrielInTheWorld/of-perm-can-forward
Adds the perm 'motion.can_forward_to_this'
2021-09-23 11:21:11 +02:00
GabrielMeyer 8b0811efa9 Adds the perm 'motion.can_forward_to_this' 2021-09-23 11:01:53 +02:00
Sean b7c97baa1e
Merge pull request #6218 from tsiegleauq/integration-cypress
Integration tests using cypress
2021-09-21 11:02:09 +02:00
Sean 4134367b8a
Merge pull request #6225 from ostcar/remove_permission_service
Remove permission service
2021-09-17 12:26:39 +02:00
Sean 85c0e50c21 Integration tests using cypress
Test client integration (client loads)
Test auth integration (client logs in)
Test backend integration (accept request)
  - broken
Test autoupdate integration (sends au to client)

Add manual cypress tests
Add dockered cypress tests

Add Readme
Add test execution to makefile
Add test execution to github-actions

TODO:
- Create user for tests
- flush db after every test
2021-09-16 17:16:51 +02:00
Oskar Hahn b504a1c51e Remove permission service 2021-09-15 16:20:39 +02:00
Emanuel Schütze 7c43e77fcb
Merge pull request #6221 from emanuelschuetze/update-20210913
(4.0-beta-20210913) Updated services.
2021-09-13 14:21:53 +02:00
Emanuel Schütze a958549adb (4.0-beta-20210913) Updated services. 2021-09-13 14:16:27 +02:00
Ralf Peschke 97ea71c8db
Merge pull request #6215 from r-peschke/fix_users_email_sender
Replace mail-address by sender name
2021-09-06 22:00:28 +02:00
Ralf Peschke 9306ee11ea Upgrade to current backend 2021-09-06 17:35:14 +02:00
Ralf Peschke c8d9f213bd Replace mail-address by sender name 2021-09-06 17:35:14 +02:00
Ralf Peschke 6fb3e40f8b
Update md5_check_initial-data.yml
For committing the new initial-data.json with changed field content users_email_sender
2021-09-06 17:26:54 +02:00
Norman Jäckel c2f1d2b4b8
Merge pull request #6217 from normanjaeckel/UpdateFormat
Update format
2021-08-31 23:04:30 +02:00
Norman Jäckel 217e3d0b75 Updated README. Updated initial-data.json to new format. Updated example-data.json to new format. Updated backend and datastore. Disabled check of unmigrated initial data. Added announcement workflow. 2021-08-31 22:00:25 +02:00
Sean 36c9ff1451
Merge pull request #6216 from tsiegleauq/update-services-08-27
Update services
2021-08-27 17:29:29 +02:00
Sean c5accdaa9c Update services 2021-08-27 17:25:34 +02:00
GabrielInTheWorld f2515decf8
Merge pull request #6214 from GabrielInTheWorld/of-update-initial-data
Updates the docker-compose.dev.yml file to use master
2021-08-26 15:55:30 +02:00
GabrielMeyer 1b630ba714 Updates the initial data used by the datastore 2021-08-20 10:52:42 +02:00
Sean 7b8139dbfe
Merge pull request #6211 from tsiegleauq/update-readme-for-stable
Update readme
2021-08-19 12:37:19 +02:00
Sean 34eaada3d0 Update readme
Remove unused parts from the readme
Update develoment.md, replace os4-dev branch with master
2021-08-19 12:32:05 +02:00
Finn Stutzenstein 901c8beca9
Merge pull request #6210 from FinnStutzenstein/initialDataCi
Add a CI check to verify that migrating the initial data works
2021-08-18 12:13:38 +02:00
Finn Stutzenstein 6b218e3c7f
Add a CI check to verify that migrating the initial data works 2021-08-18 11:58:20 +02:00
Sean f9296f017b
Merge pull request #6170 from tsiegleauq/workflow-dispatch
Workflow dispatch
2021-08-18 11:52:01 +02:00
Sean 456ecb8316 Workflow dispatch
Manually fire to build images
2021-08-18 11:48:10 +02:00
Sean 2753499167
Merge pull request #6208 from tsiegleauq/inform-repos-about-changes
Monitor and announces changes to models and perms
2021-08-17 12:49:59 +02:00
Sean de503bbb2b Monitor and announces changes to models and perms
Monitors changes to docs/models.yml and docs/perms.yml.
If changes were made and merged github actions will create an issue
and assign the maintainers of the subprojects to ackknowledge the
changes.
2021-08-17 12:45:30 +02:00
Finn Stutzenstein 3ca9918fa2
Merge pull request #6209 from FinnStutzenstein/migrations
Update Submodules to enable migrations
2021-08-17 11:39:48 +02:00
Finn Stutzenstein 1d74030d2b
Update Submodules to enable migrations 2021-08-17 11:37:15 +02:00
Finn Stutzenstein bd537b5678
Merge pull request #6205 from r-peschke/Issue6204_fields_for_archiving_meetings
Issue6204: Define fields for archiving of meetings
2021-08-17 09:04:13 +02:00
Ralf Peschke 2b58b2f2f7
Issue6204: Define fields for archiving of meetings
- set initial-data.json to migration_index 1
- md5sum checking for initial-data.json
2021-08-17 09:03:03 +02:00
Ralf Peschke 59f6fa1ed3
Merge pull request #6206 from r-peschke/github_models.yml_generate_model
github models.yml: generate models.py for backend check_json
2021-08-11 11:49:10 +02:00
Ralf Peschke f81738a06e github models.yml: generate models.py for backend check_json 2021-08-11 11:31:16 +02:00
Norman Jäckel 2a5b4a3126
Merge pull request #6152 from normanjaeckel/RemoveProdSetup
Moved production setup to manage service.
2021-08-08 18:06:21 +02:00
Norman Jäckel de2d5eb030 Moved production setup to manage service. 2021-08-08 18:01:53 +02:00
Emanuel Schütze 6192e2347c
Merge pull request #6201 from emanuelschuetze/update-20210806
Updated client
2021-08-06 17:36:11 +02:00
Emanuel Schütze 1f94532a43 Updated client 2021-08-06 17:16:51 +02:00
Finn Stutzenstein 8ff0f379a5
Merge pull request #6200 from FinnStutzenstein/update
Update Backend
2021-08-06 11:15:36 +02:00
Finn Stutzenstein c93fb68a4f
Update Backend 2021-08-06 11:14:30 +02:00
Finn Stutzenstein 07e49746c1
Merge pull request #6198 from FinnStutzenstein/update
Update submodules. Added explicit migration index to data files
2021-08-06 08:01:50 +02:00
Finn Stutzenstein 59d9c6a9a8
Update submodules. Added explicit migration index to data files 2021-08-06 08:00:52 +02:00
Emanuel Schütze 725782078c
Merge pull request #6199 from emanuelschuetze/update-20210805
Updated os4 services
2021-08-05 20:57:32 +02:00
Emanuel Schütze d402fd4b9d Updated os4 services 2021-08-05 20:55:12 +02:00
Finn Stutzenstein b85a82bc61
Merge pull request #6197 from FinnStutzenstein/CI
Enhance CI
2021-08-04 14:14:06 +02:00
Finn Stutzenstein 27d31189c1
Enhance CI
- redone the modelsvalidator
- use the datavalidator from the backend, remove old code
2021-08-04 14:10:40 +02:00
jsangmeister 816f5cc574
Merge pull request #6194 from jsangmeister/fix-settings-definitions
Add missing defaults & fix existing ones
2021-08-04 11:37:24 +02:00
Joshua Sangmeister de14009a52 Add missing defaults & fix existing ones 2021-08-04 11:35:16 +02:00
Finn Stutzenstein ec00c6c7db
Merge pull request #6192 from ostcar/models_field_groups
Add field group info in organization
2021-08-04 07:19:27 +02:00
Oskar Hahn 552ac0f9d3
Add field restriction_mode to every collectionfield 2021-08-04 07:18:06 +02:00
Emanuel Schütze 7c6fe46946
Merge pull request #6191 from emanuelschuetze/update-20210730-2
Updated autoupdate service
2021-07-30 16:49:25 +02:00
Emanuel Schütze 53ff280f45 Updated autoupdate service 2021-07-30 16:48:02 +02:00
Emanuel Schütze 4135149cc6
Merge pull request #6190 from emanuelschuetze/update-20210730
Updated os4 services
2021-07-30 16:06:32 +02:00
Emanuel Schütze dd930ae973 Updated os4 services 2021-07-30 16:04:28 +02:00
reiterl ba5bfe1477
Merge pull request #6189 from reiterl/model_submitter_weight
motion_submitter does not need a default. In the creating a weight is…
2021-07-29 14:20:34 +02:00
Ludwig Reiter 62282cc964 motion_submitter does not need a default. In the creating a weight is always set. 2021-07-29 14:14:33 +02:00
Emanuel Schütze b19f2189c9
Merge pull request #6187 from jsangmeister/fix-motion-preamble
Fix default motion preamble
2021-07-27 17:37:59 +02:00
Joshua Sangmeister 8b7a8bf6de Fix default motion preamble 2021-07-27 17:12:15 +02:00
reiterl 8d3f0a24ea
Merge pull request #6182 from reiterl/dev_model_gender
Dev model gender
2021-07-23 13:49:20 +02:00
Ludwig Reiter 1c3088841a Update initial-data. 2021-07-23 13:44:06 +02:00
Ludwig Reiter d6c5b16a8a Add some gender to example data. 2021-07-23 13:35:18 +02:00
Ludwig Reiter 6e26108531 Update models: user/gender field. 2021-07-23 12:55:27 +02:00
Emanuel Schütze 4913ac5920
Merge pull request #6178 from emanuelschuetze/update-20210720
Updated os4 services
2021-07-20 16:59:15 +02:00
Emanuel Schütze a30477ded0 Updated os4 services 2021-07-20 16:56:02 +02:00
Emanuel Schütze 01b2b53e24
Merge pull request #6169 from emanuelschuetze/update-20210716a
Updated os4 services
2021-07-16 16:03:46 +02:00
Emanuel Schütze 53fd8148c4 Updated os4 services 2021-07-16 16:02:45 +02:00
Finn Stutzenstein b04fe236f2
Merge pull request #6168 from FinnStutzenstein/adjustModelsYml
Fix limits for LOS settings (closes #6167)
2021-07-16 13:57:32 +02:00
Finn Stutzenstein 3b14153965
Fix limits for LOS settings (closes #6167) 2021-07-16 12:37:57 +02:00
Emanuel Schütze 233fb8dfca
Merge pull request #6164 from emanuelschuetze/update-20210716
Updated os4 client
2021-07-16 08:52:01 +02:00
Emanuel Schütze a63643e762 Updated os4 client 2021-07-16 08:44:26 +02:00
Emanuel Schütze c603960291
Merge pull request #6163 from emanuelschuetze/update-20210715
Updated os4 services
2021-07-15 23:37:54 +02:00
Emanuel Schütze cac529e7c3 Updated os4 services 2021-07-15 23:33:08 +02:00
jsangmeister d8b06c8c2d
Merge pull request #6162 from jsangmeister/user-cascade-deletion
Add cascade deletion to user->speaker relation
2021-07-15 16:10:44 +02:00
Joshua Sangmeister 15e37df62d Fix on_delete field in template fields 2021-07-15 16:06:47 +02:00
Joshua Sangmeister 239e0ff30f Add cascade deletion to user->speaker relation 2021-07-15 16:02:59 +02:00
Emanuel Schütze ca4d5783d7
Merge pull request #6159 from emanuelschuetze/update-20210714
Updated os4 services
2021-07-14 21:05:27 +02:00
Emanuel Schütze 6762888333 Updated os4 services 2021-07-14 21:01:19 +02:00
jsangmeister 9b3271277f
Merge pull request #6155 from jsangmeister/remove-majority-method
Remove majority method
2021-07-12 10:47:30 +02:00
Joshua Sangmeister feee6952e7 Remove majority method 2021-07-12 10:17:06 +02:00
Finn Stutzenstein 372fe17bf1
Merge pull request #6153 from GabrielInTheWorld/interaction-into-models
Adds applause configs to the meeting model
2021-07-12 07:21:27 +02:00
GabrielMeyer 0e56d1360e Adds applause configs to the meeting model 2021-07-09 15:46:40 +02:00
Emanuel Schütze 77f6516438
Merge pull request #6149 from emanuelschuetze/update-20210708
Updated services
2021-07-08 18:19:20 +02:00
Emanuel Schütze c98e10e607 Updated os4 services 2021-07-08 17:15:53 +02:00
Finn Stutzenstein 47ac8ac984
Merge pull request #6147 from FinnStutzenstein/datastoreLib
Datastore lib
2021-07-08 10:30:27 +02:00
Finn Stutzenstein ba72cb8b01
Datastore library 2021-07-08 10:29:43 +02:00
Finn Stutzenstein a0ec4be644
Merge pull request #6144 from FinnStutzenstein/openslides4-dev
clean up docs that are in the wiki
2021-07-08 09:07:49 +02:00
jsangmeister 238f8fa602
Merge pull request #6145 from jsangmeister/split-forwarding-tree-motion-ids
Split forwarding_tree_motion_ids into two fields
2021-07-08 09:07:28 +02:00
Joshua Sangmeister 7f8a00ea4e Split forwarding_tree_motion_ids into two fields 2021-07-05 14:52:29 +02:00
Finn Stutzenstein 65bd70375d
clean up docs that are in the wiki 2021-07-05 08:22:56 +02:00
Sean 610a10712d
Merge pull request #6142 from tsiegleauq/add-helpdesk-to-yml
Add conference_enable_helpdesk to models
2021-07-01 16:33:47 +02:00
Sean a131424c8f Add conference_enable_helpdesk to models
Adjust models.yml and the json files to support the helpdesk settings
2021-07-01 16:17:14 +02:00
Sean fb9e9513fc
Merge pull request #6141 from jsangmeister/fix-number-fields
Fix initial data for conference_auto_connect_next_speakers and datavalidator for number fields
2021-07-01 16:03:25 +02:00
Joshua Sangmeister d7c67248dc Fix initial data for conference_auto_connect_next_speakers and datavalidator for number fields 2021-07-01 15:58:14 +02:00
Emanuel Schütze d1d69954f5
Merge pull request #6136 from emanuelschuetze/icc-image
Added icc image
2021-06-30 16:04:43 +02:00
Emanuel Schütze 334931c0d9 Added icc image 2021-06-30 16:03:10 +02:00
Emanuel Schütze dbb146e92c
Merge pull request #6135 from emanuelschuetze/services
Updated services
2021-06-30 14:33:20 +02:00
Emanuel Schütze de01e81306 Updated services 2021-06-30 14:31:30 +02:00
Oskar Hahn d56800b510
Merge pull request #6131 from ostcar/icc-service
Add ICC Service
2021-06-29 20:22:38 +02:00
Oskar Hahn a38942b6eb Add ICC Service 2021-06-29 20:20:59 +02:00
Finn Stutzenstein a9fbac74bb
Merge pull request #6133 from FinnStutzenstein/openslides4-dev
Fix exporting the DS
2021-06-28 12:45:01 +02:00
Finn Stutzenstein 9519849c45
Fix exporting the DS
Introduced with #6055
2021-06-28 12:42:33 +02:00
Emanuel Schütze a52467f740
Merge pull request #6132 from emanuelschuetze/os4-update
Updated os4 sevices
2021-06-28 12:11:39 +02:00
Emanuel Schütze 6bbca6cc66 Updated os4 services 2021-06-28 12:05:04 +02:00
Finn Stutzenstein 1df22d4fc3
Merge pull request #6128 from FinnStutzenstein/adjustDatavalidator
Adjust datavalidator
2021-06-24 11:25:10 +02:00
Finn Stutzenstein b3f17db2a3
Adjust datavalidator
- collection whitelist instead of blacklist
- added field mediafile/blob for import
2021-06-24 11:23:07 +02:00
Sean 6f24efa544
Merge pull request #6125 from tsiegleauq/add-imported_at
Add imported_at to models.yml
2021-06-21 15:08:47 +02:00
Sean a841919c31 Add imported_at to models.yml
Adds the imported_at field to the models.yml file
2021-06-21 15:07:30 +02:00
Finn Stutzenstein 603a558b42
Merge pull request #6124 from FinnStutzenstein/datavalidator
Enhance datavalidator for template fields
2021-06-21 15:06:54 +02:00
Finn Stutzenstein e8dd5d56d1
Enhance datavalidator for template fields 2021-06-21 11:18:59 +02:00
Ralf Peschke 8c1fa5be91
Merge pull request #6116 from r-peschke/PR_Move_translation_from_org_to_meeting
- Move custom_translation from organization to meeting
- Initialization of custom_translations as {}
2021-06-15 11:36:06 +02:00
Ralf Peschke 6816419da9 Initialization of custom_translations as {} 2021-06-15 11:31:48 +02:00
Ralf Peschke 18534b6f75 Move custom_translation from organization to meeting: initial and example.json 2021-06-14 15:18:51 +02:00
Ralf Peschke 80b05fda2f Move custom_translation from organization to meeting 2021-06-14 15:08:01 +02:00
jsangmeister 41b5ee0340
Merge pull request #6115 from jsangmeister/model-locked-error-interface
Update datastore interface for model locked error
2021-06-14 09:16:17 +02:00
Joshua Sangmeister 0778681181 Update datastore interface for model locked error 2021-06-10 16:05:56 +02:00
Finn Stutzenstein a3b2784cc7
Merge pull request #6111 from reiterl/permission_chat_group_can_manage
Add chat.can_manage to permissions enum.
2021-06-08 21:51:39 +02:00
Ludwig Reiter d5a43ea8c4 Add chat.can_manage to permissions enum. 2021-06-08 12:15:06 +02:00
Finn Stutzenstein e56786d375
Merge pull request #6107 from FinnStutzenstein/chatgroups
[WIP] Added chat groups
2021-06-07 11:55:17 +02:00
Finn Stutzenstein bc310a98e6
Added chat groups 2021-06-07 11:18:05 +02:00
Finn Stutzenstein cc2a40a178
Merge pull request #6101 from FinnStutzenstein/organizationSpelling
Fixed spelling of organization and update submodules
2021-06-03 15:18:49 +02:00
Finn Stutzenstein 6572e8b20c
Fixed spelling of organization and update submodules 2021-06-03 15:15:15 +02:00
Finn Stutzenstein cdbb5f1a83
Merge pull request #6093 from r-peschke/PR_OS4_is_demo_user_not_RO
is_demo_user is no more read only
2021-06-03 07:21:35 +02:00
Ralf Peschke ac457fdb2c is_demo_user is no more read only 2021-06-01 10:01:50 +02:00
reiterl 1e52ef9148
Merge pull request #6092 from reiterl/models_orga
Rm ro from organisation.enable_electronic_voting
2021-05-31 15:25:53 +02:00
Ludwig Reiter 2f80d1a11e Rm ro from organisation.enable_electronic_voting 2021-05-31 15:23:12 +02:00
Finn Stutzenstein aaf476fba7
Merge pull request #6090 from FinnStutzenstein/fixDatastoreCommands
Fix clearing the DS.
2021-05-31 09:16:58 +02:00
Finn Stutzenstein acdb206632
Fix clearing the DS.
Introduced with #6055
2021-05-31 09:14:47 +02:00
Finn Stutzenstein d990b098c5
Merge pull request #6068 from FinnStutzenstein/fixDefaultProjectorRelation
Fix default projector relation
2021-05-20 13:22:15 +02:00
Finn Stutzenstein 56423737bc
Fix example data for default projectors 2021-05-20 13:18:33 +02:00
reiterl a956f2d996
Merge pull request #6069 from reiterl/models_jitsi_rm_ro
Remove read_only from meeting/jitsi_*
2021-05-20 13:13:24 +02:00
Ludwig Reiter 8a51e6ae85 Remove read_only from meeting/jitsi_* 2021-05-20 13:10:21 +02:00
Finn Stutzenstein 6d64c0c62b
Merge pull request #6065 from FinnStutzenstein/ownPassword
change model of can_change_own_password
2021-05-20 12:25:23 +02:00
Finn Stutzenstein fa67c4874b
change model of can_change_own_password 2021-05-20 12:23:43 +02:00
Norman Jäckel 3d44fc4730
Merge pull request #6055 from ostcar/tunnel
Update manage service
2021-05-17 00:18:52 +02:00
Oskar Hahn f473796ce6 Update manage service
It know has a tunnel command. See

manage help tunnel

for more information and examples.

There is not need to open the ports
2021-05-17 00:17:26 +02:00
Oskar Hahn 3b41fb468e
Merge pull request #6027 from normanjaeckel/ManageSetupCommand
Manage setup command
2021-05-16 23:59:41 +02:00
jsangmeister 12f465e0b8
Merge pull request #6062 from jsangmeister/fix-typo
Fix typos
2021-05-11 10:19:26 +02:00
Joshua Sangmeister ae8951ee82 Fix typos 2021-05-11 10:16:16 +02:00
jsangmeister 862bc8c182
Merge pull request #6052 from jsangmeister/fix-models
Fix models
2021-05-10 12:49:39 +02:00
Joshua Sangmeister 24e0771d05 Fix models.yml 2021-05-06 09:10:04 +02:00
Finn Stutzenstein 50fdaca46f
Merge pull request #6029 from FinnStutzenstein/remodelUsers
Remodel users
2021-05-03 14:01:19 +02:00
Norman Jäckel e99eb0bf0b Review 2021-05-01 11:15:03 +02:00
Norman Jäckel b4a034d900 Switched to GitHub Container Registry. 2021-04-30 13:54:05 +02:00
Norman Jäckel e9357b2c10 New production setup with Docker Compose
Updated openslides-manage-service and changed README.md

Added GitHub workflow.
2021-04-30 00:41:04 +02:00
Finn Stutzenstein be883f2a0d
Remodel users 2021-04-29 14:29:08 +02:00
Finn Stutzenstein 2fd4160aaa
Merge pull request #6041 from FinnStutzenstein/os4-pro-contra-note
Pro/contra/marked/note for OS4
2021-04-29 14:12:13 +02:00
Finn Stutzenstein 4c10007bab
example- and initial data 2021-04-29 14:09:21 +02:00
Finn Stutzenstein e2c7287812
Pro/contra/marked/note for OS4 2021-04-29 12:00:53 +02:00
Finn Stutzenstein 09ba505277
Merge pull request #6039 from FinnStutzenstein/revert-6032
Revert updating manageservice
2021-04-29 08:05:33 +02:00
Finn Stutzenstein eaef28029b
Revert updating manageservice
It breaks the deployment
2021-04-29 08:03:38 +02:00
GabrielInTheWorld 9efb47f2a5
Merge pull request #6032 from GabrielInTheWorld/update-subprojects
Update subprojects to their current master
2021-04-27 10:10:21 +02:00
GabrielMeyer dbe74308b7 Update subprojects to current master 2021-04-26 16:36:27 +02:00
Finn Stutzenstein 60cae02ec7
Merge pull request #6016 from FinnStutzenstein/pollBackend
poll backend
2021-04-26 07:20:48 +02:00
jsangmeister 25d8f88ea0
Merge pull request #6018 from jsangmeister/locked-fields-as-list
Change locked fields to a list
2021-04-22 15:31:44 +02:00
Joshua Sangmeister 595a3fea90 Make change non-breaking 2021-04-22 12:23:16 +02:00
Joshua Sangmeister 6ca37f8067 Lock CollectionField with array 2021-04-22 09:33:20 +02:00
Finn Stutzenstein 5fff49e336
poll backend 2021-04-19 09:22:55 +02:00
Finn Stutzenstein d216e3121f
Merge pull request #6013 from FinnStutzenstein/update
Update submodules
2021-04-15 16:24:40 +02:00
Finn Stutzenstein 6eccda1217
Update submodules 2021-04-15 16:19:11 +02:00
jsangmeister f703a77796
Merge pull request #6012 from jsangmeister/make-countdown-times-required
Make countdown times required
2021-04-15 11:27:27 +02:00
jsangmeister 1e141bda0c
Merge pull request #6011 from r-peschke/issue473_meeting_reference_projector_id_required
Issue473 meeting.reference projector id and user.username required
2021-04-15 11:27:20 +02:00
Joshua Sangmeister 178df37536 Make countdown times required 2021-04-15 10:39:04 +02:00
Ralf Peschke 29304f34f2 User.username also required 2021-04-14 15:29:28 +02:00
Ralf Peschke dea8e98e28 issue473_required_for_meeting.reference_projector_id 2021-04-14 13:45:51 +02:00
Finn Stutzenstein 097230b66e
Merge pull request #6000 from FinnStutzenstein/organisation_tags
Add organisation tags
2021-04-08 14:34:21 +02:00
Finn Stutzenstein 2b7124760d
Add organisation tags 2021-04-08 13:47:31 +02:00
Finn Stutzenstein 2c4c33dac1
Merge pull request #5999 from FinnStutzenstein/prod
OS4 productive setup changes
2021-04-08 12:54:48 +02:00
Finn Stutzenstein 79a14e15ad
OS4 productive setup changes
Now uses secrets and add the possibility to enable electronic voting
2021-04-08 12:52:59 +02:00
jsangmeister bb1ac2fc96
Merge pull request #5993 from jsangmeister/voting-changes
Adapt models.yml to new voting changes
2021-04-08 09:36:48 +02:00
Joshua Sangmeister 3eee3c1c37 Adapt example data 2021-04-06 11:20:47 +02:00
Joshua Sangmeister df7c25cc5d Adapt models.yml to new voting changes 2021-04-06 11:01:51 +02:00
Finn Stutzenstein 4088913d7d
Merge pull request #5988 from FinnStutzenstein/prod
OS4 Prod with swarm mode
2021-04-01 10:56:30 +02:00
Finn Stutzenstein ae406a3979
OS4: prod setup 2021-04-01 10:55:06 +02:00
Oskar Hahn 00924d45d0
Merge pull request #5983 from ostcar/fix_example_projection
Fix projection name in example data
2021-03-28 16:48:28 +02:00
Oskar Hahn 353c2cb50d Fix projection name in example data 2021-03-28 16:42:59 +02:00
Finn Stutzenstein 68c3e140e7
Merge pull request #5747 from GabrielInTheWorld/update-auth-service-interface
Update auth-service's interface
2021-03-22 13:57:52 +01:00
reiterl c703b29df0
Merge pull request #5973 from reiterl/model_poll_defaults
Update poll onehundred_percent_base and majority_method defaults.
2021-03-19 14:44:36 +01:00
Ludwig Reiter 787419911b Update poll onehundred_percent_base and majority_method defaults. 2021-03-19 14:42:31 +01:00
Finn Stutzenstein 471eec07fe
Merge pull request #5965 from reiterl/model_default_time
Remove default from projector_countdown/default_time.
2021-03-18 13:39:16 +01:00
Ludwig Reiter ccc77d8ea6 Remove default from projector_countdown/default_time. 2021-03-18 13:36:02 +01:00
Finn Stutzenstein 903764565c
Merge pull request #5958 from FinnStutzenstein/updateAndCaddy
Update submodules and use Caddy
2021-03-18 13:26:25 +01:00
Finn Stutzenstein 2b34a3ffc2
Update submodules and use Caddy 2021-03-18 13:25:12 +01:00
jsangmeister 89e8a5b565
Merge pull request #5963 from jsangmeister/css-class-required
Make css_class required
2021-03-18 11:53:28 +01:00
Joshua Sangmeister be08de3858 Make css_class required 2021-03-18 11:51:03 +01:00
Finn Stutzenstein a530fef898
Merge pull request #5962 from FinnStutzenstein/update
Update submodules
2021-03-18 08:49:14 +01:00
Finn Stutzenstein 471ea30171
Update submodules 2021-03-18 08:47:45 +01:00
Finn Stutzenstein 29beab391d
Merge pull request #5960 from reiterl/models_poll_globals
Change poll global default to false.
2021-03-18 07:31:04 +01:00
Ludwig Reiter f42dea91bf Change poll global default to false. 2021-03-17 11:46:43 +01:00
Oskar Hahn 737eeeca2f
Merge pull request #5956 from ostcar/manage-service
Add manage service
2021-03-15 10:46:11 +01:00
Oskar Hahn 5af507ea82 Fix docker-compose 2021-03-13 12:50:07 +01:00
Oskar Hahn 1a965b9034 Add manage service 2021-03-13 10:31:54 +01:00
Finn Stutzenstein 4a5c356c08
Merge pull request #5950 from jsangmeister/add-speaker-equal-fields
Add equal_fields for speaker
2021-03-11 16:10:21 +01:00
Joshua Sangmeister b62728c998 Add equal_fields for speaker 2021-03-11 15:28:19 +01:00
Finn Stutzenstein 33fda8788c
Merge pull request #5949 from FinnStutzenstein/losInitialClosed
List of speaker initially closed
2021-03-11 09:04:00 +01:00
Finn Stutzenstein c8e040348f
List of speaker initially closed 2021-03-11 08:59:30 +01:00
Finn Stutzenstein 41572f41cf
Merge pull request #5947 from r-peschke/issue474_N_as_new_100_percent_base
N as new 100% base
2021-03-11 07:47:15 +01:00
Ralf Peschke 82d6c43499 N as new 100% base 2021-03-09 11:45:49 +01:00
Finn Stutzenstein 1f780c0926
Merge pull request #5945 from FinnStutzenstein/updateModels
Add default_vote_weight default
2021-03-08 14:24:32 +01:00
Finn Stutzenstein 448fab6ead
Add default_vote_weight default 2021-03-08 14:22:16 +01:00
Finn Stutzenstein 9f036fe437
Merge pull request #5944 from FinnStutzenstein/updateModels
Add default countdowns
2021-03-08 14:09:14 +01:00
Finn Stutzenstein f833b61c9b
Add default countdowns 2021-03-08 14:08:15 +01:00
Finn Stutzenstein 91b9bc6461
Merge pull request #5917 from FinnStutzenstein/projectorContentObjects
Update projector_countdown
2021-03-08 12:17:07 +01:00
Finn Stutzenstein 0a3d511322
Merge pull request #5925 from FinnStutzenstein/models
Adjusted models
2021-03-08 12:00:44 +01:00
Finn Stutzenstein aa77b878c2
Merge pull request #5942 from FinnStutzenstein/updateModels
Add defaults for closed on agenda item and list of speakers
2021-03-08 11:35:07 +01:00
Finn Stutzenstein 0a4e580274
Add defaults for closed on agenda item and list of speakers 2021-03-08 11:33:21 +01:00
Finn Stutzenstein aa05abcb0e
Merge pull request #5934 from r-peschke/mediafile_without_2_required_fields
mediafile fields list_of_speakers_id and filename not required
2021-03-04 07:58:54 +01:00
Ralf Peschke fa80e7dfac mediafile fields list_of_speakers_id and filename not required anymore because of mediafile directory 2021-03-03 17:26:26 +01:00
Finn Stutzenstein f5529f5eb3
Adjusted models
- Added new permissions poll.can_manage
- Reorder permissions.yml alphabetically
- provide some more default values
2021-03-01 11:41:25 +01:00
Finn Stutzenstein d293832199
Merge pull request #5919 from jsangmeister/fix-action-interface2
Fix action interface2
2021-03-01 07:22:49 +01:00
Joshua Sangmeister 0583f05a4f Switch atomic meaning 2021-02-26 15:04:24 +01:00
jsangmeister 5aad7e87b6
Merge pull request #5907 from jsangmeister/fix-structured-fields
Add replacement to structured fields
2021-02-26 14:50:13 +01:00
jsangmeister cf349840fc
Merge pull request #5906 from jsangmeister/fix-action-interface
Fix action interface
2021-02-26 12:39:06 +01:00
Finn Stutzenstein 41af863484
Update projector_countdown 2021-02-25 11:03:36 +01:00
Finn Stutzenstein 36125dadc5
Merge pull request #5914 from r-peschke/openslides4-dev
Add 'minimum' for some fields in Projector
2021-02-23 15:56:29 +01:00
Ralf Peschke 56c8b1c15c
Add 'minimum' for some fields in Projector 2021-02-23 15:48:15 +01:00
Ralf Peschke d127541e24
Merge pull request #5904 from r-peschke/os4-permissions-in_initial-data.json
Permissions in initial-data.json and example-data.json concording to those in models.yaml
2021-02-22 11:47:21 +01:00
Ralf Peschke 9f6ad59d0b PR5904 Fix permissions initial_data.jsoin and example-data.json 2021-02-22 11:42:47 +01:00
Finn Stutzenstein f0a5e2716a
Merge pull request #5900 from FinnStutzenstein/projector
Remodel the projector
2021-02-22 08:03:43 +01:00
Finn Stutzenstein e30f4c85d8
Merge pull request #5912 from FinnStutzenstein/userTimestamp
Fix user last_email_send type
2021-02-22 07:58:20 +01:00
Finn Stutzenstein 1e194cf161
Fix user last_email_send type 2021-02-22 07:57:08 +01:00
Joshua Sangmeister 42b590c9f0 Add replacement to structured fields 2021-02-19 12:17:51 +01:00
Joshua Sangmeister a427ca69fe Fix action interface 2021-02-19 09:26:44 +01:00
Ralf Peschke f3086375cc Fix recommendations from code review 2021-02-18 16:49:21 +01:00
reiterl 21338d20c8
Merge pull request #5901 from reiterl/change_poll_global_defaults
Change poll global_* default to true.
2021-02-18 13:06:56 +01:00
Ralf Peschke d413f04585 Proposal for permissions in initial-data.json 2021-02-18 11:08:08 +01:00
Ludwig Reiter 0a9b6f2a62 Change poll global_* default to true. 2021-02-17 11:16:18 +01:00
Finn Stutzenstein 0eaad10f11
Remodel the projector 2021-02-16 10:37:20 +01:00
Finn Stutzenstein 6e0dcf71df
Merge pull request #5858 from normanjaeckel/FixModelsvalidator
Fixed modelsvalidator.
2021-02-12 07:24:02 +01:00
Finn Stutzenstein cf653fd151
Merge pull request #5880 from r-peschke/debug-port-for-backend
Map debug port for openslides-backend in docker-compose.dev
2021-02-10 14:38:07 +01:00
Finn Stutzenstein 4794f063f8
Merge pull request #5835 from reiterl/dev_check_json
Work on check_json.py
2021-02-10 13:32:32 +01:00
Finn Stutzenstein cabba247f3
Reworked checker
Credits for initial work to @reiterl
2021-02-10 12:51:41 +01:00
Ralf Peschke 762a279d1b Map debug port for openslides-backend in docker-compose.dev 2021-02-10 11:23:46 +01:00
Norman Jäckel 11213125fc Fixed modelsvalidator. 2021-02-05 17:03:56 +01:00
Norman Jäckel 4b90c1b2ba
Merge pull request #5855 from normanjaeckel/Permission
Added permission.yml.
2021-02-05 14:03:21 +01:00
Norman Jäckel d1d18c299a Added permission.yml. 2021-02-05 12:34:14 +01:00
Finn Stutzenstein 89498a12c4
Merge pull request #5831 from FinnStutzenstein/fixInitialData
Fix initial data
2021-02-04 16:07:48 +01:00
Finn Stutzenstein aa07af5329
Merge pull request #5849 from GabrielInTheWorld/update-submodules
Update submodules
2021-02-04 15:56:19 +01:00
GabrielMeyer fe6ce208e7 Update 2021-02-04 15:35:15 +01:00
Finn Stutzenstein f41349f038
Merge pull request #5848 from jsangmeister/adjust-ds-interface
Adjust datastore interface
2021-02-04 13:49:41 +01:00
Joshua Sangmeister 21876d3be6 Adjust datastore interface 2021-02-04 13:43:14 +01:00
Finn Stutzenstein 1bee13d7e9
Merge pull request #5844 from OpenSlides/revert-5843-fixActionInterface
Revert "Fix action interface"
2021-02-03 10:07:10 +01:00
Finn Stutzenstein f7ba41dd8f
Revert "Fix action interface" 2021-02-03 10:06:16 +01:00
Finn Stutzenstein 728cbdb719
Merge pull request #5843 from FinnStutzenstein/fixActionInterface
Fix action interface
2021-02-03 10:02:07 +01:00
Finn Stutzenstein 7f125165a3
Fix action interface 2021-02-03 10:00:53 +01:00
Finn Stutzenstein aa934ecc1f
more fixes 2021-02-02 08:54:28 +01:00
Finn Stutzenstein 4107bbac80
Merge pull request #5836 from normanjaeckel/ChangeModelsYml
Added permissions to models.yml
2021-02-02 08:06:37 +01:00
Norman Jäckel 41956313b5 Added permissions to models.yml 2021-01-30 13:34:15 +01:00
Finn Stutzenstein 5d82a05608
Fix initial data 2021-01-28 13:36:11 +01:00
Finn Stutzenstein 7502569211
Merge pull request #5827 from FinnStutzenstein/update
update submodules
2021-01-27 12:41:31 +01:00
Finn Stutzenstein 2360665a6f
update submodules 2021-01-27 12:22:30 +01:00
Norman Jäckel a949dc7006
Merge pull request #5802 from normanjaeckel/ChangedModelsForUsers
Added meeting specific fields for users.
2021-01-22 15:27:26 +01:00
Norman Jäckel c6b1a254a0 Added meeting specific fields for users and changed role system. 2021-01-22 14:58:44 +01:00
jsangmeister 9abf787d99
Merge pull request #5704 from jsangmeister/datastore-partial-list-updates
Update datastore specs
2021-01-14 15:05:33 +01:00
Finn Stutzenstein f0a4dc52d5
Merge pull request #5783 from reiterl/dev_model_config
Add config defaults to models.yml settings.
2021-01-14 08:54:57 +01:00
Ludwig Reiter 926e7482b1
Update defaults. 2021-01-14 08:53:24 +01:00
Ludwig Reiter 24b435bc46 Add config defaults to the settings in model.yml 2021-01-13 11:26:13 +01:00
Finn Stutzenstein b825fb2a35
Merge pull request #5768 from FinnStutzenstein/updateDSInterface
Update Datastore interface
2021-01-12 16:25:58 +01:00
Finn Stutzenstein 0d2ce2cf29
Merge pull request #5809 from FinnStutzenstein/resources
Resources
2021-01-12 16:18:26 +01:00
Finn Stutzenstein 7c3e334677
Add default test resource 2021-01-12 16:17:06 +01:00
Joshua Sangmeister 5844eabb95 more docs & rename max_position 2021-01-12 14:58:35 +01:00
Finn Stutzenstein 08691f3a1f Update Datastore interface
Add new locked field syntax
2021-01-12 14:58:32 +01:00
Finn Stutzenstein 118876dcc1
Merge pull request #5808 from FinnStutzenstein/update
update OS4 submodules
2021-01-12 09:58:08 +01:00
Finn Stutzenstein c9f14d8b78
update OS4 submodules 2021-01-12 09:56:57 +01:00
Finn Stutzenstein 62cc57c54b
Merge pull request #5723 from FinnStutzenstein/categoryPrefixOptional
Change the category prefix to optional
2021-01-12 08:40:51 +01:00
Finn Stutzenstein 7854487e4f
Merge pull request #5803 from tsiegleauq/adjust-initial-data
Update initial data
2021-01-12 08:37:54 +01:00
GabrielInTheWorld 29e79eefb4
Merge pull request #5806 from GabrielInTheWorld/fix-auth-environment
Updates environment for autoupdate
2021-01-11 15:54:44 +01:00
GabrielMeyer 4d2ba5447a Updates environment for autoupdate 2021-01-11 14:47:47 +01:00
Sean fecec21a0f Update initial data
update initial data to new naming
2021-01-08 15:11:24 +01:00
Oskar Hahn f2f9000dec
Merge pull request #5798 from ostcar/permission-interface
permission interface
2021-01-08 09:40:54 +01:00
Finn Stutzenstein 5098d71891
Merge pull request #5799 from FinnStutzenstein/update
Updating submodules
2021-01-07 17:31:51 +01:00
Finn Stutzenstein 3a3ac21e8a
Updating submodules 2021-01-07 17:30:39 +01:00
Oskar Hahn 91f2360550 permission interface 2021-01-07 17:28:16 +01:00
Oskar Hahn 14ae5736d2
Merge pull request #5792 from ostcar/example_data_permission_rename
Example data permission rename
2021-01-07 17:02:42 +01:00
Oskar Hahn ec9cdfc7a9 Fix permissions in example data
rename superadmin to admin

Rename user perm

enum values to strings

step

internal_item
2021-01-07 17:00:38 +01:00
Finn Stutzenstein 45289663c5
Merge pull request #5790 from ostcar/models-changes
models.yml: More clear enums
2021-01-07 16:52:19 +01:00
Norman Jäckel 2399414a4f
Merge pull request #5794 from normanjaeckel/Docs
Docs
2021-01-07 16:49:11 +01:00
Oskar Hahn a1702b0b8d Remove supporters autoremove 2021-01-07 16:46:21 +01:00
Finn Stutzenstein 6db4f68136
Merge pull request #5796 from FinnStutzenstein/gitignore
Add OS3 secrets to gitignore
2021-01-07 09:19:03 +01:00
Finn Stutzenstein 1ad58c503c
Add OS3 secrets to gitignore 2021-01-07 09:17:33 +01:00
Norman Jäckel b85a981dfe Added some descriptions. 2021-01-06 20:28:35 +01:00
Oskar Hahn 3c50e18cb3
Merge pull request #5793 from ostcar/modelsvalidate_external_lib
Use openslides-models-to-go
2021-01-06 11:39:21 +01:00
Oskar Hahn 98c2f5c332 Use openslides-models-to-go 2021-01-06 11:32:16 +01:00
Finn Stutzenstein c2e46afbd1
Merge pull request #5785 from reiterl/dev_model_supporter
Remove equal_fields meetings from supporter_ids.
2021-01-05 08:11:03 +01:00
Oskar Hahn 2504fbb307 Rename meeting superadmin to just admin 2021-01-04 15:46:22 +01:00
Oskar Hahn 1c5b4a5c9e More clear enums 2021-01-02 21:30:28 +01:00
Ludwig Reiter a8a5890d20 Remove equal_fields meetings from supporter_ids. 2020-12-18 11:36:50 +01:00
jsangmeister a3dce30e1d
Merge pull request #5776 from reiterl/dev_model_rm_option
Remove allow_multiple_votes_per_candidate
2020-12-17 15:22:01 +01:00
jsangmeister d224571373
Merge pull request #5782 from jsangmeister/fix-models
Fix models
2020-12-17 11:03:22 +01:00
Joshua Sangmeister e3af800597 Fix models 2020-12-17 11:00:22 +01:00
jsangmeister 3dbc5f29da
Merge pull request #5769 from jsangmeister/update-models
Removed redundant information from models.yml
2020-12-17 10:31:36 +01:00
Joshua Sangmeister 8a36731d80 Removed redundant information from models.yml and adjusted validator 2020-12-17 10:30:15 +01:00
Ludwig Reiter 7648c9c70f Remove allow_multiple_votes_per_candidate 2020-12-16 11:38:34 +01:00
Finn Stutzenstein 253126723f
Merge pull request #5767 from FinnStutzenstein/update
Update Submodules
2020-12-10 12:02:33 +01:00
Finn Stutzenstein a366c54afe
Update Submodules 2020-12-10 12:01:06 +01:00
Finn Stutzenstein bbb355cc05
Merge pull request #5759 from FinnStutzenstein/updateMediafileInterface
Update mediaservice interface
2020-12-08 10:06:13 +01:00
Finn Stutzenstein 67f3312090
Update mediaservice interface 2020-12-08 10:05:00 +01:00
Finn Stutzenstein b2909e0f1c
Merge pull request #5728 from reiterl/dev_model_number_value
Add number_value to motion model.
2020-12-08 08:39:17 +01:00
Finn Stutzenstein cbe1a133f4
Merge pull request #5758 from FinnStutzenstein/fixExampleData
Fix example data
2020-12-08 08:37:34 +01:00
Finn Stutzenstein 9504f2fb73
Fix example data 2020-12-08 08:36:09 +01:00
Ludwig Reiter 8c59d1090c Update models.py
Add description and read_only to motion/number_value.
2020-12-07 14:31:08 +01:00
Finn Stutzenstein 09d0f740c2
Merge pull request #5756 from FinnStutzenstein/changeDocs
Change modellierung.drawio
2020-12-04 16:19:47 +01:00
Finn Stutzenstein 7f9f12e750
Change modellierung.drawio 2020-12-04 16:17:37 +01:00
Finn Stutzenstein d983925930
Merge pull request #5753 from FinnStutzenstein/refinePolls
Refine poll modelling
2020-12-03 10:43:02 +01:00
Finn Stutzenstein a8f097ded4
Refine poll modelling 2020-12-03 10:40:47 +01:00
GabrielMeyer 50129c6fd0 Update auth-service's interface 2020-12-01 16:52:57 +01:00
Finn Stutzenstein 315bd7cdf3
Merge pull request #5696 from FinnStutzenstein/actionServiceInterface
Enhance the action interface
2020-12-01 14:37:43 +01:00
Finn Stutzenstein a87901aee0
Merge pull request #5741 from FinnStutzenstein/models
Propagate changes in OS3
2020-12-01 13:54:35 +01:00
Finn Stutzenstein 9826e763af
Propagate changes in OS3 2020-12-01 13:45:29 +01:00
Finn Stutzenstein 4c1f7b123a
Merge pull request #5698 from FinnStutzenstein/pollRestructure
Restructure polls
2020-12-01 11:51:14 +01:00
Finn Stutzenstein 4f02a236b4
small changes 2020-11-27 10:47:02 +01:00
Ludwig Reiter ed93d873a7 Add number_value to motion model. 2020-11-26 09:17:22 +01:00
Finn Stutzenstein 99b0bc6e3b
Restructure polls 2020-11-25 14:22:16 +01:00
Finn Stutzenstein 62f40d1447
move atomic parameter 2020-11-25 12:17:01 +01:00
Finn Stutzenstein 5019403a8e
Merge pull request #5722 from GabrielInTheWorld/new-compose
Deletes old docker-compose and adds a port to auth-service
2020-11-25 12:14:57 +01:00
GabrielMeyer c62cc7851d Deletes old docker-compose and adds a port to auth-service 2020-11-24 16:38:07 +01:00
Finn Stutzenstein a85a5458c6
Change the category prefix to optional 2020-11-24 15:25:48 +01:00
GabrielInTheWorld b2215131a4
Merge pull request #5712 from GabrielInTheWorld/fix-example-data
Fixes missing meeting_id in example-data
2020-11-19 15:05:35 +01:00
GabrielMeyer 371fb0c6ae Fixes meeting_id example-data 2020-11-19 15:01:53 +01:00
Finn Stutzenstein a0d0a4ac0b
Change name 2020-11-19 15:01:49 +01:00
Finn Stutzenstein cb7e91d8c5
Fixed ActionsResult 2020-11-19 13:54:53 +01:00
Finn Stutzenstein a73ae9961f
Include new transaction mode for bulk actions 2020-11-19 13:52:39 +01:00
jsangmeister c3bb1928b9
Merge pull request #5708 from r-peschke/feature_setCommittee_Meeting_protected
Set relation Committee -> Meeting protected
2020-11-19 10:56:20 +01:00
Ralf Peschke b39a2e397d Set relation Committee -> Meeting protected 2020-11-18 14:13:10 +01:00
Finn Stutzenstein c48b5277c4
Merge pull request #5646 from FinnStutzenstein/permissionService
Permission service
2020-11-17 07:51:20 +01:00
fistutzenste 945d1307ba
Permission Service 2020-11-17 07:49:14 +01:00
Joshua Sangmeister 8cd6e02772 Updated specs 2020-11-13 16:18:34 +01:00
Joshua Sangmeister c811926478 Update datastore specs 2020-11-13 13:05:26 +01:00
Finn Stutzenstein 989a5789ef
Merge pull request #5699 from FinnStutzenstein/update
Update Services
2020-11-12 07:34:09 +01:00
Finn Stutzenstein 95aa94e4ea
Update Services 2020-11-12 07:32:18 +01:00
jsangmeister 7a039469b0
Merge pull request #5697 from reiterl/dev_model_group
Add on delete Protect to group relations.
2020-11-11 13:50:42 +01:00
Ludwig Reiter 2304729545 Add on delete Protect to group relations. 2020-11-11 10:22:59 +01:00
Finn Stutzenstein 8cfa7d0a51
Result->Response, actions->action 2020-11-11 08:33:59 +01:00
Finn Stutzenstein 5b0047b093
Enhance the action interface 2020-11-10 17:37:53 +01:00
jsangmeister 1fb296038e
Merge pull request #5679 from jsangmeister/protect-first-state-of-workflow
Protect first_state_of_workflow_id & rename has_inherited_access_group_ids
2020-11-05 17:40:31 +01:00
Joshua Sangmeister ee41eb2df9 Protect first_state_of_workflow_id 2020-11-05 17:39:06 +01:00
jsangmeister 9c766de474
Merge pull request #5675 from reiterl/dev_model_lightblue
Add a default to motion_state css_class.
2020-11-04 16:03:39 +01:00
Ludwig Reiter 99011e6909 Add a default to motion_state css_class. 2020-11-04 14:53:57 +01:00
Finn Stutzenstein c104f235da
Merge pull request #5670 from FinnStutzenstein/fixMediafileExampleData
Fix eaxmple data: Add LOS for mediafiles
2020-11-03 15:23:58 +01:00
Finn Stutzenstein a183fa89cb
Fix eaxmple data: Add LOS for mediafiles 2020-11-03 15:22:24 +01:00
Finn Stutzenstein 0b6e855254
Merge pull request #5669 from FinnStutzenstein/locationToPlace
location -> place
2020-11-03 13:43:10 +01:00
Finn Stutzenstein ef78783b55
location -> place 2020-11-03 13:41:45 +01:00
Finn Stutzenstein ca069edb11
Merge pull request #5668 from FinnStutzenstein/structuredField
Structured fields, updated repos
2020-11-03 12:14:57 +01:00
Finn Stutzenstein 8bf3b3df6b
Structured fields, updated repos 2020-11-03 12:14:09 +01:00
jsangmeister 3e0ac15e49
Merge pull request #5662 from reiterl/dev_model_2
Update model assignment phase.
2020-10-30 10:33:59 +01:00
Ludwig Reiter e199e9dbe0 Update model assignment. Change enum and default of phase. 2020-10-30 10:03:40 +01:00
Finn Stutzenstein 4d8a53513a
Merge pull request #5659 from FinnStutzenstein/fixDockerComposeDev
Fix docker compose dev
2020-10-28 13:17:52 +01:00
Finn Stutzenstein c83d9cf806
Fix docker compose dev 2020-10-28 13:11:26 +01:00
Finn Stutzenstein 39d7353959
Merge pull request #5621 from FinnStutzenstein/prod
Productive setup
2020-10-28 10:42:42 +01:00
Finn Stutzenstein 9372386979
Prod setup 2020-10-28 08:46:03 +01:00
Finn Stutzenstein 399bf389d6
Merge pull request #5635 from FinnStutzenstein/dsHelperTools
Helper-tools for the DataStore
2020-10-23 08:32:03 +02:00
Finn Stutzenstein 4ff76444b2
Helper-tools for the DataStore 2020-10-23 08:31:20 +02:00
Finn Stutzenstein 1f049caf5d
Merge pull request #5650 from FinnStutzenstein/adoptExampleDataII
Adopt example data: Reenable the old version
2020-10-21 11:43:24 +02:00
Finn Stutzenstein e164e2b118
Adopt example data: Reenable the old version
This can be reverted, if every service can deal with the new syntax
2020-10-21 11:41:56 +02:00
Finn Stutzenstein ce5bb9dca5
Merge pull request #5649 from FinnStutzenstein/adoptExampleData
Adopt example data for new structured fields
2020-10-21 10:44:25 +02:00
Finn Stutzenstein a406d5ac88
Adopt example data for new structured fields 2020-10-21 10:39:44 +02:00
Finn Stutzenstein 81c00368f0
Merge pull request #5648 from FinnStutzenstein/enableMotionFeatures
Enable more features for motions
2020-10-21 09:22:58 +02:00
Finn Stutzenstein 5439e46866
Enable more features for motions 2020-10-21 09:21:27 +02:00
Finn Stutzenstein 44cfc2849f
Merge pull request #5642 from reiterl/dev_model
Update model.yml to switch enum of state.
2020-10-20 10:08:53 +02:00
Ludwig Reiter de65db33a7 Update model.yml to switch enum of state.
The motion_state css_class should include grey.
2020-10-20 09:35:29 +02:00
jsangmeister b6dd6086aa
Merge pull request #5632 from jsangmeister/change-weights
Change weights and add defaults
2020-10-15 09:55:36 +02:00
Finn Stutzenstein 77c03b5675
Merge pull request #5630 from GabrielInTheWorld/update-auth-service-interface
Update auth-service's interface and auth-service
2020-10-14 07:55:58 +02:00
Joshua Sangmeister 2664c39361 Change weights and add defaults 2020-10-13 17:16:35 +02:00
GabrielMeyer 049421bda9 Update auth-service's interface and auth-service 2020-10-13 15:06:18 +02:00
Finn Stutzenstein eded14f066
Merge pull request #5496 from GabrielInTheWorld/update-auth-service-interface
Update auth-service's interface
2020-10-13 08:26:15 +02:00
jsangmeister 27fdcc66d2
Merge pull request #5629 from jsangmeister/add-defaults
Add default type for motion_change_recommendation
2020-10-12 19:06:35 +02:00
Joshua Sangmeister 8530c87f77 Add default type for motion_change_recommendation 2020-10-12 19:02:26 +02:00
GabrielMeyer d123f0934a Updates auth-service's interface
- Also updates `example-data.json` corresponding to OpenSlides/openslides-auth-service#32
2020-10-12 17:17:30 +02:00
jsangmeister e9311083f2
Merge pull request #5620 from jsangmeister/add-on-delete
Add on_delete and meeting_id for all models
2020-10-12 16:20:07 +02:00
Joshua Sangmeister 064006474d Add on_delete, meeting_id and equal_fields 2020-10-12 15:13:01 +02:00
Finn Stutzenstein 9d52faaa6d
Merge pull request #5605 from FinnStutzenstein/models
OS4 Models: Add configuration and adopt schema changes in OS3
2020-10-12 14:46:20 +02:00
Finn Stutzenstein 2cc81c0168
OS4 Models: Add configuration and adopt schema changes in OS3 2020-10-12 14:39:12 +02:00
jsangmeister 3e655d3f12
Merge pull request #5626 from jsangmeister/add-default-agenda-item-type
Add default type to agenda item
2020-10-12 11:54:28 +02:00
Joshua Sangmeister f511b2f722 Add default type to agenda item 2020-10-12 11:48:23 +02:00
jsangmeister c83a82337b
Merge pull request #5612 from jsangmeister/fix-html-permissive
Fix typo
2020-10-08 09:31:14 +02:00
Joshua Sangmeister b311525b7d Fix typo 2020-10-08 09:27:04 +02:00
Finn Stutzenstein 31d6f93033
Merge pull request #5619 from FinnStutzenstein/update
Update services
2020-10-08 09:11:26 +02:00
Finn Stutzenstein 1891cc7e0b
Update services 2020-10-08 08:29:04 +02:00
Finn Stutzenstein 08b4f023cc
Merge pull request #5615 from GabrielInTheWorld/update-auth-service
Updates auth-service
2020-10-08 07:41:49 +02:00
GabrielMeyer 3971404764 Updates auth-service 2020-10-07 12:02:50 +02:00
jsangmeister 34dcb91744
Merge pull request #5593 from jsangmeister/adjust-models-yml
Adjust models.yml
2020-10-06 15:14:47 +02:00
Joshua Sangmeister ba03281c17 Adjust models.yml 2020-10-06 15:12:10 +02:00
Norman Jäckel a7248c2399
Merge pull request #5578 from FinnStutzenstein/updateModels
Update models
2020-10-02 09:04:41 +02:00
Finn Stutzenstein 248a0451df
Add topic to projected collections 2020-10-02 08:13:45 +02:00
Norman Jäckel 3b4a006754
Merge pull request #5589 from ostcar/modelsvalidate-version
Use a specific version of the modelsvalidater to make build reproducable
2020-10-01 21:10:21 +02:00
Oskar Hahn bd7e6b95cb Use a specific version of the modelsvalidater to make build reproducable 2020-09-30 20:15:07 +02:00
Norman Jäckel 2a479746cd
Merge pull request #5587 from normanjaeckel/FixBranch
Fix branch.
2020-09-30 20:11:39 +02:00
Norman Jäckel 5b6f6b50c1 Added through keyword for nested structured relations. 2020-09-29 23:24:12 +02:00
Norman Jäckel a4ed281c7e Fix branch. 2020-09-29 22:56:03 +02:00
Norman Jäckel e9fd1f108b
Merge pull request #5572 from normanjaeckel/modelsToYML
Models to yml
2020-09-29 22:49:03 +02:00
Norman Jäckel e80f7eb5e4 Added required flag and other properties to some models. 2020-09-29 22:43:39 +02:00
Finn Stutzenstein 49c77912ae
Merge pull request #5576 from jsangmeister/fix-example-data
Fix example data
2020-09-28 11:49:24 +02:00
Finn Stutzenstein b2eab8306e
Merge pull request #5558 from reiterl/dev1
Add calculated to motion_category/level field.
2020-09-28 10:45:24 +02:00
Joshua Sangmeister 9c6bb8ab7f Fix example data 2020-09-24 17:40:09 +02:00
GabrielInTheWorld 8ced3e0f38
Merge pull request #5573 from jsangmeister/fix-example-data
Fix example data & add direct db access script
2020-09-22 11:36:18 +02:00
Joshua Sangmeister 3b0df8de45 Fix example data & add direct db access script 2020-09-22 11:34:21 +02:00
Norman Jäckel a1925e87f3 Updated models.yml. Added GitHub action with validator. 2020-09-20 22:21:23 +02:00
Oskar Hahn c1b9de6e81 Added models.yml 2020-09-19 20:44:30 +02:00
jsangmeister 3dbc854d81
Merge pull request #5560 from jsangmeister/update-auth-backend
Update auth service and backend
2020-09-15 13:50:39 +02:00
Joshua Sangmeister 00eafb93b9 Update auth service and backend 2020-09-15 13:48:12 +02:00
Ludwig Reiter 4ea247a3f6 Add calculated to motion_category/level field. 2020-09-14 09:45:56 +02:00
Finn Stutzenstein 7e6ab82f8e
Merge pull request #5552 from FinnStutzenstein/updateModels
Update models.txt
2020-09-09 08:38:59 +02:00
Finn Stutzenstein de1cebae26
Update models.txt 2020-09-09 08:38:22 +02:00
Finn Stutzenstein 94219feb44
Merge pull request #5513 from FinnStutzenstein/media-interface
Mediaservice interface
2020-09-01 08:07:15 +02:00
Finn Stutzenstein e0df72bb06
Merge pull request #5527 from FinnStutzenstein/update
Update submodules
2020-08-25 16:36:10 +02:00
Finn Stutzenstein 3fe8b9b23c
Update submodules 2020-08-25 16:29:16 +02:00
Oskar Hahn 85bb8bbb5c
Update models.txt (#5523)
Update models.txt
2020-08-25 09:51:00 +02:00
Finn Stutzenstein 5846838a04
Merge pull request #5524 from FinnStutzenstein/update
Update models.txt
2020-08-24 15:57:13 +02:00
Finn Stutzenstein 0d2c91d8f9
Update models.txt 2020-08-24 15:56:14 +02:00
Finn Stutzenstein 829a44177a
Merge pull request #5518 from FinnStutzenstein/noteGenericRelations
Add reverse models for generic relations in models.txt (closes #5337)
2020-08-21 07:56:00 +02:00
Finn Stutzenstein 48a66d85bf
Add reverse models for generic relations in models.txt (closes #5337) 2020-08-21 07:55:01 +02:00
Finn Stutzenstein fc3b498511
Mediaservice interface 2020-08-18 10:39:20 +02:00
Finn Stutzenstein 3fb41bf800
Merge pull request #5512 from FinnStutzenstein/update
Fix HaProxy redirection and client
2020-08-17 13:43:02 +02:00
Finn Stutzenstein 0993c25e66
Fix HaProxy redirection and client 2020-08-17 13:36:42 +02:00
Finn Stutzenstein cb894c9d99
Merge pull request #5499 from FinnStutzenstein/moreCalculatedFields
Add calculated fields
2020-08-17 12:10:24 +02:00
Finn Stutzenstein 6ccf13bc6d
Merge pull request #5511 from FinnStutzenstein/update
Update Client and Auth service
2020-08-17 10:15:00 +02:00
Finn Stutzenstein b312dc9297
Update Client and Auth service 2020-08-17 10:14:14 +02:00
Finn Stutzenstein c86c3bdcbb
Merge pull request #5509 from tsiegleauq/slight-dev-docu-enhancement
Add https-info and enhance work-in-submodules doc
2020-08-14 11:16:00 +02:00
Finn Stutzenstein ae2b73a8e5
Merge pull request #5510 from FinnStutzenstein/haproxy-redirect
Added redirect from http to https
2020-08-14 11:14:51 +02:00
Finn Stutzenstein ff2569aba9
Added redirect from http to https 2020-08-14 11:13:56 +02:00
Sean Engelhardt 888e17369a Add https-info and enhance work-in-submodules doc 2020-08-14 11:09:49 +02:00
Finn Stutzenstein 5e0eda112d
Adding motion recommendation extension references as relations 2020-08-11 15:14:11 +02:00
Finn Stutzenstein 37d3591d69
Add calculated fields 2020-08-11 09:25:31 +02:00
Finn Stutzenstein 27725a7694
Merge pull request #5493 from OpenSlides/fixModels
Fix syntax of models.txt
2020-08-03 15:54:01 +02:00
Oskar Hahn cb0134a345 Fix syntax of models.txt 2020-08-03 15:19:03 +02:00
Finn Stutzenstein 8caf8e0c64
Merge pull request #5492 from FinnStutzenstein/fixExampleData
Fix example data II
2020-07-31 10:37:35 +02:00
fistutzenste 4fbf0a5438
Fix example data II 2020-07-31 10:36:20 +02:00
Finn Stutzenstein 51c27c4f12
Merge pull request #5491 from FinnStutzenstein/fixExampleData
Fix example data
2020-07-31 10:23:09 +02:00
fistutzenste 69ba33b117
Fix example data 2020-07-31 10:20:39 +02:00
Finn Stutzenstein 40fde015fc
Merge pull request #5490 from FinnStutzenstein/removeMkcert
Remove mkcert, update submodules
2020-07-31 09:23:19 +02:00
fistutzenste 3297e9c4e2
Remove mkcert, update submodules 2020-07-31 09:22:24 +02:00
Finn Stutzenstein 0671ca66c2
Merge pull request #5483 from FinnStutzenstein/fixDocs
Fix docs
2020-07-31 08:58:13 +02:00
Finn Stutzenstein 296a39929e
Merge pull request #5487 from FinnStutzenstein/smallerTimeouts
Smaller timeouts for HaProxy
2020-07-31 08:56:46 +02:00
FinnStutzenstein 584fe874ae
Fix docs and autoupdate interface 2020-07-30 09:09:09 +02:00
Finn Stutzenstein c9223329cc
Merge pull request #5489 from FinnStutzenstein/includeMediaService
WIP: Adding media service
2020-07-29 15:58:53 +02:00
FinnStutzenstein e518ef2784
WIP: Adding media service 2020-07-29 15:57:03 +02:00
Finn Stutzenstein 8c57d911a3
Merge pull request #5486 from GabrielInTheWorld/submodule-update
Submodule update backend
2020-07-29 08:35:40 +02:00
Finn Stutzenstein 6d2651650e
Merge pull request #5488 from GabrielInTheWorld/docker-setup
Fixes setup for auth-service
2020-07-28 14:25:29 +02:00
GabrielMeyer 6b6960c8fe Fixes setup for auth-service 2020-07-28 14:23:28 +02:00
FinnStutzenstein ded12bbea4
Smaller timeouts for HaProxy
This is needed to quickly detect the downage from a service. With these timeouts 12 seonds are needed for HaProxy to return an initial 503. All other 503 are instance since the service is not detected up again.
2020-07-28 10:49:09 +02:00
GabrielMeyer b81da7c979 Submodule update backend 2020-07-27 12:02:00 +02:00
Finn Stutzenstein a489742494
Merge pull request #5485 from swilde/openslides4-dev
If mkcert is not available, create certificates using openssl
2020-07-27 11:17:51 +02:00
Sascha Wilde 9bcffb08fb If mkcert is not available, create certificates using openssl 2020-07-24 17:32:28 +02:00
Finn Stutzenstein 5d7dc56684
Merge pull request #5476 from FinnStutzenstein/https
Https
2020-07-24 10:59:37 +02:00
FinnStutzenstein 79ddac9da8
Use HTTPS in development mode 2020-07-24 10:58:39 +02:00
FinnStutzenstein e47b5fff17
Merge branch 'update' into openslides4-dev 2020-07-23 12:16:59 +02:00
FinnStutzenstein cd007b0950
Update images 2020-07-23 12:16:35 +02:00
Finn Stutzenstein 5086df63b0
Merge pull request #5481 from FinnStutzenstein/update
Update submodules
2020-07-22 16:22:33 +02:00
FinnStutzenstein a17e88fa46
Update submodules 2020-07-22 16:21:46 +02:00
Finn Stutzenstein 708e176c2c
Merge pull request #5477 from FinnStutzenstein/schema
OS4 Schema: agenda items have tags
2020-07-22 13:29:28 +02:00
FinnStutzenstein 7d5cee923a
OS4 Schema: agenda items have tags 2020-07-17 08:38:55 +02:00
Finn Stutzenstein 846fb982f0
Merge pull request #5471 from tsiegleauq/ignore-more
Ignore venv
2020-07-17 07:04:54 +02:00
Sean ce3446bbe5 Ignore venv
.virtualenv should be ignored since it might still exist
from openslides 3.
2020-07-16 15:10:57 +02:00
Finn Stutzenstein 357e54ec93
Merge pull request #5474 from FinnStutzenstein/update
Updated auth service
2020-07-15 16:54:35 +02:00
FinnStutzenstein b6b041d0b6
Updated auth service 2020-07-15 16:54:01 +02:00
Finn Stutzenstein 06b520f3e6
Merge pull request #5466 from GabrielInTheWorld/add-auth-service
Adds submodule openslides-auth-service
2020-07-15 16:41:10 +02:00
GabrielMeyer 1f8e3d60e6 Adds submodule openslides-auth-service 2020-07-14 16:21:49 +02:00
Finn Stutzenstein 89f4323d11
Merge pull request #5460 from jsangmeister/update-models
Add new settings to meeting model
2020-07-09 12:53:00 +02:00
Joshua Sangmeister bedbb75c69 Add new settings to meeting model 2020-07-09 12:46:54 +02:00
Finn Stutzenstein 64628e4be9
Merge pull request #5458 from jsangmeister/update-datastore
Update datastore commit & config
2020-07-09 12:38:14 +02:00
Joshua Sangmeister 7cc6d1fda2 Update autoupdate, backend, client 2020-07-09 11:56:00 +02:00
Joshua Sangmeister 5dc22a7a60 Update datastore commit & config 2020-07-09 09:25:37 +02:00
Finn Stutzenstein 57b4dbd463
Merge pull request #5436 from FinnStutzenstein/clientStartup
Fixed client startup
2020-07-06 09:14:39 +02:00
FinnStutzenstein 8b537d32c0
Fixed client startup 2020-07-06 09:14:03 +02:00
Finn Stutzenstein c6fdb627f6
Merge pull request #5409 from FinnStutzenstein/integration
Integration of the backend and autoupdate service
2020-06-18 12:45:38 +02:00
FinnStutzenstein fe8a74ddf4
Integrate the autoupdate and backend service 2020-06-18 12:43:06 +02:00
jsangmeister b6a9d7dee8
Merge pull request #5400 from jsangmeister/fix-reserve-ids-specs
Fix reserveIds specification
2020-06-09 13:55:03 +02:00
Joshua Sangmeister 1f822d59dd Fix reserveIds specification 2020-06-09 13:55:40 +02:00
jsangmeister 38c67c6dd3
Merge pull request #5358 from jsangmeister/add-reader-to-dc
Add reader to docker compose files
2020-06-02 09:58:30 +02:00
jsangmeister 6c944eeaa8
Merge pull request #5379 from jsangmeister/change-return-types
Change return type of getAll and filter
2020-05-19 16:27:32 +02:00
Joshua Sangmeister ba69386f94 Change return type of getAll and filter 2020-05-19 16:04:48 +02:00
jsangmeister 209eff276e
Merge pull request #5357 from jsangmeister/datastore-rename-getids
Datastore: Rename getIds
2020-05-14 13:29:34 +02:00
Joshua Sangmeister 46ecb24f59 added reader to docker compose files 2020-05-13 16:29:46 +02:00
Joshua Sangmeister ff9acfe81d renamed getIds 2020-05-07 10:13:07 +02:00
jsangmeister 1196d1052b
Merge pull request #5350 from jsangmeister/updateDatastoreSpecs
Added routes to datastore spec
2020-05-05 14:24:24 +02:00
Joshua Sangmeister 66119dffda added routes to datastore spec 2020-05-05 14:24:40 +02:00
Finn Stutzenstein 8696beb6a9
Merge pull request #5327 from FinnStutzenstein/fixExampleData
Fixed example data II
2020-05-05 12:29:33 +02:00
jsangmeister ae6c214cd4
Merge pull request #5342 from jsangmeister/update-interface
Updated datastore interface
2020-05-05 10:05:52 +02:00
FinnStutzenstein 52d992d2ff
Fixed example data II 2020-04-30 09:11:11 +02:00
Joshua Sangmeister f66346405b updated datastore interface 2020-04-28 11:59:28 +02:00
Norman Jäckel bc19de9570
Merge pull request #5287 from FinnStutzenstein/fixExampleData
Small fixes for models and example data
2020-04-17 14:29:56 +02:00
FinnStutzenstein 4b1c9bc190 Small fixes for models and example data 2020-04-14 15:26:37 +02:00
jsangmeister d3b9404efd
Update datastore-service.txt 2020-04-08 11:20:55 +02:00
Norman Jäckel 952355ab94
Merge pull request #5292 from normanjaeckel/SmallFixes
Fixed typos in docs.
2020-04-03 14:08:51 +02:00
Norman Jäckel 252fe7dd51 Fixed typos in docs. 2020-04-03 13:24:15 +02:00
Norman Jäckel a45ac5d2e5
Merge pull request #5220 from normanjaeckel/RestrictionDev
Added first draft for restriction and presenter interfaces.
2020-04-03 13:14:29 +02:00
Finn Stutzenstein c4711cf714
Merge pull request #5261 from FinnStutzenstein/modelle
Refined models
2020-03-25 13:40:39 +01:00
jsangmeister 3ae373b8f1
Merge pull request #5263 from jsangmeister/minor-fixes
Adjustments for datastore spec
2020-03-25 13:04:15 +01:00
Joshua Sangmeister 1f80202218 adjustments to datastore specs 2020-03-25 13:04:20 +01:00
Norman Jäckel 642fae8bdc Fixed typos. 2020-03-24 21:14:43 +01:00
FinnStutzenstein 34ccd7abe8 Refined models 2020-03-24 15:49:02 +01:00
Finn Stutzenstein d77e1c4cd8
Merge pull request #5258 from GabrielInTheWorld/dev-auth
Adds interface for auth-service
2020-03-23 14:45:20 +01:00
Finn Stutzenstein a2da322a4d
Merge pull request #5268 from OpenSlides/autoupdate-interface
Fix autoupdate interface
2020-03-23 14:44:09 +01:00
Oskar Hahn e076fca655 Fix autoupdate interface 2020-03-23 14:43:19 +01:00
Finn Stutzenstein d7fd87d249
Merge pull request #5257 from FinnStutzenstein/serverClientStreaming
Client server communication
2020-03-23 14:36:18 +01:00
GabrielMeyer ce1df508cd Adds interface for auth-service 2020-03-23 13:30:26 +01:00
FinnStutzenstein e43e46fca7 Client server communication 2020-03-18 08:18:00 +01:00
FinnStutzenstein 73e6eddf89 Refine presenter service 2020-03-17 11:21:59 +01:00
Finn Stutzenstein 4f029d336c
Merge pull request #5256 from FinnStutzenstein/updatedClient
Updated Client
2020-03-17 09:02:50 +01:00
FinnStutzenstein 018556a542 Updated Client 2020-03-17 08:58:17 +01:00
Finn Stutzenstein c52821d864 Merge pull request #5241 from FinnStutzenstein/autoupdate-service
Autoupdate service interface
2020-03-16 16:05:14 +01:00
FinnStutzenstein 0a7557329c Interface and example changes 2020-03-16 16:01:26 +01:00
Finn Stutzenstein 8e3b25cd21
Merge pull request #5229 from FinnStutzenstein/os4-mediafiles-resources
[os4] no explicit resources for meetings
2020-03-13 09:18:01 +01:00
FinnStutzenstein 885b81cfe9 Autoupdate service interface 2020-03-09 14:52:20 +01:00
FinnStutzenstein 741a13eeb7 Production build uses commit hashes from submodules 2020-02-28 13:16:29 +01:00
Finn Stutzenstein 67e519a00f Merge pull request #5237 from FinnStutzenstein/os4-compose
Integrating the client and datastore into the main repo:
2020-02-26 15:11:29 +01:00
FinnStutzenstein 4484d1e4ef Integrating the client and datastore into the main repo:
- Added each service as a submodule for development
- Added conventions for naming environment variables
- Added required targets in makefiles of services
2020-02-26 14:47:13 +01:00
FinnStutzenstein f8c774abe7 [os4] no explicit resources for meetings 2020-02-20 10:59:08 +01:00
Norman Jäckel f5548770dd Added first draft for restriction and presenter interfaces. 2020-02-07 00:19:11 +01:00
Finn Stutzenstein f7a18cef65
Merge pull request #5181 from normanjaeckel/ActionDev
Updated action spec.
2020-02-05 06:52:52 +01:00
Norman Jäckel 3751593841 Review. 2020-02-03 23:32:24 +01:00
Finn Stutzenstein a15a35398c
Merge pull request #5182 from FinnStutzenstein/datastore-interface
More complete datastore interface
2020-02-03 06:51:17 +01:00
FinnStutzenstein f7f70098b0 More complete datastore interface 2020-02-03 06:50:11 +01:00
Norman Jäckel 6054eb76c5 Updated action spec. 2020-01-21 18:05:47 +01:00
FinnStutzenstein c187700af2 changes 2020-01-20 12:36:40 +01:00
Finn Stutzenstein 66e716bde1
Merge pull request #5178 from FinnStutzenstein/openslides4-dev
interface specs
2020-01-20 12:15:11 +01:00
FinnStutzenstein f889985326 interface specs 2020-01-20 12:14:36 +01:00
Emanuel Schütze a876eab9f3
Merge pull request #5170 from emanuelschuetze/readme
Added concept link to readme.
2020-01-05 15:15:10 +01:00
Emanuel Schütze 3eefc32ada Added concept link to readme. 2020-01-05 15:14:14 +01:00
Emanuel Schütze 9fe850fa0c
Merge pull request #5169 from emanuelschuetze/openslides4-dev
Prepare main repository for OpenSlides 4
2020-01-05 14:18:28 +01:00
Emanuel Schütze c3f1451966 Added OS4 system architecture image. 2020-01-05 14:16:52 +01:00
Emanuel Schütze a7302f0662 Prepared repository for OpenSlides 4. Removed OpenSlides 3 code. 2020-01-05 14:16:23 +01:00
1478 changed files with 6314 additions and 136722 deletions

View File

@ -0,0 +1,39 @@
version: "3"
services:
backend:
image: openslides-backend
ports:
- "9002:9002"
environment:
- DATASTORE_READER_HOST=reader
- DATASTORE_READER_PORT=9010
- DATASTORE_WRITER_HOST=writer
- DATASTORE_WRITER_PORT=9011
depends_on:
- writer
- reader
reader:
image: openslides-datastore-reader
environment:
- DATASTORE_ENABLE_DEV_ENVIRONMENT=1
depends_on:
- postgresql
ports:
- "9010:9010"
writer:
image: openslides-datastore-writer
environment:
- DATASTORE_ENABLE_DEV_ENVIRONMENT=1
- MESSAGE_BUS_HOST=redis
- MESSAGE_BUS_PORT=6379
depends_on:
- postgresql
- redis
postgresql:
image: postgres:11
environment:
- POSTGRES_USER=openslides
- POSTGRES_PASSWORD=openslides
- POSTGRES_DB=openslides
redis:
image: redis:alpine

90
.github/workflows/build_images.yml vendored Normal file
View File

@ -0,0 +1,90 @@
---
name: Build Docker images for all OpenSlides services
on: [push, workflow_dispatch]
env:
IMAGE_VERSION: 4.0.0-beta
TAG_SUFFIX: -$(date +%Y%m%d)-${GITHUB_SHA::7}
jobs:
build:
name: Builds Docker images
runs-on: ubuntu-latest
strategy:
matrix:
service:
- name: openslides-proxy
directory: proxy
- name: openslides-client
directory: openslides-client
- name: openslides-backend
directory: openslides-backend
- name: openslides-datastore-reader
directory: openslides-datastore-service
args:
MODULE: reader
PORT: 9010
- name: openslides-datastore-writer
directory: openslides-datastore-service
args:
MODULE: writer
PORT: 9011
- name: openslides-autoupdate
directory: openslides-autoupdate-service
- name: openslides-auth
directory: openslides-auth-service
- name: openslides-vote
directory: openslides-vote-service
- name: openslides-icc
directory: openslides-icc-service
- name: openslides-media
directory: openslides-media-service
- name: openslides-manage
directory: openslides-manage-service
steps:
- name: Check out code
uses: actions/checkout@v2
with:
submodules: true
- name: Build image
working-directory: ${{ matrix.service.directory }}
env:
DOCKER_BUILDKIT: 1
run: |
if [ "${{ matrix.service.name }}" = "openslides-client" ]
then
eval echo ${IMAGE_VERSION}${TAG_SUFFIX} > client/src/assets/version.txt
fi
if [ "${{ matrix.service.args }}" != "" ]
then
export BUILD_ARGS="--build-arg MODULE=${{ matrix.service.args.MODULE }}
--build-arg PORT=${{ matrix.service.args.PORT }}"
fi
docker build . --tag ${{ matrix.service.name }} $BUILD_ARGS
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" |
docker login ghcr.io --username ${{ github.actor }} --password-stdin
- name: Push image
run: |
IMAGE_ID=ghcr.io/${{ github.repository }}/${{ matrix.service.name }}
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
docker tag ${{ matrix.service.name }} ${IMAGE_ID}:$(eval echo ${IMAGE_VERSION}${TAG_SUFFIX})
docker tag ${{ matrix.service.name }} ${IMAGE_ID}:latest
docker push ${IMAGE_ID}:$(eval echo ${IMAGE_VERSION}${TAG_SUFFIX})
docker push ${IMAGE_ID}:latest

25
.github/workflows/test-integration.yml vendored Normal file
View File

@ -0,0 +1,25 @@
---
name: Run integration tests (cypress)
on: [push, pull_request]
jobs:
run-cypress:
name: 'Runs integration tests in cypress'
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
with:
submodules: recursive
- name: Build and run OS4 Dev
run: make run-dev ARGS="-d"
- name: Run integration tests (cypress)
uses: cypress-io/github-action@v2
with:
working-directory: integration
wait-on: 'https://localhost:8000'
wait-on-timeout: 300
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0

108
.gitignore vendored
View File

@ -1,83 +1,49 @@
# General
*.pyc
*.swp
*.swo
*.log
*~
# Virtual Environment
.virtualenv*/*
.venv/*
# Javascript tools and libraries
node_modules/*
bower_components/*
# Local user data (settings, database, media, search index, static files)
personal_data/*
openslides/static/*
collected-static/*
# Package building/IDE
docs/_build/*
*.egg-info
build/*
dist/*
debug/*
.DS_Store
.idea
*.code-workspace
# Unit test and coverage reports
# docs
docs/modelsvalidator/modelsvalidator
dev-commands/export.json
# certs
*.pem
# Deployment
/docker/docker-compose.yml
/docker/docker-stack.yml
/docker/secrets/auth_*_key
docker/secrets/*.env
# Integration testing
/integration/results
/integration/cypress/downloads
/integration/cypress/screenshots
/integration/cypress/videos
/integration/node_modules
# Old OS3 files and folders
.coverage
tests/file/*
tests/db.sqlite3.test
.pytest_cache
# Plugin development
openslides_*
# Mypy cache for typechecking
.mypy_cache
# OpenSlides 3 Client
# compiled output
client/dist
client/tmp
client/out-tsc
client/documentation
# dependencies
client/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
# misc
Compodoc
Compodocmodules
client/.sass-cache
client/connect.lock
client/coverage
client/libpeerconnection.log
client/npm-debug.log
client/yarn-error.log
client/testem.log
client/typings
client/yarn.lock
package-lock.json
__pycache__
bower_components
client
make
openslides_*
openslides
personal_data
tests
.launch/
.venv/
.virtualenv/
.vscode/
client/package-lock.json
cypress.json
# System Files
client/.DS_Store
client/Thumbs.db
server/
# OS3+-Submodules
/autoupdate/

36
.gitmodules vendored Normal file
View File

@ -0,0 +1,36 @@
[submodule "openslides-datastore-service"]
path = openslides-datastore-service
url = https://github.com/OpenSlides/openslides-datastore-service.git
branch = main
[submodule "openslides-client"]
path = openslides-client
url = https://github.com/OpenSlides/openslides-client.git
branch = main
[submodule "openslides-backend"]
path = openslides-backend
url = https://github.com/OpenSlides/openslides-backend.git
branch = main
[submodule "openslides-autoupdate-service"]
path = openslides-autoupdate-service
url = https://github.com/OpenSlides/openslides-autoupdate-service.git
branch = main
[submodule "openslides-auth-service"]
path = openslides-auth-service
url = https://github.com/OpenSlides/openslides-auth-service.git
branch = main
[submodule "openslides-media-service"]
path = openslides-media-service
url = https://github.com/OpenSlides/openslides-media-service.git
branch = main
[submodule "openslides-manage-service"]
path = openslides-manage-service
url = https://github.com/OpenSlides/openslides-manage-service.git
branch = main
[submodule "openslides-icc-service"]
path = openslides-icc-service
url = https://github.com/OpenSlides/openslides-icc-service.git
branch = main
[submodule "openslides-vote-service"]
path = openslides-vote-service
url = https://github.com/OpenSlides/openslides-vote-service.git
branch = main

View File

@ -1,136 +0,0 @@
dist: xenial
sudo: true
cache:
- directories:
- $HOME/.cache/pip
- client/node_modules
matrix:
include:
- stage: "Dependencies"
name: "Installing dependencies for python"
language: python
python:
- "3.6"
cache:
pip: true
install:
- python --version
- pip install --upgrade setuptools pip
- pip install --upgrade --requirement requirements/development.txt
- pip install --upgrade .[big_mode]
- pip freeze
script: skip
- name: "Installing npm dependencies"
language: node_js
node_js: "10.9"
cache:
- directories:
- "client/node_modules"
before_install:
- npm install -g @angular/cli@~8.0.6
- ng --version
- cd client
install:
- npm install
script: skip
- stage: "Run tests"
name: "Client: Testing"
language: node_js
node_js: "10.9"
apt:
sources:
- google-chrome
packages:
- google-chrome-stable
services:
- xvfb
install:
- cd client
- export CHROME_BIN=/usr/bin/google-chrome
- export DISPLAY=:99.0
script:
- npm run test-silently
- name: "Client: Production Build (ES5)"
language: node_js
node_js: "10.9"
install:
- cd client
- sed -i '/\"target\"/c\\"target\":\"es5\",' tsconfig.json
script:
- npm run build
- name: "Client: Production Build (ES2015)"
language: node_js
node_js: "10.9"
install:
- cd client
- echo "Firefox ESR" > browserslist
script:
- npm run build
- name: "Client: Build"
language: node_js
node_js: "10.9"
script:
- cd client
- npm run build-debug
- name: "Server: Tests Python 3.6"
language: python
python:
- "3.6"
script:
- mypy openslides/ tests/
- pytest --cov --cov-fail-under=73
- name: "Server: Tests Python 3.7"
language: python
python:
- "3.7"
script:
- flake8 openslides tests
- isort --check-only --diff --recursive openslides tests
- black --check --diff --target-version py36 openslides tests
- mypy openslides/ tests/
- pytest --cov --cov-fail-under=73
- name: "Server: Tests Python 3.8"
language: python
python:
- "3.8"
script:
- flake8 openslides tests
- isort --check-only --diff --recursive openslides tests
- black --check --diff --target-version py36 openslides tests
- mypy openslides/ tests/
- pytest --cov --cov-fail-under=73
- name: "Client: Linting"
language: node_js
node_js: "10.9"
script:
- cd client
- npm run lint-check
- name: "Client: Code Formatting Check"
language: node_js
node_js: "10.9"
script:
- cd client
- npm list --depth=0 || cat --help
- npm run prettify-check
- name: "Server: Tests Startup Routine Python 3.7"
language: python
python:
- "3.7"
script:
- set -e
- python manage.py createsettings
- python manage.py migrate
- python manage.py runserver --noreload & (sleep 15 && kill $(ps aux | grep 'manage.py runserver' | head -n -1 | awk '{print $2}'))
- set +e

View File

@ -30,4 +30,10 @@ Authors of OpenSlides in chronological order of first contribution:
Jochen Saalfeld <jochen.saalfeld@intevation.de>
Fadi Abbud <fmfn13@hotmail.com>
Gabriel Meyer <meyergabriel@live.de>
Gernot Schulz <gernot@intevation.de>
Joshua Sangmeister <joshua.sangmeister@gmail.com>
Ralf Peschke <rpeschke@peschke-it.de>
Ludwig Reiter <ludwig.reiter@intevation.de>
Adrian Richter <adrian@intevation.de>
Camille Akmut
Johannes Rolf <johannes.rolf@rwth-aachen.de>

964
CHANGELOG.md Normal file
View File

@ -0,0 +1,964 @@
# CHANGELOG of OpenSlides
https://openslides.com
## Version 3.1 (2019-12-13)
[Milestone](https://github.com/OpenSlides/OpenSlides/milestones/3.0)
**General:**
- Improved loading time of OpenSlides [#5061, #5087, #5110, #5146 - Breaks IE11].
- Improved Websocket reconnection - works now more reliable [#5060].
- Improved performance by replacing deprecated encode and decode library with faster manual approaches [#5085, #5092].
- Improved mobile views for small devices [#5106].
- Improved virtual scrolling behavior of tables: remember last scroll position [#5156].
- New SingleSignOn login method via SAML [#5000].
- New inline editing for start page, legal notice and privacy policy [#5086].
- Reordered settings in subpages for better overview [#4878, #5083, #5096].
- Added html meta noindex tag to prevent search engines finding instances of OpenSlides [#5061].
- Added server log entries for usage of notify feature [#5066].
- Added server-side HTML validation for personal notes (motions) and about me field (users) [#5121].
- Fixed an issue where projector button in lists was always not indicating the projected element [#5055].
- Fixed issues where search-filter, property-filter and property-sort in list views does not work correctly [#5065].
- Fixed an issues where list view entries using virtual scrolling become invisible [#5072].
- Fixed an error where loading spinner would not disappear while offline mode [#5151].
- Various cleanups and improvements to usability, performance and translation.
**Agenda:**
- New config option to show motion submitters as subtitle in agenda list [#5002, #5094].
- New view to sort sepakers of a list - preventing unwanted changes esp. using mobile devices [#5069].
- New button to readd the last speaker to the top of the list [#5069, #5067].
- New agenda list filter 'item type' (topic, motion, motion block, election) [#5084].
- Changed window title for current list of speakers [#5037].
- Added motion title in agenda list [#5063].
**Motions:**
- New option to export personal notes [#5075].
- New amendment filter for motion list [#5056, #5157].
- New possibility to change state and recommendation in motion list using quick edit [#5071].
- Added multi select actions to amendment list [#5041].
- Added search value selector in multi select action dialogs [#5058].
- Added support for nested lists with line numbers in PDF export [#5138].
- Improved scaling of motion tile view [#5038].
- Improved performance for large motions with dozens of amendments by implementing manual change detection in motion detail [#5074, #5139].
- Improved display of long names for states and recommendations in drop down menu in motion detail view [#5079].
- Improved amendment slide by showing only changed line(s)s without surrounding paragraph [#5144].
- Fixed line number errors during creation of amendments [#5023].
- Fixed an issue that ol/ul lists are not printed in amendment PDF [#5051].
- Fixed the amendment option "Show entire motion text" [#5052].
- Fixed a rare bug in final version where change recommendations or amendments would have been hidden [#5070].
- Fixed PDF export in final version: use modified final version if available [#5139].
- Fixes a bug where the event name was printed twice in the PDF header [#5155].
**Elections:**
- Fixed errors by entering votes and sorting candidates [#5129, #5125].
- Fixed a permission issue that prevented nominating another participants for elections [#5154].
**Users:**
- Fixed wrong permission check for set password [#5128].
**Mediafiles:**
- Fixed mediafile upload path [#4710].
- Fixed double slash in mediafile URL [#5031].
- Original filename must now be unique for files [#5123].
**Projector:**
- New projector edit dialog with preview [#5043].
- Added support for custom aspect ratios in projector edit dialog; database migration required [#5141].
- Added missing autoupdates for changed projection defaults [#5045].
- Added scaleable tile for projector list [#5043].
- Added a lock icon on the list of speaker slide if list has been closed [#5154].
- Improved autoupdates for projectors by using changeIDs [#5064].
- Improved performance by preventing high CPU usage on Firefox in projector detail view [#5022].
- Changed config option to show nice horizontal meta box on motion slide [#5088].
- Changed config option "Event date" back to string format [#5042].
- Saved countdown settings [#5053].
**Breaking Changes:**
- Due to faster model handling (using the 'Proxy' function) Internet Explorer 11 cannot be supported anymore!
## Version 3.0 (2019-09-13)
[Milestone](https://github.com/OpenSlides/OpenSlides/milestones/3.0)
**General (Client):**
- OpenSlides client completely rewritten, based on Angular 8 and Material Design.
- OpenSlides is now a Progressive Web App (PWA).
- New browser caching via IndexedDB (one cache store for all browser tabs).
- New list views optimized with virtual scrolling (improved performance for long lists).
- New global quick search using by shortcut 'Alt+Shift+F'.
- New built-in design themes for customizing user interface.
- New update notification if OpenSlides static files are updated.
- New config option for pdf page size (DIN A4 or A5).
- Added TinyMCE 5 editor (switched from CKEditor caused by changed license).
- Switched from yarn/gulp to npm.
- Improved pdf gerneration with progress bar and cancel option.
- Translations available for EN, DE, RU and CS.
**General (Server):**
- New websocket protocol for server client communication using JSON schema.
- New change-id system to send only updated elements to client.
- New global history mode (useable for admin group only).
- Updated to Channels 2.
- Dropped support for Python 3.5.
- Dropped support for Geiss.
- Complete rework of startup and caching system. Dropped restricted data cache.
- Changed URL schema.
- Changed personal settings.py.
- Changed format for elements send via autoupdate.
- Changed projector concept.
- Compressed autoupdates before sending to clients (reduced traffic).
- Fixed autoupdate system for related objects.
- Fixed logo configuration if logo file is deleted.
- Added several bulk views for motions and users (one request for updating multiple selected elements).
- Added docs for using OpenSlides in 'big mode' with Gunicorn and Uvicorn.
- Added docs for configure OpenSlides in settingy.py.
- Dropped chat functionality.
- Server performance improvements.
**Agenda:**
- Agenda items are now optional (for motions, elections and mediafiles). New config to set default behavior.
- New drag&drop view to sort agenda items.
- New config option: only present participants can be added to list of speakers.
- New config option to hide number of speakers on projector.
**Motions:**
- New call list for custom sort of motions.
- New tile layout view with all categories (each category a tile).
- New statute motions with managing statute paragraphs.
- New permission to manage metadata (state, recommendation, submitters and supporters, category, motion block and polls).
- New permission to create amendments.
- New permission to see motions in internal states.
- New access restrictions definable for each motion state in workflow.
- New 'internal' option for motion blocks.
- New sorting view for categories to create subcategories.
- New custom comment fields for all motions (read/write access can be managed via permission groups).
- New motion history (each action is stored in global OpenSlides history which can be restored any time, replaced old motion version and log features).
- New XLSX export (docx support is dropped).
- New navigation for next/previous motion in detail view (shortcut: 'Alt+Shift+Left/Right').
- New multi select actions.
- New timestampes for motions (for sorting by creation date and last modified).
- New config option to set reason as required field.
- New config option to change multiple paragraphs with an amendment.
- New config option to hide motion text on projector.
- New config option to show sequential number.
- New config option to show all motions which are referred to a special motion.
- New config option to show submitters and recommendation in table of contents of PDF.
- New config options to control identifier generation - number of digits and blanks (moved from settings.py).
- Improved PDF export (optional with toc, page numbers, date, comments and meta information)
- Improved motion numbering in (sub)categories: Motions of subcategories are also numbered, and parents of amendments needs to be in the numbered category or any subcategory.
- Improved projection layout of motion blocks.
- Changed default workflows: Allowed submitters to set state of new motions in complex and customized workflow. No migration provided.
- Change CSV import to add tags.
**User:**
- New admin group which grants all permissions. Users of existing group 'Admin' or 'Staff' are move to the new group during migration.
- New gender field.
- New password forget/reset function via email.
- New permission to change own password.
- New config option for sender name and reply email address (From address is defined in settings.py).
**Mediafiles:**
- New support for (sub)folders and permission groups.
**Projector:**
- New views to list, manage and control created OpenSlides projectors.
- New projector queue (add slide to queue), all projected slides are logged.
- New chyron for current speaker.
- New color settings for each projector.
## Version 2.3 (2018-09-20)
[Release notes](https://github.com/OpenSlides/OpenSlides/wiki/OpenSlides-2.3) · [Milestone](https://github.com/OpenSlides/OpenSlides/milestones/2.3)
**Agenda:**
- New item type 'hidden'. New visibilty filter in agenda [#3790].
**Motions:**
- New feature to scroll the projector to a specific line [#3748].
- New possibility to sort submitters [#3647].
- New representation of amendments (paragraph based creation, new diff and list views for amendments) [#3637].
- New feature to customize workflows and states [#3772, #3785].
- New table of contents with page numbers and categories in PDF [#3766].
- New teporal field "modified final version" where the final version can be edited [#3781].
- New config options to show logos on the right side in PDF [#3768].
- New config to show amendments also in motions table [#3792].
- Support to change decimal places for polls with a plugin [#3803].
**Elections:**
- Support to change decimal places for elections with a plugin [#3803]
**Core:**
- Updated Django to 2.1 [#3777, #3786].
- Support for Python 3.7 [#3786].
- Python 3.4 is not supported anymore [#3777].
- Updated pdfMake to 0.1.37 [#3766].
- Changed behavior of collectstatic management command [#3804].
## Version 2.2 (2018-06-06)
[Release notes](https://github.com/OpenSlides/OpenSlides/wiki/OpenSlides-2.2) · [Milestone](https://github.com/OpenSlides/OpenSlides/milestones/2.2)
**Agenda:**
- New permission for managing lists of speakers [#3366].
- New DOCX export of agenda [#3569].
- New collapsable agenda overview [#3567].
- New feature: mark speakers (e.g. as submitter) [#3570].
- New config option to enable numbering of items [#3697].
- New config option to hide internal items when projecting subitems [#3701].
- Hide closed agenda items in the item slide [#3567].
- Fixed wrong sorting of last speakers [#3193].
- Fixed issue when sorting a new inserted speaker [#3210].
- Fixed multiple request on creation of agenda related items [#3341].
- Autoupdates for all children if the item type has changed [#3659].
**Motions:**
- New export dialog for managers only [#3185].
- New personal note field for each motions [#3190, #3267, #3404].
- New navigation between single motions [#3459].
- New possibility to create change recommendations for motion titles [#3626].
- New support for export motions in a ZIP archive [#3189, #3251].
- New PDF export for personal note and comments [#3239].
- New config option for customize sorting of category list in pdf/docx export [#3329].
- New config optoin for pagenumber alignment in PDF [#3327].
- New config options to hide reason, recommendation and meta data box on projector [#3432, #3692].
- New inline editing for motion reason [#3361].
- New multiselect filter for motion comments [#3372].
- New support for pinning personal notes to the window [#3360].
- New warning message if an edit dialog was already opened by another client [#3212].
- New change recommendation type "other" [#3495].
- Fixed issue when creating/deleting motion comment fields in the settings [#3187].
- Fixed empty motion comment field in motion update form [#3194].
- Fixed error on category sort [#3318].
- Bugfix: Changing motion line length did not invalidate cache [#3202].
- Bugfix: Added more distance in motion PDF for DEL-tags in new lines [#3211].
- Bugfix: Creating colliding change recommendation is now prevented on server side [#3304].
- Bugfix: Several bugfixes regarding splitting list items in change recommendations [#3288].
- Bugfix: Several bugfixes regarding diff version [#3407, #3408, #3410, #3440, #3450, #3465, #3537, #3546, #3548, #3644, #3656].
- Improved the multiselect state filter [#3459].
- Save pagination state to session storage [#3569].
- Allow to delete own motions [#3516].
- Reference to motions by id in state and recommendation special field [#3498].
- Log which comment was updated [#3569].
- Split up 'can_see_and_manage_comments' permission in two seperate ones [#3565].
- Combined all boolean filters into one dropdown menu and added a filter for amendments [#3501].
- Show motion identifier in (current) list of speakers [#3442]
- Show the number of next speakers in motion list view [#3470].
- Added (shortened) motion title to motion block slide [#3700].
- Clear identifier on state reset [#3356].
- Reworked DOCX export parser and added comments to DOCX [#3258].
- Removed server side image to base64 transformation and added local transformation [#3181].
- Added karma:watch command [#3466].
**Elections:**
- New pagination for list view [#3393].
**Users:**
- New fast mass import for users [#3290].
- New default user group 'admin' [#3621].
- New feature to send invitation emails with OpenSlides login data [#3503, #3525].
- New view to toggle presence by entering participant number (can be used with barcode scanner) [#3496].
- New support for password validation using Django or custom validators
5. 7. for minimum password length [#3200].
- Hide password in change password view [#3417].
- Users without the permission 'can see users' can now see agenda item speakers, motion submitters and supporters, assignment candidates, mediafile uploader and chat message users if they have the respective permissions [#3191, #3233].
- Fixed compare of duplicated users while CSV user import [#3201].
- Added settings option to enable updating the last_login field in the database. The default is now disabled [#3400].
- Removed OPTIONS request. All permissions are now provided on startup [#3306].
**Mediafiles:**
- New form for uploading multiple files [#3650].
- New custom CKEditor plugin for browsing mediafiles [#3337].
- Project images always in fullscreen [#3355].
- Protect mediafiles for forbidden access [#3384].
- Fixed reloading of PDF on page change [#3274].
**Core:**
- New settings to upload custom fonts (for projector and pdf) [#3568].
- New custom translations to use custom wordings [#3383].
- New support for choosing image files as logos for projector, PDF and web interface header [#3184, #3207, #3208, #3310].
- New notify system [#3212].
- New config option for standard font size in PDF [#3332].
- New config option for disabling header and footer in the projector [#3357].
- New dynamic webpage title [#3404].
- New 'go to top'-link [#3404].
- New custom format cleanup plugin for CKEditor [#3576].
- Reset scroll level for each new projection [#3686].
- Scroll to top on every state change [#3689].
- Added pagination on top of lists [#3698].
- Improved performance for PDF generation significantly (by upgrading to pdfmake 0.1.30) [#3278, #3285].
- Enhanced performance esp. for server restart and first connection of all clients by refactoring autoupdate, Collection and AccessPermission [#3223, #3539].
- Improved reconnect handling if the server was flushed [#3297].
- No reload on logoff. OpenSlides is now a full single page application [#3172].
- Highlight list entries in a light blue, if a related object is projected (e. g. a list of speakers of a motion) [#3301].
- Select the projector resolution with a slider and an aspect ratio [#3311].
- Delay the 'could not load projector' error 3 seconds to not irritate users with a slow internet connection [#3323].
- Added default sorting for agenda, motions, elections, mediafiles and users [#3334, 3348].
- Added caching for the index views [#3419, #3424].
- Added projector prioritization [#3425].
- Added --debug-email flag to print all emails to stdout [#3530].
- Added --no-template-caching flag to disable template caching for easier development [#3566].
- Updated CKEditor to 4.7 [#3375].
- Reduced ckeditor toolbar for inline editing [#3368].
- New api route to project items with just one request needed [#3713].
- Use native twisted mode for daphne [#3487].
- Saved language selection to session storage [#3543].
- Set default of projector resolution to 1220x915 [#2549].
- Preparations for the SAML plugin; Fixed caching of main views [#3535].
- Removed unnecessary OPTIONS request in config [#3541].
- Switched from npm to Yarn [#3188].
- Improvements for plugin integration [#3330].
- Cleanups for the collection and autoupdate system [#3390]
- Bugfixes for PDF creation [#3227, #3251, #3279, #3286, #3346, #3347, #3342].
- Fixed error when clearing empty chat [#3199].
- Fixed autoupdate bug for a user without user.can_see_name permission [#3233].
- Fixed bug the elements are projected and the deleted [#3336].
- Several bugfixes and minor improvements.
*[#xxxx] = Pull request number to get more details on https://github.com/OpenSlides/OpenSlides/pulls*
## Version 2.1.1 (2017-04-05)
[Milestone](https://github.com/OpenSlides/OpenSlides/milestones/2.1.1)
**Agenda:**
- Fixed issue #3173 that the agenda item text cannot be changed.
**Other:**
- Set required version for optional Geiss support to <1.0.0.
## Version 2.1 (2017-03-29)
[Release notes](https://github.com/OpenSlides/OpenSlides/wiki/OpenSlides-2.1) · [Milestone](https://github.com/OpenSlides/OpenSlides/milestones/2.1)
**Agenda:**
- Added button to remove all speakers from a list of speakers.
- Added option to create or edit agenda items as subitems of others.
- Fixed security issue: Comments were shown for unprivileged users.
- Added option to choose whether to show the current list of speakers slide as a slide or an overlay.
- Manage speakers on the current list of speakers view.
- List of speakers for hidden items is always visible.
**Core:**
- Added support for multiple projectors.
- Added control for the resolution of the projectors.
- Added smooth projector scroll.
- Set the projector language in the settings.
- Added migration path from OpenSlides 2.0.
- Added support for big assemblies with lots of users.
- Django 1.10 is now supported. Dropped support for Django 1.8 and 1.9.
- Used Django Channels instead of Tornado. Refactoring of the autoupdate process. Added retry with timeout in case of ChannelFull exception.
- Made a lot of autoupdate improvements for projector and site.
- Added new caching system with support for Redis.
- Support https as websocket protocol (wss).
- Accelerated startup process (send all data to the client after login).
- Add the command getgeiss to download the latest version of Geiss.
- Add a version of has_perm that can work with cached users.
- Removed our AnonymousUser. Make sure not to use user.has_perm() anymore.
- Added function utils.auth.anonymous_is_enabled which returns true, if it is.
- Changed has_perm to support an user id or None (for anyonmous) as first argument.
- Cache the group with there permissions.
- Added watching permissions in client and change the view immediately on changes.
- Used session cookies and store filter settings in session storage.
- Removed our db-session backend and added possibility to use any django session backend.
- Added template hook system for plugins.
- Used Roboto font in all templates.
- Added HTML support for messages on the projector.
- Moved custom slides to own app "topics". Renamed it to "Topic".
- Added button to clear the chatbox.
- Better dialog handling. Show dialog just in forground without changing the state url. Added new dialog for profile, change password, tag and category update view.
- Switched editor back from TinyMCE to CKEditor which provides a better copy/paste support from MS Word.
- Validate HTML strings from CKEditor against XSS attacks.
- Use a separate dialog with CKEditor for editing projector messages.
- Use CKEditor in settings for text markup.
- Used pdfMake for clientside generation of PDFs. Run pdf creation in background (in a web worker thread).
- Introduced new table design for list views with serveral filters and CSV export.
- New CSV import layout.
- Replaced angular-csv-import by Papa Parse for CSV parsing.
- Added UTF-8 byte order mark for every CSV export.
- Removed config cache to support multiple threads or processes.
- Added success/error symbol to config to show if saving was successful.
- Fixed bug, that the last change of a config value was not send via autoupdate.
- Moved full-text search to client-side (removed the server-side search engine Whoosh).
- Made a lot of code clean up, improvements and bug fixes in client and backend.
**Motions:**
- Added adjustable line numbering mode (outside, inside, none) for each motion text.
- Allowed to add change recommendations for special motion text lines (with diff mode).
- Added projection support for change recommendations.
- Added button to sort and number all motions in a category.
- Added recommendations for motions.
- Added options to calculate percentages on different bases.
- Added calculation for required majority.
- Added blocks for motions which can be used in agenda. Set states for multiple motions of a motion block by following the recommendation for each motion.
- Used global config variable for preamble.
- Added configurable fields for comments.
- Added new origin field.
- Reimplemented amendments.
- New PDF layout.
- Added DOCX export with docxtemplater.
- Changed label of former state "commited a bill" to "refered to committee".
- Number of ballots printed can now be set in config.
- Add new personal settings to remove all whitespaces from motion identifier.
- Add new personal settings to allow amendments of amendments.
- Added inline editing for comments.
**Elections:**
- Added options to calculate percentages on different bases.
- Added calculation for required majority.
- Candidates are now sortable.
- Removed unused assignment config to publish winner election results only.
- Number of ballots printed can now be set in config.
- Added inline edit field for a specific hint on ballot papers.
**Users:**
- Added new matrix-interface for managing groups and their permissions.
- Added autoupdate on permission change (permission added).
- Improved password reset view for administrators.
- Changed field for initial password to an unchangeable field.
- Added new field for participant number.
- Added new field 'is_committee' and new default group 'Committees'.
- Improved users CSV import (use group names instead of id).
- Allowed to import/export initial user password.
- Added more multiselect actions.
- Added QR code in users access pdf.
**Mediafiles:**
- Allowed to project uploaded images (png, jpg, gif) and video files (e. g. mp4, wmv, flv, quicktime, ogg).
- Allowed to hide uploaded files in overview list for non authorized users.
- Enabled removing of files from filesystem on model instance delete.
**Other:**
- Added Russian translation (Thanks to Andreas Engler).
- Added command to create example data.
## Version 2.0 (2016-04-18)
[Milestone](https://github.com/OpenSlides/OpenSlides/milestones/2.0)
*OpenSlides 2.0 is essentially not compatible to OpenSlides 1.7. E. g. customized templates, databases and plugins can not be reused without adaption.*
**Agenda:**
- Updated the tests and changed internal parts of method of the agenda model.
- Changed API of related objects. All assignments, motions and custom slides are now agenda items and can be hidden.
- Removed django-mptt.
- Added attachments to custom sldies.
- Improved CSV import.
**Assignments:**
- Renamed app from assignment to assignments.
- Removed possibility to block candidates.
- Massive refactoring and cleanup of the app.
**Motions:**
- Renamed app from motion to motions.
- Massive refactoring and cleanup of the app.
**Mediafiles:**
- Renamed app from mediafile to mediafiles.
- Used improved pdf presentation with angular-pdf.
- Massive refactoring and cleanup of the app.
**Users:**
- Massive refactoring of the participant app. Now called 'users'.
- Used new anonymous user object instead of an authentification backend. Used special authentication class for REST requests.
- Used authentication frontend via AngularJS.
- Improved CSV import.
**Other:**
- New OpenSlides logo.
- New design for web interface.
- Added multiple countdown support.
- Added colored countdown for the last n seconds (configurable).
- Switched editor from CKEditor to TinyMCE.
- Changed supported Python version to >= 3.4.
- Used Django 1.8 as lowest requirement.
- Django 1.9 is supported
- Added Django's application configuration. Refactored loading of signals and projector elements/slides.
- Setup migrations.
- Added API using Django REST Framework 3.x. Added several views and mixins for generic Django REST Framework views in OpenSlides apps.
- Removed most of the Django views and templates.
- Removed Django error pages.
- Added page for legal notice.
- Refactored projector API using metaclasses now.
- Renamed SignalConnectMetaClass classmethod get_all_objects to get_all (private API).
- Refactored config API and moved it into the core app.
- Removed old style personal info page, main menu entries and widget API.
- Used AngularJS with additional libraries for single page frontend.
- Removed use of 'django.views.i18n.javascript_catalog'. Used angular-gettext now.
- Updated to Bootstrap 3.
- Used SockJS for automatic update of AngularJS driven single page frontend.
- Refactored plugin API.
- Refactored start script and management commands. Changed command line option and path for local installation.
- Refactored tests.
- Used Bower and gulp to manage third party JavaScript and Cascading Style Sheets libraries.
- Used setup.cfg for development tools.
- Removed code for documentation and for Windows portable version with GUI. Used new repositories for this. Cleaned up main repository.
- Updated all dependencies.
**Translations:**
- Updated DE, FR, CS and PT translations.
- Added ES translations.
## Version 1.7 (2015-02-16)
https://github.com/OpenSlides/OpenSlides/milestones/1.7
**Core:**
- New feature to tag motions, agenda and assignments.
- Fixed search index problem to index contents of many-to-many tables (e. g. tags of a motion).
- Fixed AttributeError in chatbox on_open method.
**Motions:**
- New Feature to create amendments, which are related to a parent motion.
- Added possibility to hide motions from non staff users in some states.
**Assignments:**
- Fixed permissions to alter assignment polls.
**Other:**
- Cleaned up utils.views to increase performance when fetching single objects from the database for a view (#1378).
- Fixed bug on projector which was not updated when an object was deleted.
- Fixed bug and show special characters in PDF like ampersand (#1415).
- Updated pdf.js to 1.0.907.
- Improve the usage of bsmselect jquery plugin.
- Updated translations.
## Version 1.6.1 (2014-12-08)
https://github.com/OpenSlides/OpenSlides/milestones/1.6.1
**Agenda:**
- Fixed error in item numbers.
**Motions:**
- Show supporters on motion slide if available.
- Fixed motion detail view template. Added block to enable extra content via plugins.
**Assignments:**
- Fixed PDF build error when an election has more than 20 posts or candidates.
**Participants:**
- Fixed participant csv import with group ids:
- Allowed to add multiple groups in csv group id field, e. g. "3,4".
- Fixed bug that group ids greater than 9 can not be imported.
- Updated error message if group id does not exists.
**Other:**
- Fixed CKEditor stuff (added insertpre plugin and removed unused code).
- Updated French, German and Czech translation.
## Version 1.6 (2014-06-02)
https://github.com/OpenSlides/OpenSlides/milestones/1.6
**Dashboard:**
- Added shortcuts for the countdown.
- Enabled copy and paste in widgets.
**Agenda:**
- New projector view with the current list of speakers.
- Added CSV import of agenda items.
- Added automatic numbering of agenda items.
- Fixed organizational item structuring.
**Motions:**
- New slide for vote results.
- Created new categories during CSV import.
**Assignments/Elections:**
- Coupled assignment candidates with list of speakers.
- Created a poll description field for each assignment poll.
- New slide for election results.
**Participants:**
- Disabled dashboard widgets by default.
- Added form field for multiple creation of new participants.
**Files:**
- Enabled update and delete view for uploader refering to his own files.
**Other:**
- Added global chatbox for managers.
- New config option to set the 100 % base for polls (motions/elections).
- Changed api for plugins. Used entry points to detect them automaticly. Load them automaticly from plugin directory of Windows portable version.
- Added possibility to use custom templates and static files in user data path directory.
- Changed widget api. Used new metaclass.
- Changed api for main menu entries. Used new metaclass.
- Inserted api for the personal info widget. Used new metaclass.
- Renamed config api classes. Changed permission system for config pages.
- Regrouped config collections and pages.
- Renamed some classes of the poll api.
- Renamed method and attribute of openslides.utils.views.PermissionMixin.
- Added api for absolute urls in models.
- Inserted command line option to translate config strings during database setup.
- Enhanced http error pages.
- Improved responsive design for templates.
- Fixed headings on custom slides without text.
- Moved dashboard and select widgets view from projector to core app.
- Renamed and cleaned up static direcories.
- Used jsonfield as required package. Removed jsonfield code.
- Added new package backports.ssl_match_hostname for portable build script.
- Used new app "django-ckeditor-updated" to render WYSIWYG html editors. Removed CKEditor from sources.
- Only reload the webserver in debug-mode.
## Version 1.5.1 (2014-03-31)
https://github.com/OpenSlides/OpenSlides/milestones/1.5.1
**Projector:**
- Fixed path and config help text for logo on the projector.
**Agenda:**
- Fixed permission error in the list of speakers widget.
- Fixed Item instance method is_active_slide().
**Motion:**
- Fixed sorting of motions concerning the identifier. Used natsort and DataTables Natural Sort Plugin.
**Participant:**
- Added permission to see participants to the manager group.
- Fixed user status view for use without Javascript.
**Files:**
- Fixed error when an uploaded file was removed from filesystem.
**Other:**
- Set minimum Python version to 2.6.9. Fixed setup file for use with Python 2.6.
- Used unicode font for circle in ballot pdf. Removed Pillow dependency package.
- Fixed http status code when requesting a non-existing static page using Tornado web server.
- Fixed error in main script when using other database engine.
- Fixed error on motion PDF with nested lists.
## Version 1.5 (2013-11-25)
https://github.com/OpenSlides/OpenSlides/milestones/1.5
**Projector:**
- New feature: Show PDF presentations on projector (with included pdf.js).
- Improved projector update process via new websocket API (using sockjs and tornado).
- New projector template with twitter bootstrap.
- Improved projector zoom and scroll behaviour.
**Agenda:**
- New config option: couple countdown with list of speakers.
- Used HTML editor (CKEditor) for agenda item text field.
- Added additional input format for agenda item duration field.
**Motions:**
- Enabled attachments for motions.
- Refactored warnings on CSV import view.
**Elections:**
- Refactored assignment app to use class based views instead of functions.
**Polls:**
- Added percent base to votes cast values.
**Participants:**
- Updated access data PDF: WLAN access (with QRCode for WLAN ssid/password) and OpenSlides access (with QRCode for system URL), printed on a single A4 page for each participant.
**Other:**
- Full text search integration (with Haystack and Whoosh).
- New start script with new command line options (see python manage.py --help)
- Fixed keyerror on user settings view.
- New messages on success or error of many actions like creating or editing objects.
- Changed messages backend, used Django's default now.
- A lot of template fixes and improvements.
- Extended css style options in CKEditor.
- Added feature to config app to return the default value for a key.
- Cleaned up OpenSlides utils views.
- Improved README (now with install instructions and used components).
- Updated all required package versions.
- Used flake8 instead of pep8 for style check, sort all import statements with isort.
- Added Portuguese translation (Thanks to Marco A. G. Pinto).
- Switched to more flexible versions of required third party packages.
- Updated to Django 1.6.x.
- Updated German documentation.
- Change license from GPLv2+ to MIT, see LICENSE file.
## Version 1.4.2 (2013-09-10)
https://github.com/OpenSlides/OpenSlides/milestones/1.4.2
- Used jQuery plugin bsmSelect for better ``<select multiple>`` form elements.
- New config option to disable paragraph numbering in motion pdf. (Default value: disabled.)
- Removed max value limitation in config field 'motion_min_supporters'.
- Removed supporters signature field in motion pdf.
- Fixed missing creation time of motion version. Show now string if identifier is not set (in widgets and motion detail).
- Fixed error when a person is deleted.
- Fixed deleting of assignments with related agenda items.
- Fixed wrong ordering of agenda items after order change.
- Fixed error in portable version: Open browser on localhost when server listens to 0.0.0.0.
- Fixed typo and updated translations.
- Updated CKEditor from 4.1.1 to 4.2. Fixed errors in MS Internet Explorer.
- Updated to Django 1.5.2.
## Version 1.4.1 (2013-07-29)
https://github.com/OpenSlides/OpenSlides/milestones/1.4.1
- Fixed tooltip which shows the end of each agenda item.
- Fixed duration of agenda with closed agenda items.
- Disabled deleting active version of a motion.
- Start browser on custom IP address.
- Fixed wrong URLs to polls in motion detail view.
- Added Czech translation.
## Version 1.4 (2013-07-10)
https://github.com/OpenSlides/OpenSlides/milestones/1.4
**Agenda:**
- New feature: list of speakers for each agenda item which saves begin and end time of each speaker; added new widget and overlay on the dashboard for easy managing and presenting lists of speakers.
- New item type: organisational item (vs. agenda item).
- New duration field for each item (with total time calculation for end time of event).
- Better drag'n'drop sorting of agenda items (with nestedSortable jQuery plugin).
**Motions:**
- Integrated CKEditor to use allowed HTML formatting in motion text/reason. With server-side whitelist filtering of HTML tags (with bleach) and HTML support for reportlab in motion pdf.
- New motion API.
- Support for serveral submitters.
- New workflow concept with two built-in workflows:
1) complex workflow (like in OpenSlides <= v1.3)
2) simple workflow (only 4 states: submitted -> acceptednot decided; no versioning)
- Categories for grouping motions.
- New modifiable identifier.
- New motion version diff view. Improved history table in motion detail view.
- New config variable 'Stop submitting of new motions' (for non-manager users).
- Updated motion status log.
- Updated csv import.
**Participants:**
- New feature: qr-code for system url on participants password pdf.
- Update default groups and permissions.
- New participant field: 'title'.
- Removed participants field 'type'. Use 'group' field instead. Updated csv import.
- Added warning if non-superuser removes his last group containing permission to manage participants.
**Other:**
- New html template based on twitter bootstrap.
- New GUI frontend for the Windows portable version.
- New command to backup sqlite database.
- New mediafile app (files) to upload/download files via frontend.
- Used Tornado web server (instead of Django's default development server).
- Updated win32 portable version to use Tornado.
- Integrated DataTables jQuery plugin for overview tables of motions, elections and participants (for client side sorting/filtering/pagination).
- New overlay API for projector view.
- New config app: Apps have to define config vars only once; config pages and forms are created automatically.
- Moved version page out of the config app.
- Changed version number api for plugins.
- Moved widget with personal info to account app. Inserted info about lists of speakers.
- Updated to Django 1.5.
- Dropped support for python 2.5.
- Updated packaging (setup.py and portable).
- Open all PDFs in a new tab.
- Changed Doctype to HTML5.
- Updated German documentation (especially sections about agenda and motions).
- Several minor fixes and improvements.
## Version 1.3.1 (2013-01-09)
https://github.com/OpenSlides/OpenSlides/milestones/1.3.1
- Fixed unwanted automatical language switching on projector view if more than one browser languages send projector request to OpenSlides (#434)
## Version 1.3 (2012-12-10)
https://github.com/OpenSlides/OpenSlides/milestones/1.3
**Projector:**
- New public dashboard which allows access for all users per default. (#361) (changed from the old, limited projector control page)
- New dashboard widgets:
- welcome widget (shows static welcome title and text)
- participant widget
- group widget
- personal widget (shows my motions and my elections)
- Hide scrollbar in projector view.
- Added cache for AJAX version of the projector view.
- Moved projector control icons into projector live widget. (#403)
- New weight field for custom slides (to order custom slides in widget).
- Fixed drag'n'drop behaviour of widgets into empty dashboard column.
- Fixed permissions for agenda, motion and assignment widgets (set to projector.can_manage_projector).
**Agenda:**
- Fixed slide error if agenda item deleted. (#330)
**Motions:**
- Translation: Changed 'application' to 'motion'.
- Fixed: Manager could not edit supporters. (#336)
- Fixed attribute error for anonymous users in motion view. (#329)
- Set default sorting of motions by number (in widget).
- CSV import allows to import group as submitter. (#419)
- Updated motion code for new user API.
- Rewrote motion views as class based views.
**Elections:**
- User can block himself/herself from candidate list after delete his/her candidature.
- Show blocked candidates in separate list.
- Mark elected candidates in candidate list. (#374)
- Show linebreaks in description. (#392)
- Set default sorting of elections by name (in widget).
- Fixed redirect from a poll which does not exists anymore.
- Changed default permissions of anonymous user to see elections. (#334)
- Updated assignment code for new user API.
**Participants:**
- New user and group API.
- New group option to handle a group as participant (and thus e.g. as submitter of motion).
- CSV import does not delete existing users anymore and append users as new users.
- New user field 'about me'. (#390)
- New config option for sorting users by first or last name (in participant lists, elections and motions). (#303)
- Allowed whitespaces in username, default: ``<firstname lastname>`` (#326)
- New user and group slides. (#176)
- Don't allow to deactivate the administrator or themself.
- Don't allow to delete themself.
- Renamed participant field 'groups' to 'structure level' (German: Gliederungsebene).
- Rewrote participant views as class based views.
- Made OpenSlides user a child model of Django user model.
- Appended tests.
- Fixed error to allow admins to delete anonymous group
**Other:**
- Added French translation (Thanks to Moira).
- Updated setup.py to make an openslides python package.
- Removed frontpage (welcome widget contains it's content) and redirect '/' to dashboard url.
- Added LOCALE_PATHS to openslides_settings to avoid deprecation in Django 1.5.
- Redesigned the DeleteView (append QuestionMixin to send question via the django message API).
- Fixed encoding error in settings.py. (#349)
- Renamed openslides_settings.py to openslides_global_settings.py.
- New default path to database file (XDG_DATA_HOME, e.g. ~/.local/share/openslides/).
- New default path to settings file (XDG_CONFIG_HOME, e.g. ~/.config/openslides/).
- Added special handling to determine location of database and settings file in portable version.
- Don't use similar characters in generated passwords (no 'Il10oO').
- Localised the datetime in PDF header. (#296)
- Used specific session cookie name. (#332)
- Moved code repository from hg to git (incl. some required updates, e.g. version string function).
- Updated German translations.
- Several code optimizations.
- Several minor and medium issues and errors were fixed.
## Version 1.2 (2012-07-25)
https://github.com/OpenSlides/OpenSlides/milestones/1.2
**General:**
- New welcome page with customizable title and text.
- OpenSlides portable win32 binary distribution.
- New start script (start.py) to automatically create the default settings and the database, start the server and the default browser.
- Add plugin system. Allow other django-apps to interact with OpenSlides.
**Projector:**
- New projector dashboard to control all slides on projector.
- New projector live view on projector dashboard.
- Countdown calculation works now on server-side.
- New Overlay messages to show additional information on a second projector layer.
- Add custom slides.
- Add a welcome slide.
- Project application and assignment slides without an agenda item.
- Update the projector once per second (only).
**Agenda:**
- Add new comment field for agenda items.
**Elections (Assignments):**
- New config option to publish voting results for selected winners only.
**Applications:**
- Now, it's possible to deactivate the whole supporter system.
- New import option: set status of all imported applications to 'permit'.
- More log entries for all application actions.
**Participant:**
- Add new comment field for participants.
- Show translated permissions strings in user rols form.
- Admin is redirect to 'change password' page.
- New default user name: "firstname lastname".
**Other:**
- Use Django's class based views.
- Update to Django 1.4. Drop python 2.4 support for this reason.
- Separate the code for the projector.
- Rewrite the vote results table.
- Rewrite the poll API.
- Rewrite the config API. (Now any data which are JSON serializable can be stored.)
- Improved CSV import for application and participants.
- GUI improvements of web interface (e.g. sub navigations, overview tables).
- Several minor and medium issues and errors were fixed.
## Version 1.1 (2011-11-15)
https://github.com/OpenSlides/OpenSlides/milestones/1.1
**Agenda:**
- [Feature] Agenda overview: New item-done-column for all non-manager (#7)
- [Feature] Allow HTML-Tags in agenda item of text (#12)
- [Feature] Split up hidden agenda items in new agenda table section (#13)
**Projector:**
- [Feature] Assignment projector view layout improvements (#9)
- [Bugfix] Zoom problem for sidebar div in beamer view (#5)
- [Bugfix] Blue 'candidate elected line' not visible in projector ajax view (#6)
- [Bugfix] Assignment projector view: Show results for elected candidates only (#11)
- [Bugfix] Missing beamer scaling (#2)
- [Bugfix] Assigment projector view: Removed empty character for no results cell. (#10)
**Applications:**
- [Feature] Import applications (#55)
- [Feature] Support trivial changes to an application (#56)
- [Bugfix] Order submitter and supporter form fields by full name (#53)
- [Bugfix] Application: Show profile instead of submitter username (#15)
- [Bugfix] "Application: Only check enough supports in status ""pub""" (#16)
**Elections:**
- [Feature] New button to show agenda item of selected application/assignment (#54)
- [Feature] Open add-user-url in new tab. (#32)
**Applications/Elections:**
- [Feature] Show voting results in percent (#48)
**Participants:**
- [Feature] Filter displayed permissions in group editor (#59)
- [Feature] Generate password after user creation automatically (#58)
- [Bugfix] Encoding error (#1)
- [Bugfix] List of participants (pdf) link not visible for users with see-particiants-permissions (#3)
- [Bugfix] Use user.profile.get_type_display() instead of user.profile.type (#4)
**PDF:**
- [Feature] Mark elected candidates in PDF (#31)
- [Feature] New config option to set title and preamble text for application and assignment pdf (#33)
- [Feature] New config option to set number of ballots in PDF (#26)
- [Bugfix] Assignment ballot pdf: Wrong line break in group name with brackets (#8)
- [Bugfix] Print available candidates in assignment pdf (#14)
- [Bugfix] Show "undocumented" for result "-2" in application and assignment pdf (#17)
**Other:**
- [Feature] Rights for anonymous (#45)
- [Feature] Show counter for limited speaking time (#52)
- [Feature] Reorderd config tab subpages (#61)
- [Localize] i18n German: Use gender-specific strings (#51)
- [Bugfix] ``<button>`` inside ``<a>`` tag not working in IE (#57)
- [Bugfix] Change default sort for tables of applications, assignments, participants (#27)
## Version 1.0 (2011-09-12)
https://github.com/OpenSlides/OpenSlides/tree/1.0/

View File

@ -1,980 +0,0 @@
=========================
CHANGELOG of OpenSlides
=========================
https://openslides.com
Version 3.1 (2019-12-13)
========================
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/3.0>`_
General:
- Improved loading time of OpenSlides [#5061, 5087, #5110, #5146 - Breaks IE11].
- Improved Websocket reconnection - works now more reliable [#5060].
- Improved performance by replacing deprecated encode and decode library with faster manual approaches [#5085, #5092].
- Improved mobile views for small devices [#5106].
- Improved virtual scrolling behavior of tables: remember last scroll position [#5156].
- New SingleSignOn login method via SAML [#5000].
- New inline editing for start page, legal notice and privacy policy [#5086].
- Reordered settings in subpages for better overview [#4878, #5083, #5096].
- Added html meta noindex tag to prevent search engines finding instances of OpenSlides [#5061].
- Added server log entries for usage of notify feature [#5066].
- Added server-side HTML validation for personal notes (motions) and about me field (users) [#5121].
- Fixed an issue where projector button in lists was always not indicating the projected element [#5055].
- Fixed issues where search-filter, property-filter and property-sort in list views does not work correctly [#5065].
- Fixed an issues where list view entries using virtual scrolling become invisible [#5072].
- Fixed an error where loading spinner would not disappear while offline mode [#5151].
- Various cleanups and improvements to usability, performance and translation.
Agenda:
- New config option to show motion submitters as subtitle in agenda list [#5002, #5094].
- New view to sort sepakers of a list - preventing unwanted changes esp. using mobile devices [#5069].
- New button to readd the last speaker to the top of the list [#5069, #5067].
- New agenda list filter 'item type' (topic, motion, motion block, election) [#5084].
- Changed window title for current list of speakers [#5037].
- Added motion title in agenda list [#5063].
Motions:
- New option to export personal notes [#5075].
- New amendment filter for motion list [#5056, #5157].
- New possibility to change state and recommendation in motion list using quick edit [#5071].
- Added multi select actions to amendment list [#5041].
- Added search value selector in multi select action dialogs [#5058].
- Added support for nested lists with line numbers in PDF export [#5138].
- Improved scaling of motion tile view [#5038].
- Improved performance for large motions with dozens of amendments by implementing manual change detection in motion detail [#5074, #5139].
- Improved display of long names for states and recommendations in drop down menu in motion detail view [#5079].
- Improved amendment slide by showing only changed line(s)s without surrounding paragraph [#5144].
- Fixed line number errors during creation of amendments [#5023].
- Fixed an issue that ol/ul lists are not printed in amendment PDF [#5051].
- Fixed the amendment option "Show entire motion text" [#5052].
- Fixed a rare bug in final version where change recommendations or amendments would have been hidden [#5070].
- Fixed PDF export in final version: use modified final version if available [#5139].
- Fixes a bug where the event name was printed twice in the PDF header [#5155].
Elections:
- Fixed errors by entering votes and sorting candidates [#5129, #5125].
- Fixed a permission issue that prevented nominating another participants for elections [#5154].
Users:
- Fixed wrong permission check for set password [#5128].
Mediafiles:
- Fixed mediafile upload path [#4710].
- Fixed double slash in mediafile URL [#5031].
- Original filename must now be unique for files [#5123].
Projector:
- New projector edit dialog with preview [#5043].
- Added support for custom aspect ratios in projector edit dialog; database migration required [#5141].
- Added missing autoupdates for changed projection defaults [#5045].
- Added scaleable tile for projector list [#5043].
- Added a lock icon on the list of speaker slide if list has been closed [#5154].
- Improved autoupdates for projectors by using changeIDs [#5064].
- Improved performance by preventing high CPU usage on Firefox in projector detail view [#5022].
- Changed config option to show nice horizontal meta box on motion slide [#5088].
- Changed config option "Event date" back to string format [#5042].
- Saved countdown settings [#5053].
Breaking Changes:
- Due to faster model handling (using the 'Proxy' function) Internet Explorer 11 cannot be supported anymore!
Version 3.0 (2019-09-13)
========================
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/3.0>`_
General (Client):
- OpenSlides client completely rewritten, based on Angular 8 and Material Design.
- OpenSlides is now a Progressive Web App (PWA).
- New browser caching via IndexedDB (one cache store for all browser tabs).
- New list views optimized with virtual scrolling (improved performance for long lists).
- New global quick search using by shortcut 'Alt+Shift+F'.
- New built-in design themes for customizing user interface.
- New update notification if OpenSlides static files are updated.
- New config option for pdf page size (DIN A4 or A5).
- Added TinyMCE 5 editor (switched from CKEditor caused by changed license).
- Switched from yarn/gulp to npm.
- Improved pdf gerneration with progress bar and cancel option.
- Translations available for EN, DE, RU and CS.
General (Server):
- New websocket protocol for server client communication using JSON schema.
- New change-id system to send only updated elements to client.
- New global history mode (useable for admin group only).
- Updated to Channels 2.
- Dropped support for Python 3.5.
- Dropped support for Geiss.
- Complete rework of startup and caching system. Dropped restricted data cache.
- Changed URL schema.
- Changed personal settings.py.
- Changed format for elements send via autoupdate.
- Changed projector concept.
- Compressed autoupdates before sending to clients (reduced traffic).
- Fixed autoupdate system for related objects.
- Fixed logo configuration if logo file is deleted.
- Added several bulk views for motions and users (one request for updating multiple selected elements).
- Added docs for using OpenSlides in 'big mode' with Gunicorn and Uvicorn.
- Added docs for configure OpenSlides in settingy.py.
- Dropped chat functionality.
- Server performance improvements.
Agenda:
- Agenda items are now optional (for motions, elections and mediafiles). New config to set default behavior.
- New drag&drop view to sort agenda items.
- New config option: only present participants can be added to list of speakers.
- New config option to hide number of speakers on projector.
Motions:
- New call list for custom sort of motions.
- New tile layout view with all categories (each category a tile).
- New statute motions with managing statute paragraphs.
- New permission to manage metadata (state, recommendation, submitters and supporters, category,
motion block and polls).
- New permission to create amendments.
- New permission to see motions in internal states.
- New access restrictions definable for each motion state in workflow.
- New 'internal' option for motion blocks.
- New sorting view for categories to create subcategories.
- New custom comment fields for all motions (read/write access can be managed via permission groups).
- New motion history (each action is stored in global OpenSlides history which can be restored any time,
replaced old motion version and log features).
- New XLSX export (docx support is dropped).
- New navigation for next/previous motion in detail view (shortcut: 'Alt+Shift+Left/Right').
- New multi select actions.
- New timestampes for motions (for sorting by creation date and last modified).
- New config option to set reason as required field.
- New config option to change multiple paragraphs with an amendment.
- New config option to hide motion text on projector.
- New config option to show sequential number.
- New config option to show all motions which are referred to a special motion.
- New config option to show submitters and recommendation in table of contents of PDF.
- New config options to control identifier generation - number of digits and blanks (moved from settings.py).
- Improved PDF export (optional with toc, page numbers, date, comments and meta information)
- Improved motion numbering in (sub)categories: Motions of subcategories are also numbered, and parents of
amendments needs to be in the numbered category or any subcategory.
- Improved projection layout of motion blocks.
- Changed default workflows: Allowed submitters to set state of new motions in complex and customized workflow.
No migration provided.
- Change CSV import to add tags.
User:
- New admin group which grants all permissions. Users of existing group 'Admin' or 'Staff' are move to the
new group during migration.
- New gender field.
- New password forget/reset function via email.
- New permission to change own password.
- New config option for sender name and reply email address (From address is defined in settings.py).
Mediafiles:
- New support for (sub)folders and permission groups.
Projector:
- New views to list, manage and control created OpenSlides projectors.
- New projector queue (add slide to queue), all projected slides are logged.
- New chyron for current speaker.
- New color settings for each projector.
Version 2.3 (2018-09-20)
========================
`Release notes <https://github.com/OpenSlides/OpenSlides/wiki/OpenSlides-2.3>`_ ·
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/2.3>`_
Agenda:
- New item type 'hidden'. New visibilty filter in agenda [#3790].
Motions:
- New feature to scroll the projector to a specific line [#3748].
- New possibility to sort submitters [#3647].
- New representation of amendments (paragraph based creation, new diff
and list views for amendments) [#3637].
- New feature to customize workflows and states [#3772, #3785].
- New table of contents with page numbers and categories in PDF [#3766].
- New teporal field "modified final version" where the final version can
be edited [#3781].
- New config options to show logos on the right side in PDF [#3768].
- New config to show amendments also in motions table [#3792].
- Support to change decimal places for polls with a plugin [#3803].
Elections:
- Support to change decimal places for elections with a plugin [#3803]
Core:
- Updated Django to 2.1 [#3777, #3786].
- Support for Python 3.7 [#3786].
- Python 3.4 is not supported anymore [#3777].
- Updated pdfMake to 0.1.37 [#3766].
- Changed behavior of collectstatic management command [#3804].
Version 2.2 (2018-06-06)
========================
`Release notes <https://github.com/OpenSlides/OpenSlides/wiki/OpenSlides-2.2>`_ ·
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/2.2>`_
Agenda:
- New permission for managing lists of speakers [#3366].
- New DOCX export of agenda [#3569].
- New collapsable agenda overview [#3567].
- New feature: mark speakers (e.g. as submitter) [#3570].
- New config option to enable numbering of items [#3697].
- New config option to hide internal items when projecting subitems [#3701].
- Hide closed agenda items in the item slide [#3567].
- Fixed wrong sorting of last speakers [#3193].
- Fixed issue when sorting a new inserted speaker [#3210].
- Fixed multiple request on creation of agenda related items [#3341].
- Autoupdates for all children if the item type has changed [#3659].
Motions:
- New export dialog for managers only [#3185].
- New personal note field for each motions [#3190, #3267, #3404].
- New navigation between single motions [#3459].
- New possibility to create change recommendations for motion titles [#3626].
- New support for export motions in a ZIP archive [#3189, #3251].
- New PDF export for personal note and comments [#3239].
- New config option for customize sorting of category list in pdf/docx export [#3329].
- New config optoin for pagenumber alignment in PDF [#3327].
- New config options to hide reason, recommendation and meta data box on projector [#3432, #3692].
- New inline editing for motion reason [#3361].
- New multiselect filter for motion comments [#3372].
- New support for pinning personal notes to the window [#3360].
- New warning message if an edit dialog was already opened by another client [#3212].
- New change recommendation type "other" [#3495].
- Fixed issue when creating/deleting motion comment fields in the settings [#3187].
- Fixed empty motion comment field in motion update form [#3194].
- Fixed error on category sort [#3318].
- Bugfix: Changing motion line length did not invalidate cache [#3202].
- Bugfix: Added more distance in motion PDF for DEL-tags in new lines [#3211].
- Bugfix: Creating colliding change recommendation is now prevented on server side [#3304].
- Bugfix: Several bugfixes regarding splitting list items in change recommendations [#3288].
- Bugfix: Several bugfixes regarding diff version [#3407, #3408, #3410,
#3440, #3450, #3465, #3537, #3546, #3548, #3644, #3656].
- Improved the multiselect state filter [#3459].
- Save pagination state to session storage [#3569].
- Allow to delete own motions [#3516].
- Reference to motions by id in state and recommendation special field [#3498].
- Log which comment was updated [#3569].
- Split up 'can_see_and_manage_comments' permission in two seperate ones [#3565].
- Combined all boolean filters into one dropdown menu and added a filter for amendments [#3501].
- Show motion identifier in (current) list of speakers [#3442]
- Show the number of next speakers in motion list view [#3470].
- Added (shortened) motion title to motion block slide [#3700].
- Clear identifier on state reset [#3356].
- Reworked DOCX export parser and added comments to DOCX [#3258].
- Removed server side image to base64 transformation and added local transformation [#3181].
- Added karma:watch command [#3466].
Elections:
- New pagination for list view [#3393].
Users:
- New fast mass import for users [#3290].
- New default user group 'admin' [#3621].
- New feature to send invitation emails with OpenSlides login data [#3503, #3525].
- New view to toggle presence by entering participant number (can be used with barcode scanner) [#3496].
- New support for password validation using Django or custom validators
e. g. for minimum password length [#3200].
- Hide password in change password view [#3417].
- Users without the permission 'can see users' can now see agenda item speakers,
motion submitters and supporters, assignment candidates, mediafile uploader
and chat message users if they have the respective permissions [#3191, #3233].
- Fixed compare of duplicated users while CSV user import [#3201].
- Added settings option to enable updating the last_login field in the database. The
default is now disabled [#3400].
- Removed OPTIONS request. All permissions are now provided on startup [#3306].
Mediafiles:
- New form for uploading multiple files [#3650].
- New custom CKEditor plugin for browsing mediafiles [#3337].
- Project images always in fullscreen [#3355].
- Protect mediafiles for forbidden access [#3384].
- Fixed reloading of PDF on page change [#3274].
Core:
- New settings to upload custom fonts (for projector and pdf) [#3568].
- New custom translations to use custom wordings [#3383].
- New support for choosing image files as logos for projector, PDF and
web interface header [#3184, #3207, #3208, #3310].
- New notify system [#3212].
- New config option for standard font size in PDF [#3332].
- New config option for disabling header and footer in the projector [#3357].
- New dynamic webpage title [#3404].
- New 'go to top'-link [#3404].
- New custom format cleanup plugin for CKEditor [#3576].
- Reset scroll level for each new projection [#3686].
- Scroll to top on every state change [#3689].
- Added pagination on top of lists [#3698].
- Improved performance for PDF generation significantly (by upgrading to pdfmake 0.1.30) [#3278, #3285].
- Enhanced performance esp. for server restart and first connection of all
clients by refactoring autoupdate, Collection and AccessPermission [#3223, #3539].
- Improved reconnect handling if the server was flushed [#3297].
- No reload on logoff. OpenSlides is now a full single page application [#3172].
- Highlight list entries in a light blue, if a related object is projected
(e. g. a list of speakers of a motion) [#3301].
- Select the projector resolution with a slider and an aspect ratio [#3311].
- Delay the 'could not load projector' error 3 seconds to not irritate users
with a slow internet connection [#3323].
- Added default sorting for agenda, motions, elections, mediafiles and users [#3334, 3348].
- Added caching for the index views [#3419, #3424].
- Added projector prioritization [#3425].
- Added --debug-email flag to print all emails to stdout [#3530].
- Added --no-template-caching flag to disable template caching for
easier development [#3566].
- Updated CKEditor to 4.7 [#3375].
- Reduced ckeditor toolbar for inline editing [#3368].
- New api route to project items with just one request needed [#3713].
- Use native twisted mode for daphne [#3487].
- Saved language selection to session storage [#3543].
- Set default of projector resolution to 1220x915 [#2549].
- Preparations for the SAML plugin; Fixed caching of main views [#3535].
- Removed unnecessary OPTIONS request in config [#3541].
- Switched from npm to Yarn [#3188].
- Improvements for plugin integration [#3330].
- Cleanups for the collection and autoupdate system [#3390]
- Bugfixes for PDF creation [#3227, #3251, #3279, #3286, #3346, #3347, #3342].
- Fixed error when clearing empty chat [#3199].
- Fixed autoupdate bug for a user without user.can_see_name permission [#3233].
- Fixed bug the elements are projected and the deleted [#3336].
- Several bugfixes and minor improvements.
*[#xxxx] = Pull request number to get more details on https://github.com/OpenSlides/OpenSlides/pulls*
Version 2.1.1 (2017-04-05)
==========================
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/2.1.1>`_
Agenda:
- Fixed issue #3173 that the agenda item text cannot be changed.
Other:
- Set required version for optional Geiss support to <1.0.0.
Version 2.1 (2017-03-29)
========================
`Release notes <https://github.com/OpenSlides/OpenSlides/wiki/OpenSlides-2.1>`_ ·
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/2.1>`_
Agenda:
- Added button to remove all speakers from a list of speakers.
- Added option to create or edit agenda items as subitems of others.
- Fixed security issue: Comments were shown for unprivileged users.
- Added option to choose whether to show the current list of speakers slide
as a slide or an overlay.
- Manage speakers on the current list of speakers view.
- List of speakers for hidden items is always visible.
Core:
- Added support for multiple projectors.
- Added control for the resolution of the projectors.
- Added smooth projector scroll.
- Set the projector language in the settings.
- Added migration path from OpenSlides 2.0.
- Added support for big assemblies with lots of users.
- Django 1.10 is now supported. Dropped support for Django 1.8 and 1.9.
- Used Django Channels instead of Tornado. Refactoring of the autoupdate
process. Added retry with timeout in case of ChannelFull exception.
- Made a lot of autoupdate improvements for projector and site.
- Added new caching system with support for Redis.
- Support https as websocket protocol (wss).
- Accelerated startup process (send all data to the client after login).
- Add the command getgeiss to download the latest version of Geiss.
- Add a version of has_perm that can work with cached users.
- Removed our AnonymousUser. Make sure not to use user.has_perm() anymore.
- Added function utils.auth.anonymous_is_enabled which returns true, if it is.
- Changed has_perm to support an user id or None (for anyonmous) as first argument.
- Cache the group with there permissions.
- Added watching permissions in client and change the view immediately on changes.
- Used session cookies and store filter settings in session storage.
- Removed our db-session backend and added possibility to use any django session backend.
- Added template hook system for plugins.
- Used Roboto font in all templates.
- Added HTML support for messages on the projector.
- Moved custom slides to own app "topics". Renamed it to "Topic".
- Added button to clear the chatbox.
- Better dialog handling. Show dialog just in forground without changing the state url.
Added new dialog for profile, change password, tag and category update view.
- Switched editor back from TinyMCE to CKEditor which provides a
better copy/paste support from MS Word.
- Validate HTML strings from CKEditor against XSS attacks.
- Use a separate dialog with CKEditor for editing projector messages.
- Use CKEditor in settings for text markup.
- Used pdfMake for clientside generation of PDFs.
Run pdf creation in background (in a web worker thread).
- Introduced new table design for list views with serveral filters and
CSV export.
- New CSV import layout.
- Replaced angular-csv-import by Papa Parse for CSV parsing.
- Added UTF-8 byte order mark for every CSV export.
- Removed config cache to support multiple threads or processes.
- Added success/error symbol to config to show if saving was successful.
- Fixed bug, that the last change of a config value was not send via autoupdate.
- Moved full-text search to client-side (removed the server-side search engine Whoosh).
- Made a lot of code clean up, improvements and bug fixes in client and
backend.
Motions:
- Added adjustable line numbering mode (outside, inside, none) for each
motion text.
- Allowed to add change recommendations for special motion text lines
(with diff mode).
- Added projection support for change recommendations.
- Added button to sort and number all motions in a category.
- Added recommendations for motions.
- Added options to calculate percentages on different bases.
- Added calculation for required majority.
- Added blocks for motions which can be used in agenda. Set states for
multiple motions of a motion block by following the recommendation for
each motion.
- Used global config variable for preamble.
- Added configurable fields for comments.
- Added new origin field.
- Reimplemented amendments.
- New PDF layout.
- Added DOCX export with docxtemplater.
- Changed label of former state "commited a bill" to "refered to committee".
- Number of ballots printed can now be set in config.
- Add new personal settings to remove all whitespaces from motion identifier.
- Add new personal settings to allow amendments of amendments.
- Added inline editing for comments.
Elections:
- Added options to calculate percentages on different bases.
- Added calculation for required majority.
- Candidates are now sortable.
- Removed unused assignment config to publish winner election results only.
- Number of ballots printed can now be set in config.
- Added inline edit field for a specific hint on ballot papers.
Users:
- Added new matrix-interface for managing groups and their permissions.
- Added autoupdate on permission change (permission added).
- Improved password reset view for administrators.
- Changed field for initial password to an unchangeable field.
- Added new field for participant number.
- Added new field 'is_committee' and new default group 'Committees'.
- Improved users CSV import (use group names instead of id).
- Allowed to import/export initial user password.
- Added more multiselect actions.
- Added QR code in users access pdf.
Mediafiles:
- Allowed to project uploaded images (png, jpg, gif) and video files
(e. g. mp4, wmv, flv, quicktime, ogg).
- Allowed to hide uploaded files in overview list for non authorized users.
- Enabled removing of files from filesystem on model instance delete.
Other:
- Added Russian translation (Thanks to Andreas Engler).
- Added command to create example data.
Version 2.0 (2016-04-18)
========================
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/2.0>`_
*OpenSlides 2.0 is essentially not compatible to OpenSlides 1.7. E. g.
customized templates, databases and plugins can not be reused without
adaption.*
Agenda:
- Updated the tests and changed internal parts of method of the agenda model.
- Changed API of related objects. All assignments, motions and custom slides
are now agenda items and can be hidden.
- Removed django-mptt.
- Added attachments to custom sldies.
- Improved CSV import.
Assignments:
- Renamed app from assignment to assignments.
- Removed possibility to block candidates.
- Massive refactoring and cleanup of the app.
Motions:
- Renamed app from motion to motions.
- Massive refactoring and cleanup of the app.
Mediafiles:
- Renamed app from mediafile to mediafiles.
- Used improved pdf presentation with angular-pdf.
- Massive refactoring and cleanup of the app.
Users:
- Massive refactoring of the participant app. Now called 'users'.
- Used new anonymous user object instead of an authentification backend. Used
special authentication class for REST requests.
- Used authentication frontend via AngularJS.
- Improved CSV import.
Other:
- New OpenSlides logo.
- New design for web interface.
- Added multiple countdown support.
- Added colored countdown for the last n seconds (configurable).
- Switched editor from CKEditor to TinyMCE.
- Changed supported Python version to >= 3.4.
- Used Django 1.8 as lowest requirement.
- Django 1.9 is supported
- Added Django's application configuration. Refactored loading of signals
and projector elements/slides.
- Setup migrations.
- Added API using Django REST Framework 3.x. Added several views and mixins
for generic Django REST Framework views in OpenSlides apps.
- Removed most of the Django views and templates.
- Removed Django error pages.
- Added page for legal notice.
- Refactored projector API using metaclasses now.
- Renamed SignalConnectMetaClass classmethod get_all_objects to get_all
(private API).
- Refactored config API and moved it into the core app.
- Removed old style personal info page, main menu entries and widget API.
- Used AngularJS with additional libraries for single page frontend.
- Removed use of 'django.views.i18n.javascript_catalog'. Used angular-gettext
now.
- Updated to Bootstrap 3.
- Used SockJS for automatic update of AngularJS driven single page frontend.
- Refactored plugin API.
- Refactored start script and management commands. Changed command line
option and path for local installation.
- Refactored tests.
- Used Bower and gulp to manage third party JavaScript and Cascading Style
Sheets libraries.
- Used setup.cfg for development tools.
- Removed code for documentation and for Windows portable version with GUI.
Used new repositories for this. Cleaned up main repository.
- Updated all dependencies.
Translations:
- Updated DE, FR, CS and PT translations.
- Added ES translations.
Version 1.7 (2015-02-16)
========================
https://github.com/OpenSlides/OpenSlides/milestones/1.7
Core:
- New feature to tag motions, agenda and assignments.
- Fixed search index problem to index contents of many-to-many tables
(e. g. tags of a motion).
- Fixed AttributeError in chatbox on_open method.
Motions:
- New Feature to create amendments, which are related to a parent motion.
- Added possibility to hide motions from non staff users in some states.
Assignments:
- Fixed permissions to alter assignment polls.
Other:
- Cleaned up utils.views to increase performance when fetching single objects
from the database for a view (#1378).
- Fixed bug on projector which was not updated when an object was deleted.
- Fixed bug and show special characters in PDF like ampersand (#1415).
- Updated pdf.js to 1.0.907.
- Improve the usage of bsmselect jquery plugin.
- Updated translations.
Version 1.6.1 (2014-12-08)
==========================
https://github.com/OpenSlides/OpenSlides/milestones/1.6.1
Agenda:
- Fixed error in item numbers.
Motions:
- Show supporters on motion slide if available.
- Fixed motion detail view template. Added block to enable extra content via
plugins.
Assignments:
- Fixed PDF build error when an election has more than 20 posts or candidates.
Participants:
- Fixed participant csv import with group ids:
* Allowed to add multiple groups in csv group id field, e. g. "3,4".
* Fixed bug that group ids greater than 9 can not be imported.
* Updated error message if group id does not exists.
Other:
- Fixed CKEditor stuff (added insertpre plugin and removed unused code).
- Updated French, German and Czech translation.
Version 1.6 (2014-06-02)
========================
https://github.com/OpenSlides/OpenSlides/milestones/1.6
Dashboard:
- Added shortcuts for the countdown.
- Enabled copy and paste in widgets.
Agenda:
- New projector view with the current list of speakers.
- Added CSV import of agenda items.
- Added automatic numbering of agenda items.
- Fixed organizational item structuring.
Motions:
- New slide for vote results.
- Created new categories during CSV import.
Assignments/Elections:
- Coupled assignment candidates with list of speakers.
- Created a poll description field for each assignment poll.
- New slide for election results.
Participants:
- Disabled dashboard widgets by default.
- Added form field for multiple creation of new participants.
Files:
- Enabled update and delete view for uploader refering to his own files.
Other:
- Added global chatbox for managers.
- New config option to set the 100 % base for polls (motions/elections).
- Changed api for plugins. Used entry points to detect them automaticly. Load
them automaticly from plugin directory of Windows portable version.
- Added possibility to use custom templates and static files in user data path
directory.
- Changed widget api. Used new metaclass.
- Changed api for main menu entries. Used new metaclass.
- Inserted api for the personal info widget. Used new metaclass.
- Renamed config api classes. Changed permission system for config pages.
- Regrouped config collections and pages.
- Renamed some classes of the poll api.
- Renamed method and attribute of openslides.utils.views.PermissionMixin.
- Added api for absolute urls in models.
- Inserted command line option to translate config strings during database setup.
- Enhanced http error pages.
- Improved responsive design for templates.
- Fixed headings on custom slides without text.
- Moved dashboard and select widgets view from projector to core app.
- Renamed and cleaned up static direcories.
- Used jsonfield as required package. Removed jsonfield code.
- Added new package backports.ssl_match_hostname for portable build script.
- Used new app "django-ckeditor-updated" to render WYSIWYG html editors.
Removed CKEditor from sources.
- Only reload the webserver in debug-mode.
Version 1.5.1 (2014-03-31)
==========================
https://github.com/OpenSlides/OpenSlides/milestones/1.5.1
Projector:
- Fixed path and config help text for logo on the projector.
Agenda:
- Fixed permission error in the list of speakers widget.
- Fixed Item instance method is_active_slide().
Motion:
- Fixed sorting of motions concerning the identifier. Used natsort and
DataTables Natural Sort Plugin.
Participant:
- Added permission to see participants to the manager group.
- Fixed user status view for use without Javascript.
Files:
- Fixed error when an uploaded file was removed from filesystem.
Other:
- Set minimum Python version to 2.6.9. Fixed setup file for use with Python 2.6.
- Used unicode font for circle in ballot pdf. Removed Pillow dependency package.
- Fixed http status code when requesting a non-existing static page using
Tornado web server.
- Fixed error in main script when using other database engine.
- Fixed error on motion PDF with nested lists.
Version 1.5 (2013-11-25)
========================
https://github.com/OpenSlides/OpenSlides/milestones/1.5
Projector:
- New feature: Show PDF presentations on projector (with included pdf.js).
- Improved projector update process via new websocket API (using sockjs and tornado).
- New projector template with twitter bootstrap.
- Improved projector zoom and scroll behaviour.
Agenda:
- New config option: couple countdown with list of speakers.
- Used HTML editor (CKEditor) for agenda item text field.
- Added additional input format for agenda item duration field.
Motions:
- Enabled attachments for motions.
- Refactored warnings on CSV import view.
Elections:
- Refactored assignment app to use class based views instead of functions.
Polls:
- Added percent base to votes cast values.
Participants:
- Updated access data PDF: WLAN access (with QRCode for WLAN ssid/password)
and OpenSlides access (with QRCode for system URL), printed on a single A4 page
for each participant.
Other:
- Full text search integration (with Haystack and Whoosh).
- New start script with new command line options (see python manage.py --help)
- Fixed keyerror on user settings view.
- New messages on success or error of many actions like creating or editing objects.
- Changed messages backend, used Django's default now.
- A lot of template fixes and improvements.
- Extended css style options in CKEditor.
- Added feature to config app to return the default value for a key.
- Cleaned up OpenSlides utils views.
- Improved README (now with install instructions and used components).
- Updated all required package versions.
- Used flake8 instead of pep8 for style check, sort all import statements with isort.
- Added Portuguese translation (Thanks to Marco A. G. Pinto).
- Switched to more flexible versions of required third party packages.
- Updated to Django 1.6.x.
- Updated German documentation.
- Change license from GPLv2+ to MIT, see LICENSE file.
Version 1.4.2 (2013-09-10)
==========================
https://github.com/OpenSlides/OpenSlides/milestones/1.4.2
- Used jQuery plugin bsmSelect for better <select multiple> form elements.
- New config option to disable paragraph numbering in motion pdf. (Default value: disabled.)
- Removed max value limitation in config field 'motion_min_supporters'.
- Removed supporters signature field in motion pdf.
- Fixed missing creation time of motion version. Show now string if identifier is not set (in widgets and motion detail).
- Fixed error when a person is deleted.
- Fixed deleting of assignments with related agenda items.
- Fixed wrong ordering of agenda items after order change.
- Fixed error in portable version: Open browser on localhost when server listens to 0.0.0.0.
- Fixed typo and updated translations.
- Updated CKEditor from 4.1.1 to 4.2. Fixed errors in MS Internet Explorer.
- Updated to Django 1.5.2.
Version 1.4.1 (2013-07-29)
==========================
https://github.com/OpenSlides/OpenSlides/milestones/1.4.1
- Fixed tooltip which shows the end of each agenda item.
- Fixed duration of agenda with closed agenda items.
- Disabled deleting active version of a motion.
- Start browser on custom IP address.
- Fixed wrong URLs to polls in motion detail view.
- Added Czech translation.
Version 1.4 (2013-07-10)
========================
https://github.com/OpenSlides/OpenSlides/milestones/1.4
Agenda:
- New feature: list of speakers for each agenda item which saves begin and end
time of each speaker; added new widget and overlay on the dashboard for easy
managing and presenting lists of speakers.
- New item type: organisational item (vs. agenda item).
- New duration field for each item (with total time calculation for end time of event).
- Better drag'n'drop sorting of agenda items (with nestedSortable jQuery plugin).
Motions:
- Integrated CKEditor to use allowed HTML formatting in motion text/reason.
With server-side whitelist filtering of HTML tags (with bleach) and HTML support
for reportlab in motion pdf.
- New motion API.
- Support for serveral submitters.
- New workflow concept with two built-in workflows:
a) complex workflow (like in OpenSlides <= v1.3)
b) simple workflow (only 4 states: submitted -> accepted|rejected|not decided; no versioning)
- Categories for grouping motions.
- New modifiable identifier.
- New motion version diff view. Improved history table in motion detail view.
- New config variable 'Stop submitting of new motions' (for non-manager users).
- Updated motion status log.
- Updated csv import.
Participants:
- New feature: qr-code for system url on participants password pdf.
- Update default groups and permissions.
- New participant field: 'title'.
- Removed participants field 'type'. Use 'group' field instead. Updated csv import.
- Added warning if non-superuser removes his last group containing permission to
manage participants.
Other:
- New html template based on twitter bootstrap.
- New GUI frontend for the Windows portable version.
- New command to backup sqlite database.
- New mediafile app (files) to upload/download files via frontend.
- Used Tornado web server (instead of Django's default development server).
- Updated win32 portable version to use Tornado.
- Integrated DataTables jQuery plugin for overview tables of motions, elections
and participants (for client side sorting/filtering/pagination).
- New overlay API for projector view.
- New config app: Apps have to define config vars only once; config pages and
forms are created automatically.
- Moved version page out of the config app.
- Changed version number api for plugins.
- Moved widget with personal info to account app. Inserted info about lists of speakers.
- Updated to Django 1.5.
- Dropped support for python 2.5.
- Updated packaging (setup.py and portable).
- Open all PDFs in a new tab.
- Changed Doctype to HTML5.
- Updated German documentation (especially sections about agenda and motions).
- Several minor fixes and improvements.
Version 1.3.1 (2013-01-09)
==========================
https://github.com/OpenSlides/OpenSlides/milestones/1.3.1
- Fixed unwanted automatical language switching on projector view if more than
one browser languages send projector request to OpenSlides (#434)
Version 1.3 (2012-12-10)
========================
https://github.com/OpenSlides/OpenSlides/milestones/1.3
Projector:
- New public dashboard which allows access for all users per default. (#361)
(changed from the old, limited projector control page)
- New dashboard widgets:
* welcome widget (shows static welcome title and text)
* participant widget
* group widget
* personal widget (shows my motions and my elections)
- Hide scrollbar in projector view.
- Added cache for AJAX version of the projector view.
- Moved projector control icons into projector live widget. (#403)
- New weight field for custom slides (to order custom slides in widget).
- Fixed drag'n'drop behaviour of widgets into empty dashboard column.
- Fixed permissions for agenda, motion and assignment widgets (set to projector.can_manage_projector).
Agenda:
- Fixed slide error if agenda item deleted. (#330)
Motions:
- Translation: Changed 'application' to 'motion'.
- Fixed: Manager could not edit supporters. (#336)
- Fixed attribute error for anonymous users in motion view. (#329)
- Set default sorting of motions by number (in widget).
- CSV import allows to import group as submitter. (#419)
- Updated motion code for new user API.
- Rewrote motion views as class based views.
Elections:
- User can block himself/herself from candidate list after delete his/her candidature.
- Show blocked candidates in separate list.
- Mark elected candidates in candidate list. (#374)
- Show linebreaks in description. (#392)
- Set default sorting of elections by name (in widget).
- Fixed redirect from a poll which does not exists anymore.
- Changed default permissions of anonymous user to see elections. (#334)
- Updated assignment code for new user API.
Participants:
- New user and group API.
- New group option to handle a group as participant (and thus e.g. as submitter of motion).
- CSV import does not delete existing users anymore and append users as new users.
- New user field 'about me'. (#390)
- New config option for sorting users by first or last name (in participant lists, elections and motions). (#303)
- Allowed whitespaces in username, default: <firstname lastname>. (#326)
- New user and group slides. (#176)
- Don't allow to deactivate the administrator or themself.
- Don't allow to delete themself.
- Renamed participant field 'groups' to 'structure level' (German: Gliederungsebene).
- Rewrote participant views as class based views.
- Made OpenSlides user a child model of Django user model.
- Appended tests.
- Fixed error to allow admins to delete anonymous group
Other:
- Added French translation (Thanks to Moira).
- Updated setup.py to make an openslides python package.
- Removed frontpage (welcome widget contains it's content) and redirect '/' to dashboard url.
- Added LOCALE_PATHS to openslides_settings to avoid deprecation in Django 1.5.
- Redesigned the DeleteView (append QuestionMixin to send question via the django message API).
- Fixed encoding error in settings.py. (#349)
- Renamed openslides_settings.py to openslides_global_settings.py.
- New default path to database file (XDG_DATA_HOME, e.g. ~/.local/share/openslides/).
- New default path to settings file (XDG_CONFIG_HOME, e.g. ~/.config/openslides/).
- Added special handling to determine location of database and settings file in portable version.
- Don't use similar characters in generated passwords (no 'Il10oO').
- Localised the datetime in PDF header. (#296)
- Used specific session cookie name. (#332)
- Moved code repository from hg to git (incl. some required updates, e.g. version string function).
- Updated German translations.
- Several code optimizations.
- Several minor and medium issues and errors were fixed.
Version 1.2 (2012-07-25)
========================
https://github.com/OpenSlides/OpenSlides/milestones/1.2
General:
- New welcome page with customizable title and text.
- OpenSlides portable win32 binary distribution.
- New start script (start.py) to automatically create the default settings and the
database, start the server and the default browser.
- Add plugin system. Allow other django-apps to interact with OpenSlides.
Projector:
- New projector dashboard to control all slides on projector.
- New projector live view on projector dashboard.
- Countdown calculation works now on server-side.
- New Overlay messages to show additional information on a second
projector layer.
- Add custom slides.
- Add a welcome slide.
- Project application and assignment slides without an agenda item.
- Update the projector once per second (only).
Agenda:
- Add new comment field for agenda items.
Elections (Assignments):
- New config option to publish voting results for selected winners only.
Applications:
- Now, it's possible to deactivate the whole supporter system.
- New import option: set status of all imported applications to
'permit'.
- More log entries for all application actions.
Participant:
- Add new comment field for participants.
- Show translated permissions strings in user rols form.
- Admin is redirect to 'change password' page.
- New default user name: "firstname lastname".
Other:
- Use Django's class based views.
- Update to Django 1.4. Drop python 2.4 support for this reason.
- Separate the code for the projector.
- Rewrite the vote results table.
- Rewrite the poll API.
- Rewrite the config API. (Now any data which are JSON serializable
can be stored.)
- Improved CSV import for application and participants.
- GUI improvements of web interface (e.g. sub navigations, overview tables).
- Several minor and medium issues and errors were fixed.
Version 1.1 (2011-11-15)
========================
https://github.com/OpenSlides/OpenSlides/milestones/1.1
Agenda:
- [Feature] Agenda overview: New item-done-column for all non-manager (#7)
- [Feature] Allow HTML-Tags in agenda item of text (#12)
- [Feature] Split up hidden agenda items in new agenda table section (#13)
Projector:
- [Feature] Assignment projector view layout improvements (#9)
- [Bugfix] Zoom problem for sidebar div in beamer view (#5)
- [Bugfix] Blue 'candidate elected line' not visible in projector ajax view (#6)
- [Bugfix] Assignment projector view: Show results for elected candidates only (#11)
- [Bugfix] Missing beamer scaling (#2)
- [Bugfix] Assigment projector view: Removed empty character for no results cell. (#10)
Applications:
- [Feature] Import applications (#55)
- [Feature] Support trivial changes to an application (#56)
- [Bugfix] Order submitter and supporter form fields by full name (#53)
- [Bugfix] Application: Show profile instead of submitter username (#15)
- [Bugfix] "Application: Only check enough supports in status ""pub""" (#16)
Elections:
- [Feature] New button to show agenda item of selected application/assignment (#54)
- [Feature] Open add-user-url in new tab. (#32)
Applications/Elections:
- [Feature] Show voting results in percent (#48)
Participants:
- [Feature] Filter displayed permissions in group editor (#59)
- [Feature] Generate password after user creation automatically (#58)
- [Bugfix] Encoding error (#1)
- [Bugfix] List of participants (pdf) link not visible for users with see-particiants-permissions (#3)
- [Bugfix] Use user.profile.get_type_display() instead of user.profile.type (#4)
PDF:
- [Feature] Mark elected candidates in PDF (#31)
- [Feature] New config option to set title and preamble text for application and assignment pdf (#33)
- [Feature] New config option to set number of ballots in PDF (#26)
- [Bugfix] Assignment ballot pdf: Wrong line break in group name with brackets (#8)
- [Bugfix] Print available candidates in assignment pdf (#14)
- [Bugfix] "Show ""undocumented"" for result ""-2"" in application and assignment pdf" (#17)
Other:
- [Feature] Rights for anonymous (#45)
- [Feature] Show counter for limited speaking time (#52)
- [Feature] Reorderd config tab subpages (#61)
- [Localize] i18n German: Use gender-specific strings (#51)
- [Bugfix] <button> inside <a> tag not working in IE (#57)
- [Bugfix] Change default sort for tables of applications, assignments, participants (#27)
Version 1.0 (2011-09-12)
========================
https://github.com/OpenSlides/OpenSlides/tree/1.0/

159
DEVELOPMENT.md Normal file
View File

@ -0,0 +1,159 @@
# Development of OpenSlides 4
## Requirements
You need git, bash, docker, docker-compose, make and openssl installed.
Go is needed to install https://github.com/FiloSottile/mkcert (but Go is not a requirement to start the development server). The development setup uses HTTPS per default. OpenSlides does not work with HTTP anymore since features are required (like http2) that only works in a secure environment.
## Before starting the development
Clone this repository:
$ git clone --recurse-submodules git@github.com:OpenSlides/OpenSlides.git
After cloning you need to initialize all submodules:
$ git submodule update --init
Finally, start the development server:
$ make run-dev
(This command won't run without sudo, or without having set up Docker to run without sudo - see their documentation)
You can access the services independently using their corresponding ports
or access the full stack on
$ https://localhost:8000
## Running tests
To run all tests of all services, execute `run-service-tests`. TODO: Systemtests in this repo.
## Adding a new Service
$ git submodule add <git@myrepo.git>
Append `branch = main` to the new entry in the `.gitmodules` file. Verify,
that it is there (the folder should have 160000 permissions: Submodule) with the
current commit:
$ git diff --cached
Then, commit changes and create a pull request.
## Work in submodules
Create your own fork at github.
Remove the upstream repo as the origin in the submodule:
$ cd <submodule>
$ git remote remove origin
Add your fork and the main repo as origin and upstream
$ git remote add origin `<your fork>`
$ git remote add upstream `<main repo>`
$ git fetch --all
$ git checkout origin main
You can verify that your setup is correct using
$ git remote -v
The output should be similar to
origin git@github.com:<GithubUsername>/OpenSlides.git (fetch)
origin git@github.com:<GithubUsername>/OpenSlides.git (push)
upstream git@github.com:OpenSlides/OpenSlides.git (fetch)
upstream git@github.com:OpenSlides/OpenSlides.git (push)
## Requirements for services
### Environment variables
These environment variables are available:
- `<SERVICE>_HOST`: The host from a required service
- `<SERVICE>_PORT`: The port from a required service
Required services can be `MESSAGE_BUS`, `DATASTORE_WRITER`, `PERMISSION`, `AUTOUPDATE`,
etc. For private services (e.g. a database dedicated to exactly one service),
use the following syntax: `<SERVICE>_<PRIV_SERVICE>_<ATTRIBUTE>`, e.g. the
Postgresql user for the datastore: `DATASTORE_POSTGRESQL_USER`.
### Makefile
A makefile must be provided at the root-level of the service. The currently
required (phony) targets are:
- `run-tests`: Execute all tests from the submodule
- `build-dev`: Build an image with the tag `openslides-<service>-dev`
### Build arguments in the Dockerfile
These build arguments should be supported by every service:
- `REPOSITORY_URL`: The git-url for the repository to use
- `GIT_CHECKOUT`: A branch/tag/commit to check out during the build
Note that meaningful defaults should be provided in the Dockerfile.
## Developing on a single service
Go to the serivce and create a new branch (from main):
$ cd my-service
$ git status # -> on main?
$ git checkout -b my-feature
Run OpenSlides in development mode (e.g. in a new terminal):
$ make run-dev
After making some changes in my-service, create a commit and push to your fork
$ git add -A
$ git commit -m "A meaningful commit message here"
$ git push origin -u my-feature
As the last step, you can create a PR on Github. After merging, these steps are
required to be executed in the main repo:
$ cd my-service
$ git pull upstream main
$ cd ..
$ git diff # -> commit hash changed for my-service
If the update commit should be a PR:
$ git checkout -b updated-my-service
$ git commit -am "Updated my-service"
$ git push origin updated-my-service
Or a direct push on main:
$ git commit -am "Updated my-service"
$ git push origin main
## Working with Submodules
After working in many services with different branches, this command checks
out `main` (or the given branch in the .gitmodules) in all submodules and
pulls main from upstream (This requres to have `upstream`set up as a remote
in all submodules):
$ git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo main); git pull upstream $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo main)'
This command has can also be called from the makefile using:
$ make services-to-main
When changing the branch in the main repo (this one), the submodules do not
automatically gets changed. THis ocmmand checks out all submodules to the given
commits in the main repo:
$ git submodule update

View File

@ -1,281 +0,0 @@
========================
OpenSlides Development
========================
This instruction helps you to setup a development environment for OpenSlides.
Installation and start of the development version
=================================================
1. Installation on GNU/Linux or Mac OS X
----------------------------------------
a. Check requirements
'''''''''''''''''''''
Make sure that you have installed `Python (>= 3.6) <https://www.python.org/>`_,
`Node.js (>=10.x) <https://nodejs.org/>`_ and `Git <http://git-scm.com/>`_ on
your system. You also need build-essential packages and header files and a
static library for Python.
For Debian based systems (Ubuntu, etc) run::
$ sudo apt-get install git nodejs npm build-essential python3-dev
b. Get OpenSlides source code
'''''''''''''''''''''''''''''
Clone current master version from `OpenSlides GitHub repository
<https://github.com/OpenSlides/OpenSlides/>`_::
$ git clone https://github.com/OpenSlides/OpenSlides.git
$ cd OpenSlides
c. Setup a virtual Python environment (optional)
''''''''''''''''''''''''''''''''''''''''''''''''
See step 1. b. in the installation section in the `README.rst
<https://github.com/OpenSlides/OpenSlides/blob/master/README.rst>`_.
d. Finish the server
''''''''''''''''''''
Install all required Python packages::
$ pip install --requirement requirements.txt
Create a settings file, run migrations and start the server::
$ python manage.py createsettings
$ python manage.py migrate
$ python manage.py runserver
To get help on the command line options run::
$ python manage.py --help
Later you might want to restart the server with one of the following commands.
To start OpenSlides with this command and to avoid opening new browser windows
run::
$ python manage.py start --no-browser
When debugging something email related change the email backend to console::
$ python manage.py start --debug-email
e. Debugging the server
'''''''''''''''''''''''
If you wish to have even further debugging, enable `django-extensions
<https://django-extensions.readthedocs.io/>`_ in the ``settings.py`` by adding
``django_extensions`` to the list of ``INSTALLED_PLLUGINS``. Make sure, you
install the following packages::
$ pip install Werkzeug pyparsing pydot django-extensions
You can start the enhanced debugging-server via::
$ python manage.py runserver_plus
f. Setup and start the client
'''''''''''''''''''''''''''''
Go in the client's directory in a second command-line interface::
$ cd client/
Install all dependencies and start the development server::
$ npm install
$ npm start
Now the client is available under ``localhost:4200``.
If you want to provide the client statically, you can build it via::
$ npm run build
The build client files are availible from the root directory in
``openslides/static`` and can be provided via NGINX.
2. Installation on Windows
--------------------------
Follow the instructions above (Installation on GNU/Linux or Mac OS X) but care
of the following variations.
To get Python download and run the latest `Python 3.7 32-bit (x86) executable
installer <https://www.python.org/downloads/windows/>`_. Note that the 32-bit
installer is required even on a 64-bit Windows system. If you use the 64-bit
installer, step d. of the instruction might fail unless you installed some
packages manually.
In some cases you have to install `MS Visual C++ 2015 build tools
<https://www.microsoft.com/en-us/download/details.aspx?id=48159>`_ before you
install the required python packages for OpenSlides (unfortunately Twisted
needs it).
To setup and activate the virtual environment in step c. use::
> .virtualenv\Scripts\activate.bat
All other commands are the same as for GNU/Linux and Mac OS X.
3. Running the test cases
-------------------------
a. Running server tests
'''''''''''''''''''''''
To run some server tests see `.travis.yml
<https://github.com/OpenSlides/OpenSlides/blob/master/.travis.yml>`_.
You can generate an class-structure image when having `django_extensions`
enabled (see above)::
$ python manage.py graph_models -a -g -o my_project_visualized.png
b. Client tests and commands
''''''''''''''''''''''''''''
Change to the client's directory to run every client related command. Run
client tests::
$ npm test
Fix the code format and lint it with::
$ npm run prettify-write
$ npm run lint
To extract translations run::
$ npm run extract
When updating, adding or changing used packages from npm, please update the
README.md using following command::
$ npm run licenses
OpenSlides in big mode
======================
To install OpenSlides for big assemblies (in 'big mode') you have to setup some
additional components and configurations. In the 'big mode' you should use a webserver
like NGINX to serve the static and media files as proxy server in front of your OpenSlides
interface server. You should also use a database like PostgreSQL. Use Redis as channels backend,
cache backend and session engine. Finally you should use gunicorn with uvicorn as interface server.
1. Install and configure PostgreSQL and Redis
---------------------------------------------
Install `PostgreSQL <https://www.postgresql.org/>`_ and `Redis
<https://redis.io/>`_. For Ubuntu 18.04 e. g. run::
$ sudo apt-get install postgresql libpq-dev redis-server
Be sure that database and redis server is running. For Ubuntu 18.04 e. g. this
was done automatically if you used the package manager.
Then add database user and database. For Ubuntu 18.04 e. g. run::
$ sudo -u postgres createuser --pwprompt --createdb openslides
$ sudo -u postgres createdb --owner=openslides openslides
2. Change OpenSlides settings
-----------------------------
Create OpenSlides settings file if it does not exist::
$ python manage.py createsettings
Change OpenSlides settings file (usually called settings.py): Setup
`DATABASES` entry as mentioned in the settings file. Set `use_redis` to
`True`.
Populate your new database::
$ python manage.py migrate
3. Run OpenSlides
-----------------
To start Daphne run::
$ export DJANGO_SETTINGS_MODULE=settings
$ export PYTHONPATH=personal_data/var/
$ daphne -b 0.0.0.0 -p 8000 openslides.asgi:application
The last line may be interchangeable with gunicorn and uvicorn as protocol
server::
$ gunicorn -w 4 -b 0.0.0.0:8000 -k uvicorn.workers.UvicornWorker openslides.asgi:application
4. Use NGINX (optional)
-----------------------
When using NGINX as a proxy for delivering static files the performance of the
setup will increase.
This is an example ``nginx.conf`` configuration for Daphne listing on port
8000::
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
root $YOUR_OS_ROOT_FOLDER/openslides/static;
index index.html index.htm;
include /etc/nginx/mime.types;
client_max_body_size 100M;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
location / {
try_files $uri $uri/ /index.html;
}
location /apps {
proxy_pass http://localhost:8000;
}
location /media {
proxy_pass http://localhost:8000;
}
location /rest {
proxy_pass http://localhost:8000;
}
location /ws {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
}

View File

@ -1,30 +0,0 @@
FROM python:3.7-slim
RUN mkdir /app
RUN apt -y update && \
apt -y upgrade && \
apt install -y libpq-dev curl wget xz-utils bzip2 git gcc gnupg2 make g++
RUN curl -sL https://deb.nodesource.com/setup_11.x | bash -
RUN apt -y install nodejs
RUN npm install -g @angular/cli@latest
RUN useradd -m openslides
RUN chown -R openslides /app
WORKDIR /app
COPY . /app
RUN rm -rf /app/.virtualenv* && \
rm -rf /app/client/node_modules
RUN chown -R openslides /app
# Installing python dependencies
RUN pip install -r requirements.txt
RUN rm -rf /var/lib/apt/lists/*
# installing client
USER openslides
RUN ng config -g cli.warnings.versionMismatch false && \
cd client && \
npm install
RUN cd client && \
npm run build && \
./node_modules/.bin/compodoc -t -p src/tsconfig.app.json -n 'OpenSlides Documentation' -d ../openslides/static/doc -e html

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
MIT License
Copyright (c) 2011-2019 Authors of OpenSlides, see AUTHORS
Copyright (c) Since 2011 Authors of OpenSlides, see AUTHORS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,9 +0,0 @@
include AUTHORS
include CHANGELOG
include LICENSE
include README.rst
include SETTINGS.rst
include requirements/production.txt
include requirements/big_mode.txt
recursive-include openslides *.*
exclude openslides/__pycache__/*

52
Makefile Normal file
View File

@ -0,0 +1,52 @@
run-integration-tests:
@echo "Start OpenSlides Dev"
make run-dev ARGS="-d"
@echo "Start integration tests"
make cypress-docker
docker-compose -f integration/docker-compose.yml up
@echo "Stop OpenSlides Dev"
make stop-dev
run-service-tests:
git submodule foreach 'make run-tests'
build-dev:
./dev-commands/submodules-do.sh 'make build-dev'
make -C proxy build-dev
run-dev: | build-dev
docker-compose -f docker/docker-compose.dev.yml up $(ARGS)
run-dev-otel: | build-dev
docker-compose -f docker/docker-compose.dev.yml -f docker/dc.otel.dev.yml up $(ARGS)
stop-dev:
docker-compose -f docker/docker-compose.dev.yml down --volumes --remove-orphans
stop-dev-otel:
docker-compose -f docker/docker-compose.dev.yml -f docker/dc.otel.dev.yml down --volumes --remove-orphans
copy-node-modules:
docker-compose -f docker/docker-compose.dev.yml exec client bash -c "cp -r /app/node_modules/ /app/src/"
mv openslides-client/client/src/node_modules/ openslides-client/client/
reload-proxy:
docker-compose -f docker/docker-compose.dev.yml exec -w /etc/caddy proxy caddy reload
services-to-main:
./services-to-main.sh
submodules-origin-to-upstream:
# You may only use this one time after cloning this repository.
# Will set the upstream remote to "origin"
git submodule foreach -q --recursive 'git remote rename origin upstream'
cypress-open:
cd integration; npm run cypress:open
cypress-run:
cd integration; npm run cypress:run
cypress-docker:
docker-compose -f integration/docker-compose.yml build
docker-compose -f integration/docker-compose.yml up

57
README.md Normal file
View File

@ -0,0 +1,57 @@
# OpenSlides
## What is OpenSlides?
OpenSlides is a free, web based presentation and assembly system for
managing and projecting agenda, motions and elections of an assembly. See
https://openslides.com for more information.
## Using OpenSlides productively
__OpenSlides 4 (this) is currently in beta version!__
If you are just looking to use OpenSlides in a productive manner, please refer
to the [OpenSlides 3.4 (stable)](https://github.com/OpenSlides/OpenSlides/tree/stable/3.4.x)
## Installation
### Requirements
You need [Docker](https://docs.docker.com/engine/install/) and [Docker
Compose](https://docs.docker.com/compose/install/).
### Setup OpenSlides
For a productive setup of OpenSlides get the [OpenSlides manage
tool](https://github.com/OpenSlides/openslides-manage-service/releases/tag/latest)
from GitHub and make it executable. E. g. run:
$ wget https://github.com/OpenSlides/openslides-manage-service/releases/download/latest/openslides
$ chmod +x openslides
Then follow the instructions outlined in the [OpenSlides Manage
Service](https://github.com/OpenSlides/openslides-manage-service).
## Development
For further information about developing OpenSlides, refer to [the development
readme](DEVELOPMENT.md).
### Architecture of OpenSlides 4
![System architecture of OpenSlides 4](https://raw.githubusercontent.com/wiki/OpenSlides/OpenSlides/OS4/img/OpenSlides4.svg)
Read more about our [concept of OpenSlides 4.0](https://github.com/OpenSlides/OpenSlides/wiki/DE%3AKonzept-OpenSlides-4).
The technical documentation about the internals, requests and functionality can
be found [in the wiki](https://github.com/OpenSlides/OpenSlides/wiki/DE%3AKonzept-OpenSlides-4).
## License and authors
OpenSlides is Free/Libre Open Source Software (FLOSS), and distributed
under the MIT License, see ``LICENSE`` file. The authors of OpenSlides are
mentioned in the ``AUTHORS`` file.

View File

@ -1,178 +0,0 @@
============
OpenSlides
============
What is OpenSlides?
===================
OpenSlides is a free, web based presentation and assembly system for
managing and projecting agenda, motions and elections of an assembly. See
https://openslides.com for more information.
Installation
============
The OpenSlides server runs everywhere where Python is running (for example on
GNU/Linux, Mac or Windows). For the OpenSlides client a current web browser is required.
1. Installation on GNU/Linux or Mac OS X
----------------------------------------
a. Check requirements
'''''''''''''''''''''
Make sure that you have installed `Python (>= 3.6) <https://www.python.org/>`_
on your system.
Additional you need build-essential packages, header files and a static
library for Python and also the pyvenv-3 binary package for python3.
E.g. run on Debian/Ubuntu::
$ sudo apt-get install build-essential python3-dev python3-venv
b. Setup a virtual Python environment (optional)
''''''''''''''''''''''''''''''''''''''''''''''''
You can setup a virtual Python environment using the virtual environment
(venv) package for Python to install OpenSlides as non-root user.
Create your OpenSlides directory and change to it::
$ mkdir OpenSlides
$ cd OpenSlides
Setup and activate the virtual environment::
$ python3 -m venv .virtualenv
$ source .virtualenv/bin/activate
$ pip install --upgrade setuptools pip
c. Install OpenSlides
'''''''''''''''''''''
To install OpenSlides just run::
$ pip install openslides
This installs the latest stable version. To install a specific (beta)
version use ``openslides==x.y``.
You can also use the package from the `OpenSlides website
<https://openslides.com/>`_. Download latest OpenSlides release as
compressed tar archive and run::
$ pip install openslides-x.y.tar.gz
This will install all required Python packages (see
``requirements/production.txt``).
d. Start OpenSlides
'''''''''''''''''''
To start OpenSlides simply run::
$ openslides
If you run this command the first time, a new database and the admin account
(Username: ``admin``, Password: ``admin``) will be created. Please change the
password after first login!
OpenSlides will start a webserver. It will also try to open the webinterface in
your default webbrowser. The server will try to listen on the local ip address
on port 8000. That means that the server will be available to everyone on your
local network (at least for commonly used network configurations).
If you use a virtual environment (see step b.), do not forget to activate
the environment before restart after you closed the terminal::
$ source .virtualenv/bin/activate
To get help on the command line options run::
$ openslides --help
You can store settings, database and other personal files in a local
subdirectory and use these files e. g. if you want to run multiple
instances of OpenSlides::
$ openslides start --local-installation
2. Installation on Windows
--------------------------
Follow the instructions above (1. Installation on GNU/Linux or Mac OS X) but care
of the following variations.
To get Python download and run the latest `Python 3.7 32-bit (x86) executable
installer <https://www.python.org/downloads/windows/>`_. Note that the 32-bit
installer is required even on a 64-bit Windows system. If you use the 64-bit
installer, step 1c of the instruction might fail unless you installed some
packages manually.
In some cases you have to install `MS Visual C++ 2015 build tools
<https://www.microsoft.com/en-us/download/details.aspx?id=48159>`_ before you
install the required python packages for OpenSlides (unfortunately Twisted
needs it).
To setup and activate the virtual environment in step 1b use::
> .virtualenv\Scripts\activate.bat
All other commands are the same as for GNU/Linux and Mac OS X.
3. Installation with Docker
---------------------------
The installation instruction for (1) and (2) described a way to use OpenSlides in a
'small mode' with max 10 concurrent clients. To install OpenSlides for big assemblies
('big mode') you have to setup some additional components and configurations.
The easiest way to run the OpenSlides 'big mode' environment (with PostgreSQL, Redis
and NGINX) with Docker Compose: use our docker compose suite. Follow the instruction in
the `openslides-doccker-compose Repository <https://github.com/OpenSlides/openslides-docker-compose>`_.
To install and configure all components of our 'big mode' manually you can read the
`big-mode-instruction <https://github.com/OpenSlides/OpenSlides/blob/master/DEVELOPMENT.rst#openslides-in-big-mode>`_
Configuration
=============
Please consider reading the `OpenSlides configuration
<https://github.com/OpenSlides/OpenSlides/blob/master/SETTINGS.rst>`_ page to
find out about all configurations, especially when using OpenSlides for big
assemblies.
Development
===========
To setup a development environment for OpenSlides follow the instruction of
`DEVELOPMENT.rst
<https://github.com/OpenSlides/OpenSlides/blob/master/DEVELOPMENT.rst>`_.
Used software
=============
OpenSlides uses the following projects or parts of them:
* Several Python packages (see ``requirements/production.txt`` and ``requirements/big_mode.txt``).
* Several JavaScript packages (see ``client/package.json``)
License and authors
===================
OpenSlides is Free/Libre Open Source Software (FLOSS), and distributed
under the MIT License, see ``LICENSE`` file. The authors of OpenSlides are
mentioned in the ``AUTHORS`` file.

View File

@ -1,125 +0,0 @@
==========================
OpenSlides configuration
==========================
First, locate your `settings.py`. Since this is a regular python file,
experienced users can also write more advanced configurations with e.g. swithing
between two sets of configs. This also means, that the syntax need to be correct
for OpenSlides to start.
All presented settings must be written `<SETTINGS_NAME> = <value>` to follow the
correct syntax.
The `settings.py` is just an extension for Django settings. Please visit the
`Django settings documentation
<https://docs.djangoproject.com/en/2.2/ref/settings/>`_ to get an overview about
all existing settings.
SECURITY
========
For `DEBUG` and `SECRET_KEY` see the sections in the django settings
documenataion.
`RESET_PASSWORD_VERBOSE_ERRORS`: Default: `True`. Set to `False` to disable.
Controls the verbosity on errors during a reset password. If enabled, an error
will be shown, if there does not exist a user with a given email address. So one
can check, if a email is registered. If this is not wanted, disable verbose
messages. An success message will always be shown.
`AUTH_PASSWORD_VALIDATORS`: Add custom password validators, e.g. a min-length
validator. See `django auth docs
<https://docs.djangoproject.com/en/2.2/topics/auth/passwords/#module-django.contrib.auth.password_validation>`_
for mor information.
Directories
===========
`OPENSLIDES_USER_DATA_DIR`: The path, where all user data is saved, like static
files, mediafiles and the default database. This path can be different to the
location of the `settings.py`.
`STATICFILES_DIRS` and `STATIC_ROOT`: Managing static files. Because the clint
is not delivered by the server anymore, these settings are obsolete.
`MEDIA_ROOT`: The location of mediafiles. The default is a `media` folder inside
`OPENSLIDES_USER_DATA_DIR`, but can be altered to another path.
Email
=====
Please refer to the Django settings documentation. A changed email backend is
useful for debugging to print all email the the console::
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Logging
=======
To setup basic logging see `logging
<https://docs.djangoproject.com/en/2.2/topics/logging/>`_.
We recommend to enable all OpenSlides related logging with level `INFO` per
default::
LOGGING = {
'formatters':
'lessnoise': {
'format': '[{levelname}] {name} {message}',
'style': '{',
'datefmt': '[%Y-%m-%d %H:%M:%S %z]',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'lessnoise',
},
},
'loggers': {
'openslides': {
'handlers': ['console'],
'level': os.getenv('OPENSLIDES_LOG_LEVEL', 'INFO'),
},
},
}
With the environment variable `OPENSLIDES_LOG_LEVEL` the level can be adjusted
without changing the `settings.py`.
Big mode and caching
====================
When running multiple workers redis is required as a message broker between the
workers. Set `use_redis = True` to enable redis and visit `OpenSLides in big
mode
<https://github.com/OpenSlides/OpenSlides/blob/master/DEVELOPMENT.rst#openslides-in-big-mode>`_.
When seting `use_redis = True`, three settings are important:
- Caching: `REDIS_ADDRESS` is used to provide caching with redis across all
workers.
- Channels: The "message queue" for the workers. Adjust the `hosts`-part to the
redis address.
- Sessions: All sessions are managed in redis to ensure them across all workers.
Please adjust the `SESSION_REDIS` fields to point to the redis instance.
Advanced
========
`PING_INTERVAL` and `PING_TIMEOUT` are settings for the clients how frequently
to ping the server (interval) and how big is the timeout. If a ping took longer
than the timeout, the clients does a forced reconnect.
`COMPRESSION`: Enable or disables the compression when sending data. This does
not affect the client.
`PRIORITIZED_GROUP_IDS`: A list of group ids. If one client is logged in and the
operator is in one of these groups, the client disconnected and reconnects again.
All requests urls (including websockets) are now prefixed with `/prioritize`, so
these requests from "prioritized clients" can be routed to different servers.

View File

@ -1,13 +0,0 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@ -1,11 +0,0 @@
{
"printWidth": 120,
"tabWidth": 4,
"singleQuote": true,
"useTabs": false,
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "css",
"semi": true,
"trailingComma": "none",
"arrowParens": "avoid"
}

View File

@ -1,134 +0,0 @@
# OpenSlides 3 Client
Prototype application for OpenSlides 3.0 (Client).
Currently under constant heavy maintenance.
## Development Info
As an Angular project, Angular CLI is highly recommended to create components and services.
See https://angular.io/guide/quickstart for details.
### Contribution Info
Please respect the code-style defined in `.editorconf` and `.pretierrc`.
Code alignment should be automatically corrected by the pre-commit hooks.
Adjust your editor to the `.editorconfig` to avoid surprises.
See https://editorconfig.org/ for details.
### Pre-Commit Hooks
Before commiting, new code will automatically be aligned to the definitions set in the
`.prettierrc`.
Furthermore, new code has to pass linting.
Our pre-commit hooks are:
`pretty-quick --staged` and `lint`
See `package.json` for details.
### Documentation Info
The documentation can be generated by running `npm run compodoc`.
A new web server will be started on http://localhost:8080
Once running, the documentation will be updated automatically.
You can run it on another port, with adding your local port after the
command. If no port specified, it will try to use 8080.
Please document new code using JSDoc tags.
See https://compodoc.app/guides/jsdoc-tags.html for details.
### Development server
Run `npm start` for a development server. Navigate to `http://localhost:4200/`.
The app will automatically reload if you change any of the source files.
A running OpenSlides (2.2 or higher) instance is expected on port 8000.
Start OpenSlides as usual using
`python manage.py start --no-browser --host 0.0.0.0`
### Translation
We are using ngx-translate for translation purposes.
Use `npm run extract` to extract strings and update elements an with translation functions.
Language files can be found in `/src/assets/i18n`.
### Used software
OpenSlides uses the following software or parts of them:
- [@angular/animations@8.2.4](https://github.com/angular/angular), License: MIT
- [@angular/cdk-experimental@8.1.4](https://github.com/angular/components), License: MIT
- [@angular/cdk@8.1.4](https://github.com/angular/components), License: MIT
- [@angular/common@8.2.4](https://github.com/angular/angular), License: MIT
- [@angular/compiler@8.2.4](https://github.com/angular/angular), License: MIT
- [@angular/core@8.2.4](https://github.com/angular/angular), License: MIT
- [@angular/forms@8.2.4](https://github.com/angular/angular), License: MIT
- [@angular/material-moment-adapter@8.1.4](https://github.com/angular/components), License: MIT
- [@angular/material@8.1.4](https://github.com/angular/components), License: MIT
- [@angular/platform-browser-dynamic@8.2.4](https://github.com/angular/angular), License: MIT
- [@angular/platform-browser@8.2.4](https://github.com/angular/angular), License: MIT
- [@angular/pwa@0.803.2](https://github.com/angular/angular-cli), License: MIT
- [@angular/router@8.2.4](https://github.com/angular/angular), License: MIT
- [@angular/service-worker@8.2.4](https://github.com/angular/angular), License: MIT
- [@ngx-pwa/local-storage@8.2.1](https://github.com/cyrilletuzi/angular-async-local-storage), License: MIT
- [@ngx-translate/core@11.0.1](https://github.com/ngx-translate/core), License: MIT
- [@ngx-translate/http-loader@4.0.0](https://github.com/ngx-translate/http-loader), License: MIT
- [@pebula/ngrid-material@1.0.0-rc.5](https://github.com/shlomiassaf/ngrid), License: MIT
- [@pebula/ngrid@1.0.0-rc.5](https://github.com/shlomiassaf/ngrid), License: MIT
- [@pebula/utils@1.0.0](https://github.com/shlomiassaf/ngrid), License: MIT
- [@tinymce/tinymce-angular@3.3.0](https://github.com/tinymce/tinymce-angular), License: Apache-2.0
- [acorn@7.0.0](https://github.com/acornjs/acorn), License: MIT
- [core-js@3.2.1](https://github.com/zloirock/core-js), License: MIT
- [css-element-queries@1.2.1](https://github.com/marcj/css-element-queries), License: MIT
- [exceljs@1.15.0](https://github.com/exceljs/exceljs), License: MIT
- [file-saver@2.0.2](https://github.com/eligrey/FileSaver.js), License: MIT
- [hammerjs@2.0.8](https://github.com/hammerjs/hammer.js), License: MIT
- [lz4js@0.2.0](https://github.com/Benzinga/lz4js), License: ISC
- [material-icon-font@0.1.0](https://github.com//petergng/svgFontCreator), License: ISC
- [moment@2.24.0](https://github.com/moment/moment), License: MIT
- [ng2-pdf-viewer@5.3.4](git+https://vadimdez@github.com/VadimDez/ng2-pdf-viewer), License: MIT
- [ngx-file-drop@8.0.7](https://github.com/georgipeltekov/ngx-file-drop), License: MIT
- [ngx-mat-select-search@1.8.0](https://github.com/bithost-gmbh/ngx-mat-select-search), License: MIT
- [ngx-material-timepicker@4.0.2](https://github.com/Agranom/ngx-material-timepicker), License: MIT
- [ngx-papaparse@4.0.2](https://github.com/alberthaff/ngx-papaparse), License: MIT
- [pdfmake@0.1.58](https://github.com/bpampuch/pdfmake), License: MIT
- [po2json@1.0.0-alpha](https://github.com/mikeedwards/po2json), License: GNU Library General Public License
- [rxjs@6.5.2](https://github.com/reactivex/rxjs), License: Apache-2.0
- [text-encoding@0.7.0](https://github.com/inexorabletash/text-encoding), License: (Unlicense OR Apache-2.0)
- [tinymce@5.0.14](https://github.com/tinymce/tinymce-dist), License: LGPL-2.1
- [tslib@1.10.0](https://github.com/Microsoft/tslib), License: Apache-2.0
- [uuid@3.3.3](https://github.com/kelektiv/node-uuid), License: MIT
- [zone.js@0.9.1](https://github.com/angular/zone.js), License: MIT
- [@angular-devkit/build-angular@0.803.2](https://github.com/angular/angular-cli), License: MIT
- [@angular/cli@8.3.2](https://github.com/angular/angular-cli), License: MIT
- [@angular/compiler-cli@8.2.4](https://github.com/angular/angular), License: MIT
- [@angular/language-service@8.2.4](https://github.com/angular/angular), License: MIT
- [@biesbjerg/ngx-translate-extract@3.0.5](https://github.com/biesbjerg/ngx-translate-extract), License: MIT
- [@compodoc/compodoc@1.1.10](https://github.com/compodoc/compodoc), License: MIT
- [@types/jasmine@3.4.0](https://github.com/DefinitelyTyped/DefinitelyTyped), License: MIT
- [@types/jasminewd2@2.0.6](https://github.com/DefinitelyTyped/DefinitelyTyped), License: MIT
- [@types/node@12.7.3](https://github.com/DefinitelyTyped/DefinitelyTyped), License: MIT
- [@types/yargs@13.0.2](https://github.com/DefinitelyTyped/DefinitelyTyped), License: MIT
- [codelyzer@5.1.0](https://github.com/mgechev/codelyzer), License: MIT
- [husky@3.0.4](https://github.com/typicode/husky), License: MIT
- [jasmine-core@3.4.0](https://github.com/jasmine/jasmine), License: MIT
- [jasmine-spec-reporter@4.2.1](https://github.com/bcaudan/jasmine-spec-reporter), License: Apache-2.0
- [karma-chrome-launcher@3.1.0](https://github.com/karma-runner/karma-chrome-launcher), License: MIT
- [karma-coverage-istanbul-reporter@2.1.0](https://github.com/mattlewis92/karma-coverage-istanbul-reporter), License: MIT
- [karma-jasmine-html-reporter@1.4.2](https://github.com/dfederm/karma-jasmine-html-reporter), License: MIT
- [karma-jasmine@2.0.1](https://github.com/karma-runner/karma-jasmine), License: MIT
- [karma@4.3.0](https://github.com/karma-runner/karma), License: MIT
- [npm-license-crawler@0.2.1](https://github.com/mwittig/npm-license-crawler), License: BSD-3-Clause
- [npm-run-all@4.1.5](https://github.com/mysticatea/npm-run-all), License: MIT
- [prettier@1.18.2](https://github.com/prettier/prettier), License: MIT
- [protractor@5.4.2](https://github.com/angular/protractor), License: MIT
- [resize-observer-polyfill@1.5.1](https://github.com/que-etc/resize-observer-polyfill), License: MIT
- [source-map-explorer@2.0.1](https://github.com/danvk/source-map-explorer), License: Apache-2.0
- [ts-node@8.3.0](https://github.com/TypeStrong/ts-node), License: MIT
- [tslint@5.19.0](https://github.com/palantir/tslint), License: Apache-2.0
- [tsutils@3.17.1](https://github.com/ajafff/tsutils), License: MIT
- [typescript@3.5.3](https://github.com/Microsoft/TypeScript), License: Apache-2.0
- [webpack-bundle-analyzer@3.4.1](https://github.com/webpack-contrib/webpack-bundle-analyzer), License: MIT

View File

@ -1,150 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"client": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"styleext": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "os",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "../openslides/static",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/assets",
"src/manifest.json",
{
"glob": "**/*",
"input": "node_modules/tinymce/skins",
"output": "/tinymce/skins/"
},
{
"glob": "**/*",
"input": "node_modules/tinymce/themes",
"output": "/tinymce/themes/"
},
{
"glob": "**/*",
"input": "node_modules/tinymce/plugins",
"output": "/tinymce/plugins/"
}
],
"styles": ["src/styles.scss"],
"scripts": ["node_modules/tinymce/tinymce.min.js"],
"webWorkerTsConfig": "tsconfig.worker.json"
},
"configurations": {
"production": {
"fileReplacements": [{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"serviceWorker": true,
"budgets": [{
"type": "initial",
"maximumWarning": "5mb",
"maximumError": "10mb"
}]
},
"es5": {
"tsConfig": "./tsconfig-es5.app.json"
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "client:build"
},
"configurations": {
"production": {
"browserTarget": "client:build:production"
},
"es5": {
"browserTarget": "client:build:es5"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "client:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"styles": ["src/styles.scss"],
"scripts": ["node_modules/tinymce/tinymce.min.js"],
"assets": [
"src/assets",
"src/manifest.json",
{
"glob": "**/*",
"input": "node_modules/tinymce/skins",
"output": "/tinymce/skins/"
},
{
"glob": "**/*",
"input": "node_modules/tinymce/themes",
"output": "/tinymce/themes/"
},
{
"glob": "**/*",
"input": "node_modules/tinymce/plugins",
"output": "/tinymce/plugins/"
}
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "tsconfig.spec.json",
"format": "stylish",
"exclude": ["**/node_modules/**"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "client:serve"
},
"configurations": {
"production": {
"devServerTarget": "client:serve:production"
}
}
}
}
}
},
"defaultProject": "client"
}

View File

@ -1,9 +0,0 @@
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
IE 9-11

View File

@ -1,26 +0,0 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: ['./src/**/*.e2e-spec.ts'],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.e2e.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@ -1,14 +0,0 @@
import { AppPage } from './app.po';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to client!');
});
});

View File

@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('os-root h1')).getText();
}
}

View File

@ -1,9 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": ["jasmine", "jasminewd2", "node"]
}
}

View File

@ -1,46 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser
jasmine: {
timeoutInterval: 10000
}
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: ['--no-sandbox']
}
},
singleRun: false,
proxies: {
'/apps/': 'http://localhost:8000/apps/',
'/media/': 'http://localhost:8000/media/',
'/rest/': 'http://localhost:8000/rest/',
'/ws/': 'ws://localhost:8000/'
}
});
};

View File

@ -1,40 +0,0 @@
{
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.png",
"/index.html",
"/*.css",
"/*.js",
"/fira-sans*",
"/Material-Icons-Baseline.*"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**"
]
}
}
],
"dataGroups": [
{
"name": "api",
"urls": ["/rest/*", "/apps/*"],
"cacheConfig": {
"maxSize": 0,
"maxAge": "0u",
"strategy": "freshness"
}
}
]
}

View File

@ -1,109 +0,0 @@
{
"name": "OpenSlides3-Client",
"version": "3.1.1",
"repository": {
"type": "git",
"url": "git://github.com/OpenSlides/OpenSlides.git"
},
"license": "MIT",
"description": "OpenSlides 3.0 (Client)",
"README": "https://github.com/OpenSlides/OpenSlides/blob/master/client/README.md",
"scripts": {
"ng": "ng",
"ng-high-memory": "node --max_old_space_size=4096 ./node_modules/@angular/cli/bin/ng",
"start": "ng serve --proxy-config proxy.conf.json --host=0.0.0.0",
"start-es5": "ng serve --proxy-config proxy.conf.json --host=0.0.0.0 --configuration es5",
"build": "npm run ng-high-memory -- build --prod",
"build-debug": "npm run ng-high-memory -- build",
"test": "ng test",
"test-silently": "npm run test -- --watch=false --no-progress --browsers=ChromeHeadlessNoSandbox",
"lint-check": "ng lint",
"lint-write": "ng lint --fix",
"e2e": "ng e2e",
"licenses": "node src/crawler.js",
"compodoc": "./node_modules/.bin/compodoc --hideGenerator -p tsconfig.app.json -n 'OpenSlides Documentation' -d ../Compodoc -s -o -r",
"extract": "ngx-translate-extract -i ./src -o ./src/assets/i18n/template-en.pot --clean --sort --format pot -m _",
"po2json": "./node_modules/.bin/po2json -f mf src/assets/i18n/de.po src/assets/i18n/de.json && ./node_modules/.bin/po2json -f mf src/assets/i18n/cs.po src/assets/i18n/cs.json && ./node_modules/.bin/po2json -f mf src/assets/i18n/ru.po src/assets/i18n/ru.json",
"po2json-tempfix": "./node_modules/.bin/po2json -f mf src/assets/i18n/de.po /dev/stdout | sed -f sed_replacements > src/assets/i18n/de.json && ./node_modules/.bin/po2json -f mf src/assets/i18n/cs.po /dev/stdout | sed -f sed_replacements > src/assets/i18n/cs.json && ./node_modules/.bin/po2json -f mf src/assets/i18n/ru.po /dev/stdout | sed -f sed_replacements > src/assets/i18n/ru.json",
"prettify-check": "prettier --config ./.prettierrc --list-different \"src/{app,environments}/**/*{.ts,.js,.json,.css,.scss}\"",
"prettify-write": "prettier --config ./.prettierrc --write \"src/{app,environments}/**/*{.ts,.js,.json,.css,.scss}\"",
"cleanup": "npm run prettify-write; npm run lint-write",
"cleanup-win": "npm run prettify-write & npm run lint-write"
},
"dependencies": {
"@angular/animations": "~8.2.4",
"@angular/cdk": "~8.1.4",
"@angular/cdk-experimental": "~8.1.4",
"@angular/common": "~8.2.4",
"@angular/compiler": "~8.2.4",
"@angular/core": "~8.2.4",
"@angular/forms": "~8.2.4",
"@angular/material": "~8.1.4",
"@angular/material-moment-adapter": "~8.1.4",
"@angular/platform-browser": "~8.2.4",
"@angular/platform-browser-dynamic": "~8.2.4",
"@angular/pwa": "^0.803.1",
"@angular/router": "~8.2.4",
"@angular/service-worker": "~8.2.4",
"@ngx-pwa/local-storage": "~8.2.1",
"@ngx-translate/core": "~11.0.1",
"@ngx-translate/http-loader": "^4.0.0",
"@pebula/ngrid": "1.0.0-rc.9",
"@pebula/ngrid-material": "1.0.0-rc.9",
"@pebula/utils": "1.0.0",
"@tinymce/tinymce-angular": "^3.2.0",
"acorn": "^7.0.0",
"core-js": "^3.2.1",
"css-element-queries": "^1.2.1",
"exceljs": "1.15.0",
"file-saver": "^2.0.2",
"hammerjs": "^2.0.8",
"lz4js": "^0.2.0",
"material-icon-font": "git+https://github.com/petergng/materialIconFont.git",
"moment": "^2.24.0",
"ng2-pdf-viewer": "^5.3.4",
"ngx-file-drop": "~8.0.7",
"ngx-mat-select-search": "^1.8.0",
"ngx-material-timepicker": "^4.0.2",
"ngx-papaparse": "^4.0.2",
"pdfmake": "^0.1.58",
"po2json": "^1.0.0-alpha",
"rxjs": "^6.5.2",
"tinymce": "^5.0.14",
"tslib": "^1.10.0",
"uuid": "^3.3.2",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.803.2",
"@angular/cli": "~8.3.2",
"@angular/compiler-cli": "~8.2.4",
"@angular/language-service": "~8.2.4",
"@biesbjerg/ngx-translate-extract": "^3.0.5",
"@compodoc/compodoc": "^1.1.8",
"@types/jasmine": "^3.3.9",
"@types/jasminewd2": "^2.0.6",
"@types/node": "~12.7.2",
"@types/yargs": "^13.0.0",
"codelyzer": "^5.0.1",
"husky": "^3.0.4",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "^4.1.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "^2.0.5",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"npm-license-crawler": "^0.2.1",
"npm-run-all": "^4.1.5",
"prettier": "^1.19.1",
"protractor": "^5.4.2",
"resize-observer-polyfill": "^1.5.1",
"source-map-explorer": "^2.0.1",
"ts-node": "~8.3.0",
"tslint": "~5.19.0",
"tsutils": "3.17.1",
"typescript": "~3.5.3",
"webpack-bundle-analyzer": "^3.3.2"
}
}

View File

@ -1,19 +0,0 @@
{
"/apps/": {
"target": "http://localhost:8000",
"secure": false
},
"/media/": {
"target": "http://localhost:8000",
"secure": false
},
"/rest/": {
"target": "http://localhost:8000",
"secure": false
},
"/ws/": {
"target": "ws://localhost:8000",
"secure": false,
"ws": true
}
}

View File

@ -1,4 +0,0 @@
s/\\\\{/{/g
s/\\\\}/}/g
s/{0}ser%/%user%/g
s/{0}um%/%num%/g

View File

@ -1,13 +0,0 @@
import { AppRoutingModule } from './app-routing.module';
describe('AppRoutingModule', () => {
let appRoutingModule: AppRoutingModule;
beforeEach(() => {
appRoutingModule = new AppRoutingModule();
});
it('should create an instance', () => {
expect(appRoutingModule).toBeTruthy();
});
});

View File

@ -1,40 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginLegalNoticeComponent } from './site/login/components/login-legal-notice/login-legal-notice.component';
import { LoginMaskComponent } from './site/login/components/login-mask/login-mask.component';
import { LoginPrivacyPolicyComponent } from './site/login/components/login-privacy-policy/login-privacy-policy.component';
import { LoginWrapperComponent } from './site/login/components/login-wrapper/login-wrapper.component';
import { ResetPasswordConfirmComponent } from './site/login/components/reset-password-confirm/reset-password-confirm.component';
import { ResetPasswordComponent } from './site/login/components/reset-password/reset-password.component';
/**
* Global app routing
*/
const routes: Routes = [
{
path: 'login',
component: LoginWrapperComponent,
children: [
{ path: '', component: LoginMaskComponent, pathMatch: 'full' },
{ path: 'reset-password', component: ResetPasswordComponent },
{ path: 'reset-password-confirm', component: ResetPasswordConfirmComponent },
{ path: 'legalnotice', component: LoginLegalNoticeComponent },
{ path: 'privacypolicy', component: LoginPrivacyPolicyComponent }
]
},
{
path: 'projector',
loadChildren: () =>
import('./fullscreen-projector/fullscreen-projector.module').then(m => m.FullscreenProjectorModule),
data: { noInterruption: true }
},
{ path: '', loadChildren: () => import('./site/site.module').then(m => m.SiteModule) },
{ path: '**', redirectTo: '' }
];
@NgModule({
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })],
exports: [RouterModule]
})
export class AppRoutingModule {}

View File

@ -1,3 +0,0 @@
<div class="content">
<router-outlet></router-outlet>
</div>

View File

@ -1,3 +0,0 @@
.content {
flex: 1;
}

View File

@ -1,35 +0,0 @@
import { async, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { TranslateService } from '@ngx-translate/core';
import { AppComponent } from './app.component';
import { E2EImportsModule } from './../e2e-imports.module';
import { ServertimeService } from './core/core-services/servertime.service';
describe('AppComponent', () => {
let servertimeService, translate;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule]
}).compileComponents();
servertimeService = TestBed.get(ServertimeService);
translate = TestBed.get(TranslateService);
spyOn(servertimeService, 'startScheduler').and.stub();
spyOn(translate, 'addLangs').and.stub();
spyOn(translate, 'setDefaultLang').and.stub();
spyOn(translate, 'getBrowserLang').and.stub();
spyOn(translate, 'getLangs').and.returnValue([]);
spyOn(translate, 'use').and.stub();
}));
it('should create the app', fakeAsync(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
tick(1000);
fixture.whenStable().then(() => {
expect(servertimeService.startScheduler).toHaveBeenCalled();
});
}));
});

View File

@ -1,159 +0,0 @@
import { ApplicationRef, Component } from '@angular/core';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { filter, take } from 'rxjs/operators';
import { ConfigService } from './core/ui-services/config.service';
import { ConstantsService } from './core/core-services/constants.service';
import { CountUsersService } from './core/ui-services/count-users.service';
import { DataStoreUpgradeService } from './core/core-services/data-store-upgrade.service';
import { LoadFontService } from './core/ui-services/load-font.service';
import { LoginDataService } from './core/ui-services/login-data.service';
import { OperatorService } from './core/core-services/operator.service';
import { OverlayService } from './core/ui-services/overlay.service';
import { PingService } from './core/core-services/ping.service';
import { PrioritizeService } from './core/core-services/prioritize.service';
import { RoutingStateService } from './core/ui-services/routing-state.service';
import { ServertimeService } from './core/core-services/servertime.service';
import { ThemeService } from './core/ui-services/theme.service';
declare global {
/**
* Enhance array with own functions
* TODO: Remove once flatMap made its way into official JS/TS (ES 2019?)
*/
interface Array<T> {
flatMap(o: any): any[];
}
/**
* Enhances the number object to calculate real modulo operations.
* (not remainder)
*/
interface Number {
modulo(n: number): number;
}
}
/**
* Angular's global App Component
*/
@Component({
selector: 'os-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
/**
* Master-component of all apps.
*
* Inits the translation service, the operator, the login data and the constants.
*
* Handles the altering of Array.toString()
*
* @param translate To set the default language
* @param operator To call the constructor of the OperatorService
* @param loginDataService to call the constructor of the LoginDataService
* @param constantService to call the constructor of the ConstantService
* @param servertimeService executes the scheduler early on
* @param themeService used to listen to theme-changes
* @param countUsersService to call the constructor of the CountUserService
* @param configService to call the constructor of the ConfigService
* @param loadFontService to call the constructor of the LoadFontService
* @param dataStoreUpgradeService
*/
public constructor(
translate: TranslateService,
appRef: ApplicationRef,
servertimeService: ServertimeService,
router: Router,
operator: OperatorService,
loginDataService: LoginDataService,
constantsService: ConstantsService, // Needs to be started, so it can register itself to the WebsocketService
themeService: ThemeService,
overlayService: OverlayService,
countUsersService: CountUsersService, // Needed to register itself.
configService: ConfigService,
loadFontService: LoadFontService,
dataStoreUpgradeService: DataStoreUpgradeService, // to start it.
prioritizeService: PrioritizeService,
pingService: PingService,
routingState: RoutingStateService
) {
// manually add the supported languages
translate.addLangs(['en', 'de', 'cs', 'ru']);
// this language will be used as a fallback when a translation isn't found in the current language
translate.setDefaultLang('en');
// get the browsers default language
const browserLang = translate.getBrowserLang();
// try to use the browser language if it is available. If not, uses english.
translate.use(translate.getLangs().includes(browserLang) ? browserLang : 'en');
// change default JS functions
this.overloadArrayToString();
this.overloadFlatMap();
this.overloadModulo();
// Wait until the App reaches a stable state.
// Required for the Service Worker.
appRef.isStable
.pipe(
// take only the stable state
filter(s => s),
take(1)
)
.subscribe(() => servertimeService.startScheduler());
}
/**
* Function to alter the normal Array.toString - function
*
* Will add a whitespace after a comma and shorten the output to
* three strings.
*
* TODO: There might be a better place for overloading functions than app.component
* TODO: Overloading can be extended to more functions.
*/
private overloadArrayToString(): void {
Array.prototype.toString = function(): string {
let string = '';
const iterations = Math.min(this.length, 3);
for (let i = 0; i <= iterations; i++) {
if (i < iterations) {
string += this[i];
}
if (i < iterations - 1) {
string += ', ';
} else if (i === iterations && this.length > iterations) {
string += ', ...';
}
}
return string;
};
}
/**
* Adds an implementation of flatMap.
* TODO: Remove once flatMap made its way into official JS/TS (ES 2019?)
*/
private overloadFlatMap(): void {
const concat = (x: any, y: any) => x.concat(y);
const flatMap = (f: any, xs: any) => xs.map(f).reduce(concat, []);
Array.prototype.flatMap = function(f: any): any[] {
return flatMap(f, this);
};
}
/**
* Enhances the number object with a real modulo operation (not remainder).
* TODO: Remove this, if the remainder operation is changed to modulo.
*/
private overloadModulo(): void {
Number.prototype.modulo = function(n: number): number {
return ((this % n) + n) % n;
};
}
}

View File

@ -1,47 +0,0 @@
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ServiceWorkerModule } from '@angular/service-worker';
import { AppLoadService } from './core/core-services/app-load.service';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
import { environment } from '../environments/environment';
import { LoginModule } from './site/login/login.module';
import { OpenSlidesTranslateModule } from './core/translate/openslides-translate-module';
import { SlidesModule } from './slides/slides.module';
/**
* Returns a function that returns a promis that will be resolved, if all apps are loaded.
* @param appLoadService The service that loads the apps.
*/
export function AppLoaderFactory(appLoadService: AppLoadService): () => Promise<void> {
return () => appLoadService.loadApps();
}
/**
* Global App Module. Keep it as clean as possible.
*/
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'OpenSlidesCsrfToken',
headerName: 'X-CSRFToken'
}),
BrowserAnimationsModule,
OpenSlidesTranslateModule.forRoot(),
AppRoutingModule,
CoreModule,
LoginModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
SlidesModule.forRoot()
],
providers: [{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true }],
bootstrap: [AppComponent]
})
export class AppModule {}

View File

@ -1,108 +0,0 @@
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
/**
* Provides functionalities that will be used by most components
* currently able to set the title with the suffix ' - OpenSlides'
*
* A BaseComponent is an OpenSlides Component.
* Components in the 'Side'- or 'projector' Folder are BaseComponents
*/
export abstract class BaseComponent {
/**
* To manipulate the browser title bar, adds the Suffix "OpenSlides"
*
* Might be a config variable later at some point
*/
private titleSuffix = ' - OpenSlides';
/**
* Holds the coordinates where a swipe gesture was used
*/
protected swipeCoord?: [number, number];
/**
* Holds the time when the user was swiping
*/
protected swipeTime?: number;
/**
* Determine to display a save hint
*/
public saveHint: boolean;
/**
* Settings for the TinyMCE editor selector
*/
public tinyMceSettings = {
base_url: '/tinymce', // Root for resources
suffix: '.min', // Suffix to use when loading resources
theme: 'silver',
language: null,
language_url: null,
inline: false,
statusbar: false,
browser_spellcheck: true,
image_advtab: true,
image_description: false,
link_title: false,
height: 320,
plugins: `autolink charmap code fullscreen image imagetools
lists link paste searchreplace`,
menubar: false,
contextmenu: false,
toolbar: `styleselect | bold italic underline strikethrough |
forecolor backcolor removeformat | bullist numlist |
link image charmap | code fullscreen`,
mobile: {
theme: 'mobile',
plugins: ['autosave', 'lists', 'autolink']
}
};
public constructor(protected titleService: Title, protected translate: TranslateService) {
this.tinyMceSettings.language_url = '/assets/tinymce/langs/' + this.translate.currentLang + '.js';
this.tinyMceSettings.language = this.translate.currentLang;
}
/**
* Set the title in web browser using angulars TitleService
* @param prefix The title prefix. Should be translated here.
*/
public setTitle(prefix: string): void {
const translatedPrefix = this.translate.instant(prefix);
this.titleService.setTitle(translatedPrefix + this.titleSuffix);
}
/**
* Helper for indexed *ngFor components
*
* @param index
*/
public trackByIndex(index: number): number {
return index;
}
/**
* TinyMCE Init callback. Used for certain mobile editors
* @param event
*/
protected onInitTinyMce(event: any): void {
console.log('tinyMCE event: ', event);
if (event.event.target.settings.theme === 'mobile') {
console.log('is mobile editor');
this.saveHint = true;
} else {
console.log('is no mobile editor');
event.editor.focus();
}
}
protected onLeaveTinyMce(event: any): void {
console.log('tinyevent:', event.event.type);
this.saveHint = false;
// console.log("event: ", event.event.type);
}
}

View File

@ -1,16 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { AppLoadService } from './app-load.service';
import { E2EImportsModule } from '../../../e2e-imports.module';
describe('AppLoadService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [AppLoadService]
});
});
it('should be created', inject([AppLoadService], (service: AppLoadService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,118 +0,0 @@
import { Injectable, Injector } from '@angular/core';
import { AgendaAppConfig } from '../../site/agenda/agenda.config';
import { AppConfig, ModelEntry, SearchableModelEntry } from '../definitions/app-config';
import { BaseRepository } from 'app/core/repositories/base-repository';
import { HistoryAppConfig } from 'app/site/history/history.config';
import { ProjectorAppConfig } from 'app/site/projector/projector.config';
import { TopicsAppConfig } from 'app/site/topics/topics.config';
import { AssignmentsAppConfig } from '../../site/assignments/assignments.config';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { CommonAppConfig } from '../../site/common/common.config';
import { ConfigAppConfig } from '../../site/config/config.config';
import { ServicesToLoadOnAppsLoaded } from '../core.module';
import { FallbackRoutesService } from './fallback-routes.service';
import { MainMenuService } from './main-menu.service';
import { MediafileAppConfig } from '../../site/mediafiles/mediafile.config';
import { MotionsAppConfig } from '../../site/motions/motions.config';
import { OnAfterAppsLoaded } from '../definitions/on-after-apps-loaded';
import { SearchService } from '../ui-services/search.service';
import { isSearchable } from '../../site/base/searchable';
import { TagAppConfig } from '../../site/tags/tag.config';
import { UsersAppConfig } from '../../site/users/users.config';
/**
* A list of all app configurations of all delivered apps.
*/
const appConfigs: AppConfig[] = [
CommonAppConfig,
ConfigAppConfig,
AgendaAppConfig,
AssignmentsAppConfig,
MotionsAppConfig,
MediafileAppConfig,
TagAppConfig,
UsersAppConfig,
HistoryAppConfig,
ProjectorAppConfig,
TopicsAppConfig
];
/**
* Handles loading of all apps during the bootup process.
*/
@Injectable({
providedIn: 'root'
})
export class AppLoadService {
/**
* Constructor.
*
* @param modelMapper
* @param mainMenuService
* @param searchService
*/
public constructor(
private modelMapper: CollectionStringMapperService,
private mainMenuService: MainMenuService,
private searchService: SearchService,
private injector: Injector,
private fallbackRoutesService: FallbackRoutesService
) {}
public async loadApps(): Promise<void> {
const repositories: OnAfterAppsLoaded[] = [];
appConfigs.forEach((config: AppConfig) => {
if (config.models) {
config.models.forEach(entry => {
let repository: BaseRepository<any, any, any> = null;
repository = this.injector.get(entry.repository);
repositories.push(repository);
this.modelMapper.registerCollectionElement(
entry.collectionString,
entry.model,
entry.viewModel,
repository
);
if (this.isSearchableModelEntry(entry)) {
this.searchService.registerModel(
entry.collectionString,
repository,
entry.searchOrder,
entry.openInNewTab
);
}
});
}
if (config.mainMenuEntries) {
this.mainMenuService.registerEntries(config.mainMenuEntries);
this.fallbackRoutesService.registerFallbackEntries(config.mainMenuEntries);
}
});
// Collect all services to notify for the OnAfterAppsLoadedHook
const onAfterAppsLoadedItems = ServicesToLoadOnAppsLoaded.map(service => {
return this.injector.get(service);
}).concat(repositories);
// Notify them.
onAfterAppsLoadedItems.forEach(repo => {
repo.onAfterAppsLoaded();
});
}
private isSearchableModelEntry(entry: ModelEntry | SearchableModelEntry): entry is SearchableModelEntry {
if ((<SearchableModelEntry>entry).searchOrder !== undefined) {
// We need to double check, because Typescipt cannot check contructors. If typescript could differentiate
// between (ModelConstructor<BaseModel>) and (new (...args: any[]) => (BaseModel & Searchable)), we would not have
// to check if the result of the contructor (the model instance) is really a searchable.
if (!isSearchable(new entry.viewModel())) {
throw Error(
`Wrong configuration for ${entry.collectionString}: you gave a searchOrder, but the model is not searchable.`
);
}
return true;
}
return false;
}
}

View File

@ -1,91 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router } from '@angular/router';
import { FallbackRoutesService } from './fallback-routes.service';
import { OpenSlidesService } from './openslides.service';
import { OperatorService } from './operator.service';
/**
* Classical Auth-Guard. Checks if the user has to correct permissions to enter a page, and forwards to login if not.
*/
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanActivateChild {
/**
* Constructor
*
* @param router To navigate to a target URL
* @param operator Asking for the required permission
* @param openSlidesService Handle OpenSlides functions
*/
public constructor(
private router: Router,
private operator: OperatorService,
private openSlidesService: OpenSlidesService,
private fallbackRoutesService: FallbackRoutesService
) {}
/**
* Checks of the operator has the required permission to see the state.
*
* One can set extra data to the state with `data: {basePerm: '<perm>'}` or
* `data: {basePerm: ['<perm1>', '<perm2>']}` to lock the access to users
* only with the given permission(s).
*
* @param route the route the user wants to navigate to
*/
public canActivate(route: ActivatedRouteSnapshot): boolean {
const basePerm: string | string[] = route.data.basePerm;
if (!basePerm) {
return true;
} else if (basePerm instanceof Array) {
return this.operator.hasPerms(...basePerm);
} else {
return this.operator.hasPerms(basePerm);
}
}
/**
* Calls {@method canActivate}. Should have the same logic.
*
* @param route the route the user wants to navigate to
*/
public async canActivateChild(route: ActivatedRouteSnapshot): Promise<boolean> {
await this.operator.loaded;
if (this.canActivate(route)) {
return true;
} else {
this.handleForbiddenRoute(route);
}
}
/**
* Handles a forbidden route. If the route is "/" (start page), It is tried to
* use a fallback route provided by AuthGuardFallbackRoutes. If this won't work
* or it wasn't the start page in the first place, the operator will be redirected
* to an error page.
*/
private handleForbiddenRoute(route: ActivatedRouteSnapshot): void {
if (route.url.length === 0) {
// start page
const fallbackRoute = this.fallbackRoutesService.getFallbackRoute();
if (fallbackRoute) {
this.router.navigate([fallbackRoute]);
return;
}
}
// Fall-through: If the url is the start page, but no other fallback was found,
// navigate to the error page.
this.openSlidesService.redirectUrl = location.pathname;
this.router.navigate(['/error'], {
queryParams: {
error: 'Authentication Error',
msg: route.data.basePerm
}
});
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
import { E2EImportsModule } from '../../../e2e-imports.module';
describe('ConfigService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [AuthService]
});
});
it('should be created', inject([AuthService], (service: AuthService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,117 +0,0 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { environment } from 'environments/environment';
import { OperatorService, WhoAmI } from 'app/core/core-services/operator.service';
import { DEFAULT_AUTH_TYPE, UserAuthType } from 'app/shared/models/users/user';
import { DataStoreService } from './data-store.service';
import { HttpService } from './http.service';
import { OpenSlidesService } from './openslides.service';
/**
* Authenticates an OpenSlides user with username and password
*/
@Injectable({
providedIn: 'root'
})
export class AuthService {
/**
* Initializes the httpClient and the {@link OperatorService}.
*
* @param http HttpService to send requests to the server
* @param operator Who is using OpenSlides
* @param OpenSlides The openslides service
* @param router To navigate
*/
public constructor(
private http: HttpService,
private operator: OperatorService,
private OpenSlides: OpenSlidesService,
private router: Router,
private DS: DataStoreService
) {}
/**
* Try to log in a user with a given auth type
*
* - Type "default": username and password needed; the earlySuccessCallback will be called.
* - Type "saml": The windows location will be changed to the single-sign-on service initiator.
*/
public async login(
authType: UserAuthType,
username: string,
password: string,
earlySuccessCallback: () => void
): Promise<void> {
if (authType === 'default') {
const user = {
username: username,
password: password
};
const response = await this.http.post<WhoAmI>(environment.urlPrefix + '/users/login/', user);
earlySuccessCallback();
await this.OpenSlides.shutdown();
await this.operator.setWhoAmI(response);
await this.OpenSlides.afterLoginBootup(response.user_id);
await this.redirectUser(response.user_id);
} else if (authType === 'saml') {
window.location.href = environment.urlPrefix + '/saml/?sso'; // Bye
} else {
throw new Error(`Unsupported auth type "${authType}"`);
}
}
/**
* Redirects the user to the page where he came from. Boots OpenSlides,
* if it wasn't done before.
*/
public async redirectUser(userId: number): Promise<void> {
if (!this.OpenSlides.booted) {
await this.OpenSlides.afterLoginBootup(userId);
}
let redirect = this.OpenSlides.redirectUrl ? this.OpenSlides.redirectUrl : '/';
const excludedUrls = ['login'];
if (excludedUrls.some(url => redirect.includes(url))) {
redirect = '/';
}
this.router.navigate([redirect]);
}
/**
* Login for guests.
*/
public async guestLogin(): Promise<void> {
this.redirectUser(null);
}
/**
* Logout function for both the client and the server.
*
* Will clear the datastore, update the current operator and
* send a `post`-request to `/apps/users/logout/'`. Restarts OpenSlides.
*/
public async logout(): Promise<void> {
const authType = this.operator.authType.getValue();
if (authType === DEFAULT_AUTH_TYPE) {
let response = null;
try {
response = await this.http.post<WhoAmI>(environment.urlPrefix + '/users/logout/', {});
} catch (e) {
// We do nothing on failures. Reboot OpenSlides anyway.
}
this.router.navigate(['/']);
await this.DS.clear();
await this.operator.setWhoAmI(response);
await this.OpenSlides.reboot();
} else if (authType === 'saml') {
await this.DS.clear();
await this.operator.setWhoAmI(null);
window.location.href = environment.urlPrefix + '/saml/?slo'; // Bye
} else {
throw new Error(`Unsupported auth type "${authType}"`);
}
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { AutoupdateService } from './autoupdate.service';
import { E2EImportsModule } from '../../../e2e-imports.module';
describe('AutoupdateService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [AutoupdateService]
});
});
it('should be created', inject([AutoupdateService], (service: AutoupdateService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,190 +0,0 @@
import { Injectable } from '@angular/core';
import { BaseModel } from '../../shared/models/base/base-model';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { DataStoreService, DataStoreUpdateManagerService } from './data-store.service';
import { WEBSOCKET_ERROR_CODES, WebsocketService } from './websocket.service';
interface AutoupdateFormat {
/**
* All changed (and created) items as their full/restricted data grouped by their collection.
*/
changed: {
[collectionString: string]: object[];
};
/**
* All deleted items (by id) grouped by their collection.
*/
deleted: {
[collectionString: string]: number[];
};
/**
* The lower change id bond for this autoupdate
*/
from_change_id: number;
/**
* The upper change id bound for this autoupdate
*/
to_change_id: number;
/**
* Flag, if this autoupdate contains all data. If so, the DS needs to be resetted.
*/
all_data: boolean;
}
/**
* Handles the initial update and automatic updates using the {@link WebsocketService}
* Incoming objects, usually BaseModels, will be saved in the dataStore (`this.DS`)
* This service usually creates all models
*/
@Injectable({
providedIn: 'root'
})
export class AutoupdateService {
/**
* Constructor to create the AutoupdateService. Calls the constructor of the parent class.
* @param websocketService
* @param DS
* @param modelMapper
*/
public constructor(
private websocketService: WebsocketService,
private DS: DataStoreService,
private modelMapper: CollectionStringMapperService,
private DSUpdateManager: DataStoreUpdateManagerService
) {
this.websocketService.getOberservable<AutoupdateFormat>('autoupdate').subscribe(response => {
this.storeResponse(response);
});
// Check for too high change id-errors. If this happens, reset the DS and get fresh data.
this.websocketService.errorResponseObservable.subscribe(error => {
if (error.code === WEBSOCKET_ERROR_CODES.CHANGE_ID_TOO_HIGH) {
this.doFullUpdate();
}
});
}
/**
* Handle the answer of incoming data via {@link WebsocketService}.
*
* Detects the Class of an incomming model, creates a new empty object and assigns
* the data to it using the deserialize function. Also models that are flagged as deleted
* will be removed from the data store.
*
* Handles the change ids of all autoupdates.
*/
private async storeResponse(autoupdate: AutoupdateFormat): Promise<void> {
if (autoupdate.all_data) {
await this.storeAllData(autoupdate);
} else {
await this.storePartialAutoupdate(autoupdate);
}
}
/**
* Stores all data from the autoupdate. This means, that the DS is resettet and filled with just the
* given data from the autoupdate.
* @param autoupdate The autoupdate
*/
private async storeAllData(autoupdate: AutoupdateFormat): Promise<void> {
let elements: BaseModel[] = [];
Object.keys(autoupdate.changed).forEach(collection => {
elements = elements.concat(this.mapObjectsToBaseModels(collection, autoupdate.changed[collection]));
});
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS);
await this.DS.set(elements, autoupdate.to_change_id);
this.DSUpdateManager.commit(updateSlot, autoupdate.to_change_id, true);
}
/**
* handles a normal autoupdate that is not a full update (all_data=false).
* @param autoupdate The autoupdate
*/
private async storePartialAutoupdate(autoupdate: AutoupdateFormat): Promise<void> {
const maxChangeId = this.DS.maxChangeId;
if (autoupdate.from_change_id <= maxChangeId && autoupdate.to_change_id <= maxChangeId) {
console.log(`Ignore. Clients change id: ${maxChangeId}`);
return; // Ignore autoupdates, that lay full behind our changeid.
}
// Normal autoupdate
if (autoupdate.from_change_id <= maxChangeId + 1 && autoupdate.to_change_id > maxChangeId) {
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS);
// Delete the removed objects from the DataStore
for (const collection of Object.keys(autoupdate.deleted)) {
await this.DS.remove(collection, autoupdate.deleted[collection]);
}
// Add the objects to the DataStore.
for (const collection of Object.keys(autoupdate.changed)) {
await this.DS.add(this.mapObjectsToBaseModels(collection, autoupdate.changed[collection]));
}
await this.DS.flushToStorage(autoupdate.to_change_id);
this.DSUpdateManager.commit(updateSlot, autoupdate.to_change_id);
} else {
// autoupdate fully in the future. we are missing something!
this.requestChanges();
}
}
/**
* Creates baseModels for each plain object. If the collection is not registered,
* A console error will be issued and an empty list returned.
*
* @param collection The collection all models have to be from.
* @param models All models that should be mapped to BaseModels
* @returns A list of basemodels constructed from the given models.
*/
private mapObjectsToBaseModels(collection: string, models: object[]): BaseModel[] {
if (this.modelMapper.isCollectionRegistered(collection)) {
const targetClass = this.modelMapper.getModelConstructor(collection);
return models.map(model => new targetClass(model));
} else {
console.error(`Unregistered collection "${collection}". Ignore it.`);
return [];
}
}
/**
* Sends a WebSocket request to the Server with the maxChangeId of the DataStore.
* The server should return an autoupdate with all new data.
*/
public requestChanges(): void {
const changeId = this.DS.maxChangeId === 0 ? 0 : this.DS.maxChangeId + 1;
console.log(`requesting changed objects with DS max change id ${changeId}`);
this.websocketService.send('getElements', { change_id: changeId });
}
/**
* Does a full update: Requests all data from the server and sets the DS to the fresh data.
*/
public async doFullUpdate(): Promise<void> {
const oldChangeId = this.DS.maxChangeId;
const response = await this.websocketService.sendAndGetResponse<{}, AutoupdateFormat>('getElements', {});
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS);
let allModels: BaseModel[] = [];
for (const collection of Object.keys(response.changed)) {
if (this.modelMapper.isCollectionRegistered(collection)) {
allModels = allModels.concat(this.mapObjectsToBaseModels(collection, response.changed[collection]));
} else {
console.error(`Unregistered collection "${collection}". Ignore it.`);
}
}
await this.DS.set(allModels, response.to_change_id);
this.DSUpdateManager.commit(updateSlot, response.to_change_id, true);
console.log(`Full update done from ${oldChangeId} to ${response.to_change_id}`);
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { E2EImportsModule } from '../../../e2e-imports.module';
describe('CollectionStringMapperService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [CollectionStringMapperService]
});
});
it('should be created', inject([CollectionStringMapperService], (service: CollectionStringMapperService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,140 +0,0 @@
import { Injectable } from '@angular/core';
import { BaseRepository } from 'app/core/repositories/base-repository';
import { BaseViewModel, TitleInformation, ViewModelConstructor } from 'app/site/base/base-view-model';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
/**
* Unifies the ModelConstructor and ViewModelConstructor.
*/
interface UnifiedConstructors {
COLLECTIONSTRING: string;
new (...args: any[]): any;
}
/**
* Every types supported: (View)ModelConstructors, repos and collectionstrings.
*/
type TypeIdentifier = UnifiedConstructors | BaseRepository<any, any, any> | string;
type CollectionStringMappedTypes = [
ModelConstructor<BaseModel>,
ViewModelConstructor<BaseViewModel>,
BaseRepository<BaseViewModel<any>, BaseModel<any>, TitleInformation>
];
/**
* Registeres the mapping between collection strings, models constructors, view
* model constructors and repositories.
* All models need to be registered!
*/
@Injectable({
providedIn: 'root'
})
export class CollectionStringMapperService {
/**
* Maps collection strings to mapping entries
*/
private collectionStringMapping: {
[collectionString: string]: CollectionStringMappedTypes;
} = {};
public constructor() {}
/**
* Registers the combination of a collection string, model, view model and repository
* @param collectionString
* @param model
*/
public registerCollectionElement<V extends BaseViewModel<M>, M extends BaseModel>(
collectionString: string,
model: ModelConstructor<M>,
viewModel: ViewModelConstructor<V>,
repository: BaseRepository<V, M, TitleInformation>
): void {
this.collectionStringMapping[collectionString] = [model, viewModel, repository];
}
/**
* @param obj The object to get the collection string from.
* @returns the collectionstring
*/
public getCollectionString(obj: TypeIdentifier): string {
if (typeof obj === 'string') {
return obj;
} else {
return obj.COLLECTIONSTRING;
}
}
/**
* @returns true, if the given collection is known by this service.
*/
public isCollectionRegistered(collectionString: string): boolean {
return !!this.collectionStringMapping[collectionString];
}
/**
* @param obj The object to get the model constructor from.
* @returns the model constructor
*/
public getModelConstructor<M extends BaseModel>(obj: TypeIdentifier): ModelConstructor<M> | null {
if (this.isCollectionRegistered(this.getCollectionString(obj))) {
return this.collectionStringMapping[this.getCollectionString(obj)][0] as ModelConstructor<M>;
}
}
/**
* @param obj The object to get the view model constructor from.
* @returns the view model constructor
*/
public getViewModelConstructor<M extends BaseViewModel>(obj: TypeIdentifier): ViewModelConstructor<M> | null {
if (this.isCollectionRegistered(this.getCollectionString(obj))) {
return this.collectionStringMapping[this.getCollectionString(obj)][1] as ViewModelConstructor<M>;
}
}
/**
* @param obj The object to get the repository from.
* @returns the repository
*/
public getRepository<V extends BaseViewModel, M extends BaseModel, T extends TitleInformation>(
obj: TypeIdentifier
): BaseRepository<V & T, M, T> | null {
if (this.isCollectionRegistered(this.getCollectionString(obj))) {
return this.collectionStringMapping[this.getCollectionString(obj)][2] as BaseRepository<V & T, M, T>;
}
}
/**
* @returns all registered repositories.
*/
public getAllRepositories(): BaseRepository<any, any, any>[] {
return Object.values(this.collectionStringMapping).map((types: CollectionStringMappedTypes) => types[2]);
}
/**
* Validates the given element id. It must have the form `<collection>:<id>`, with
* <collection> being a registered collection and the id a valid integer greater then 0.
*
* @param elementId The element id.
* @returns true, if the element id is valid.
*/
public isElementIdValid(elementId: any): boolean {
if (!elementId || typeof elementId !== 'string') {
return false;
}
const splitted = elementId.split(':');
if (splitted.length !== 2) {
return false;
}
const id = parseInt(splitted[1], 10);
if (isNaN(id) || id <= 0) {
return false;
}
return Object.keys(this.collectionStringMapping).some(collection => collection === splitted[0]);
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { ConstantsService } from './constants.service';
import { E2EImportsModule } from '../../../e2e-imports.module';
describe('ConstantsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [ConstantsService]
});
});
it('should be created', inject([ConstantsService], (service: ConstantsService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,79 +0,0 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
import { WebsocketService } from './websocket.service';
/**
* constants have a key associated with the data.
*/
interface Constants {
[key: string]: any;
}
/**
* Get constants from the server.
*
* @example
* ```ts
* this.constantsService.get('Settings').subscribe(constant => {
* console.log(constant);
* });
* ```
*/
@Injectable({
providedIn: 'root'
})
export class ConstantsService {
/**
* The constants
*/
private constants: Constants = {};
/**
* Pending requests will be notified by these subjects, one per key.
*/
private subjects: { [key: string]: BehaviorSubject<any> } = {};
/**
* @param websocketService
*/
public constructor(private websocketService: WebsocketService) {
// The hook for recieving constants.
websocketService.getOberservable<Constants>('constants').subscribe(constants => {
this.constants = constants;
Object.keys(this.subjects).forEach(key => {
this.subjects[key].next(this.constants[key]);
});
});
// We can request constants, if the websocket connection opens.
// On retries, the `refresh()` method is called by the OpenSlidesService, so
// here we do not need to take care about this.
websocketService.noRetryConnectEvent.subscribe(() => {
this.refresh();
});
}
/**
* Get the constant named by key.
* @param key The constant to get.
*/
public get<T>(key: string): Observable<T> {
if (!this.subjects[key]) {
this.subjects[key] = new BehaviorSubject<any>(this.constants[key]);
}
return this.subjects[key].asObservable().pipe(filter(x => !!x));
}
/**
* Refreshed the constants
*/
public refresh(): Promise<void> {
if (!this.websocketService.isConnected) {
return;
}
this.websocketService.send('constants', {});
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { DataSendService } from './data-send.service';
import { E2EImportsModule } from '../../../e2e-imports.module';
describe('DataSendService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [DataSendService]
});
});
it('should be created', inject([DataSendService], (service: DataSendService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,62 +0,0 @@
import { Injectable } from '@angular/core';
import { BaseModel } from '../../shared/models/base/base-model';
import { HttpService } from './http.service';
import { Identifiable } from '../../shared/models/base/identifiable';
/**
* Send data back to server. Cares about the right REST routes.
*
* Contrast to dataStore service
*/
@Injectable({
providedIn: 'root'
})
export class DataSendService {
/**
* Construct a DataSendService
*
* @param httpService The HTTP Service
*/
public constructor(private httpService: HttpService) {}
/**
* Sends a post request with the model to the server to create it.
*
* @param model The model to create.
*/
public async createModel(model: BaseModel): Promise<Identifiable> {
const restPath = `/rest/${model.collectionString}/`;
return await this.httpService.post<Identifiable>(restPath, model);
}
/**
* Function to fully update a model on the server.
*
* @param model The model that is meant to be changed.
*/
public async updateModel(model: BaseModel): Promise<void> {
const restPath = `/rest/${model.collectionString}/${model.id}/`;
await this.httpService.put(restPath, model);
}
/**
* Updates a model partially on the server.
*
* @param model The model to partially update.
*/
public async partialUpdateModel(model: BaseModel): Promise<void> {
const restPath = `/rest/${model.collectionString}/${model.id}/`;
await this.httpService.patch(restPath, model);
}
/**
* Deletes the given model on the server.
*
* @param model the model that shall be deleted.
*/
public async deleteModel(model: BaseModel): Promise<void> {
const restPath = `/rest/${model.collectionString}/${model.id}/`;
await this.httpService.delete(restPath);
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { DataStoreUpgradeService } from './data-store-upgrade.service';
import { E2EImportsModule } from '../../../e2e-imports.module';
describe('DataStoreUpgradeService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [DataStoreUpgradeService]
});
});
it('should be created', inject([DataStoreUpgradeService], (service: DataStoreUpgradeService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,91 +0,0 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { AutoupdateService } from './autoupdate.service';
import { ConstantsService } from './constants.service';
import { StorageService } from './storage.service';
interface SchemaVersion {
db: string;
config: number;
migration: number;
}
function isSchemaVersion(obj: any): obj is SchemaVersion {
if (!obj || typeof obj !== 'object') {
return false;
}
return obj.db !== undefined && obj.config !== undefined && obj.migration !== undefined;
}
const SCHEMA_VERSION = 'SchemaVersion';
/**
* Manages upgrading the DataStore, if the migration version from the server is higher than the current one.
*/
@Injectable({
providedIn: 'root'
})
export class DataStoreUpgradeService {
/**
* Notify, when upgrade has checked.
*/
public readonly upgradeChecked = new BehaviorSubject(false);
/**
* @param autoupdateService
* @param constantsService
* @param storageService
*/
public constructor(
private autoupdateService: AutoupdateService,
private constantsService: ConstantsService,
private storageService: StorageService
) {
// Prevent the schema version to be cleard. This is important
// after a reset from OpenSlides, because the complete data is
// queried from the server and we do not want also to trigger a reload
// by changing the schema from null -> <schema>.
this.storageService.addNoClearKey(SCHEMA_VERSION);
this.constantsService
.get<SchemaVersion>(SCHEMA_VERSION)
.subscribe(serverVersion => this.checkForUpgrade(serverVersion));
}
public async checkForUpgrade(serverVersion: SchemaVersion): Promise<boolean> {
this.upgradeChecked.next(false);
console.log('Server schema version:', serverVersion);
const clientVersion = await this.storageService.get<SchemaVersion>(SCHEMA_VERSION);
await this.storageService.set(SCHEMA_VERSION, serverVersion);
let doUpgrade = false;
if (isSchemaVersion(clientVersion)) {
if (clientVersion.db !== serverVersion.db) {
console.log(`\tDB id changed from ${clientVersion.db} to ${serverVersion.db}`);
doUpgrade = true;
}
if (clientVersion.config !== serverVersion.config) {
console.log(`\tConfig changed from ${clientVersion.config} to ${serverVersion.config}`);
doUpgrade = true;
}
if (clientVersion.migration !== serverVersion.migration) {
console.log(`\tMigration changed from ${clientVersion.migration} to ${serverVersion.migration}`);
doUpgrade = true;
}
} else {
console.log('\tNo client schema version.');
doUpgrade = true;
}
if (doUpgrade) {
console.log('\t-> In result of a schema version change: Do full update.');
await this.autoupdateService.doFullUpdate();
} else {
console.log('\t-> No upgrade needed.');
}
this.upgradeChecked.next(true);
return doUpgrade;
}
}

View File

@ -1,11 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { DataStoreService } from './data-store.service';
describe('DS', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [DataStoreService]
});
});
});

View File

@ -1,651 +0,0 @@
import { EventEmitter, Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { Deferred } from '../promises/deferred';
import { RelationCacheService } from './relation-cache.service';
import { StorageService } from './storage.service';
/**
* Represents information about a deleted model.
*
* As the model doesn't exist anymore, just the former id and collection is known.
*/
export interface DeletedInformation {
collection: string;
id: number;
}
export interface CollectionIds {
[collection: string]: number[];
}
/**
* Helper class for collecting data during the update phase of the DataStore.
*/
export class UpdateSlot {
/**
* Count instnaces of this class to easily compare them.
*/
private static ID_COUTNER = 1;
/**
* Mapping of changed model ids to their collection.
*/
private changedModels: CollectionIds = {};
/**
* Mapping of deleted models to their collection.
*/
private deletedModels: CollectionIds = {};
/**
* The object's id.
*/
private _id: number;
/**
* @param DS Carries the DataStore: TODO (see below `DataStoreUpdateManagerService.getNewUpdateSlot`)
*/
public constructor(public readonly DS: DataStoreService) {
this._id = UpdateSlot.ID_COUTNER++;
}
/**
* Adds changed model information
*
* @param collection The collection
* @param id The id
*/
public addChangedModel(collection: string, id: number): void {
if (!this.changedModels[collection]) {
this.changedModels[collection] = [];
}
this.changedModels[collection].push(id);
}
/**
* Adds deleted model information
*
* @param collection The collection
* @param id The id
*/
public addDeletedModel(collection: string, id: number): void {
if (!this.deletedModels[collection]) {
this.deletedModels[collection] = [];
}
this.deletedModels[collection].push(id);
}
/**
* @param collection The collection
* @returns the list of changed model ids for the collection
*/
public getChangedModelIdsForCollection(collection: string): number[] {
return this.changedModels[collection] || [];
}
/**
* @returns the mapping of all changed models
*/
public getChangedModels(): CollectionIds {
return this.changedModels;
}
/**
* @param collection The collection
* @returns the list of deleted model ids for the collection
*/
public getDeletedModelIdsForCollection(collection: string): number[] {
return this.deletedModels[collection] || [];
}
/**
* @returns the mapping of all deleted models
*/
public getDeletedModels(): CollectionIds {
return this.deletedModels;
}
/**
* @returns all changed and deleted model ids in one array. If an id was
* changed and deleted, it will be there twice! But this should not be the case.
*/
public getAllModelsIdsForCollection(collection: string): number[] {
return this.getDeletedModelIdsForCollection(collection).concat(
this.getChangedModelIdsForCollection(collection)
);
}
/**
* Compares this object to another update slot.
*/
public equal(other: UpdateSlot): boolean {
return this._id === other._id;
}
}
/**
* represents a collection on the Django server, uses an ID to access a {@link BaseModel}.
*
* Part of {@link DataStoreService}
*/
interface ModelCollection {
[id: number]: BaseModel;
}
/**
* Represents a serialized collection.
*/
interface JsonCollection {
[id: number]: string;
}
/**
* The actual storage that stores collections, accessible by strings.
*
* {@link DataStoreService}
*/
interface ModelStorage {
[collectionString: string]: ModelCollection;
}
/**
* A storage of serialized collection elements.
*/
interface JsonStorage {
[collectionString: string]: JsonCollection;
}
/**
* TODO: Avoid circular dependencies between `DataStoreUpdateManagerService` and `DataStoreService` and split them into two files
*/
@Injectable({
providedIn: 'root'
})
export class DataStoreUpdateManagerService {
/**
* The current update slot
*/
private currentUpdateSlot: UpdateSlot | null = null;
/**
* Requests for getting an update slot.
*/
private updateSlotRequests: Deferred[] = [];
/**
* @param mapperService
*/
public constructor(
private mapperService: CollectionStringMapperService,
private relationCacheService: RelationCacheService
) {}
/**
* Retrieve the current update slot.
*/
public getCurrentUpdateSlot(): UpdateSlot | null {
return this.currentUpdateSlot;
}
/**
* Get a new update slot. Returns a promise that must be awaited, if there is another
* update in progress.
*
* @param DS The DataStore. This is a hack, becuase we cannot use the DataStore
* here, because these are cyclic dependencies... --> TODO
*/
public async getNewUpdateSlot(DS: DataStoreService): Promise<UpdateSlot> {
if (this.currentUpdateSlot) {
const request = new Deferred();
this.updateSlotRequests.push(request);
await request;
}
this.currentUpdateSlot = new UpdateSlot(DS);
return this.currentUpdateSlot;
}
/**
* Commits the given update slot. This slot must be the current one. If there are requests
* for update slots queued, the next one will be served.
*
* Note: I added this param to make sure, that only the user of the slot
* can commit the update and no one else.
*
* @param slot The slot to commit
*/
public commit(slot: UpdateSlot, changeId: number, resetCache: boolean = false): void {
if (!this.currentUpdateSlot || !this.currentUpdateSlot.equal(slot)) {
throw new Error('No or wrong update slot to be finished!');
}
this.currentUpdateSlot = null;
// notify repositories in two phases
const repositories = this.mapperService.getAllRepositories();
if (resetCache) {
this.relationCacheService.reset();
}
// Phase 1: deleting and creating of view models (in this order)
repositories.forEach(repo => {
const deletedModelIds = slot.getDeletedModelIdsForCollection(repo.collectionString);
repo.deleteModels(deletedModelIds);
this.relationCacheService.registerDeletedModels(repo.collectionString, deletedModelIds);
const changedModelIds = slot.getChangedModelIdsForCollection(repo.collectionString);
repo.changedModels(changedModelIds);
this.relationCacheService.registerChangedModels(repo.collectionString, changedModelIds, changeId);
});
// Phase 2: updating all repositories
repositories.forEach(repo => {
repo.commitUpdate(slot.getAllModelsIdsForCollection(repo.collectionString));
});
slot.DS.triggerModifiedObservable();
// serve next slot request
if (this.updateSlotRequests.length > 0) {
const request = this.updateSlotRequests.pop();
request.resolve();
}
}
}
/**
* All mighty DataStore that comes with all OpenSlides components.
* Use this.DS in an OpenSlides Component to Access the store.
* Used by a lot of components, classes and services.
* Changes can be observed
*/
@Injectable({
providedIn: 'root'
})
export class DataStoreService {
private static cachePrefix = 'DS:';
/** We will store the data twice: One as instances of the actual models in the _store
* and one serialized version in the _serializedStore for the cache. Both should be updated in
* all cases equal!
*/
private modelStore: ModelStorage = {};
private jsonStore: JsonStorage = {};
/**
* Subjects for changed elements (notified, even if there is a current update slot) for
* a specific collection.
*/
private changedSubjects: { [collection: string]: Subject<BaseModel> } = {};
/**
* Observable subject for changed or deleted models in the datastore.
*/
private readonly modifiedSubject: Subject<void> = new Subject<void>();
/**
* Observe the datastore for changes and deletions.
*
* @return an observable for changed and deleted objects.
*/
public get modifiedObservable(): Observable<void> {
return this.modifiedSubject.asObservable();
}
/**
* Observable subject for changed or deleted models in the datastore.
*/
private readonly clearEvent: EventEmitter<void> = new EventEmitter<void>();
/**
* Observe the datastore for changes and deletions.
*
* @return an observable for changed and deleted objects.
*/
public get clearObservable(): Observable<void> {
return this.clearEvent.asObservable();
}
/**
* The maximal change id from this DataStore.
*/
private _maxChangeId = 0;
/**
* returns the maxChangeId of the DataStore.
*/
public get maxChangeId(): number {
return this._maxChangeId;
}
/**
* @param storageService use StorageService to preserve the DataStore.
* @param modelMapper
* @param DSUpdateManager
*/
public constructor(
private storageService: StorageService,
private modelMapper: CollectionStringMapperService,
private DSUpdateManager: DataStoreUpdateManagerService
) {}
/**
* Get an model observable for models from a given collection. These observable will be notified,
* even if there is an active update slot. So use this with caution (-> only collections with less models).
*
* @param collectionType The collection
*/
public getChangeObservable<T extends BaseModel>(collectionType: ModelConstructor<T> | string): Observable<T> {
const collection = this.getCollectionString(collectionType);
if (!this.changedSubjects[collection]) {
this.changedSubjects[collection] = new Subject();
}
return this.changedSubjects[collection].asObservable() as Observable<T>;
}
/**
* Gets the DataStore from cache and instantiate all models out of the serialized version.
* @returns The max change id.
*/
public async initFromStorage(): Promise<number> {
// This promise will be resolved with cached datastore.
const store = await this.storageService.get<JsonStorage>(DataStoreService.cachePrefix + 'DS');
if (store) {
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this);
// There is a store. Deserialize it
this.jsonStore = store;
this.modelStore = this.deserializeJsonStore(this.jsonStore);
// Get the maxChangeId from the cache
let maxChangeId = await this.storageService.get<number>(DataStoreService.cachePrefix + 'maxChangeId');
if (!maxChangeId) {
maxChangeId = 0;
}
this._maxChangeId = maxChangeId;
// update observers
Object.keys(this.modelStore).forEach(collection => {
Object.keys(this.modelStore[collection]).forEach(id => {
this.publishChangedInformation(this.modelStore[collection][id]);
});
});
this.DSUpdateManager.commit(updateSlot, maxChangeId, true);
} else {
await this.clear();
}
return this.maxChangeId;
}
/**
* Deserialze the given serializedStorage and returns a Storage.
* @param serializedStore The store to deserialize
* @returns The serialized storage
*/
private deserializeJsonStore(serializedStore: JsonStorage): ModelStorage {
const storage: ModelStorage = {};
Object.keys(serializedStore).forEach(collectionString => {
storage[collectionString] = {} as ModelCollection;
const target = this.modelMapper.getModelConstructor(collectionString);
if (target) {
Object.keys(serializedStore[collectionString]).forEach(id => {
const data = JSON.parse(serializedStore[collectionString][id]);
storage[collectionString][id] = new target(data);
});
}
});
return storage;
}
/**
* Clears the complete DataStore and Cache.
*/
public async clear(): Promise<void> {
this.modelStore = {};
this.jsonStore = {};
this._maxChangeId = 0;
await this.storageService.remove(DataStoreService.cachePrefix + 'DS');
await this.storageService.remove(DataStoreService.cachePrefix + 'maxChangeId');
this.clearEvent.next();
}
/**
* Returns the collection _string_ based on the model given. If a string is given, it's just returned.
* @param collectionType Either a Model constructor or a string.
* @returns the collection string
*/
private getCollectionString<T extends BaseModel<T>>(collectionType: ModelConstructor<T> | string): string {
if (typeof collectionType === 'string') {
return collectionType;
} else {
return this.modelMapper.getCollectionString(collectionType);
}
}
/**
* Read one model based on the collection and id from the DataStore.
*
* @param collectionType The desired BaseModel or collectionString to be read from the dataStore
* @param ids One ID of the BaseModel
* @return The given BaseModel-subclass instance
* @example: this.DS.get(User, 1)
* @example: this.DS.get<Countdown>('core/countdown', 2)
*/
public get<T extends BaseModel<T>>(collectionType: ModelConstructor<T> | string, id: number): T {
const collectionString = this.getCollectionString<T>(collectionType);
const collection: ModelCollection = this.modelStore[collectionString];
if (!collection) {
return;
} else {
return collection[id] as T;
}
}
/**
* Read multiple ID's from dataStore.
*
* @param collectionType The desired BaseModel or collectionString to be read from the dataStore
* @param ids Multiple IDs as a list of IDs of BaseModel
* @return The BaseModel-list corresponding to the given ID(s)
* @example: this.DS.getMany(User, [1,2,3,4,5])
* @example: this.DS.getMany<User>('users/user', [1,2,3,4,5])
*/
public getMany<T extends BaseModel<T>>(collectionType: ModelConstructor<T> | string, ids: number[]): T[] {
const collectionString = this.getCollectionString<T>(collectionType);
const collection: ModelCollection = this.modelStore[collectionString];
if (!collection) {
return [];
}
const models = ids
.map(id => {
return collection[id];
})
.filter(model => !!model); // remove non valid models.
return models as T[];
}
/**
* Get all models of the given collection from the DataStore.
*
* @param collectionType The desired BaseModel or collectionString to be read from the dataStore
* @return The BaseModel-list of all instances of T
* @example: this.DS.getAll(User)
* @example: this.DS.getAll<User>('users/user')
*/
public getAll<T extends BaseModel<T>>(collectionType: ModelConstructor<T> | string): T[] {
const collectionString = this.getCollectionString<T>(collectionType);
const collection: ModelCollection = this.modelStore[collectionString];
if (!collection) {
return [];
} else {
return Object.values(collection);
}
}
/**
* Filters the dataStore by type.
*
* @param collectionType The desired BaseModel type to be read from the dataStore
* @param callback a filter function
* @return The BaseModel-list corresponding to the filter function
* @example this.DS.filter<User>(User, myUser => myUser.first_name === "Max")
*/
public filter<T extends BaseModel<T>>(
collectionType: ModelConstructor<T> | string,
callback: (model: T) => boolean
): T[] {
return this.getAll<T>(collectionType).filter(callback);
}
/**
* Finds a model item in the dataStore by type.
*
* @param collectionType The desired BaseModel type to be read from the dataStore
* @param callback a find function
* @return The first BaseModel item matching the filter function
* @example this.DS.find<User>(User, myUser => myUser.first_name === "Jenny")
*/
public find<T extends BaseModel<T>>(
collectionType: ModelConstructor<T> | string,
callback: (model: T) => boolean
): T {
return this.getAll<T>(collectionType).find(callback);
}
/**
* Add one or multiple models to dataStore.
*
* @param models BaseModels to add to the store
* @param changeId The changeId of this update. If given, the storage will be flushed to the
* cache. Else one can call {@method flushToStorage} to do this manually.
* @example this.DS.add([new User(1)])
* @example this.DS.add([new User(2), new User(3)])
* @example this.DS.add(arrayWithUsers, changeId)
*/
public async add(models: BaseModel[], changeId?: number): Promise<void> {
models.forEach(model => {
const collection = model.collectionString;
if (this.modelStore[collection] === undefined) {
this.modelStore[collection] = {};
}
this.modelStore[collection][model.id] = model;
if (this.jsonStore[collection] === undefined) {
this.jsonStore[collection] = {};
}
this.jsonStore[collection][model.id] = JSON.stringify(model);
this.publishChangedInformation(model);
});
if (changeId) {
await this.flushToStorage(changeId);
}
}
/**
* removes one or multiple models from dataStore.
*
* @param collectionString The desired BaseModel type to be removed from the datastore
* @param ids A list of IDs of BaseModels to remove from the datastore
* @param changeId The changeId of this update. If given, the storage will be flushed to the
* cache. Else one can call {@method flushToStorage} to do this manually.
* @example this.DS.remove('users/user', [myUser.id, 3, 4])
*/
public async remove(collectionString: string, ids: number[], changeId?: number): Promise<void> {
ids.forEach(id => {
if (this.modelStore[collectionString]) {
delete this.modelStore[collectionString][id];
}
if (this.jsonStore[collectionString]) {
delete this.jsonStore[collectionString][id];
}
this.publishDeletedInformation({
collection: collectionString,
id: id
});
});
if (changeId) {
await this.flushToStorage(changeId);
}
}
/**
* Resets the DataStore and set the given models as the new content.
* @param models A list of models to set the DataStore to.
* @param newMaxChangeId Optional. If given, the max change id will be updated
* and the store flushed to the storage
*/
public async set(models?: BaseModel[], newMaxChangeId?: number): Promise<void> {
const modelStoreReference = this.modelStore;
this.modelStore = {};
this.jsonStore = {};
// Inform about the deletion
Object.keys(modelStoreReference).forEach(collectionString => {
Object.keys(modelStoreReference[collectionString]).forEach(id => {
this.publishDeletedInformation({
collection: collectionString,
id: +id // needs casting, because Objects.keys gives all keys as strings...
});
});
});
if (models && models.length) {
await this.add(models, newMaxChangeId);
}
}
/**
* Informs the changed and changedOrDeleted subject about a change.
*
* @param model The model to publish
*/
private publishChangedInformation(model: BaseModel): void {
const slot = this.DSUpdateManager.getCurrentUpdateSlot();
if (slot) {
slot.addChangedModel(model.collectionString, model.id);
// triggerModifiedObservable will be called by committing the update slot.
} else {
this.triggerModifiedObservable();
}
if (this.changedSubjects[model.collectionString]) {
this.changedSubjects[model.collectionString].next(model);
}
}
/**
* Informs the deleted and changedOrDeleted subject about a deletion.
*
* @param information The information about the deleted model
*/
private publishDeletedInformation(information: DeletedInformation): void {
const slot = this.DSUpdateManager.getCurrentUpdateSlot();
if (slot) {
slot.addDeletedModel(information.collection, information.id);
// triggerModifiedObservable will be called by committing the update slot.
} else {
this.triggerModifiedObservable();
}
}
/**
* Triggers the modified subject.
*/
public triggerModifiedObservable(): void {
this.modifiedSubject.next();
}
/**
* Updates the cache by inserting the serialized DataStore. Also changes the chageId, if it's larger
* @param changeId The changeId from the update. If it's the highest change id seen, it will be set into the cache.
*/
public async flushToStorage(changeId: number): Promise<void> {
this._maxChangeId = changeId;
await this.storageService.set(DataStoreService.cachePrefix + 'DS', this.jsonStore);
await this.storageService.set(DataStoreService.cachePrefix + 'maxChangeId', changeId);
}
}

View File

@ -1,43 +0,0 @@
import { Injectable } from '@angular/core';
import { OperatorService } from './operator.service';
export interface AuthGuardFallbackEntry {
route: string;
weight: number;
permission: string;
}
/**
* Classical Auth-Guard. Checks if the user has to correct permissions to enter a page, and forwards to login if not.
*/
@Injectable({
providedIn: 'root'
})
export class FallbackRoutesService {
private fallbackEntries: AuthGuardFallbackEntry[] = [];
/**
* Constructor
*
* @param operator Asking for the required permission
*/
public constructor(private operator: OperatorService) {}
/**
* Adds fallback navigation entries for the start page.
* @param entries The entries to add
*/
public registerFallbackEntries(entries: AuthGuardFallbackEntry[]): void {
this.fallbackEntries.push(...entries);
this.fallbackEntries = this.fallbackEntries.sort((a, b) => a.weight - b.weight);
}
public getFallbackRoute(): string | null {
for (const entry of this.fallbackEntries) {
if (this.operator.hasPerms(entry.permission)) {
return entry.route;
}
}
}
}

View File

@ -1,16 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { HttpService } from './http.service';
describe('HttpService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [HttpService]
});
});
// TODO: Write a working Test
// it('should be created', () => {
// const service: HttpService = TestBed.get(HttpService);
// expect(service).toBeTruthy();
// });
});

View File

@ -1,257 +0,0 @@
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { OpenSlidesStatusService } from './openslides-status.service';
import { formatQueryParams, QueryParams } from '../definitions/query-params';
/**
* Enum for different HTTPMethods
*/
export enum HTTPMethod {
GET = 'get',
POST = 'post',
PUT = 'put',
PATCH = 'patch',
DELETE = 'delete'
}
export interface DetailResponse {
detail: string | string[];
args?: string[];
}
function isDetailResponse(obj: any): obj is DetailResponse {
return (
obj &&
typeof obj === 'object' &&
(typeof obj.detail === 'string' || obj.detail instanceof Array) &&
(!obj.args || obj.args instanceof Array)
);
}
/**
* Service for managing HTTP requests. Allows to send data for every method. Also (TODO) will do generic error handling.
*/
@Injectable({
providedIn: 'root'
})
export class HttpService {
/**
* http headers used by most requests
*/
private defaultHeaders: HttpHeaders;
/**
* Construct a HttpService
*
* Sets the default headers to application/json
*
* @param http The HTTP Client
* @param translate
* @param timeTravel requests are only allowed if history mode is disabled
*/
public constructor(
private http: HttpClient,
private translate: TranslateService,
private OSStatus: OpenSlidesStatusService
) {
this.defaultHeaders = new HttpHeaders().set('Content-Type', 'application/json');
}
/**
* Send the a http request the the given path.
* Optionally accepts a request body.
*
* @param path the target path, usually starting with /rest
* @param method the required HTTP method (i.e get, post, put)
* @param data optional, if sending a data body is required
* @param queryParams optional queryparams to append to the path
* @param customHeader optional custom HTTP header of required
* @param responseType optional response type, default set to json (i.e 'arraybuffer')
* @returns a promise containing a generic
*/
private async send<T>(
path: string,
method: HTTPMethod,
data?: any,
queryParams?: QueryParams,
customHeader?: HttpHeaders,
responseType?: string
): Promise<T> {
// end early, if we are in history mode
if (this.OSStatus.isInHistoryMode && method !== HTTPMethod.GET) {
throw this.handleError('You cannot make changes while in history mode');
}
// there is a current bug with the responseType.
// https://github.com/angular/angular/issues/18586
// castting it to 'json' allows the usage of the current array
if (!responseType) {
responseType = 'json';
}
let url = path + formatQueryParams(queryParams);
if (url[0] !== '/') {
console.warn(`Please prefix the URL "${url}" with a slash.`);
url = '/' + url;
}
if (this.OSStatus.isPrioritizedClient) {
url = '/prioritize' + url;
}
const options = {
body: data,
headers: customHeader ? customHeader : this.defaultHeaders,
responseType: responseType as 'json'
};
try {
return await this.http.request<T>(method, url, options).toPromise();
} catch (e) {
throw this.handleError(e);
}
}
/**
* Takes an error thrown by the HttpClient. Processes it to return a string that can
* be presented to the user.
* @param e The error thrown.
* @returns The prepared and translated message for the user
*/
private handleError(e: any): string {
let error = this.translate.instant('Error') + ': ';
// If the error is a string already, return it.
if (typeof e === 'string') {
return error + e;
}
// If the error is no HttpErrorResponse, it's not clear what is wrong.
if (!(e instanceof HttpErrorResponse)) {
console.error('Unknown error thrown by the http client: ', e);
error += this.translate.instant('An unknown error occurred.');
return error;
}
if (e.status === 405) {
// this should only happen, if the url is wrong -> a bug.
error += this.translate.instant(
'The requested method is not allowed. Please contact your system administrator.'
);
} else if (!e.error) {
error += this.translate.instant("The server didn't respond.");
} else if (typeof e.error === 'object') {
if (isDetailResponse(e.error)) {
error += this.processDetailResponse(e.error);
} else {
error = Object.keys(e.error)
.map(key => {
const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
return this.translate.instant(capitalizedKey) + ': ' + this.processDetailResponse(e.error[key]);
})
.join(', ');
}
} else if (e.status === 500) {
error += this.translate.instant('A server error occured. Please contact your system administrator.');
} else if (e.status > 500) {
error += this.translate.instant('The server could not be reached.') + ` (${e.status})`;
} else {
error += e.message;
}
return error;
}
/**
* Errors from the servers may be string or array of strings. This function joins the strings together,
* if an array is send.
* @param str a string or a string array to join together.
* @returns Error text(s) as single string
*/
private processDetailResponse(response: DetailResponse): string {
let message: string;
if (response instanceof Array) {
message = response.join(' ');
} else if (response.detail instanceof Array) {
message = response.detail.join(' ');
} else {
message = response.detail;
}
message = this.translate.instant(message);
if (response.args && response.args.length > 0) {
for (let i = 0; i < response.args.length; i++) {
message = message.replace(`{${i}}`, response.args[i].toString());
}
}
return message;
}
/**
* Executes a get on a path with a certain object
* @param path The path to send the request to.
* @param data An optional payload for the request.
* @param queryParams Optional params appended to the path as the query part of the url.
* @param header optional HTTP header if required
* @param responseType option expected response type by the request (i.e 'arraybuffer')
* @returns A promise holding a generic
*/
public async get<T>(
path: string,
data?: any,
queryParams?: QueryParams,
header?: HttpHeaders,
responseType?: string
): Promise<T> {
return await this.send<T>(path, HTTPMethod.GET, data, queryParams, header, responseType);
}
/**
* Executes a post on a path with a certain object
* @param path The path to send the request to.
* @param data An optional payload for the request.
* @param queryParams Optional params appended to the path as the query part of the url.
* @param header optional HTTP header if required
* @returns A promise holding a generic
*/
public async post<T>(path: string, data?: any, queryParams?: QueryParams, header?: HttpHeaders): Promise<T> {
return await this.send<T>(path, HTTPMethod.POST, data, queryParams, header);
}
/**
* Executes a put on a path with a certain object
* @param path The path to send the request to.
* @param data An optional payload for the request.
* @param queryParams Optional params appended to the path as the query part of the url.
* @param header optional HTTP header if required
* @returns A promise holding a generic
*/
public async patch<T>(path: string, data?: any, queryParams?: QueryParams, header?: HttpHeaders): Promise<T> {
return await this.send<T>(path, HTTPMethod.PATCH, data, queryParams, header);
}
/**
* Executes a put on a path with a certain object
* @param path The path to send the request to.
* @param data An optional payload for the request.
* @param queryParams Optional params appended to the path as the query part of the url.
* @param header optional HTTP header if required
* @returns A promise holding a generic
*/
public async put<T>(path: string, data?: any, queryParams?: QueryParams, header?: HttpHeaders): Promise<T> {
return await this.send<T>(path, HTTPMethod.PUT, data, queryParams, header);
}
/**
* Makes a delete request.
* @param url The path to send the request to.
* @param data An optional payload for the request.
* @param queryParams Optional params appended to the path as the query part of the url.
* @param header optional HTTP header if required
* @returns A promise holding a generic
*/
public async delete<T>(path: string, data?: any, queryParams?: QueryParams, header?: HttpHeaders): Promise<T> {
return await this.send<T>(path, HTTPMethod.DELETE, data, queryParams, header);
}
}

View File

@ -1,15 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { MainMenuService } from './main-menu.service';
describe('MainMenuService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [MainMenuService]
});
});
it('should be created', inject([MainMenuService], (service: MainMenuService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,76 +0,0 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
/**
* This represents one entry in the main menu
*/
export interface MainMenuEntry {
/**
* The route for the router to navigate to on click.
*/
route: string;
/**
* The display string to be shown.
*/
displayName: string;
/**
* The font awesom icon to display.
*/
icon: string;
/**
* For sorting the entries.
*/
weight: number;
/**
* The permission to see the entry.
*/
permission: string;
}
/**
* Collects main menu entries and provides them to the main menu component.
*/
@Injectable({
providedIn: 'root'
})
export class MainMenuService {
/**
* A list of sorted entries.
*/
private _entries: MainMenuEntry[] = [];
/**
* Observed by the site component.
* If a new value appears the sideNavContainer gets toggled
*/
public toggleMenuSubject = new Subject<void>();
/**
* Make the entries public.
*/
public get entries(): MainMenuEntry[] {
return this._entries;
}
public constructor() {}
/**
* Adds entries to the mainmenu.
* @param entries The entries to add
*/
public registerEntries(entries: MainMenuEntry[]): void {
this._entries.push(...entries);
this._entries = this._entries.sort((a, b) => a.weight - b.weight);
}
/**
* Emit signal to toggle the main Menu
*/
public toggleMenu(): void {
this.toggleMenuSubject.next();
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { NotifyService } from './notify.service';
describe('NotifyService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [NotifyService]
});
});
it('should be created', inject([NotifyService], (service: NotifyService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,168 +0,0 @@
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { OperatorService } from './operator.service';
import { WebsocketService } from './websocket.service';
/**
* Encapslates the name and content of every message regardless of being a request or response.
*/
interface NotifyBase<T> {
/**
* The name of the notify message.
*/
name: string;
/**
* The content to send.
*/
content: T;
}
/**
* This interface has all fields for a notify request to the server. Next to name and content
* one can give an array of user ids (or the value `true` for all users) and an array of
* channel names.
*/
export interface NotifyRequest<T> extends NotifyBase<T> {
/**
* User ids (or `true` for all users) to send this message to.
*/
users?: number[] | boolean;
/**
* An array of channels to send this message to.
*/
replyChannels?: string[];
}
/**
* This is the notify-format one recieves from the server.
*/
export interface NotifyResponse<T> extends NotifyBase<T> {
/**
* This is the channel name of the one, who sends this message. Can be use to directly
* answer this message.
*/
senderChannelName: string;
/**
* The user id of the user who sends this message. It is 0 for Anonymous.
*/
senderUserId: number;
/**
* This is validated here and is true, if the senderUserId matches the current operator's id.
* It's also true, if one recieves a request from an anonymous and the operator itself is the anonymous.
*/
sendByThisUser: boolean;
}
/**
* Handles all incoming and outgoing notify messages via {@link WebsocketService}.
*/
@Injectable({
providedIn: 'root'
})
export class NotifyService {
/**
* A general subject for all messages.
*/
private notifySubject = new Subject<NotifyResponse<any>>();
/**
* Subjects for specific messages.
*/
private messageSubjects: {
[name: string]: Subject<NotifyResponse<any>>;
} = {};
/**
* Constructor to create the NotifyService. Registers itself to the WebsocketService.
* @param websocketService
*/
public constructor(private websocketService: WebsocketService, private operator: OperatorService) {
websocketService.getOberservable<NotifyResponse<any>>('notify').subscribe(notify => {
notify.sendByThisUser = notify.senderUserId === (this.operator.user ? this.operator.user.id : 0);
this.notifySubject.next(notify);
if (this.messageSubjects[notify.name]) {
this.messageSubjects[notify.name].next(notify);
}
});
}
/**
* Sents a notify message to all users (so all clients that are online).
* @param name The name of the notify message
* @param content The payload to send
*/
public sendToAllUsers<T>(name: string, content: T): void {
this.send(name, content);
}
/**
* Sends a notify message to all open clients with the given users logged in.
* @param name The name of th enotify message
* @param content The payload to send.
* @param users Multiple user ids.
*/
public sendToUsers<T>(name: string, content: T, ...users: number[]): void {
this.send(name, content, users);
}
/**
* Sends a notify message to all given channels.
* @param name The name of th enotify message
* @param content The payload to send.
* @param channels Multiple channels to send this message to.
*/
public sendToChannels<T>(name: string, content: T, ...channels: string[]): void {
if (channels.length < 1) {
throw new Error('You have to provide at least one channel');
}
this.send(name, content, null, channels);
}
/**
* General send function for notify messages.
* @param name The name of the notify message
* @param content The payload to send.
* @param users Either an array of IDs or `true` meaning of sending this message to all online users clients.
* @param channels An array of channels to send this message to.
*/
public send<T>(name: string, content: T, users?: number[] | boolean, channels?: string[]): void {
const notify: NotifyRequest<T> = {
name: name,
content: content
};
if (typeof users === 'boolean' && users !== true) {
throw new Error('You just can give true as a boolean to send this message to all users.');
}
if (users !== null) {
notify.users = users;
}
if (channels !== null) {
notify.replyChannels = channels;
}
this.websocketService.send('notify', notify);
}
/**
* Returns a general observalbe of all notify messages.
*/
public getObservable(): Observable<NotifyResponse<any>> {
return this.notifySubject.asObservable();
}
/**
* Returns an observable for a specific type of messages.
* @param name The name of all messages to observe.
*/
public getMessageObservable<T>(name: string): Observable<NotifyResponse<T>> {
if (!this.messageSubjects[name]) {
this.messageSubjects[name] = new Subject<NotifyResponse<any>>();
}
return this.messageSubjects[name].asObservable() as Observable<NotifyResponse<T>>;
}
}

View File

@ -1,18 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module';
import { OfflineService } from './offline.service';
describe('OfflineService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [OfflineService]
});
});
it('should be created', inject([OfflineService], (service: OfflineService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,55 +0,0 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
/**
* This service handles everything connected with being offline.
*
* TODO: This is just a stub. Needs to be done in the future; Maybe we cancel this whole concept
* of this service. We'll see what happens here..
*/
@Injectable({
providedIn: 'root'
})
export class OfflineService {
/**
* BehaviorSubject to receive further status values.
*/
private offline = new BehaviorSubject<boolean>(false);
/**
* Determines of you are either in Offline mode or not connected via websocket
*
* @returns whether the client is offline or not connected
*/
public isOffline(): Observable<boolean> {
return this.offline;
}
/**
* Sets the offline flag. Restores the DataStoreService to the last known configuration.
*/
public goOfflineBecauseFailedWhoAmI(): void {
if (!this.offline.getValue()) {
console.log('offline because whoami failed.');
}
this.offline.next(true);
}
/**
* Sets the offline flag, because there is no connection to the server.
*/
public goOfflineBecauseConnectionLost(): void {
if (!this.offline.getValue()) {
console.log('offline because connection lost.');
}
this.offline.next(true);
}
/**
* Function to return to online-status.
*/
public goOnline(): void {
this.offline.next(false);
}
}

View File

@ -1,18 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module';
import { OpenSlidesStatusService } from './openslides-status.service';
describe('OpenSlidesStatusService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [OpenSlidesStatusService]
});
});
it('should be created', inject([OpenSlidesStatusService], (service: OpenSlidesStatusService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,55 +0,0 @@
import { Injectable } from '@angular/core';
import { History } from 'app/shared/models/core/history';
/**
* Holds information about OpenSlides. This is not included into other services to
* avoid circular dependencies.
*/
@Injectable({
providedIn: 'root'
})
export class OpenSlidesStatusService {
/**
* in History mode, saves the history point.
*/
private history: History = null;
/**
* Returns, if OpenSlides is in the history mode.
*/
public get isInHistoryMode(): boolean {
return !!this.history;
}
public isPrioritizedClient = false;
/**
* Ctor, does nothing.
*/
public constructor() {}
/**
* Calls the getLocaleString function of the history object, if present.
*
* @param format the required date representation format
* @returns the timestamp as string
*/
public getHistoryTimeStamp(format: string): string {
return this.history ? this.history.getLocaleString(format) : null;
}
/**
* Enters the history mode
*/
public enterHistoryMode(history: History): void {
this.history = history;
}
/**
* Leaves the history mode
*/
public leaveHistoryMode(): void {
this.history = null;
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { OpenSlidesService } from './openslides.service';
describe('OpenSlidesService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [OpenSlidesService]
});
});
it('should be created', inject([OpenSlidesService], (service: OpenSlidesService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,198 +0,0 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { AutoupdateService } from './autoupdate.service';
import { ConstantsService } from './constants.service';
import { DataStoreService } from './data-store.service';
import { OperatorService } from './operator.service';
import { StorageService } from './storage.service';
import { WebsocketService } from './websocket.service';
/**
* Handles the bootup/showdown of this application.
*/
@Injectable({
providedIn: 'root'
})
export class OpenSlidesService {
/**
* If the user tries to access a certain URL without being authenticated, the URL will be stored here
*/
public redirectUrl: string;
/**
* Subject to hold the flag `booted`.
*/
public readonly booted = new BehaviorSubject(false);
/**
* Saves, if OpenSlides is fully booted. This means, that a user must be logged in
* (Anonymous is also a user in this case). This is the case after `afterLoginBootup`.
*/
public get isBooted(): boolean {
return this.booted.value;
}
/**
* Constructor to create the OpenSlidesService. Registers itself to the WebsocketService.
* @param storageService
* @param operator
* @param websocketService
* @param router
* @param autoupdateService
* @param DS
*/
public constructor(
private storageService: StorageService,
private operator: OperatorService,
private websocketService: WebsocketService,
private router: Router,
private autoupdateService: AutoupdateService,
private DS: DataStoreService,
private constantsService: ConstantsService
) {
// Handler that gets called, if the websocket connection reconnects after a disconnection.
// There might have changed something on the server, so we check the operator, if he changed.
websocketService.retryReconnectEvent.subscribe(() => {
this.checkOperator();
});
this.bootup();
}
/**
* the bootup-sequence: Do a whoami request and if it was successful, do
* {@method afterLoginBootup}. If not, redirect the user to the login page.
*/
public async bootup(): Promise<void> {
// start autoupdate if the user is logged in:
let response = await this.operator.whoAmIFromStorage();
const needToCheckOperator = !!response;
if (!response) {
response = await this.operator.whoAmI();
}
if (!response.user && !response.guest_enabled) {
if (!location.pathname.includes('error')) {
this.redirectUrl = location.pathname;
}
this.redirectToLoginIfNotSubpage();
} else {
await this.afterLoginBootup(response.user_id);
}
if (needToCheckOperator) {
// Check for the operator via a async whoami (so no await here)
// to validate, that the cache was correct.
this.checkOperator(false);
}
}
/**
* Redirects the user to /login, if he isn't on a subpage.
*/
private redirectToLoginIfNotSubpage(): void {
if (!this.redirectUrl || !this.redirectUrl.includes('/login/')) {
// Goto login, if the user isn't on a subpage like
// legal notice or reset passwort view.
// If other routing requests are active (e.g. to `/` or `/error`)
// wait for the authguard to finish to navigate to /login. This
// redirect is more important than the other ones.
setTimeout(() => {
this.router.navigate(['/login']);
});
}
}
/**
* the login bootup-sequence: Check (and maybe clear) the cache und setup the DataStore
* and websocket. This "login" also may be the "login" of an anonymous when he is using
* OpenSlides as a guest.
* @param userId the id or null for guest
*/
public async afterLoginBootup(userId: number | null): Promise<void> {
// Check, which user was logged in last time
const lastUserId = await this.storageService.get<number>('lastUserLoggedIn');
// if the user changed, reset the cache and save the new user.
if (userId !== lastUserId) {
await this.DS.clear();
await this.storageService.set('lastUserLoggedIn', userId);
}
await this.setupDataStoreAndWebSocket();
// Now finally booted.
this.booted.next(true);
}
/**
* Init DS from cache and after this start the websocket service.
*/
private async setupDataStoreAndWebSocket(): Promise<void> {
let changeId = await this.DS.initFromStorage();
if (changeId > 0) {
changeId += 1;
}
// disconnect the WS connection, if there was one. This is needed
// to update the connection parameters, namely the cookies. If the user
// is changed, the WS needs to reconnect, so the new connection holds the new
// user information.
if (this.websocketService.isConnected) {
await this.websocketService.close(); // Wait for the disconnect.
}
await this.websocketService.connect({ changeId: changeId }); // Request changes after changeId.
}
/**
* Shuts down OpenSlides. The websocket connection is closed and the operator is not set.
*/
public async shutdown(): Promise<void> {
await this.websocketService.close();
this.booted.next(false);
}
/**
* Shutdown and bootup.
*/
public async reboot(): Promise<void> {
await this.shutdown();
await this.bootup();
}
/**
* Clears the client cache and restarts OpenSlides. Results in "flickering" of the
* login mask, because the cached operator is also cleared.
*/
public async reset(): Promise<void> {
await this.shutdown();
await this.storageService.clear();
await this.bootup();
}
/**
* Verify that the operator is the same as it was before. Should be alled on a reconnect.
*/
private async checkOperator(requestChanges: boolean = true): Promise<void> {
const response = await this.operator.whoAmI();
// User logged off.
if (!response.user && !response.guest_enabled) {
this.websocketService.cancelReconnectenRetry();
await this.shutdown();
this.redirectToLoginIfNotSubpage();
} else {
if (
(this.operator.user && this.operator.user.id !== response.user_id) ||
(!this.operator.user && response.user_id)
) {
// user changed
await this.DS.clear();
await this.reboot();
} else if (requestChanges) {
// User is still the same, but check for missed autoupdates.
this.autoupdateService.requestChanges();
this.constantsService.refresh();
}
}
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { OperatorService } from './operator.service';
describe('OperatorService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [OperatorService]
});
});
it('should be created', inject([OperatorService], (service: OperatorService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,431 +0,0 @@
import { Injectable } from '@angular/core';
import { environment } from 'environments/environment';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { auditTime, filter } from 'rxjs/operators';
import { Group } from 'app/shared/models/users/group';
import { ViewUser } from 'app/site/users/models/view-user';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { DataStoreService } from './data-store.service';
import { Deferred } from '../promises/deferred';
import { HttpService } from './http.service';
import { OfflineService } from './offline.service';
import { OnAfterAppsLoaded } from '../definitions/on-after-apps-loaded';
import { OpenSlidesStatusService } from './openslides-status.service';
import { StorageService } from './storage.service';
import { DEFAULT_AUTH_TYPE, User, UserAuthType } from '../../shared/models/users/user';
import { UserRepositoryService } from '../repositories/users/user-repository.service';
/**
* Permissions on the client are just strings. This makes clear, that
* permissions instead of arbitrary strings should be given.
*/
export type Permission = string;
/**
* Response format of the WhoAmI request.
*/
export interface WhoAmI {
user_id: number;
guest_enabled: boolean;
user: User;
auth_type: UserAuthType;
permissions: Permission[];
}
function isWhoAmI(obj: any): obj is WhoAmI {
if (!obj) {
return false;
}
const whoAmI = obj as WhoAmI;
return (
whoAmI.guest_enabled !== undefined &&
whoAmI.user !== undefined &&
whoAmI.user_id !== undefined &&
whoAmI.permissions !== undefined &&
whoAmI.auth_type !== undefined
);
}
const WHOAMI_STORAGE_KEY = 'whoami';
/**
* The operator represents the user who is using OpenSlides.
*
* Changes in operator can be observed, directives do so on order to show
* or hide certain information.
*/
@Injectable({
providedIn: 'root'
})
export class OperatorService implements OnAfterAppsLoaded {
/**
* The operator.
*/
private _user: User;
public get user(): User {
return this._user;
}
/**
* The operator as a view user. We need a separation here, because
* we need to acces the operators permissions, before we get data
* from the server to build the view user.
*/
private _viewUser: ViewUser;
/**
* Get the user that corresponds to operator.
*/
public get viewUser(): ViewUser {
return this._viewUser;
}
public get isAnonymous(): boolean {
return !this.user || this.user.id === 0;
}
public get isSuperAdmin(): boolean {
return this.isInGroupIdsNonAdminCheck(2);
}
public readonly authType: BehaviorSubject<UserAuthType> = new BehaviorSubject(DEFAULT_AUTH_TYPE);
/**
* Save, if guests are enabled.
*/
public get guestsEnabled(): boolean {
return this.currentWhoAmI ? this.currentWhoAmI.guest_enabled : false;
}
/**
* The permissions of the operator. Updated via {@method updatePermissions}.
*/
private permissions: Permission[] = [];
/**
* The subject that can be observed by other instances using observing functions.
*/
private operatorSubject: BehaviorSubject<User> = new BehaviorSubject<User>(null);
/**
* Subject for the operator as a view user.
*/
private viewOperatorSubject: BehaviorSubject<ViewUser> = new BehaviorSubject<ViewUser>(null);
/**
* Do not access the repo before it wasn't loaded. Will be true after `onAfterAppsLoaded`.
*/
private userRepository: UserRepositoryService | null;
private _currentWhoAmI: WhoAmI | null = null;
private _defaultWhoAmI: WhoAmI = {
user_id: null,
guest_enabled: false,
user: null,
auth_type: DEFAULT_AUTH_TYPE,
permissions: []
};
/**
* The current WhoAmI response to extract the user (the operator) from.
*/
private get currentWhoAmI(): WhoAmI {
return this._currentWhoAmI || this._defaultWhoAmI;
}
private set currentWhoAmI(value: WhoAmI | null) {
this._currentWhoAmI = value;
// Resetting the default whoami, when the current whoami isn't there. This
// is for a fresh restart and do not have (old) changed values in this.defaultWhoAmI
if (!value) {
this._defaultWhoAmI = this.getDefaultWhoAmIResponse();
}
}
private readonly _loaded: Deferred<void> = new Deferred();
public get loaded(): Promise<void> {
return this._loaded;
}
/**
* This is for the viewUser check, if the user id has changed, on a user update.
*/
private lastUserId: number | null = null;
/**
* The subscription to the viewuser from the user repository.
*/
private viewOperatorSubscription: Subscription;
/**
* Sets up an observer for watching changes in the DS. If the operator user or groups are changed,
* the operator's permissions are updated.
*
* @param http
* @param DS
* @param offlineService
*/
public constructor(
private http: HttpService,
private DS: DataStoreService,
private offlineService: OfflineService,
private collectionStringMapper: CollectionStringMapperService,
private storageService: StorageService,
private OSStatus: OpenSlidesStatusService
) {
this.DS.getChangeObservable(User).subscribe(newModel => {
if (this._user && this._user.id === newModel.id) {
this._user = newModel;
this.updateUserInCurrentWhoAmI();
}
});
this.DS.getChangeObservable(Group)
.pipe(
filter(
model =>
// Any group has changed if we have an operator or
// group 1 (default) for anonymous changed
!!this._user || model.id === 1
),
auditTime(10)
)
.subscribe(() => this.updatePermissions());
// Watches the user observable to update the viewUser for the operator.
this.getUserObservable().subscribe(user => {
const userId = user ? user.id : null;
if ((!user && this.lastUserId === null) || userId === this.lastUserId) {
return; // The user didn't changed.
}
this.lastUserId = userId;
// User changed: clear subscription and subscribe to the new user (if there is one)
if (this.viewOperatorSubscription) {
this.viewOperatorSubscription.unsubscribe();
}
if (user && this.userRepository) {
this.viewOperatorSubscription = this.userRepository
.getViewModelObservable(user.id)
.subscribe(viewUser => {
this._viewUser = viewUser;
this.viewOperatorSubject.next(viewUser);
});
} else {
// The operator is anonymous.
this.viewOperatorSubject.next(null);
}
});
}
/**
* Load the repo to get a view user.
*/
public onAfterAppsLoaded(): void {
this.userRepository = this.collectionStringMapper.getRepository(ViewUser) as UserRepositoryService;
if (this.user) {
this._viewUser = this.userRepository.getViewModel(this.user.id);
}
this.viewOperatorSubject.next(this._viewUser);
}
/**
* Gets the current WhoAmI response from the storage.
*/
public async whoAmIFromStorage(): Promise<WhoAmI | null> {
let response: WhoAmI | null = null;
try {
response = await this.storageService.get<WhoAmI>(WHOAMI_STORAGE_KEY);
if (!isWhoAmI(response)) {
response = null;
}
} catch (e) {}
if (response) {
await this.updateCurrentWhoAmI(response);
}
return response;
}
/**
* Sets the operator user. Will be saved to storage
* @param user The new operator.
*/
public async setWhoAmI(whoami: WhoAmI | null): Promise<void> {
if (whoami === null) {
whoami = this.getDefaultWhoAmIResponse();
}
await this.updateCurrentWhoAmI(whoami);
}
/**
* Calls `/apps/users/whoami` to find out the real operator.
*
* @returns The response of the WhoAmI request.
*/
public async whoAmI(): Promise<WhoAmI> {
try {
const response = await this.http.get(environment.urlPrefix + '/users/whoami/');
if (isWhoAmI(response)) {
await this.updateCurrentWhoAmI(response);
} else {
this.offlineService.goOfflineBecauseFailedWhoAmI();
}
} catch (e) {
this.offlineService.goOfflineBecauseFailedWhoAmI();
}
return this.currentWhoAmI;
}
/**
* Saves the user to storage by wrapping it into a (maybe existing)
* WhoAMI response.
*/
private async updateUserInCurrentWhoAmI(): Promise<void> {
if (this.isAnonymous) {
this.currentWhoAmI.user_id = null;
this.currentWhoAmI.user = null;
} else {
this.currentWhoAmI.user_id = this.user.id;
this.currentWhoAmI.user = this.user;
}
this.currentWhoAmI.permissions = this.permissions;
await this.updateCurrentWhoAmI();
}
/**
* Updates the user and update the permissions.
*/
private async updateCurrentWhoAmI(whoami?: WhoAmI): Promise<void> {
if (whoami) {
this.currentWhoAmI = whoami;
} else {
whoami = this.currentWhoAmI;
}
this._user = whoami ? whoami.user : null;
this.authType.next(whoami ? whoami.auth_type : DEFAULT_AUTH_TYPE);
await this.updatePermissions();
this._loaded.resolve();
}
/**
* @returns an observable for the operator as a user.
*/
public getUserObservable(): Observable<User> {
return this.operatorSubject.asObservable();
}
/**
* @returns an observable for the operator as a viewUser. Note, that
* the viewUser might not be there, so for reliable (and not display) information,
* use the `getUserObservable`.
*/
public getViewUserObservable(): Observable<ViewUser> {
return this.viewOperatorSubject.asObservable();
}
/**
* Checks, if the operator has at least one of the given permissions.
* @param checkPerms The permissions to check, if at least one matches.
*/
public hasPerms(...checkPerms: Permission[]): boolean {
if (this._user && this._user.groups_id.includes(2)) {
return true;
}
return checkPerms.some(permission => {
return this.permissions.includes(permission);
});
}
/**
* Returns true, if the operator is in at least one group or he is in the admin group.
* @param groups The groups to check
*/
public isInGroup(...groups: Group[]): boolean {
return this.isInGroupIds(...groups.map(group => group.id));
}
/**
* Returns true, if the operator is in at least one group or he is in the admin group.
* @param groups The group ids to check
*/
public isInGroupIds(...groupIds: number[]): boolean {
if (!this.isInGroupIdsNonAdminCheck(...groupIds)) {
// An admin has all perms and is technically in every group.
return this.user && this.user.groups_id.includes(2);
}
return true;
}
/**
* Returns true, if the operator is in at least one group.
* @param groups The group ids to check
*/
public isInGroupIdsNonAdminCheck(...groupIds: number[]): boolean {
if (!this.user) {
return groupIds.includes(1); // any anonymous is in the default group.
}
return groupIds.some(id => this.user.groups_id.includes(id));
}
/**
* Update the operators permissions and publish the operator afterwards.
* Saves the current WhoAmI to storage with the updated permissions
*/
private async updatePermissions(): Promise<void> {
this.permissions = [];
// If we do not have any groups, take the permissions from the
// latest WhoAmI response.
if (this.DS.getAll(Group).length === 0) {
if (this.currentWhoAmI) {
this.permissions = this.currentWhoAmI.permissions;
}
} else {
// Anonymous or users in the default group.
if (!this.user || this.user.groups_id.length === 0) {
const defaultGroup = this.DS.get<Group>('users/group', 1);
if (defaultGroup && defaultGroup.permissions instanceof Array) {
this.permissions = defaultGroup.permissions;
}
} else {
const permissionSet = new Set<string>();
this.DS.getMany(Group, this.user.groups_id).forEach(group => {
group.permissions.forEach(permission => {
permissionSet.add(permission);
});
});
this.permissions = Array.from(permissionSet.values());
}
}
// Save perms to current WhoAmI
this.currentWhoAmI.permissions = this.permissions;
if (!this.OSStatus.isInHistoryMode) {
await this.storageService.set(WHOAMI_STORAGE_KEY, this.currentWhoAmI);
}
// publish changes in the operator.
this.operatorSubject.next(this.user);
}
/**
* Returns a default WhoAmI response
*/
private getDefaultWhoAmIResponse(): WhoAmI {
return {
user_id: null,
guest_enabled: false,
user: null,
auth_type: DEFAULT_AUTH_TYPE,
permissions: []
};
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { PingService } from './ping.service';
describe('PingService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [PingService]
});
});
it('should be created', inject([PingService], (service: PingService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,86 +0,0 @@
import { Injectable } from '@angular/core';
import { ConstantsService } from './constants.service';
import { Deferred } from '../promises/deferred';
import { TimeoutPromise } from '../promises/timeout-promise';
import { WebsocketService } from './websocket.service';
interface OpenSlidesSettings {
PING_INTERVAL?: number;
PING_TIMEOUT?: number;
}
@Injectable({
providedIn: 'root'
})
export class PingService {
/**
* The interval.
*/
private pingInterval: any;
private intervalTime = 30000;
private timeoutTime = 5000;
private lastLatency: number | null = null;
public constructor(private websocketService: WebsocketService, private constantsService: ConstantsService) {
this.setup();
}
private async setup(): Promise<void> {
const gotConstants = new Deferred();
this.constantsService.get<OpenSlidesSettings>('Settings').subscribe(settings => {
this.intervalTime = settings.PING_INTERVAL || 30000;
this.timeoutTime = settings.PING_TIMEOUT || 5000;
gotConstants.resolve();
});
await gotConstants;
// Connects the ping-pong mechanism to the opening and closing of the connection.
this.websocketService.closeEvent.subscribe(() => this.stopPing());
this.websocketService.generalConnectEvent.subscribe(() => this.startPing());
if (this.websocketService.isConnected) {
this.startPing();
}
}
/**
* Starts the ping-mechanism
*/
private startPing(): void {
if (this.pingInterval) {
return;
}
this.pingInterval = setInterval(async () => {
const start = performance.now();
try {
await TimeoutPromise(
this.websocketService.sendAndGetResponse('ping', this.lastLatency),
this.timeoutTime
);
this.lastLatency = performance.now() - start;
if (this.lastLatency > 1000) {
console.warn(`Ping took ${this.lastLatency / 1000} seconds.`);
}
} catch (e) {
console.warn(`The server didn't respond to ping within ${this.timeoutTime / 1000} seconds.`);
this.stopPing();
this.websocketService.simulateAbnormalClose();
}
}, this.intervalTime);
}
/**
* Clears the ping interval
*/
private stopPing(): void {
if (this.pingInterval) {
clearInterval(this.pingInterval);
this.pingInterval = null;
}
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { PrioritizeService } from './prioritize.service';
describe('PrioritizeService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [PrioritizeService]
});
});
it('should be created', inject([PrioritizeService], (service: PrioritizeService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,46 +0,0 @@
import { Injectable } from '@angular/core';
import { ConstantsService } from './constants.service';
import { DataStoreService } from './data-store.service';
import { OpenSlidesStatusService } from './openslides-status.service';
import { OperatorService } from './operator.service';
import { WebsocketService } from './websocket.service';
interface OpenSlidesSettings {
PRIORITIZED_GROUP_IDS?: number[];
}
/**
* Cares about prioritizing a client. Checks, if the operator is in one of
* some prioritized groups. These group ids come from the server. If the prio-
* ritization changes, the websocket connection gets reconnected.
*/
@Injectable({
providedIn: 'root'
})
export class PrioritizeService {
private prioritizedGroupIds: number[] = [];
public constructor(
constantsService: ConstantsService,
private websocketService: WebsocketService,
private DS: DataStoreService,
private openSlidesStatusService: OpenSlidesStatusService,
private operator: OperatorService
) {
constantsService.get<OpenSlidesSettings>('Settings').subscribe(settings => {
this.prioritizedGroupIds = settings.PRIORITIZED_GROUP_IDS || [];
this.checkPrioritization();
});
operator.getUserObservable().subscribe(() => this.checkPrioritization());
}
private checkPrioritization(): void {
const opPrioritized = this.operator.isInGroupIdsNonAdminCheck(...this.prioritizedGroupIds);
if (this.openSlidesStatusService.isPrioritizedClient !== opPrioritized) {
console.log('Alter prioritization:', opPrioritized);
this.openSlidesStatusService.isPrioritizedClient = opPrioritized;
this.websocketService.reconnect({ changeId: this.DS.maxChangeId });
}
}
}

View File

@ -1,16 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { ProjectorDataService } from './projector-data.service';
describe('ProjectorDataService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [ProjectorDataService]
});
});
it('should be created', inject([ProjectorDataService], (service: ProjectorDataService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,154 +0,0 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { auditTime } from 'rxjs/operators';
import { WebsocketService } from 'app/core/core-services/websocket.service';
import { Projector, ProjectorElement } from 'app/shared/models/core/projector';
export interface SlideData<T = { error?: string }, P extends ProjectorElement = ProjectorElement> {
data: T;
element: P;
error?: string;
}
export type ProjectorData = SlideData[];
interface AllProjectorData {
[id: number]: ProjectorData | { error: string };
}
/**
* Received data from server.
*/
interface ProjectorWebsocketMessage {
/**
* The `change_id` of the current update.
*/
change_id: number;
/**
* The necessary new projector-data.
*/
data: AllProjectorData;
}
/**
* This service handles the websocket connection for the projector data.
* Each projector instance registers itself by calling `getProjectorObservable`.
* A projector should deregister itself, when the component is destroyed.
*/
@Injectable({
providedIn: 'root'
})
export class ProjectorDataService {
/**
* Counts the open projector instances per projector id.
*/
private openProjectorInstances: { [id: number]: number } = {};
/**
* Holds the current projector data for each projector.
*/
private currentProjectorData: { [id: number]: BehaviorSubject<ProjectorData | null> } = {};
/**
* When multiple projectory are requested, debounce these requests to just issue
* one request, with all the needed projectors.
*/
private readonly updateProjectorDataDebounceSubject = new Subject<void>();
/**
* Holds the current change id to check, if the update contains new content or a deprecated one.
*/
private currentChangeId = 0;
/**
* Constructor.
*
* @param websocketService
*/
public constructor(private websocketService: WebsocketService) {
// Dispatch projector data.
this.websocketService.getOberservable('projector').subscribe((update: ProjectorWebsocketMessage) => {
if (this.currentChangeId > update.change_id) {
return;
}
Object.keys(update.data).forEach(_id => {
const id = parseInt(_id, 10);
if (this.currentProjectorData[id]) {
this.currentProjectorData[id].next(update.data[id] as ProjectorData);
}
});
this.currentChangeId = update.change_id;
});
// The service need to re-register, if the websocket connection was lost.
this.websocketService.generalConnectEvent.subscribe(() => this.updateProjectorDataSubscription());
// With a bit of debounce, update the needed projectors.
this.updateProjectorDataDebounceSubject.pipe(auditTime(10)).subscribe(() => {
const allActiveProjectorIds = Object.keys(this.openProjectorInstances)
.map(id => parseInt(id, 10))
.filter(id => this.openProjectorInstances[id] > 0);
this.websocketService.send('listenToProjectors', { projector_ids: allActiveProjectorIds });
});
}
/**
* Gets an observable for the projector data.
*
* @param projectorId The requested projector
* @return an observable for the projector data of the given projector.
*/
public getProjectorObservable(projectorId: number): Observable<ProjectorData | null> {
// Count projectors.
if (!this.openProjectorInstances[projectorId]) {
this.openProjectorInstances[projectorId] = 1;
if (!this.currentProjectorData[projectorId]) {
this.currentProjectorData[projectorId] = new BehaviorSubject<ProjectorData | null>(null);
}
} else {
this.openProjectorInstances[projectorId]++;
}
// Projector opened the first time.
if (this.openProjectorInstances[projectorId] === 1) {
this.updateProjectorDataSubscription();
}
return this.currentProjectorData[projectorId].asObservable();
}
/**
* Unsubscribe data from the server, if the last projector was closed.
*
* @param projectorId the projector.
*/
public projectorClosed(projectorId: number): void {
if (this.openProjectorInstances[projectorId]) {
this.openProjectorInstances[projectorId]--;
}
if (this.openProjectorInstances[projectorId] === 0) {
this.updateProjectorDataSubscription();
this.currentProjectorData[projectorId].next(null);
}
}
/**
* Requests to update the data subscription to the server.
*/
private updateProjectorDataSubscription(): void {
this.updateProjectorDataDebounceSubject.next();
}
/**
* @returns the available projectior data for the given projector. Note that the data
* might not be there, if there is no subscribtion for this projector. But the
* data, if exist, is always the current data.
*/
public getAvailableProjectorData(projector: Projector): ProjectorData | null {
if (this.currentProjectorData[projector.id]) {
return this.currentProjectorData[projector.id].getValue();
}
}
}

View File

@ -1,18 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module';
import { ProjectorService } from './projector.service';
describe('ProjectorService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [ProjectorService]
});
});
it('should be created', inject([ProjectorService], (service: ProjectorService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,424 +0,0 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { BaseModel } from 'app/shared/models/base/base-model';
import { ProjectionDefault } from 'app/shared/models/core/projection-default';
import {
elementIdentifies,
IdentifiableProjectorElement,
Projector,
ProjectorElement,
ProjectorElements
} from 'app/shared/models/core/projector';
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
import {
isProjectable,
isProjectorElementBuildDeskriptor,
Projectable,
ProjectorElementBuildDeskriptor
} from 'app/site/base/projectable';
import { SlideManager } from 'app/slides/services/slide-manager.service';
import { ConfigService } from '../ui-services/config.service';
import { DataStoreService } from './data-store.service';
import { HttpService } from './http.service';
import { ProjectorDataService } from './projector-data.service';
import { ViewModelStoreService } from './view-model-store.service';
/**
* This service cares about Projectables being projected and manage all projection-related
* actions.
*
* We cannot access the ProjectorRepository here, so we will deal with plain projector objects.
*/
@Injectable({
providedIn: 'root'
})
export class ProjectorService {
/**
* Constructor.
*
* @param DS
* @param dataSend
*/
public constructor(
private DS: DataStoreService,
private http: HttpService,
private slideManager: SlideManager,
private viewModelStore: ViewModelStoreService,
private translate: TranslateService,
private configService: ConfigService,
private projectorDataService: ProjectorDataService
) {}
/**
* Retusn the identifiable projector element from the given types of slides/elements/descriptors
*
* @param obj Something related to IdentifiableProjectorElement
* @returns the identifiable projector element from obj.
*/
private getProjectorElement(
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
): IdentifiableProjectorElement {
if (isProjectable(obj)) {
return obj.getSlide(this.configService).getBasicProjectorElement({});
} else if (isProjectorElementBuildDeskriptor(obj)) {
return obj.getBasicProjectorElement({});
} else {
return obj;
}
}
/**
* Checks, if a given object is projected.
*
* @param obj The object in question
* @returns true, if the object is projected on one projector.
*/
public isProjected(obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement): boolean {
const element = this.getProjectorElement(obj);
return this.DS.getAll<Projector>('core/projector').some(projector => {
return projector.isElementShown(element);
});
}
/**
* Get all projectors where the object is prejected on.
*
* @param obj The object in question
* @return All projectors, where this Object is projected on
*/
public getProjectorsWhichAreProjecting(
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
): Projector[] {
const element = this.getProjectorElement(obj);
return this.DS.getAll<Projector>('core/projector').filter(projector => {
return projector.isElementShown(element);
});
}
/**
* Checks, if the object is projected on the given projector.
*
* @param obj The object
* @param projector The projector to test
* @returns true, if the object is projected on the projector.
*/
public isProjectedOn(
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement,
projector: Projector
): boolean {
return projector.isElementShown(this.getProjectorElement(obj));
}
/**
* Projects the given ProjectorElement on the given projectors. Removes the element
* from all non-given projectors
*
* @param projectors All projectors where to add the element.
* @param element The element in question.
*/
public projectOnMultiple(projectors: Projector[], element: IdentifiableProjectorElement): void {
this.DS.getAll<Projector>('core/projector').forEach(projector => {
if (projectors.includes(projector)) {
this.projectOn(projector, element);
} else if (projector.isElementShown(element)) {
this.removeFrom(projector, element);
}
});
}
/**
* Projcets the given object on the projector. If the object is non-stable, all other non-stable
* elements will be removed and added to the history.
*
* @param projector The projector to add the object to.
* @param obj The object to project
*/
public async projectOn(
projector: Projector,
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
): Promise<void> {
const element = this.getProjectorElement(obj);
if (element.stable) {
// remove the same element, if it is currently projected
projector.removeElements(element);
// Add this stable element
projector.addElement(element);
await this.projectRequest(projector, projector.elements);
} else {
// For non-stable elements remove all other non-stable elements, add them to the history and
// add the one new element to the projector.
const removedElements = projector.removeAllNonStableElements();
let changed = removedElements.length > 0;
if (element) {
projector.addElement(element);
changed = true;
}
if (changed) {
await this.projectRequest(projector, projector.elements, null, removedElements, false, true);
}
}
}
/**
* Removes the given object from the projector. Non stable elements will be added to the history.
*
* @param projector The projector
* @param obj the object to unproject
*/
public async removeFrom(
projector: Projector,
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
): Promise<void> {
const element = this.getProjectorElement(obj);
const removedElements = projector.removeElements(element);
if (removedElements.length > 0) {
if (element.stable) {
await this.projectRequest(projector, projector.elements);
} else {
// For non-stable elements: Add removed elements to the history.
await this.projectRequest(projector, projector.elements, null, removedElements);
}
}
}
public async updateElement(
projector: Projector,
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
): Promise<void> {
const element = this.getProjectorElement(obj);
projector.replaceElements(element);
await this.projectRequest(projector, projector.elements, projector.elements_preview);
}
/**
* Executes the request to change projector elements.
*
* Note: Just one of `appendToHistory` and `deleteLastHistoryElement` can be given.
*
* @param projector The affected projector
* @param elements (optional) Elements to set.
* @param preview (optional) preview to set
* @param appendToHistory (optional) Elements to be appended to the history
* @param deleteLastHistroyElement (optional) If given, the last history element will be removed.
*/
private async projectRequest(
projector: Projector,
elements?: ProjectorElements,
preview?: ProjectorElements,
appendToHistory?: ProjectorElements,
deleteLastHistroyElement?: boolean,
resetScroll?: boolean
): Promise<void> {
const requestData: any = {};
if (elements) {
requestData.elements = this.cleanupElements(projector, elements);
}
if (preview) {
requestData.preview = preview;
}
if (appendToHistory && appendToHistory.length) {
requestData.append_to_history = appendToHistory;
}
if (deleteLastHistroyElement) {
requestData.delete_last_history_element = true;
}
if (appendToHistory && appendToHistory.length && deleteLastHistroyElement) {
throw new Error('You cannot append to the history and delete the last element at the same time');
}
if (resetScroll) {
requestData.reset_scroll = resetScroll;
}
await this.http.post(`/rest/core/projector/${projector.id}/project/`, requestData);
}
/**
* Cleans up stable elements with errors from the projector
*
* @param projector The projector
* @param elements The elements to clean up
* @reutns the cleaned up elements.
*/
private cleanupElements(projector: Projector, elements: ProjectorElements): ProjectorElements {
const projectorData = this.projectorDataService.getAvailableProjectorData(projector);
if (projectorData) {
projectorData.forEach(entry => {
if (entry.data.error && entry.element.stable) {
// Remove this element
const idElementToRemove = this.slideManager.getIdentifialbeProjectorElement(entry.element);
elements = elements.filter(element => {
return !elementIdentifies(idElementToRemove, element);
});
}
});
}
return elements;
}
/**
* Given a projectiondefault, we want to retrieve the projector, that is assigned
* to this default.
*
* @param projectiondefault The projection default
* @return the projector associated to the given projectiondefault.
*/
public getProjectorForDefault(projectiondefault: string): Projector | null {
const pd = this.DS.find(ProjectionDefault, _pd => _pd.name === projectiondefault);
if (pd) {
return this.DS.get<Projector>(Projector, pd.projector_id);
} else {
return null;
}
}
/**
* Asserts, that the given element is mappable to a model or view model.
* Throws an error, if this assertion fails.
*
* @param element The element to check
*/
private assertElementIsMappable(element: IdentifiableProjectorElement): void {
if (!this.slideManager.canSlideBeMappedToModel(element.name)) {
throw new Error('This projector element cannot be mapped to a model');
}
const identifiers = element.getIdentifiers();
if (!identifiers.includes('name') || !identifiers.includes('id')) {
throw new Error('To map this element to a model, a name and id is needed.');
}
}
/**
* Returns a model associated with the identifiable projector element. Throws an error,
* if the element is not mappable.
*
* @param element The projector element
* @returns the model from the projector element
*/
public getModelFromProjectorElement<T extends BaseModel>(element: IdentifiableProjectorElement): T {
this.assertElementIsMappable(element);
return this.DS.get<T>(element.name, element.id);
}
/**
* Returns a view model associated with the identifiable projector element. Throws an error,
* if the element is not mappable.
*
* @param element The projector element
* @returns the view model from the projector element
*/
public getViewModelFromProjectorElement<T extends BaseProjectableViewModel>(
element: IdentifiableProjectorElement
): T {
this.assertElementIsMappable(element);
const viewModel = this.viewModelStore.get<T>(element.name, element.id);
if (viewModel && !isProjectable(viewModel)) {
console.error('The view model is not projectable', viewModel, element);
}
return viewModel;
}
/**
*/
public getSlideTitle(element: ProjectorElement): string {
if (this.slideManager.canSlideBeMappedToModel(element.name)) {
const idElement = this.slideManager.getIdentifialbeProjectorElement(element);
const viewModel = this.getViewModelFromProjectorElement(idElement);
if (viewModel) {
return viewModel.getProjectorTitle();
}
}
const configuration = this.slideManager.getSlideConfiguration(element.name);
if (configuration.getSlideTitle) {
return configuration.getSlideTitle(element, this.translate, this.viewModelStore);
}
return this.translate.instant(this.slideManager.getSlideVerboseName(element.name));
}
/**
* Projects the next slide in the queue. Moves all currently projected
* non-stable slides to the history.
*
* @param projector The projector
*/
public async projectNextSlide(projector: Projector): Promise<void> {
await this.projectPreviewSlide(projector, 0);
}
/**
* Projects one slide (given by the index of the preview) on the given projector. Moves
* all current projected non-stable elements to the history.
*
* @param projector The projector
* @param previewIndex The index in the `elements_preview` array.
*/
public async projectPreviewSlide(projector: Projector, previewIndex: number): Promise<void> {
if (projector.elements_preview.length === 0 || previewIndex >= projector.elements_preview.length) {
return;
}
const removedElements = projector.removeAllNonStableElements();
projector.addElement(projector.elements_preview.splice(previewIndex, 1)[0]);
await this.projectRequest(
projector,
projector.elements,
projector.elements_preview,
removedElements,
false,
true
);
}
/**
* Projects the last slide of the history. This slide will be removed from the history.
*
* @param projector The projector
*/
public async projectPreviousSlide(projector: Projector): Promise<void> {
if (projector.elements_history.length === 0) {
return;
}
// Get the last element from the history
const lastElements: ProjectorElements = projector.elements_history[projector.elements_history.length - 1];
let lastElement: ProjectorElement = null;
if (lastElements.length > 0) {
lastElement = lastElements[0];
}
// Add all current elements to the preview.
const removedElements = projector.removeAllNonStableElements();
removedElements.forEach(e => projector.elements_preview.unshift(e));
// Add last element
if (lastElement) {
projector.addElement(lastElement);
}
await this.projectRequest(projector, projector.elements, projector.elements_preview, null, true, true);
}
/**
* Saves the preview of the projector
*
* @param projector The projector to save the preview.
*/
public async savePreview(projector: Projector): Promise<void> {
await this.projectRequest(projector, null, projector.elements_preview);
}
/**
* Appends the given element to the preview.
*
* @param projector The projector
* @param element The element to add to the preview.
*/
public async addElementToPreview(projector: Projector, element: ProjectorElement): Promise<void> {
projector.elements_preview.push(element);
await this.projectRequest(projector, null, projector.elements_preview);
}
}

View File

@ -1,18 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module';
import { PwaService } from './pwa.service';
describe('PwaService', () => {
beforeEach(() =>
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [PwaService]
})
);
it('should be created', inject([PwaService], (service: PwaService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,25 +0,0 @@
import { Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
/**
* Service for Progressive Web App options
*/
@Injectable({
providedIn: 'root'
})
export class PwaService {
public promptEvent;
public constructor(swUpdate: SwUpdate) {
// check if an update is available
swUpdate.available.subscribe(event => {
// TODO: ask user if app should update now
window.location.reload();
});
// install button
window.addEventListener('beforeinstallprompt', event => {
this.promptEvent = event;
});
}
}

View File

@ -1,16 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { RelationCacheService } from './relation-cache.service';
describe('RelationCacheService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [RelationCacheService]
});
});
it('should be created', inject([RelationCacheService], (service: RelationCacheService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,84 +0,0 @@
import { Injectable } from '@angular/core';
export interface CacheChangeIds {
[elementId: string]: number;
}
/**
* Handles caching metadata for the relation manager.
*
* Mainly holds an object mapping element ids to their last updates change ids.
* The manager can detect invalid caches, if the change id in the cache's metadata
* diverges from the change ids in this service.
*/
@Injectable({
providedIn: 'root'
})
export class RelationCacheService {
private cache: {
[elementId: string]: number;
} = {};
public constructor() {}
/**
* Reset the cache.
*/
public reset(): void {
this.cache = {};
}
/**
* Deletes models from this cache.
*
* @param collection Collection
* @param ids Ids from all models in the collection
*/
public registerDeletedModels(collection: string, ids: number[]): void {
ids.forEach(id => {
const elementId = collection + ':' + id;
delete this.cache[elementId];
});
}
/**
* Adds models to the cache with the given change id.
*
* @param collection Collection
* @param ids Ids from all models in the collection
* @param changeId The change id to put into the cache
*/
public registerChangedModels(collection: string, ids: number[], changeId: number): void {
ids.forEach(id => {
const elementId = collection + ':' + id;
this.cache[elementId] = changeId;
});
}
/**
* Queries the change id for one element.
*
* @param elementId The element to query.
*/
public query(elementId: string): number | null {
return this.cache[elementId] || null;
}
/**
* Checks, if all given change ids are valid.
*/
public checkCacheValidity(changeIds: CacheChangeIds): boolean {
if (!changeIds) {
return false;
}
const elementIds = Object.keys(changeIds);
if (!elementIds.length) {
return false;
}
return elementIds.every(elementId => {
return this.query(elementId) === changeIds[elementId];
});
}
}

View File

@ -1,16 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { RelationManagerService } from './relation-manager.service';
describe('RelationManagerService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [RelationManagerService]
});
});
it('should be created', inject([RelationManagerService], (service: RelationManagerService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,231 +0,0 @@
import { Injectable } from '@angular/core';
import { BaseModel } from 'app/shared/models/base/base-model';
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
import { ModelDescriptor, NestedModelDescriptors } from '../repositories/base-repository';
import { CacheChangeIds, RelationCacheService } from './relation-cache.service';
import {
isCustomRelationDefinition,
isGenericRelationDefinition,
isNormalRelationDefinition,
isReverseRelationDefinition,
RelationDefinition
} from '../definitions/relations';
import { ViewModelStoreService } from './view-model-store.service';
/**
* Manages relations between view models. This service is and should only used by the
* base repository to offload managing relations between view models.
*/
@Injectable({
providedIn: 'root'
})
export class RelationManagerService {
public constructor(
private viewModelStoreService: ViewModelStoreService,
private relationCacheService: RelationCacheService
) {}
public handleRelation<M extends BaseModel, V extends BaseViewModel>(
model: M,
viewModel: V,
relation: RelationDefinition
): any {
if (isNormalRelationDefinition(relation)) {
if (
(relation.type === 'M2M' || relation.type === 'O2M') &&
model[relation.ownIdKey] &&
model[relation.ownIdKey].constructor === Array
) {
const foreignViewModels = this.viewModelStoreService.getMany(
relation.foreignViewModel,
model[relation.ownIdKey]
);
this.sortViewModels(foreignViewModels, relation.order);
return foreignViewModels;
} else if (relation.type === 'M2O') {
const foreignViewModel = this.viewModelStoreService.get(
relation.foreignViewModel,
model[relation.ownIdKey]
);
return foreignViewModel;
}
} else if (isReverseRelationDefinition(relation)) {
if (relation.type === 'M2M') {
const foreignViewModels = this.viewModelStoreService.filter(
relation.foreignViewModel,
foreignViewModel =>
foreignViewModel[relation.foreignIdKey] &&
foreignViewModel[relation.foreignIdKey].constructor === Array &&
foreignViewModel[relation.foreignIdKey].includes(model.id)
);
this.sortViewModels(foreignViewModels, relation.order);
return foreignViewModels;
} else if (relation.type === 'O2M') {
const foreignViewModels = this.viewModelStoreService.filter(
relation.foreignViewModel,
foreignViewModel =>
foreignViewModel[relation.foreignIdKey] && foreignViewModel[relation.foreignIdKey] === model.id
);
this.sortViewModels(foreignViewModels, relation.order);
return foreignViewModels;
} else if (relation.type === 'M2O') {
const foreignViewModel = this.viewModelStoreService.find(
relation.foreignViewModel,
_foreignViewModel =>
_foreignViewModel[relation.foreignIdKey] &&
_foreignViewModel[relation.foreignIdKey] === model.id
);
return foreignViewModel;
}
} else if (isGenericRelationDefinition(relation)) {
const contentObject = this.viewModelStoreService.get<BaseViewModel>(
model[relation.ownContentObjectDataKey].collection,
model[relation.ownContentObjectDataKey].id
);
if (contentObject && relation.isVForeign(contentObject)) {
return contentObject;
}
} else if (isCustomRelationDefinition(relation)) {
return relation.get(model, viewModel);
}
}
public handleCachedRelation<V extends BaseViewModel>(
property: string,
target: V,
model: BaseModel,
viewModel: BaseViewModel,
relation: RelationDefinition
): any {
let result: any;
const cacheProperty = '__' + property;
const cachePropertyChangeIds = cacheProperty + '_cids';
let cached: boolean = cacheProperty in target;
let changeIds: CacheChangeIds | null = null;
if (cached) {
result = target[cacheProperty];
changeIds = target[cachePropertyChangeIds];
}
if (!isCustomRelationDefinition(relation)) {
if (cached) {
cached = this.relationCacheService.checkCacheValidity(changeIds);
}
if (!cached) {
result = this.handleRelation(model, viewModel, relation) as BaseViewModel | BaseViewModel[];
if (result) {
// Cache it:
target[cacheProperty] = result;
const newChangeIds = {};
if (Array.isArray(result)) {
result.forEach(
(_vm: BaseViewModel) =>
(newChangeIds[_vm.elementId] = this.relationCacheService.query(_vm.elementId))
);
} else {
newChangeIds[result.elementId] = this.relationCacheService.query(result.elementId);
}
target[cachePropertyChangeIds] = newChangeIds;
} else {
delete target[cacheProperty];
}
}
} else {
// Custom relations
const obj = relation.getCacheObjectToCheck(viewModel);
if (cached) {
if (obj && changeIds && changeIds[obj.elementId]) {
cached = this.relationCacheService.query(obj.elementId) === changeIds[obj.elementId];
} else {
cached = false;
}
}
if (!cached) {
result = this.handleRelation(model, viewModel, relation);
if (result && obj) {
target[cacheProperty] = result;
target[cachePropertyChangeIds] = {};
target[cachePropertyChangeIds][obj.elementId] = this.relationCacheService.query(obj.elementId);
} else {
delete target[cachePropertyChangeIds];
}
}
}
return result;
}
/**
* Sorts the array of foreign view models in the given view models for the given relation.
*/
public sortViewModels(viewModels: BaseViewModel[], order?: string): void {
viewModels.sort((a: BaseViewModel, b: BaseViewModel) => {
if (!order || a[order] === b[order]) {
return a.id - b.id;
} else {
return a[order] - b[order];
}
});
}
public createViewModel<M extends BaseModel, V extends BaseViewModel>(
model: M,
viewModelCtor: ViewModelConstructor<V>,
relationsByKey: { [key: string]: RelationDefinition },
nestedModelDescriptors: NestedModelDescriptors
): V {
let viewModel = new viewModelCtor(model);
viewModel = new Proxy(viewModel, {
get: (target: V, property) => {
let result: any;
const _model: M = target.getModel();
const relation = typeof property === 'string' ? relationsByKey[property] : null;
if (property in target) {
const descriptor = Object.getOwnPropertyDescriptor(viewModelCtor.prototype, property);
if (descriptor && descriptor.get) {
result = descriptor.get.bind(viewModel)();
} else {
result = target[property];
}
} else if (property in _model) {
result = _model[property];
} else if (relation) {
result = this.handleCachedRelation(<any>property, target, _model, viewModel, relation);
}
return result;
}
});
// set nested models
(nestedModelDescriptors[model.collectionString] || []).forEach(
(modelDescriptor: ModelDescriptor<BaseModel, BaseViewModel>) => {
const nestedModels = (model[modelDescriptor.ownKey] || []).map((nestedModel: object) => {
return new modelDescriptor.foreignModel(nestedModel);
});
const nestedViewModels = nestedModels.map(nestedModel => {
const nestedViewModel = this.createViewModel(
nestedModel,
modelDescriptor.foreignViewModel,
modelDescriptor.relationDefinitionsByKey,
nestedModelDescriptors
);
Object.keys(modelDescriptor.titles || {}).forEach(name => {
nestedViewModel[name] = () => modelDescriptor.titles[name](nestedViewModel);
});
return nestedViewModel;
});
this.sortViewModels(nestedViewModels, modelDescriptor.order);
viewModel[modelDescriptor.ownKey] = nestedViewModels;
}
);
return viewModel;
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { ServertimeService } from './servertime.service';
describe('ServertimeService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [ServertimeService]
});
});
it('should be created', inject([ServertimeService], (service: ServertimeService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,80 +0,0 @@
import { Injectable } from '@angular/core';
import { environment } from 'environments/environment.prod';
import { BehaviorSubject, Observable } from 'rxjs';
import { HttpService } from './http.service';
/**
* This service provides the timeoffset to the server and a user of this service
* can query the servertime.
*
* This service needs to be started with `startScheduler` which will update
* the servertime frequently.
*/
@Injectable({
providedIn: 'root'
})
export class ServertimeService {
// TODO: couple this with the offlineService: Just retry often, if we are online.
// When we are offline, this is not necessary.
private static FAILURE_TIMEOUT = 30;
private static NORMAL_TIMEOUT = 60 * 5;
/**
* The server offset in milliseconds
*/
private serverOffsetSubject = new BehaviorSubject<number>(0);
public constructor(private http: HttpService) {}
/**
* Starts the scheduler to sync with the server.
*/
public startScheduler(): void {
this.scheduleNextRefresh(0.1);
}
/**
* Get an observable for the server offset.
*/
public getServerOffsetObservable(): Observable<number> {
return this.serverOffsetSubject.asObservable();
}
/**
* Schedules the next sync with the server.
*
* @param seconds The timeout in seconds to the refresh.
*/
private scheduleNextRefresh(seconds: number): void {
setTimeout(async () => {
let timeout = ServertimeService.NORMAL_TIMEOUT;
try {
await this.refreshServertime();
} catch (e) {
timeout = ServertimeService.FAILURE_TIMEOUT;
}
this.scheduleNextRefresh(timeout);
}, 1000 * seconds);
}
/**
* Queries the servertime and calculates the offset.
*/
private async refreshServertime(): Promise<void> {
// servertime is the time in seconds.
const servertime = await this.http.get<number>(environment.urlPrefix + '/core/servertime/');
if (typeof servertime !== 'number') {
throw new Error('The returned servertime is not a number');
}
this.serverOffsetSubject.next(Math.floor(Date.now() - servertime * 1000));
}
/**
* Calculate the time of the server.
*/
public getServertime(): number {
return Date.now() - this.serverOffsetSubject.getValue();
}
}

View File

@ -1,18 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module';
import { StorageService } from './storage.service';
describe('StorageService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [StorageService]
});
});
it('should be created', inject([StorageService], (service: StorageService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,90 +0,0 @@
import { Injectable } from '@angular/core';
import { LocalStorage } from '@ngx-pwa/local-storage';
import { Observable } from 'rxjs';
import { OpenSlidesStatusService } from './openslides-status.service';
/**
* Provides an async API to an key-value store using ngx-pwa which is internally
* using IndexedDB or localStorage as a fallback.
*/
@Injectable({
providedIn: 'root'
})
export class StorageService {
private noClearKeys: string[] = [];
/**
* Constructor to create the StorageService. Needs the localStorage service.
* @param localStorage
*/
public constructor(private localStorage: LocalStorage, private OSStatus: OpenSlidesStatusService) {}
public addNoClearKey(key: string): void {
this.noClearKeys.push(key);
}
/**
* Sets the item into the store asynchronously.
* @param key
* @param item
*/
public async set(key: string, item: any): Promise<void> {
this.assertNotHistoryMode();
if (item === null || item === undefined) {
await this.remove(key); // You cannot do a setItem with null or undefined...
} else {
if (!(await this.localStorage.setItem(key, item).toPromise())) {
throw new Error('Could not set the item.');
}
}
}
/**
* get a value from the store. You need to subscribe to the request to retrieve the value.
*
* @param key The key to get the value from
* @returns The requested value to the key
*/
public async get<T>(key: string): Promise<T> {
return ((await this.localStorage.getItem<T>(key)) as Observable<T>).toPromise();
}
/**
* Remove the key from the store.
* @param key The key to remove the value from
*/
public async remove(key: string): Promise<void> {
this.assertNotHistoryMode();
if (!(await this.localStorage.removeItem(key).toPromise())) {
throw new Error('Could not delete the item.');
}
}
/**
* Clear the whole cache except for keys given in `addNoClearKey`.
*/
public async clear(): Promise<void> {
this.assertNotHistoryMode();
const savedData: { [key: string]: any } = {};
for (const key of this.noClearKeys) {
savedData[key] = await this.get(key);
}
if (!(await this.localStorage.clear().toPromise())) {
throw new Error('Could not clear the storage.');
}
for (const key of this.noClearKeys) {
await this.set(key, savedData[key]);
}
}
/**
* Throws an error, if we are in history mode.
*/
private assertNotHistoryMode(): void {
if (this.OSStatus.isInHistoryMode) {
throw new Error('You cannot use the storageService in histroy mode.');
}
}
}

View File

@ -1,19 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module';
import { TimeTravelService } from './time-travel.service';
describe('TimeTravelService', () => {
beforeEach(() =>
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [TimeTravelService]
})
);
it('should be created', () => {
const service: TimeTravelService = TestBed.get(TimeTravelService);
expect(service).toBeTruthy();
});
});

View File

@ -1,107 +0,0 @@
import { Injectable } from '@angular/core';
import { environment } from 'environments/environment';
import { BaseModel } from 'app/shared/models/base/base-model';
import { History } from 'app/shared/models/core/history';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { DataStoreService, DataStoreUpdateManagerService } from './data-store.service';
import { HttpService } from './http.service';
import { OpenSlidesStatusService } from './openslides-status.service';
import { OpenSlidesService } from './openslides.service';
import { WebsocketService } from './websocket.service';
interface HistoryData {
[collection: string]: BaseModel[];
}
/**
* Service to enable browsing OpenSlides in a previous version.
*
* This should stop auto updates, save the current ChangeID and overwrite the DataStore with old Values
* from the servers History.
*
* Restoring is nor possible yet. Simply reload
*/
@Injectable({
providedIn: 'root'
})
export class TimeTravelService {
/**
* Constructs the time travel service
*
* @param httpService To fetch the history data
* @param webSocketService to disable websocket connection
* @param modelMapperService to cast history objects into models
* @param DS to overwrite the dataStore
* @param OSStatus Sets the history status
* @param OpenSlides For restarting OpenSlide when exiting the history mode
*/
public constructor(
private httpService: HttpService,
private webSocketService: WebsocketService,
private modelMapperService: CollectionStringMapperService,
private DS: DataStoreService,
private OSStatus: OpenSlidesStatusService,
private OpenSlides: OpenSlidesService,
private DSUpdateManager: DataStoreUpdateManagerService
) {}
/**
* Main entry point to set OpenSlides to another history point.
*
* @param history the desired point in the history of OpenSlides
*/
public async loadHistoryPoint(history: History): Promise<void> {
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS);
await this.stopTime(history);
const historyData: HistoryData = await this.getHistoryData(history);
const allModels = [];
Object.keys(historyData).forEach(collection => {
const targetClass = this.modelMapperService.getModelConstructor(collection);
historyData[collection].forEach(model => {
allModels.push(new targetClass(model));
});
});
await this.DS.set(allModels, 0);
this.DSUpdateManager.commit(updateSlot, 1, true);
}
/**
* Leaves the history mode. Just restart OpenSlides:
* The active user is checked, a new WS connection established and
* all missed auto updates are requested.
*/
public async resumeTime(): Promise<void> {
this.OSStatus.leaveHistoryMode();
await this.DS.set();
await this.OpenSlides.reboot();
}
/**
* Read the history on a given time
*
* @param date the Date object
* @returns the full history on the given date
*/
private async getHistoryData(history: History): Promise<HistoryData> {
const queryParams = { timestamp: Math.ceil(history.timestamp) };
return await this.httpService.get<HistoryData>(
`${environment.urlPrefix}/core/history/data/`,
null,
queryParams
);
}
/**
* Clears the DataStore and stops the WebSocket connection
*/
private async stopTime(history: History): Promise<void> {
await this.webSocketService.close();
await this.DS.set(); // Same as clear, but not persistent.
this.OSStatus.enterHistoryMode(history);
}
}

View File

@ -1,16 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { ViewModelStoreService } from './view-model-store.service';
describe('ViewModelStoreService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [ViewModelStoreService]
});
});
it('should be created', inject([ViewModelStoreService], (service: ViewModelStoreService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,96 +0,0 @@
import { Injectable } from '@angular/core';
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
import { BaseRepository } from '../repositories/base-repository';
import { CollectionStringMapperService } from './collection-string-mapper.service';
/**
* This service takes care of handling view models.
*/
@Injectable({
providedIn: 'root'
})
export class ViewModelStoreService {
/**
* @param mapperService
*/
public constructor(private mapperService: CollectionStringMapperService) {}
/**
* gets the repository from a collection string or a view model constructor.
*
* @param collectionType The collection string or constructor.
*/
private getRepository<V extends BaseViewModel>(
collectionType: ViewModelConstructor<V> | string
): BaseRepository<V, any, any> {
return this.mapperService.getRepository(collectionType) as BaseRepository<V, any, any>;
}
/**
* Returns the view model identified by the collectionString and id
*
* @param collectionString The collection of the view model
* @param id The id of the view model
*/
public get<V extends BaseViewModel>(collectionType: ViewModelConstructor<V> | string, id: number): V {
return this.getRepository(collectionType).getViewModel(id);
}
/**
* Returns all view models for the given ids.
*
* @param collectionType The collection of the view model
* @param ids All ids to match
*/
public getMany<T extends BaseViewModel>(collectionType: ViewModelConstructor<T> | string, ids?: number[]): T[] {
if (!ids) {
return [];
}
const repository = this.getRepository<T>(collectionType);
return ids
.map(id => {
return repository.getViewModel(id);
})
.filter(model => !!model); // remove non valid models.
}
/**
* Gets all view models from a collection
*
* @param collectionString The collection
* @returns all models from the collection
*/
public getAll<T extends BaseViewModel>(collectionType: ViewModelConstructor<T> | string): T[] {
return this.getRepository(collectionType).getViewModelList();
}
/**
* Get all view models from a collection, that satisfy the callback
*
* @param collectionString The collection
* @param callback The function to check
* @returns all matched view models of the collection
*/
public filter<T extends BaseViewModel>(
collectionType: ViewModelConstructor<T> | string,
callback: (model: T) => boolean
): T[] {
return this.getAll<T>(collectionType).filter(callback);
}
/**
* Finds one view model from the collection, that satisfies the callback
*
* @param collectionString The collection
* @param callback THe callback to satisfy
* @returns a found view model or null, if nothing was found.
*/
public find<T extends BaseViewModel>(
collectionType: ViewModelConstructor<T> | string,
callback: (model: T) => boolean
): T {
return this.getAll<T>(collectionType).find(callback);
}
}

View File

@ -1,17 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { WebsocketService } from './websocket.service';
describe('WebsocketService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [WebsocketService]
});
});
it('should be created', inject([WebsocketService], (service: WebsocketService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,567 +0,0 @@
import { EventEmitter, Injectable, NgZone } from '@angular/core';
import { MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { compress, decompress } from 'lz4js';
import { Observable, Subject } from 'rxjs';
import { take } from 'rxjs/operators';
import { OfflineService } from './offline.service';
import { OpenSlidesStatusService } from './openslides-status.service';
import { formatQueryParams, QueryParams } from '../definitions/query-params';
/**
* The generic message format in which messages are send and recieved by the server.
*/
interface BaseWebsocketMessage {
type: string;
content: any;
}
/**
* Outgoing messages must have an id.
*/
interface OutgoingWebsocketMessage extends BaseWebsocketMessage {
id: string;
}
/**
* Incomming messages may have an `in_response`, if they are an answer to a previously
* submitted request.
*/
interface IncommingWebsocketMessage extends BaseWebsocketMessage {
in_response?: string;
}
/**
* The format of a messages content, if the message type is "error"
*/
interface WebsocketErrorContent {
code: number;
message: string;
}
function isWebsocketErrorContent(obj: any): obj is WebsocketErrorContent {
return !!obj && obj.code !== undefined && obj.message !== undefined;
}
/**
* All (custom) error codes that are used to pass error information
* from the server to the client
*/
export const WEBSOCKET_ERROR_CODES = {
NOT_AUTHORIZED: 100,
CHANGE_ID_TOO_HIGH: 101,
WRONG_FORMAT: 102
};
/*
* Options for (re-)connecting.
*/
interface ConnectOptions {
changeId?: number;
enableAutoupdates?: boolean;
}
/**
* Service that handles WebSocket connections. Other services can register themselfs
* with {@method getOberservable} for a specific type of messages. The content will be published.
*/
@Injectable({
providedIn: 'root'
})
export class WebsocketService {
/**
* The reference to the snackbar entry that is shown, if the connection is lost.
*/
private connectionErrorNotice: MatSnackBarRef<SimpleSnackBar>;
/**
* Subjects that will be called, if a reconnect after a retry (e.g. with a previous
* connection loss) was successful.
*/
private readonly _retryReconnectEvent: EventEmitter<void> = new EventEmitter<void>();
/**
* Getter for the retry reconnect event.
*/
public get retryReconnectEvent(): EventEmitter<void> {
return this._retryReconnectEvent;
}
/**
* Subjects that will be called, if connect took place, but not a retry reconnect.
* THis is the complement from the generalConnectEvent to the retryReconnectEvent.
*/
private readonly _noRetryConnectEvent: EventEmitter<void> = new EventEmitter<void>();
/**
* Getter for the no-retry connect event.
*/
public get noRetryConnectEvent(): EventEmitter<void> {
return this._noRetryConnectEvent;
}
/**
* Listeners will be nofitied, if the wesocket connection is establiched.
*/
private readonly _generalConnectEvent: EventEmitter<void> = new EventEmitter<void>();
/**
* Getter for the connect event.
*/
public get generalConnectEvent(): EventEmitter<void> {
return this._generalConnectEvent;
}
/**
* Listeners will be nofitied, if the wesocket connection is closed.
*/
private readonly _closeEvent: EventEmitter<void> = new EventEmitter<void>();
/**
* Getter for the close event.
*/
public get closeEvent(): EventEmitter<void> {
return this._closeEvent;
}
/**
* The subject for all websocket *message* errors (no connection errors).
*/
private readonly _errorResponseSubject = new Subject<WebsocketErrorContent>();
/**
* The error response obersable for all websocket message errors.
*/
public get errorResponseObservable(): Observable<WebsocketErrorContent> {
return this._errorResponseSubject.asObservable();
}
/**
* Saves, if the connection is open
*/
private _connectionOpen = false;
/**
* Whether the WebSocket connection is established
*/
public get isConnected(): boolean {
return this._connectionOpen;
}
private sendQueueWhileNotConnected: (string | ArrayBuffer)[] = [];
/**
* The websocket.
*/
private websocket: WebSocket | null;
private websocketId: string | null;
/**
* Subjects for types of websocket messages. A subscriber can get an Observable by {@function getOberservable}.
*/
private subjects: { [type: string]: Subject<any> } = {};
/**
* Callbacks for a waiting response. If any callback returns true, the message/error will not be propagated with the
* responsible subjects for the message type.
*/
private responseCallbacks: {
[id: string]: [(val: any) => boolean, (error: WebsocketErrorContent) => boolean];
} = {};
/**
* Saves, if the WS Connection should be closed (e.g. after an explicit `close()`). Prohibits
* retry connection attempts.
*/
private shouldBeClosed = true;
/**
* Counter for delaying the offline message.
*/
private retryCounter = 0;
/**
* The timeout in the onClose-handler for the next reconnect retry.
*/
private retryTimeout: any = null;
/**
* Constructor that handles the router
*
* @param zone
* @param router
* @param openSlidesStatusService
* @param offlineService
*/
public constructor(
private zone: NgZone,
private router: Router,
private openSlidesStatusService: OpenSlidesStatusService,
private offlineService: OfflineService
) {}
/**
* Creates a new WebSocket connection and handles incomming events.
*
* Uses NgZone to let all callbacks run in the angular context.
*/
public async connect(options: ConnectOptions = {}, retry: boolean = false): Promise<void> {
const websocketId = Math.random()
.toString(36)
.substring(7);
this.websocketId = websocketId;
if (this.websocket) {
this.websocket.close();
this.websocket = null;
}
if (!retry) {
this.shouldBeClosed = false;
}
// set defaults
options = Object.assign(options, {
enableAutoupdates: true
});
const queryParams: QueryParams = {
autoupdate: options.enableAutoupdates
};
if (options.changeId !== undefined) {
queryParams.change_id = options.changeId;
}
// Create the websocket
let socketPath = location.protocol === 'https:' ? 'wss://' : 'ws://';
socketPath += window.location.host;
if (this.openSlidesStatusService.isPrioritizedClient) {
socketPath += '/prioritize';
}
socketPath += '/ws/';
socketPath += formatQueryParams(queryParams);
this.websocket = new WebSocket(socketPath);
this.websocket.binaryType = 'arraybuffer';
// connection established. If this connect attept was a retry,
// The error notice will be removed and the reconnectSubject is published.
this.websocket.onopen = (event: Event) => {
if (this.websocketId !== websocketId) {
return;
}
this.zone.run(() => {
this.retryCounter = 0;
if (this.shouldBeClosed) {
this.offlineService.goOnline();
return;
}
this._connectionOpen = true;
if (retry) {
this.offlineService.goOnline();
this._retryReconnectEvent.emit();
} else {
this._noRetryConnectEvent.emit();
}
this._generalConnectEvent.emit();
this.sendQueueWhileNotConnected.forEach(entry => {
this.websocket.send(entry);
});
this.sendQueueWhileNotConnected = [];
});
};
this.websocket.onmessage = (event: MessageEvent) => {
if (this.websocketId !== websocketId) {
return;
}
this.zone.run(() => {
this.handleMessage(event.data);
});
};
this.websocket.onclose = (event: CloseEvent) => {
if (this.websocketId !== websocketId) {
return;
}
this.zone.run(() => {
this.onclose();
});
};
this.websocket.onerror = (event: ErrorEvent) => {
if (this.websocketId !== websocketId) {
return;
}
// place for proper error handling and debugging.
// Required to get more information about errors
this.zone.run(() => {
console.warn('WS error event:', event);
});
};
}
/**
* Handles an incomming message.
*
* @param data The message
*/
private handleMessage(data: string | ArrayBuffer): void {
if (data instanceof ArrayBuffer) {
const compressedSize = data.byteLength;
const decompressedBuffer: Uint8Array = decompress(new Uint8Array(data));
console.debug(
`Recieved ${compressedSize / 1024} KB (${decompressedBuffer.byteLength /
1024} KB uncompressed), ratio ${decompressedBuffer.byteLength / compressedSize}`
);
data = this.arrayBufferToString(decompressedBuffer);
}
const message: IncommingWebsocketMessage = JSON.parse(data);
console.debug('Received', message);
const type = message.type;
const inResponse = message.in_response;
const callbacks = this.responseCallbacks[inResponse];
if (callbacks) {
delete this.responseCallbacks[inResponse];
}
if (type === 'error') {
if (!isWebsocketErrorContent(message.content)) {
console.error('Websocket error without standard form!', message);
return;
}
// Print this to the console.
const error = message.content;
const errorDescription =
Object.keys(WEBSOCKET_ERROR_CODES).find(key => WEBSOCKET_ERROR_CODES[key] === error.code) ||
'unknown code';
console.error(`Websocket error with code=${error.code} (${errorDescription}):`, error.message);
// call the error callback, if there is any. If it returns true (means "handled"),
// the errorResponseSubject will not be called
if (inResponse && callbacks && callbacks[1] && callbacks[1](error)) {
return;
}
this._errorResponseSubject.next(error);
return;
}
// Try to fire a response callback directly. If it returnes true, the message is handeled
// and not distributed further
if (inResponse && callbacks && callbacks[0](message.content)) {
return;
}
if (this.subjects[type]) {
// Pass the content to the registered subscribers.
this.subjects[type].next(message.content);
} else {
console.warn(
`Got unknown websocket message type "${type}" (inResponse: ${inResponse}) with content`,
message.content
);
}
}
/**
* Closes the connection error notice
*/
private onclose(): void {
if (this.websocket) {
this.websocketId = null; // set to null, so now further events will be
// registered with the line below.
this.websocket.close(); // Cleanup old connection
this.websocket = null;
}
this._connectionOpen = false;
// 1000 is a normal close, like the close on logout
this._closeEvent.emit();
if (!this.shouldBeClosed) {
// Do not show the message snackbar on the projector
// tests for /projector and /projector/<id>
const onProjector = this.router.url.match(/^\/projector(\/[0-9]+\/?)?$/);
if (this.retryCounter <= 3) {
this.retryCounter++;
}
if (!this.connectionErrorNotice && !onProjector && this.retryCounter > 3) {
this.offlineService.goOfflineBecauseConnectionLost();
}
// A random retry timeout between 2000 and 5000 ms.
const timeout = Math.floor(Math.random() * 3000 + 2000);
this.retryTimeout = setTimeout(() => {
this.retryTimeout = null;
this.connect({ enableAutoupdates: true }, true);
}, timeout);
}
}
public cancelReconnectenRetry(): void {
if (this.retryTimeout) {
clearTimeout(this.retryTimeout);
this.retryTimeout = null;
}
}
/**
* Closes the websocket connection.
*/
public async close(): Promise<void> {
this.shouldBeClosed = true;
this.offlineService.goOnline();
if (this.websocket) {
this.websocket.close();
this.websocket = null;
await this.closeEvent.pipe(take(1)).toPromise();
}
}
/**
* Simulates an abnormal close.
*
* Internally does not set `shouldBeClosed`, so a reconnect is forced.
*/
public simulateAbnormalClose(): void {
this.onclose();
}
/**
* closes and reopens the connection. If the connection was closed before,
* it will be just opened.
*
* @param options The options for the new connection
*/
public async reconnect(options: ConnectOptions = {}): Promise<void> {
await this.close();
await this.connect(options);
}
/**
* Returns an observable for messages of the given type.
* @param type the message type
*/
public getOberservable<T>(type: string): Observable<T> {
if (!this.subjects[type]) {
this.subjects[type] = new Subject<T>();
}
return this.subjects[type].asObservable();
}
/**
* Sends a message to the server with the content and the given type.
*
* @param type the message type
* @param content the actual content
* @param success an optional success callback for a response. If it returns true, the message will not be
* propagated through the recieve subjects.
* @param error an optional error callback for a response. If it returns true, the error will not be propagated
* with the error subject.
* @param id an optional id for the message. If not given, a random id will be generated and returned.
* @returns the message id
*/
public send<T, R>(
type: string,
content: T,
success?: (val: R) => boolean,
error?: (error: WebsocketErrorContent) => boolean,
id?: string
): string {
if (!this.websocket) {
return;
}
const message: OutgoingWebsocketMessage = {
type: type,
content: content,
id: id
};
// create message id if not given. Required by the server.
if (!message.id) {
message.id = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
for (let i = 0; i < 8; i++) {
message.id += possible.charAt(Math.floor(Math.random() * possible.length));
}
}
if (success) {
this.responseCallbacks[message.id] = [success, error];
}
const jsonMessage = JSON.stringify(message);
const bytesMessage = this.stringToBuffer(jsonMessage);
const compressedMessage: ArrayBuffer = compress(bytesMessage);
const ratio = bytesMessage.byteLength / compressedMessage.byteLength;
const toSend = ratio > 1 ? compressedMessage : jsonMessage;
if (this.isConnected) {
this.websocket.send(toSend);
} else {
this.sendQueueWhileNotConnected.push(toSend);
}
return message.id;
}
/**
* Sends a message and waits for the response
*
* @param type the message type
* @param content the actual content
* @param id an optional id for the message. If not given, a random id will be generated and returned.
*/
public sendAndGetResponse<T, R>(type: string, content: T, id?: string): Promise<R> {
return new Promise<R>((resolve, reject) => {
this.send<T, R>(
type,
content,
val => {
resolve(val);
return true;
},
val => {
reject(val);
return true;
},
id
);
});
}
/**
* Converts an ArrayBuffer to a String.
*
* @param buffer - Buffer to convert
* @returns String
*/
private arrayBufferToString(buffer: Uint8Array): string {
return Array.from(buffer)
.map(code => String.fromCharCode(code))
.join('');
}
/**
* Converts a String to an ArrayBuffer.
*
* @param str - String to convert.
* @returns bufferView.
*/
private stringToBuffer(str: string): Uint8Array {
const bufferView = new Uint8Array();
for (let i = 0; i < str.length; i++) {
bufferView[i] = str.charCodeAt(i);
}
return bufferView;
}
}

View File

@ -1,13 +0,0 @@
import { CoreModule } from './core.module';
describe('CoreModule', () => {
let coreModule: CoreModule;
beforeEach(() => {
coreModule = new CoreModule(null);
});
it('should create an instance', () => {
expect(coreModule).toBeTruthy();
});
});

View File

@ -1,32 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule, Optional, SkipSelf, Type } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ProjectionDialogComponent } from 'app/shared/components/projection-dialog/projection-dialog.component';
import { ChoiceDialogComponent } from '../shared/components/choice-dialog/choice-dialog.component';
import { OnAfterAppsLoaded } from './definitions/on-after-apps-loaded';
import { OperatorService } from './core-services/operator.service';
import { PromptDialogComponent } from '../shared/components/prompt-dialog/prompt-dialog.component';
export const ServicesToLoadOnAppsLoaded: Type<OnAfterAppsLoaded>[] = [OperatorService];
/**
* Global Core Module.
*/
@NgModule({
imports: [CommonModule],
providers: [Title],
entryComponents: [PromptDialogComponent, ChoiceDialogComponent, ProjectionDialogComponent]
})
export class CoreModule {
/** make sure CoreModule is imported only by one NgModule, the AppModule */
public constructor(
@Optional()
@SkipSelf()
parentModule: CoreModule
) {
if (parentModule) {
throw new Error('CoreModule is already loaded. Import only in AppModule');
}
}
}

View File

@ -1,43 +0,0 @@
import { Type } from '@angular/core';
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
import { BaseRepository } from '../repositories/base-repository';
import { MainMenuEntry } from '../core-services/main-menu.service';
import { Searchable } from '../../site/base/searchable';
interface BaseModelEntry {
collectionString: string;
repository: Type<BaseRepository<any, any, any>>;
model: ModelConstructor<BaseModel>;
}
export interface ModelEntry extends BaseModelEntry {
viewModel: ViewModelConstructor<BaseViewModel>;
}
export interface SearchableModelEntry extends BaseModelEntry {
viewModel: ViewModelConstructor<BaseViewModel & Searchable>;
searchOrder: number;
openInNewTab?: boolean;
}
/**
* The configuration of an app.
*/
export interface AppConfig {
/**
* The name.
*/
name: string;
/**
* All models, that should be registered.
*/
models?: (ModelEntry | SearchableModelEntry)[];
/**
* Main menu entries.
*/
mainMenuEntries?: MainMenuEntry[];
}

View File

@ -1,9 +0,0 @@
/**
* A lifecyclehook to be called, after all apps are loaded.
*/
export interface OnAfterAppsLoaded {
/**
* The hook to call
*/
onAfterAppsLoaded(): void;
}

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