Compare commits

...

1134 Commits

Author SHA1 Message Date
Brain ba688a9668
Enable audio streaming 2022-06-20 21:33:40 +02:00
Emanuel Schütze 5a8689827c
Updated translation for delegation of vote (filter) (#6427) 2022-05-21 11:49:25 +02:00
Magnus Schieder 2c1cc31e52
Fix the font the h1 headings in the projector (#6412) 2022-05-02 09:11:03 +02:00
jsangmeister b2d05af928
Remove topic/option_ids from export (#6413) 2022-04-29 09:58:07 +02:00
Magnus Schieder 27682f3742
Make the style of the project titles customizable (#6409)
allow custom font for h1 and h2 projector title
2022-04-26 09:46:31 +02:00
Magnus Schieder 58d5bf7d6c
Fix unwanted line break in pdf export (#6408)
for inactive linenumbering
2022-04-24 17:48:40 +02:00
jsangmeister ff1d5c0c4d
Fix OS3 export to MI 3 (#6404) 2022-04-20 07:37:09 +02:00
jsangmeister 847b61f0ba
Update Readme & fix dependencies (#6401)
* Update Readme

* Fix typo & requirements mismatch

* Fix all dependencies

* Fix bug report typos
2022-04-19 10:27:30 +02:00
Emanuel Schütze 420bef680a
Updated Italian translation (#6392) 2022-03-28 14:19:23 +02:00
Emanuel Schütze d716bd5414
Fix pdf making of a motion with line numbers (#6391)
Fixed line breaks by using change recommendations in li elements.
Migrated from OS4 (see #966)
2022-03-28 14:19:04 +02:00
GabrielInTheWorld 941b0be524
Adjusts docker-dev setup to match docker-prod setup (#6394) 2022-03-28 13:26:07 +02:00
bug0001 a6446ce55c
documentation overhaul (#6353) 2022-02-16 12:52:12 +01:00
bug0001 b24951d806
Dependency resolve (#6359)
* Updated install instructions in Readme

* Fix black version to prevent mismatch with typing-extensions

Co-authored-by: Joshua Sangmeister <joshua.sangmeister@gmail.com>
2022-02-16 10:11:22 +01:00
Jasper Roloff c035839892
Fix build.sh for stable/3.4.x (#6329)
Fix issue where `build.sh all` failed with `unable to prepare context: unable to 'git clone' to temporary context directory: error fetching: fatal: Couldn't find remote ref master`
2022-01-18 20:31:22 +01:00
jsangmeister fceb892005
Fix various exporter issues (#6327)
* Fix various exporter issues

* Fix formatting
2022-01-11 12:47:41 +01:00
Emanuel Schütze 31f59e069d
Merge pull request #6318 from GabrielInTheWorld/fix-candidate-percentbase
Fixes percentbase of candidates in assignments
2022-01-06 18:04:43 +01:00
GabrielInTheWorld 23577be953 Fixes: Percentbases of candidates in assignments 2022-01-06 17:38:32 +01:00
Emanuel Schütze 1e22ffbf7e
Merge pull request #6299 from normanjaeckel/GetUser2
Changed GetUser view. Now it retrieves one or more users.
2021-12-20 08:55:39 +01:00
Norman Jäckel b0bc4129ce Changed GetUser view. Now it retrieves one or more users. 2021-11-30 00:30:54 +01:00
Emanuel Schütze 87b8fcd42b
Merge pull request #6296 from jsangmeister/fix-export
Update exporter to newest backend
2021-11-25 18:32:09 +01:00
Joshua Sangmeister 93e0b53b9e Add chat message collection & fields
Add & update poll backends
2021-11-25 18:00:35 +01:00
Emanuel Schütze 2dfc926ba2
Merge pull request #6294 from emanuelschuetze/translation-20211124
Updated translations and autoupdate service
2021-11-24 23:47:54 +01:00
Emanuel Schütze cbfffddec7 Updated translations and autoupdate service
Fixed #3607
2021-11-24 23:08:20 +01:00
Sean dc555a4116
Merge pull request #6287 from GabrielInTheWorld/of-groups-view
Fixes high amount of groups
2021-11-23 11:54:58 +01:00
Emanuel Schütze d4953ed81a
Merge pull request #6272 from normanjaeckel/GetUser
Added route to get a user e. g. for third-party applications.
2021-11-22 17:32:04 +01:00
Norman Jäckel 9cd0416938 Added route to get a user e. g. for third-party applications. 2021-11-22 17:21:27 +01:00
GabrielInTheWorld 19ea4a312e Fixes high amount of groups 2021-11-19 10:42:44 +01:00
Emanuel Schütze 4efc591d21
Merge pull request #6285 from jsangmeister/restrictions-fix
Various fixes for the exporter
2021-11-18 17:27:29 +01:00
Joshua Sangmeister cc6cf9ea42 Fix various export errors 2021-11-18 17:17:18 +01:00
Joshua Sangmeister 740f164ac3 Fix for motion restrictions in case migrations weren't applied properly 2021-11-18 09:18:30 +01:00
Emanuel Schütze f04d64d48d
Merge pull request #6277 from emanuelschuetze/motion-id-column
Increase motion id column
2021-11-13 23:35:54 +01:00
Emanuel Schütze e8803bde0c Increase motion id column 2021-11-13 22:58:22 +01:00
Emanuel Schütze adb9d95ff3
Merge pull request #6276 from GabrielInTheWorld/motion-block-and-list-width
Makes the motion block detail and motion list more user friendly
2021-11-13 14:48:31 +01:00
GabrielInTheWorld 6ac24f5f6c Bigger meta column width for tables
motion block detail, motion list, user list, user statistics
2021-11-13 12:00:00 +01:00
Emanuel Schütze a3d8dd01da
Merge pull request #6264 from emanuelschuetze/livestream-poster-image-helptext
Fixed livestream poster image size in help text.
2021-10-22 11:06:26 +02:00
Emanuel Schütze 82ad1d2222 Fixed livestream poster image size in help text. 2021-10-22 11:04:27 +02:00
Emanuel Schütze af4ff015ae
Merge pull request #6263 from tsiegleauq/bump-jszip-version
Bump jszip version to 3.7.1
2021-10-21 17:56:53 +02:00
Sean 34f27ea4c0 Bump jszip version to 3.7.1 2021-10-21 17:45:05 +02:00
Emanuel Schütze bbdbdd3c10
Merge pull request #6245 from emanuelschuetze/release3.4
Release 3.4
2021-10-07 08:03:05 +02:00
Emanuel Schütze f2ae6d1cae Update version to 3.4.1-master 2021-10-07 08:00:10 +02:00
Emanuel Schütze 420422e2cf Release 3.4 2021-10-07 07:59:38 +02:00
Emanuel Schütze 8d20c40ca1
Merge pull request #6181 from tsiegleauq/changelog-3.4
Changelog 3.4
2021-10-06 09:28:33 +02:00
Sean 1b3af8cecd Changelog 3.4 2021-10-06 09:27:47 +02:00
Emanuel Schütze bd22957740
Merge pull request #6228 from tsiegleauq/stream-nanocosmos
Add Support for streams from Nanocosmos
2021-09-23 16:04:42 +02:00
Emanuel Schütze 74b1527bbe
Merge pull request #6222 from GabrielInTheWorld/of-fix-settingsgroup
Fixes the usage of groups in the settings
2021-09-23 10:24:56 +02:00
Sean 91a9e19f09 Add Support for streams from Nanocosmos
Add a full nanocosmus stream URL.
The URL has to include: "nanocosmos.de"
the ID will be extracted by looking for an equals symbol.
So: something.nanocosmos.de/somethingelse=1234-5678

will detect 1234-5678 as videoID.
The iframe that will be used is fix.
2021-09-21 18:11:23 +02:00
GabrielMeyer 932eb647ab Fixes the usage of groups in the settings 2021-09-13 11:38:12 +02:00
Emanuel Schütze 0c30be5308
Merge pull request #6219 from emanuelschuetze/stable/3.4.x
Updated autoupdate service
2021-09-06 12:17:51 +02:00
Emanuel Schütze ac5a975a4c Updated autoupdate service 2021-09-06 11:56:41 +02:00
GabrielInTheWorld 70d5b32bd7
Merge pull request #6212 from tsiegleauq/clean-html-on-motion-paste
Clean HTML before pasting in tinymce
2021-08-20 08:30:59 +02:00
Sean eab60ab31a Clean HTML before pasting in tinymce
If the user decides to copy-paste HTML (like from another OpenSlides motion detail)
 - remove all classes
 - remove data-line-number="X"
 - remove contenteditable="false"

Not doing so would save control sequences from diff/linenumbering into the
model which will open pandoras pox during PDF generation (and potentially web view)
2021-08-19 18:55:23 +02:00
Sean 8c7a770f9b
Merge pull request #6202 from tsiegleauq/unhide-help-desk
Show Helpdesk without live streams
2021-08-09 14:16:18 +02:00
Sean b2ae972ce0 Show Helpdesk without live streams 2021-08-09 12:13:12 +02:00
Emanuel Schütze 8d90c6821e
Merge pull request #6193 from emanuelschuetze/translations-20210802
Updated all translations
2021-08-02 14:40:34 +02:00
Emanuel Schütze 2287f3bf4f Updated all translations 2021-08-02 12:59:12 +02:00
Sean 18ffcaca92
Merge pull request #6186 from tsiegleauq/import-supporter
Add CSV import for supporters
2021-07-27 17:54:49 +02:00
Sean 802ac1aee8 Add CSV import for supporters
Supporters can now be imported via csv
this was forgotten before
2021-07-27 17:35:14 +02:00
Emanuel Schütze 759e23e15b
Merge pull request #6184 from tsiegleauq/fix-mat-table-after-upgrade
Fix mat-table no flex class
2021-07-27 16:56:30 +02:00
Sean e8507414d9 Fix mat-table no flex class
After upgrading angular material, mat table had a different set of
classes which would break a view
2021-07-27 16:50:59 +02:00
Emanuel Schütze c48658210b
Merge pull request #6185 from tsiegleauq/agenda-list-error
Fix an error where the workflow would break agenda
2021-07-27 14:31:34 +02:00
Sean 0ee14c9986 Fix an error where the workflow would break agenda
If the projected element was null due to workflow restrictions, the
agenda list would throw an error
2021-07-27 13:07:30 +02:00
Oskar Hahn 6c01c2b99b
Merge pull request #6183 from ostcar/remove_prometus
Update autoupdate, remove prometeus
2021-07-26 11:31:53 +02:00
Oskar Hahn cd7421b732 Update autoupdate, remove prometeus 2021-07-24 07:20:29 +02:00
Emanuel Schütze 1b2169dd45
Merge pull request #6177 from tsiegleauq/jitsi-hide-chat
Hide chat in jitsi iFrame
2021-07-20 16:37:55 +02:00
Sean b2ac759379 Hide chat in jitsi iFrame 2021-07-20 15:21:12 +02:00
Emanuel Schütze 9d60951344
Merge pull request #6176 from tsiegleauq/jitsi-user-scroll
Scrollable sorted jitsi user list
2021-07-20 14:58:11 +02:00
Sean 06a34a8f03 Scrollable sorted jitsi user list
Sorts and scrolls through the jitsi users
2021-07-20 13:22:15 +02:00
Sean 30bf5bc808
Merge pull request #6173 from tsiegleauq/more-special-diff-characters
Add rdquo und Prime to special diff entities
2021-07-19 16:38:18 +02:00
Sean 73f98ff55e
Merge pull request #6172 from tsiegleauq/zip-export-dot
Fix missing zip extension when filename had a dot
2021-07-19 16:30:29 +02:00
Sean d643028410
Merge pull request #6171 from tsiegleauq/hide-generate-button
Hide pw generate button while editing a user
2021-07-19 16:30:15 +02:00
Sean cbb9bd14d8 Add rdquo und Prime to special diff entities 2021-07-19 16:25:22 +02:00
Sean bf7eba603d Fix missing zip extension when filename had a dot
while exporting mediafiles, the zip extension was missing if the file
name hat a dot
2021-07-19 16:01:00 +02:00
Sean 7022ab8cec Hide pw generate button while editing a user 2021-07-19 15:54:37 +02:00
Sean f86f8ec9b4
Merge pull request #6166 from FinnStutzenstein/serverRequirements
Clean up server requirements
2021-07-16 16:10:30 +02:00
Sean 5bb9553cd3
Merge pull request #6161 from gsiv/dev/pg_isready
Avoid triggering errors with pg_isready
2021-07-16 12:37:23 +02:00
Sean d4df7ebaed
Merge pull request #6165 from tsiegleauq/update-dockerfiles-for-node16
Update dockerfiles for node 16 and a12
2021-07-16 11:54:05 +02:00
Finn Stutzenstein b656d354cd
Clean up server requirements 2021-07-16 11:52:37 +02:00
Sean 98e4f05b90 Update dockerfiles for node 16 and a12
adjust the productive dockerfiles to work with angular12 and use node16
2021-07-16 11:21:29 +02:00
Gernot Schulz 301c9bd35a Avoid triggering errors with pg_isready
pg_isready was not connecting to the configured database or as the
configured user.  While that did not cause problems here, it did trigger
error messages on the database side.
2021-07-15 15:27:49 +02:00
Gernot Schulz 7dc35dce40
Merge pull request #6157 from gsiv/dev/db-table-config
Make database table configurable through .env
2021-07-15 10:38:26 +02:00
Gernot Schulz 1d8856b501
Merge pull request #6158 from gsiv/dev/media-service-db-config
Add missing mediafile service DB config parameters
2021-07-15 10:37:16 +02:00
Gernot Schulz 157293530c
Merge pull request #6156 from gsiv/dev/swarm-ip
Docker: Bind to 0.0.0.0 for Docker Swarm
2021-07-15 10:22:20 +02:00
Gernot Schulz ed64e7bd13 Add missing mediafile service DB config parameters 2021-07-14 15:03:22 +02:00
Gernot Schulz f00c4391a2 Make database table configurable through .env 2021-07-14 14:41:38 +02:00
Sean 18f2e3326d
Merge pull request #6146 from tsiegleauq/angular12
Update to Angular12
2021-07-13 14:36:38 +02:00
Gernot Schulz 5b28763bcc Docker: Bind to 0.0.0.0 for Docker Swarm
Swarm uses a cluster-wide ingress network and does not permit binding
ports to localhost.  It simply ignores such a binding and prints
a warning instead.

This patch fixes this recent regression to avoid the warning.
2021-07-12 11:38:59 +02:00
Emanuel Schütze 52f35c856d
Merge pull request #6150 from FinnStutzenstein/fixExporter
Fix exporter
2021-07-09 08:34:53 +02:00
Finn Stutzenstein 1c857fb08c
Fix exporter
poll ids were used for options
2021-07-09 08:18:31 +02:00
Sean 0a3ea54a45 Update to Angular12
Updates the client to auglar12
2021-07-08 18:14:09 +02:00
Emanuel Schütze c593599e10
Merge pull request #6148 from FinnStutzenstein/fixExporter
Fix OS4 exporter
2021-07-08 14:13:22 +02:00
Finn Stutzenstein d17a050d8b
Fix OS4 exporter 2021-07-08 11:34:18 +02:00
Finn Stutzenstein 19a9eedf17
Merge pull request #6026 from FinnStutzenstein/variableMediafileTablename
Setting for the mediafile database tablename
2021-07-08 11:10:40 +02:00
Finn Stutzenstein 27831154fa
Setting for the mediafile database tablename 2021-07-08 10:48:51 +02:00
jsangmeister b4b0a958d5
Merge pull request #6134 from jsangmeister/fix-projected-analog-motion-poll
Fix projection of analog polls & prevent percent base 'entitled' for analog polls
2021-07-05 09:13:34 +02:00
Joshua Sangmeister 5d13f94e40 Fix projection of analog polls & prevent percent base 'entitled' for analog polls 2021-07-01 09:38:00 +02:00
Emanuel Schütze 0322436cf5
Merge pull request #6120 from FinnStutzenstein/os4export
OS4 export
2021-06-24 21:40:28 +02:00
Finn Stutzenstein 272c3de9fc OS4 export 2021-06-24 21:08:24 +02:00
Emanuel Schütze 416b2cc26f
Merge pull request #6130 from jsangmeister/fix-analog-poll-diagramm
Remove chart for analog polls with a majority & fix random TypeError
2021-06-24 21:02:38 +02:00
Joshua Sangmeister 3532c55924 Remove chart for analog polls with a majority & fix random TypeError 2021-06-24 16:32:22 +02:00
Emanuel Schütze f1e2996c6e
Merge pull request #6121 from tsiegleauq/applause-timeout-to-service
Move applause timeout to service
2021-06-24 16:20:23 +02:00
Emanuel Schütze c0582e4b1c
Merge pull request #6129 from tsiegleauq/firefox-stream-no-app-stable
Fix firefox endless spinner in stream
2021-06-24 16:09:33 +02:00
Sean eed3bca25b Fix firefox endless spinner in stream
fix a firefox bug where the spinner would not disappear, apparently when
the user would see a stream.
The reason seems to be ngparticles in combination with firefox, where
firefox would be stuck in never-stable when ngparticles was loaded
before the browser was ready
2021-06-24 15:50:15 +02:00
Sean 57755ebf4b Move applause timeout to service
Fixes a bug where the applaus timeout would require reload to work
Fixes a but where the "Show applause amount" config was ignored

Moves the applause timeout logic to service
comonents can now lazily listen to events from sendsApplauseObservable
to know if their applause is in progress

Show applause level depending on config
2021-06-24 13:20:31 +02:00
Emanuel Schütze c286e0ff76
Merge pull request #6123 from FinnStutzenstein/fixDeletionOfMediafiles
Fix updating logo and font configs when deleting a mediafile
2021-06-23 16:29:45 +02:00
Emanuel Schütze a14ab8c5e2
Merge pull request #6127 from tsiegleauq/stop-vote-show-please-wait
Use vote pending state as subject
2021-06-23 16:18:38 +02:00
Sean 235bbf6c0d Use vote pending state as subject
Fixes an issue where the vote pending state was not updating correctly
when using state changes over the choice service
2021-06-23 13:58:21 +02:00
Finn Stutzenstein 8423433f66
Fix updating logo and font configs when deleting a mediafile 2021-06-21 07:46:13 +02:00
Emanuel Schütze 382fcf4a67
Merge pull request #6056 from ostcar/performance_assignment_poll
Performance assignment poll
2021-06-17 13:11:12 +02:00
Oskar Hahn 9a9ef8e039 performance test for assinment polls 2021-06-17 11:57:06 +02:00
Emanuel Schütze eeb0e54985
Merge pull request #6118 from FinnStutzenstein/fixFontsAndLogos
Automatically write changes to logo and font definitions to configs
2021-06-17 11:51:44 +02:00
Finn Stutzenstein 5d434484b5
Automatically write changes to logo and font definitions to configs 2021-06-17 11:29:54 +02:00
Emanuel Schütze 8d73f08072
Merge pull request #6114 from emanuelschuetze/video-player-height
Use max-height for video js player
2021-06-10 14:18:38 +02:00
Emanuel Schütze d8713dad11 Use max-height for video js player 2021-06-10 13:58:58 +02:00
Emanuel Schütze bf22e1b6e1
Merge pull request #6112 from tsiegleauq/remove-cd-from-ap-completely
Remove CD from Cinema completely
2021-06-10 11:59:25 +02:00
Sean 83949ad402 Remove CD from Cinema completely
removes all manuall CD related code from autopilot
2021-06-10 11:04:55 +02:00
Emanuel Schütze 5a8a87070a
Merge pull request #6113 from FinnStutzenstein/clearEntitledVotesAfterReset
Clear entitled votes after reset
2021-06-10 11:01:38 +02:00
Finn Stutzenstein bd3c8e09d8
Clear entitled votes after reset 2021-06-10 09:14:30 +02:00
Emanuel Schütze d9d07c3c9b
Merge pull request #6110 from tsiegleauq/revert-ap-onpush-cd
Set change detection in cinema to default
2021-06-08 12:14:05 +02:00
Sean 99acdab35f Set change detection in cinema to default 2021-06-08 11:49:08 +02:00
Emanuel Schütze 1daee9c6d6
Merge pull request #6105 from tsiegleauq/limit-filter-update-on-autoupdate
Limit pushing of filter updates in filter service
2021-06-07 15:21:03 +02:00
Emanuel Schütze a8611904cc
Merge pull request #6106 from tsiegleauq/weird-icons-in-contra-speech
Fix some icon error in contra speech
2021-06-07 09:10:17 +02:00
Sean 6d63c90649 Fix some icon error in contra speech
Contra speech hat some undesired icon config
2021-06-04 16:20:23 +02:00
Sean 15fb19ad2f Limit pushing of filter updates in filter service
Limits the pushing updateFilteredData in the baseFilterList service
to only listen for actual changes of the list input data
This should decrease client stress on autoupdates
2021-06-04 14:26:18 +02:00
Emanuel Schütze 136e0a0569
Merge pull request #6103 from tsiegleauq/jitsi-api-update
Update jitsi iFrame and API
2021-06-03 22:30:51 +02:00
Sean 1ed1896653 Update jitsi iFrame and API
Adjst some callbacks and configs to be compatible with the newest jitsi
release. This is compatible with both our current jitsi server as
well as the newest jitsi releases
2021-06-03 22:21:34 +02:00
Emanuel Schütze 7a076b1d2d
Merge pull request #6100 from tsiegleauq/pending-chat
Add pending state for chat message
2021-06-03 20:53:49 +02:00
Emanuel Schütze 4938c34d50
Merge pull request #6102 from tsiegleauq/hide-conf-bar-sometimes
Hide conference bar without interaction
2021-06-03 20:50:24 +02:00
Sean 8b22f5ff0e Hide conference bar without interaction
Hide the conference bar if there is no stream and no entering
permission.

Hides the "see stream" button if use user has no permission to see the
stream (call list window)

Use rxjs combineLatest for easier "dead state" detection with less
change pushing
2021-06-03 15:12:49 +02:00
Sean 6b8ccb8c33 Add pending state for chat message
Waits untill the server answers something before enabling chatting again
shows some spinner to indicate pending chat requests
2021-06-03 11:12:56 +02:00
Emanuel Schütze 7dcdbb4ee1
Merge pull request #6099 from emanuelschuetze/translation-20210602
Updated translations
2021-06-02 17:15:29 +02:00
Emanuel Schütze 51568b652e Updated translations 2021-06-02 16:59:34 +02:00
Emanuel Schütze 8c0aba83bc
Merge pull request #5961 from tsiegleauq/chat-and-rtc-modules
Restructure communication components
2021-06-02 15:29:54 +02:00
Emanuel Schütze cccfec9154
Merge pull request #6098 from emanuelschuetze/link
Fixed faq link
2021-06-02 15:28:42 +02:00
Sean 1504e33607 Restructure communication components
separates the "Jitsi component" into an own module, several services and components.
2021-06-02 15:09:26 +02:00
Emanuel Schütze d570ed2b72 Fixed faq link 2021-06-02 13:27:00 +02:00
Emanuel Schütze 5f8bb17cac
Merge pull request #6097 from emanuelschuetze/autoupdate
Updated autoupdate service
2021-06-01 16:27:47 +02:00
Emanuel Schütze 654162dbce Updated autoupdate service 2021-06-01 14:45:33 +02:00
Emanuel Schütze e0da18a0e6
Merge pull request #6087 from FinnStutzenstein/multiselectListOfSpeakersClosed
Multiselect open/close LOS in agenda
2021-06-01 14:43:35 +02:00
Finn Stutzenstein 946f07f34a Multiselect open/close LOS in agenda 2021-06-01 11:05:25 +02:00
Emanuel Schütze 3a2773aa5d
Merge pull request #6086 from tsiegleauq/motion-nop-nav-regress
Fix non existing top nav for motions
2021-06-01 10:58:27 +02:00
Sean e7448aa378
Merge pull request #6088 from OpenSlides/dependabot/npm_and_yarn/client/tinymce-5.7.1
Bump tinymce from 5.6.0 to 5.7.1 in /client
2021-05-31 11:51:13 +02:00
dependabot[bot] 75ed60168e
Bump tinymce from 5.6.0 to 5.7.1 in /client
Bumps [tinymce](https://github.com/tinymce/tinymce-dist) from 5.6.0 to 5.7.1.
- [Release notes](https://github.com/tinymce/tinymce-dist/releases)
- [Changelog](https://github.com/tinymce/tinymce-dist/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tinymce/tinymce-dist/compare/5.6.0...5.7.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-28 19:24:33 +00:00
Sean be36a8a40f Fix non existing top nav for motions
fixes a regression where motions had no top-navigation anymore
2021-05-28 14:10:54 +02:00
Emanuel Schütze 37782fcae8
Merge pull request #6083 from FinnStutzenstein/fixCaddyEntrypointForRestarts
Prevent wrong config files for caddy on container restarts (closes #6071)
2021-05-27 21:13:07 +02:00
Emanuel Schütze 5d57301fb0
Merge pull request #6084 from tsiegleauq/cinema-interaction-enhanced
Refine AP Interaction
2021-05-27 21:10:29 +02:00
Emanuel Schütze bf209d023c
Merge pull request #6082 from emanuelschuetze/translations-20210527
Updated translations and autoupdate serivce
2021-05-27 16:17:16 +02:00
Emanuel Schütze f3b6ceb9af Updated translations and autoupdate serivce 2021-05-27 16:07:17 +02:00
Sean e7dd5d87a2 Refine AP Interaction
In auto pilot view:
klicking the projector opens the content object (rather than the
projector detail view)
clicking AP title now opens the content objects as normal link, rather
than having an "open external" icon
clicking the projector name opens the projector detail view
add "readd last speaker" in AP

Some CD Enhancements for AP Projector
2021-05-27 11:57:15 +02:00
Finn Stutzenstein 899257791d
Prevent wrong config files for caddy on container restarts 2021-05-27 10:21:53 +02:00
Emanuel Schütze 3eb7386a58
Merge pull request #6081 from tsiegleauq/enum-assignment-candidates-again
Respect candidate enumeration per assignment
2021-05-26 18:35:00 +02:00
Emanuel Schütze c60553e376
Merge pull request #6070 from tsiegleauq/stop-voting-publish-prompt
stop-voting shows prompt
2021-05-26 18:13:00 +02:00
Sean 8734e48aef Respect candidate enumeration per assignment
Uses candidate enum per assignment as given in the form.
Changes enumeration in drag-list and poll-meta-info
2021-05-26 18:12:41 +02:00
Emanuel Schütze 79e5b8e337
Merge pull request #6078 from tsiegleauq/count-poos-in-statistics
Show point-of-order amount in speaker statistics
2021-05-26 17:56:09 +02:00
Sean 6dc5c3bfa9 stop-voting shows prompt
Stop voting shows options to either simply stop, publish directly or
abort. Was done using choice service.
Alter vote repo to simply choose with voting state to adress rather than
calculate the next state

Add server-side option to publish a poll in the started state
2021-05-26 17:53:46 +02:00
Sean d4025296fe Show point-of-order amount in speaker statistics
Shows the amount of point of orders in speaker statistics
2021-05-26 17:38:26 +02:00
Emanuel Schütze a7f392b997
Merge pull request #6074 from tsiegleauq/show-unpublish-ap
Show "unpublished" for finished polls in AP
2021-05-26 17:35:42 +02:00
Sean 6451cdf590 Show "unpublished" for finished polls in AP
Also: Change APs change detection to onPush.
This is the last possible option to enhance AP performance
2021-05-26 17:07:40 +02:00
Emanuel Schütze 2eda8fea22
Merge pull request #6063 from tsiegleauq/cryon-font
Add own font for chyron
2021-05-26 16:59:03 +02:00
Sean 74b32af293 Add own font for chyron
Alters Current Speakery Chyron, optionally shows the structure level in
a new line (slightly smaler, faint)
Font for user name in chyron can be configured
Support way more font mime types
Adds an own font for chyron (server, client)
Extends loads-font service
Cleanup countdown-time component from font loading
2021-05-26 16:41:27 +02:00
Emanuel Schütze 43d73a87f1
Merge pull request #6076 from tsiegleauq/more-lenient-word-breaking
Add some hyphenation
2021-05-25 21:49:16 +02:00
Emanuel Schütze 66945231f4
Merge pull request #6075 from tsiegleauq/poo-dialog-cannot-enter
Enhance point-of-order dialog
2021-05-25 21:40:50 +02:00
Emanuel Schütze 5a9767004d
Merge pull request #6073 from tsiegleauq/keep-poo-infos
Keep point-of-order infos
2021-05-25 21:37:15 +02:00
Emanuel Schütze a0c9f3b6da
Merge pull request #6066 from tsiegleauq/fullscreen-projector-cursor
Hide cursor in full screen projector
2021-05-25 21:37:01 +02:00
Sean f05dd8c448 Keep point-of-order infos
Keeps pro, contra, poo infos in active speaker and finished speakers.
Cleans up a lot of template related code
2021-05-25 20:59:18 +02:00
Emanuel Schütze 14fe614c4d
Merge pull request #6072 from tsiegleauq/client-chat-enter-limit
Restrict Chat message length in client
2021-05-25 20:47:39 +02:00
Sean 7a6b31d8f8 Hide cursor in full screen projector
Hides the cursor when hovering over the full screen projectors
containers
2021-05-25 20:41:15 +02:00
Sean f707615875 Add some hyphenation
Use hyphenation for motion details poll display.
More lenient display of lpng poll names using hyphenation
2021-05-25 15:56:12 +02:00
Sean fbb60cb0b6 Enhance point-of-order dialog
Escape and enter do what they should
2021-05-25 15:15:08 +02:00
Sean 861726fb9c Restrict Chat message length in client
Restricts Chat Messages in client to enforce a maximum length of 512
characters
enhance chat form validation
show chat message length and characters left if close to the limit of
512
2021-05-21 13:14:03 +02:00
Sean 4d4a3bb0db
Merge pull request #6067 from FinnStutzenstein/httpsDevSetup
Reenable https in dev mode
2021-05-20 13:41:59 +02:00
Finn Stutzenstein 9a7c9c19a0
Reenable https in dev mode 2021-05-20 12:51:10 +02:00
Finn Stutzenstein f4c237a18e
Merge pull request #5998 from FinnStutzenstein/421
Add ALLOWED_HOSTS
2021-05-17 07:55:23 +02:00
Emanuel Schütze 710e825961
Merge pull request #6060 from emanuelschuetze/los-issues
Fixed minor list of speakers issues
2021-05-16 20:23:48 +02:00
Emanuel Schütze cb13c8cd47 Always allow speakers to remove from list of speakers (button always enabled) 2021-05-10 21:40:38 +02:00
Emanuel Schütze 83efb19562
Merge pull request #6053 from tsiegleauq/motion-poll-has-general-x
remove Motion polls show global values
2021-05-06 17:53:49 +02:00
Emanuel Schütze 964a41386d
Merge pull request #6054 from FinnStutzenstein/speedupPollStop
Speed up stopping a poll
2021-05-06 17:49:42 +02:00
Finn Stutzenstein 1e78f2a534 Speed up stopping a poll
Reduced the amount of queries in BasePoll.stop from 4+<amount entitled
users> to 3.
2021-05-06 16:49:07 +02:00
Sean 3402928935 remove Motion polls show global values
Motion polls were displaying global abstain, etc
2021-05-06 14:21:04 +02:00
Emanuel Schütze 2a7b55f11e
Merge pull request #6051 from tsiegleauq/enumerate-all-assignments
Enumber Voting results
2021-05-05 14:57:23 +02:00
Sean 18c75a6d12 Enumber Voting results
Enumber the rank of voting results in PDF, Projector and Detail View
2021-05-05 14:49:42 +02:00
Emanuel Schütze 7cf22b86ab
Merge pull request #6050 from tsiegleauq/assignment-analog-general-selection
Add General x to analog polls
2021-05-05 14:08:01 +02:00
Sean 0011d63a40 Add General x to analog polls
Adds general approval/rejection/abstain to analog polls
Fix a bug where the 100% base "Entitled" would render analog polls unusable
enumerate poll candidates
2021-05-05 12:03:41 +02:00
Sean 08fa38a89c
Merge pull request #6049 from tsiegleauq/users-cannot-see-candidate-names
Fix user cannot see candidate names
2021-05-04 16:23:39 +02:00
Emanuel Schütze 5cee662058
Merge pull request #5915 from gsiv/dev/prom
Docker: Improve Prometheus integration
2021-05-04 16:08:04 +02:00
Sean 0b23806db6 Fix user cannot see candidate names
Assignment detail view sort list "getTitle" was hidden behind
"assignment_can_manage", thus users could not see the names of candidates
2021-05-04 15:47:15 +02:00
Emanuel Schütze acf416b024
Merge pull request #6048 from emanuelschuetze/autoupdate
Updated autoupdate service
2021-05-03 13:56:30 +02:00
Emanuel Schütze ec2c905f7d Updated autoupdate 2021-05-03 13:13:49 +02:00
Emanuel Schütze 74981e26c0
Merge pull request #6037 from tsiegleauq/poll-progress-speaker-manage
Allow LOS-Manager to see poll progress
2021-05-03 13:11:30 +02:00
Emanuel Schütze 261c69387f
Merge pull request #6047 from CatoTH/bugfix-6030-amending-list-items
Fixes #6030 - amending a list item does not show the change inline in…
2021-05-03 13:01:49 +02:00
Tobias Hößl 705c42bd11
Fixes #6030 - amending a list item does not show the change inline in diff box 2021-05-02 20:10:19 +02:00
Emanuel Schütze d86fcd80b7
Merge pull request #6031 from jsangmeister/fix-entitled-users
Fix entitled user calculation and display of voting banner in case of vote delegations
2021-04-29 15:38:49 +02:00
Emanuel Schütze 88e870c9df
Merge pull request #6040 from tsiegleauq/search-cannot-find-uml
Find umlauts using search
2021-04-29 15:38:29 +02:00
Emanuel Schütze 325469bc82
Merge pull request #6038 from FinnStutzenstein/fix-los
Fix LOS (closes #6035)
2021-04-29 15:03:54 +02:00
Emanuel Schütze 14687bba0e
Merge pull request #6042 from emanuelschuetze/new-languages
Added new translations (it, es).
2021-04-29 14:35:36 +02:00
Emanuel Schütze db4c593adf Added new translations (it, es). 2021-04-29 14:12:52 +02:00
Finn Stutzenstein 75bd3c50e5
Fix LOS (closes #6035) 2021-04-29 13:46:36 +02:00
Sean d08752db21 Find umlauts using search
TinyMCE currently escapes all special chatacters pretty unconviniently.
We now try to parse HTML using DOMParser and extract the text content so
we can actually search for it
2021-04-29 11:09:53 +02:00
Sean 85bb9f751d Allow LOS-Manager to see poll progress
Adjust poll progress bar to be visible for LOS-Manager.
Cleans up redundant permission asking
Adds push CD to progress bar to react better on permission changs
2021-04-28 13:10:29 +02:00
Emanuel Schütze d030925e14
Merge pull request #6036 from emanuelschuetze/translation-20210427
Updated translations
2021-04-27 23:53:20 +02:00
Emanuel Schütze 22df847c78 Updated translations 2021-04-27 23:05:23 +02:00
Emanuel Schütze 672e5ca544
Merge pull request #6017 from FinnStutzenstein/samlGroups
Adding attribute matchers for group assignments to SAML
2021-04-27 21:30:38 +02:00
Emanuel Schütze 9f543697ad
Merge pull request #6023 from FinnStutzenstein/proContraNoteMarkSpeakers
pro/contra speakers and notes for point of order speakers
2021-04-27 20:45:02 +02:00
Finn Stutzenstein c9c90cd4a3 pro/contra speakers and notes for point of order speakers
Enhance Pro Contra UI/UX by Sean
2021-04-27 19:53:17 +02:00
Finn Stutzenstein 91a15d24a8
Add ALLOWED_HOSTS
the proxy responds with a 421, if a host header with an invalid host
name is encountered. If nothing is provided (see default .env) all hosts
are allowed. Examples:

ALLOWED_HOSTS="localhost:8000 127.0.0.1:8000"

ALLOWED_HOSTS="some.domain.example.com"

Add EXTERNAL_HTTPS_PORT. See .env for the configuration.
2021-04-26 16:01:35 +02:00
Joshua Sangmeister 8ad008d9de Fix entitled user calculation and display of voting banner in case of vote delegations 2021-04-26 15:44:42 +02:00
Finn Stutzenstein ee8702aff1
Merge pull request #5921 from gsiv/dev/yml-image-override
Docker: Allow overriding image names from .env
2021-04-26 10:16:02 +02:00
Finn Stutzenstein 0d4673d182
Merge pull request #6028 from FinnStutzenstein/updateBlack
Update black
2021-04-26 09:30:07 +02:00
Finn Stutzenstein f9d19db9e2
Update black 2021-04-26 09:00:04 +02:00
Emanuel Schütze 7315626e18
Merge pull request #6024 from jsangmeister/vote-delegaton-error
Fix vote delegation update error
2021-04-22 21:10:30 +02:00
Joshua Sangmeister e2d4fafe6d Fix vote delegation update error 2021-04-22 20:51:44 +02:00
Emanuel Schütze fafcf5d583
Merge pull request #6025 from FinnStutzenstein/fromEmailValidation
Validate the from email for invalid characters
2021-04-22 15:34:15 +02:00
Finn Stutzenstein 0a2483a94b
Adding attribute matchers for group assignments to SAML 2021-04-22 14:53:26 +02:00
Finn Stutzenstein a8e329253c
Validate the from email for invalid characters
This might not be sufficient for all cases. If some other strange
IndexErrors appear, more validation has to be done. For now, it catches
all observed cases.
2021-04-22 12:33:33 +02:00
Emanuel Schütze 4f35770769
Merge pull request #6009 from tsiegleauq/some-vscroll-for-motion
Some enhancements for motion detail
2021-04-15 21:21:11 +02:00
Sean 697177640b Some enhancements for motion detail
Vscroll for the list of changes
List of changes can collapse
direct link to an amendment over the list of changes
Vscroll for the amendment create wizzard
Line numbers in the amendment create wizzard
Some css cleanup
2021-04-15 20:58:08 +02:00
Emanuel Schütze 196e39ad15
Merge pull request #6006 from jsangmeister/fix-voting-race-condition
Lock poll to prevent race conditions
2021-04-15 20:46:43 +02:00
Joshua Sangmeister ee31c1e633 Lock poll to prevent race conditions
Add migrations
2021-04-15 15:19:30 +02:00
Emanuel Schütze bdbb5839cc
Merge pull request #6007 from emanuelschuetze/autoupdate
Updated autoupdate service
2021-04-13 08:46:43 +02:00
Emanuel Schütze dff5ae4a89 Updated autoupdate service 2021-04-12 21:13:52 +02:00
Finn Stutzenstein 79d9781a1b
Merge pull request #6005 from FinnStutzenstein/updateDeps
Update dependencies
2021-04-12 14:04:05 +02:00
Finn Stutzenstein e3c627b504
Update dependencies
Update DRF to resolve https://github.com/advisories/GHSA-fx83-3ph3-9j2q

-> https://www.django-rest-framework.org/community/3.9-announcement/
replaces `list_route` and `detail_route`

Update autoupdate submodule
2021-04-12 13:36:46 +02:00
Emanuel Schütze 963986b91d
Merge pull request #6004 from FinnStutzenstein/smallFixes
Small fixes
2021-04-12 12:14:17 +02:00
Finn Stutzenstein 2b3d1db3bf
Merge pull request #5952 from Piratenpartei/server_setup_env
Add ENV for server-setup host and port
2021-04-12 08:04:58 +02:00
Finn Stutzenstein a1e7920b34
Small fixes 2021-04-12 07:52:47 +02:00
Emanuel Schütze feb54c52a3
Merge pull request #6002 from emanuelschuetze/autoupdate
Updated autoupdate service
2021-04-08 21:18:04 +02:00
Emanuel Schütze bb651b67eb Updated autoupdate service 2021-04-08 19:05:04 +02:00
Emanuel Schütze 5b58730cca
Merge pull request #5995 from ostcar/performance_create_motions
Create amendments
2021-04-08 18:57:53 +02:00
Oskar Hahn 23fcc3a7d0 Create dummy amendments with performance test script. 2021-04-08 18:38:25 +02:00
Emanuel Schütze d80919f0e7
Merge pull request #6001 from jsangmeister/fix-recommendation-extension-bug
Recommendation extension can be set with can_manage_metadata
2021-04-08 18:30:08 +02:00
Joshua Sangmeister 8891a52bdc State and recommendation extension can be set with can_manage_metadata 2021-04-08 17:47:11 +02:00
Emanuel Schütze 26d5d81b6f
Merge pull request #5997 from jsangmeister/remove-duplicates-from-entitled-users
Prevent multiple entries in entitled_users_at_stop
2021-04-08 15:39:14 +02:00
Joshua Sangmeister 1e6b042d71 Add migrations to remove duplicates from existing polls 2021-04-08 11:33:26 +02:00
Joshua Sangmeister f54050a83c Prevent multiple entries in entitled_users_at_stop 2021-04-08 11:12:13 +02:00
Finn Stutzenstein a94f00672b
Merge pull request #5996 from FinnStutzenstein/fixDeps
Locked mypy, added new OS4 files to gitignore
2021-04-08 10:51:05 +02:00
Finn Stutzenstein 7ca761bdb0
Locked mypy, added new OS4 files to gitignore 2021-04-08 09:01:02 +02:00
Emanuel Schütze 1edf4437a0
Merge pull request #5992 from CatoTH/bugfix/diff-only-formatting-changes
Allow formatting-only-changes without breaking the inline diff
2021-04-06 22:35:07 +02:00
Tobias Hößl ba177a89d4
Allow formatting-only-changes without breaking the inline diff 2021-04-05 11:58:09 +02:00
Emanuel Schütze d059afac5a
Merge pull request #5989 from emanuelschuetze/translations-20210401
Updated translations
2021-04-01 18:02:10 +02:00
Emanuel Schütze 045648eddb Updated translations 2021-04-01 17:45:29 +02:00
jsangmeister 787390c899
Merge pull request #5978 from jsangmeister/voting-changes
Implement voting changes
2021-04-01 16:12:31 +02:00
Joshua Sangmeister 4b13ff681e user_token, single votes table, entitled_users server changes
server changes finished, tests, cleanup

Add entitled user table

Fix formatting

Finish entitled users table & implement new percent base

Fix typing

Fixed remarks & errors

Fix new precent base representation

Update autoupdate service

Remove unneeded prettier entry

Remove unneeded css
2021-04-01 16:02:47 +02:00
Adrian Nöthlich 6430727590
Use ENV for redis in db-setup and fix defaults (#5951)
* Use ENV for redis in db-setup and fix defaults

* Add default value for `DATABASE_HOST`
* Use ENV for `wait-for-it` redis and redis-slave

Signed-off-by: Adrian Nöthlich <git@promasu.tech>

* Match default value for DATABASE_HOST
2021-04-01 13:10:19 +02:00
Emanuel Schütze 83549ce02b
Merge pull request #5916 from ostcar/performance
Performance
2021-04-01 10:02:26 +02:00
Oskar Hahn dca6143041 Add performance tool 2021-04-01 09:19:21 +02:00
Emanuel Schütze 2d4419530e
Merge pull request #5987 from CatoTH/bugfix/escape-special-characters
Bugfix: change detection for additional special characters
2021-04-01 09:08:44 +02:00
Finn Stutzenstein 28980afbd5
Merge pull request #5981 from FinnStutzenstein/spinnerErrors
Do not show old errors on the spinner
2021-04-01 09:02:57 +02:00
Tobias Hößl 7275aa69af
Bugfix: change detection for additional special characters 2021-03-31 21:44:53 +02:00
Gernot Schulz 1ad0a61524 Docker: Simplify image name/registry handling
For a subset of the images required by a Compose or Swarm Setup,
non-default names can be configured in .env.  Originally, the names were
treated as the images' complete names, i.e., including an optional
registry domain.  Using this setup, it was possible to pull the
irregularly updated auxiliary images from a default registry while, at
the same time, obtaining certain images from different registries.

Commit e225a57f97 changed this behavior.  Since then, the names in .env
can only be used to change part of the image name, excluding the
registry.  If a default registry is configured it is always prepended to
the given image name, breaking the original use case.

This patch removes the ability to override image names in .env.
Instead, the registry of each image can be customized.

The reasoning here is that the only common reason to change an image
name is to change its Docker registry.  For example, while the default
registry may be set to default.example.com, it may be necessary to
obtain the backend image private.example.com/openslides-server.  With
this patch, that would be achieved by the following configuration in
.env:

    DOCKER_OPENSLIDES_BACKEND_REGISTRY="private.example.com"

For special cases, for which the images' basename must indeed be changed
as well, the template would need to be customized.

The templates are not backwards-compatible.
2021-03-31 15:08:32 +02:00
Finn Stutzenstein ca298960ae
Do not show old errors on the spinner 2021-03-25 15:23:06 +01:00
Emanuel Schütze 2f9b6aba95
Merge pull request #5979 from tsiegleauq/poll-method-makes-sense
Disable general no/yes depending on poll method
2021-03-25 10:06:58 +01:00
Sean 49dba31d56 Disable general no/yes depending on poll method
During the creation of an assignment poll:
Disable and unset the general yes control if the poll method is "yes"
Disables and unset the general no control if the poll method if "no"
2021-03-25 09:55:13 +01:00
Emanuel Schütze f68fca8c83
Merge pull request #5980 from emanuelschuetze/translations-20210322
Updated translations
2021-03-22 21:39:29 +01:00
Emanuel Schütze 2e43a17987 Updated translations 2021-03-22 21:20:15 +01:00
Emanuel Schütze 2e5cea512e
Merge pull request #5957 from CatoTH/bugfix/add-warning-when-editing-motion-with-amendments
Add a warning when editing a motion with existing amendments
2021-03-22 21:15:02 +01:00
Tobias Hößl 9c2e49692c Add a warning when editing a motion with existing amendments 2021-03-22 21:00:04 +01:00
Emanuel Schütze c9b924d79a
Merge pull request #5976 from FinnStutzenstein/fixListOfSpeakerInitialClosed
Fix LOS initially closed migration issue
2021-03-22 20:48:47 +01:00
Finn Stutzenstein 644d3b2fee
Merge pull request #5977 from FinnStutzenstein/enablePytestInDocker
Enable pytest in docker
2021-03-22 13:17:30 +01:00
Finn Stutzenstein f897bb01a3
Enable pytest in docker
The DJANGO_SETTINGS_MODULE is set in the develop container, so the
tests.settings from the setup.cfg was ignored. Executing pytest with

    DJANGO_SETTINGS_MODULE=tests.settings pytest

works fine. The line added in the setup.cfg takes over precedence: It
auto-adds the --ds parameter which has a higher precedence than the
environment variable.

Also removed an unnecessary setting.
2021-03-22 13:01:26 +01:00
Finn Stutzenstein 41a3447357
Fix LOS initially closed migration issue 2021-03-22 08:54:16 +01:00
Finn Stutzenstein 7bbd8688a2
Merge pull request #5970 from FinnStutzenstein/updateClientDeps
Client dependencies
2021-03-22 08:52:31 +01:00
Finn Stutzenstein c10a0ad70d
Client dependencies:
- Update ng2-pdf-viewer
- Lock down tsparticles and ng-particles to the version in the
package-lock.json. In fresh installs newer versions do not work
currently
2021-03-22 07:55:37 +01:00
Emanuel Schütze 3595245663
Merge pull request #5972 from emanuelschuetze/translations20210319
Updated translations
2021-03-19 14:39:00 +01:00
Emanuel Schütze bd29777d83 Updated translations 2021-03-19 14:20:15 +01:00
Emanuel Schütze eadb0e2f0e
Merge pull request #5971 from emanuelschuetze/autoupdate
Updated autoupdate service
2021-03-19 10:01:13 +01:00
Emanuel Schütze d1aba2ef94 Updated autoupdate service 2021-03-19 09:46:07 +01:00
Emanuel Schütze 2e1690d2d0
Merge pull request #5967 from FinnStutzenstein/loginErrorNonActive
Add login errors for inactive users
2021-03-19 09:42:44 +01:00
Finn Stutzenstein a48fe86791
Add login errors for inactive users 2021-03-19 07:35:45 +01:00
Finn Stutzenstein 63132fdbc5
Merge pull request #5966 from FinnStutzenstein/smallMediafileFixes
Small mediafile fixes
2021-03-19 07:34:02 +01:00
Finn Stutzenstein e45d83de5a
Small mediafile fixes 2021-03-18 15:21:00 +01:00
Finn Stutzenstein 365d0d55ea
Merge pull request #5937 from FinnStutzenstein/deleteUnusedCode
Delete unused code
2021-03-15 14:52:04 +01:00
Adrian Nöthlich 52108cd0c4
Add ENV for server-setup host and port
Signed-off-by: Adrian Nöthlich <git@promasu.tech>
2021-03-12 01:07:39 +01:00
Finn Stutzenstein 520915c3f5
Merge pull request #5910 from mdickopp/mdickopp/list-of-speakers-inital-state-configurable
Make configurable whether a newly created list of speakers is initial…
2021-03-11 08:56:29 +01:00
Emanuel Schütze b76e75ae96
Merge pull request #5948 from emanuelschuetze/autoupdate
Updated autoupdate service
2021-03-09 21:08:27 +01:00
Emanuel Schütze 1a538e241d Updated autoupdate service 2021-03-09 20:49:52 +01:00
Martin Dickopp a9d223121e Make configurable whether a newly created list of speakers is initially open or closed 2021-03-08 16:28:00 +01:00
Emanuel Schütze 4d706f648f
Merge pull request #5943 from emanuelschuetze/translationDiffService
Add translation service to DiffService.
2021-03-08 13:28:59 +01:00
Emanuel Schütze ba0e9b3bc6 Add translation service to DiffService. 2021-03-08 12:54:22 +01:00
Emanuel Schütze 92afd07b62
Merge pull request #5940 from CatoTH/bugfix/dont-show-diff-view-with-no-crs-on-slides
Don't show diff view on slides if no change recos exist
2021-03-08 11:49:05 +01:00
Finn Stutzenstein 3ba4f99876
Delete unused code
@oscar: Only the user restricter is still present in the code since it is needed for whoami
2021-03-08 08:57:21 +01:00
Tobias Hößl bef322d0a4
Don't show diff view on slides if no change recos exist 2021-03-06 19:16:22 +01:00
Emanuel Schütze a11682a708
Merge pull request #5939 from emanuelschuetze/translation-20210304
Updated translations
2021-03-05 08:33:10 +01:00
Emanuel Schütze bc9b028624 Updated translations 2021-03-05 07:44:49 +01:00
Emanuel Schütze 291402e159
Merge pull request #5920 from CatoTH/bugfix/inconsistency-amendment-paragraphs
Bugfix: better handling of inconsistent states in amendments if the b…
2021-03-04 21:26:12 +01:00
Emanuel Schütze bd7fa9b3db
Merge pull request #5935 from FinnStutzenstein/userAutoupdateOnPermissionChange
Update users on can_see_extra_data permission change
2021-03-04 21:23:04 +01:00
Tobias Hößl ac50d6f8dc Bugfix: better handling of inconsistent states in amendments if the base motion has shrunk 2021-03-04 21:17:14 +01:00
Finn Stutzenstein 265145f001
Merge pull request #5936 from FinnStutzenstein/redisTimeoutErrors
Attempt on handling timeout errors
2021-03-04 09:09:38 +01:00
Finn Stutzenstein 39fb2fadec
Attempt on handling timeout errors
This code retries to load sessions three times. If not an error will be
thrown. There are other solutions (Like not throwing the error), but
I would like to see errors in production usage to see, if this helps or
not.
2021-03-04 08:45:45 +01:00
Finn Stutzenstein d62d1a687b
Merge pull request #5918 from FinnStutzenstein/removeVotedIds
Remove voted_ids
2021-03-04 08:20:19 +01:00
Finn Stutzenstein d0c1879521
Update users on can_see_extra_data permission change 2021-03-04 08:20:05 +01:00
Finn Stutzenstein 93da435e7c
Remove voted_id for ongoing polls 2021-03-04 07:56:11 +01:00
Emanuel Schütze 45948c47fb
Merge pull request #5881 from FinnStutzenstein/updateDeps
Update Dependencies
2021-03-02 23:15:53 +01:00
Finn Stutzenstein 3504a87295 Update Dependencies 2021-03-02 22:53:48 +01:00
Emanuel Schütze d83b7c0ea9
Merge pull request #5933 from emanuelschuetze/translations-20210302
Updated translations
2021-03-02 22:48:41 +01:00
Emanuel Schütze 155ade1a8c Updated translations 2021-03-02 22:27:45 +01:00
Emanuel Schütze f80ac1d9c5
Merge pull request #5932 from emanuelschuetze/autoupdate
Updated autoupdate service
2021-03-01 20:45:56 +01:00
Emanuel Schütze 2c7196493d Updated autoupdate service 2021-03-01 20:24:19 +01:00
Emanuel Schütze 46223328f7
Merge pull request #5926 from FinnStutzenstein/quotaexceedederror
Error handling
2021-03-01 17:38:55 +01:00
Finn Stutzenstein 619a698272 Error handling:
- catch QuotaExceededError
- add generic error message to the spinner
2021-03-01 17:10:22 +01:00
GabrielInTheWorld a450a1dff5
Merge pull request #5898 from GabrielInTheWorld/cleanup-fix-spinner
Some cleanup in favor of spinner-fixes
2021-03-01 16:49:02 +01:00
Emanuel Schütze d73b2142b7
Merge pull request #5928 from FinnStutzenstein/orderAssignmentOptions
order assignment options by weight
2021-03-01 16:25:14 +01:00
Emanuel Schütze 05fcf40b51
Merge pull request #5929 from FinnStutzenstein/chatPersistance
Do not clear chat notifications from local storage
2021-03-01 16:19:28 +01:00
Emanuel Schütze 83ff7b938c
Merge pull request #5927 from FinnStutzenstein/pointOfOrderOnlyCanSpeak
Point of order only for agenda.can_be_speaker
2021-03-01 16:12:44 +01:00
GabrielMeyer 7314bf0999 Cleanup spinner-fixes
- Rename: noop-interceptor -> stable-interceptor
- Put logic from stable.service to openslides-status.service
2021-03-01 16:11:08 +01:00
Emanuel Schütze d3530a3657
Merge pull request #5931 from emanuelschuetze/caddy-dev
Disabled caddy debug mode in dev setup.
2021-03-01 15:49:30 +01:00
Emanuel Schütze 3d8f3a69af Disabled caddy debug mode in dev setup. 2021-03-01 15:39:51 +01:00
Emanuel Schütze 7dcc0ad42a
Merge pull request #5924 from emanuelschuetze/agenda-config
Fixed showSubtitle config. Reordered agenda config.
2021-03-01 15:37:17 +01:00
Finn Stutzenstein c35cacebb1
Merge pull request #5930 from FinnStutzenstein/losPermissions
Fix usage of osPerms
2021-03-01 15:37:06 +01:00
Gernot Schulz 614e0f2d5f Docker: Add Prometheus as an optional service 2021-03-01 15:24:06 +01:00
Emanuel Schütze 4e6f0850c4 Fixed showSubtitle config. Reordered agenda config. 2021-03-01 15:21:43 +01:00
Finn Stutzenstein 33fca309c4
Fix usage of osPerms 2021-03-01 15:14:13 +01:00
Finn Stutzenstein b13732f9ec
Do not clear chat notifications from local storage 2021-03-01 15:00:47 +01:00
Finn Stutzenstein 0a8274e6e2
order assignment options by weight 2021-03-01 14:40:06 +01:00
Finn Stutzenstein a9045b6a1c
Point of order only for agenda.can_be_speaker 2021-03-01 14:31:40 +01:00
Emanuel Schütze 2d4ece84a0
Merge pull request #5922 from gsiv/dev/build-proxy-default
Docker: Remove proxy as a default build target
2021-03-01 13:26:36 +01:00
Emanuel Schütze a796b2a8b8
Merge pull request #5923 from gsiv/dev/yml-nocmd
Docker: Remove server command override examples
2021-03-01 13:25:17 +01:00
Gernot Schulz cd98502b1c Docker: Remove server command override examples
The YAML configuration files include comments outlining how to override
the server image's default command.  They are supposed to help with, for
example, replacing gunicorn with daphne.

These comments have diverged from the actual command used in current
images.  Furthermore, at least to my knowledge, the setup has not been
used or tested with daphne in a long time.  For these reasons, this
patch removes the comments without a replacement.

If there still is, in fact, a use case for the instructions, they would
need to be updated and their location should be reconsidered.
2021-03-01 11:06:52 +01:00
Gernot Schulz 8d393ba17f Docker: Remove proxy as a default build target
The proxy image is not going to be subject to regular change and is
instead more similar to services such as the database.  Therefore, it
does not need to be a default build target of build.sh.
2021-03-01 10:48:47 +01:00
Emanuel Schütze e75bdeb0f7
Merge pull request #5913 from FinnStutzenstein/fixPointOfOrderWeighting
Fix point of order weighting
2021-02-23 11:11:51 +01:00
Finn Stutzenstein f8446ee609
Fix point of order weighting 2021-02-22 13:33:36 +01:00
Emanuel Schütze baad950698
Merge pull request #5902 from FinnStutzenstein/ChatAccessGroups
Change chat access groups
2021-02-19 15:18:08 +01:00
Finn Stutzenstein 4929e2b6f6 Change chat access groups
Adjust some client chat feature

- Cleanup some stuff
- Read and write chatting
2021-02-19 15:08:02 +01:00
Emanuel Schütze e72cebca4a
Merge pull request #5909 from emanuelschuetze/autoupdate-20210219
Updated autoupdate service
2021-02-19 13:20:15 +01:00
Emanuel Schütze e11f0f6f25
Merge pull request #5908 from fwolfst/README-IMPROVEMENTS
improve links in README
2021-02-19 13:20:03 +01:00
Emanuel Schütze 9f16bfee21 Updated autoupdate service 2021-02-19 13:01:07 +01:00
Felix Wolfsteller 5152a448be improve links in README 2021-02-19 12:52:17 +01:00
Emanuel Schütze 500b773ee1
Merge pull request #5903 from emanuelschuetze/autoupdate
Updated autoupdate service
2021-02-18 11:52:48 +01:00
Emanuel Schütze a8fcb89f48 Updated autoupdate service 2021-02-18 11:39:20 +01:00
Finn Stutzenstein 085ada3dc4
Merge pull request #5894 from FinnStutzenstein/caddyHttps
add optional https for caddy
2021-02-17 08:56:20 +01:00
Finn Stutzenstein ce3e2588c5
add optional https for caddy 2021-02-17 08:28:47 +01:00
Emanuel Schütze 7e875c45db
Merge pull request #5897 from GabrielInTheWorld/fix-spinner
Fixes an infinite running spinner
2021-02-15 15:35:29 +01:00
GabrielMeyer e74df38a0f Fixes an infinite running spinner
The ApplicationRef propagates never a stable state, when in cinema mode. This is, because in the `cinema.component.ts` asynchronous requests are made, before the app was getting stable.
2021-02-15 15:18:10 +01:00
Emanuel Schütze ef451afae1
Merge pull request #5896 from tsiegleauq/more-jitsi-cd
More change detection in jitsi bar
2021-02-15 14:50:09 +01:00
Sean 4490ee91d0 More change detection in jitsi bar 2021-02-15 14:34:36 +01:00
Emanuel Schütze 3012fabf4f
Merge pull request #5895 from FinnStutzenstein/removeDebugPrints
Remove debug prints
2021-02-15 12:56:23 +01:00
Finn Stutzenstein fe0f8d28f4
Remove debug prints 2021-02-15 11:32:24 +01:00
Emanuel Schütze a3a126f930
Merge pull request #5891 from ostcar/prometheus
Prometheus
2021-02-15 11:07:26 +01:00
Oskar Hahn 69bf46a5ff Prometheus 2021-02-15 10:50:12 +01:00
Finn Stutzenstein 5b91ba4597
Merge pull request #5850 from FinnStutzenstein/ownSessionRedis
Own session redis
2021-02-15 08:14:04 +01:00
Finn Stutzenstein 35e8f84fda
Own session redis
- removed big mode artifacts
- removed django_session_redis
- Enforce newly created settings.py when invalid options are set:
  * OPENSLIDES_USER_DATA_DIR
  * SESSION_ENGINE
- Overwrites the secret key during development to "development"
2021-02-15 07:37:46 +01:00
Finn Stutzenstein 05ec54927b
Merge pull request #5892 from FinnStutzenstein/PdfKeyError
Catch pyPDF2 key error
2021-02-15 07:37:10 +01:00
Finn Stutzenstein e9c2dc90d5
Catch pyPDF2 key error 2021-02-15 07:22:38 +01:00
Emanuel Schütze 7af39a5570
Merge pull request #5888 from tsiegleauq/some-iOS-for-fixes
Fix paragraph based amendments in iOS
2021-02-12 14:58:53 +01:00
Sean eda242e83f Fix paragraph based amnedments in iOS
Fixes an issue on iOS devices regarding click-bindings in structural
loops.
iOS devices did not respond to any touch inputs in the given view.
The same behavious was observed in #5351
This is most a bug in angular or iOS webkit
2021-02-12 13:11:46 +01:00
Emanuel Schütze c1b4d3154d
Merge pull request #5886 from emanuelschuetze/translations-20210211
Updated translations
2021-02-11 21:57:54 +01:00
Emanuel Schütze 1cf2763ed6 Updated translations 2021-02-11 21:33:18 +01:00
Emanuel Schütze c0dad72eb4
Merge pull request #5882 from FinnStutzenstein/fixPointOfOrderSorting
Fix point of order sorting
2021-02-11 21:15:42 +01:00
Emanuel Schütze aac8ec8f2e
Merge pull request #5876 from tsiegleauq/chat
Chat
2021-02-11 20:42:36 +01:00
Sean 69adc1d41c Add Chat UI Components
Add Chat User Interface
Restructure some services
Virtual Scrolling
Manual change detection for message updates
Enhanced Date pipe
Message layout
Tabbed reusable chat window
Deleting messages
Further permission checks
Delete-prompts
Mobile friendly chat usage
automatically scroll to bottom
2021-02-11 17:10:25 +01:00
Finn Stutzenstein b0ccb1ea7e
Fix point of order sorting 2021-02-10 15:58:01 +01:00
Finn Stutzenstein 7fffffb497
Merge pull request #5877 from FinnStutzenstein/fixCaddyDevSetup
fix Caddy dev setup
2021-02-10 07:55:12 +01:00
Finn Stutzenstein f65e8ae819
fix Caddy dev setup 2021-02-10 07:30:36 +01:00
Finn Stutzenstein 8e5b1fa99d Add Chat on Server and client as draft 2021-02-09 16:07:52 +01:00
Emanuel Schütze 8e98966db2
Merge pull request #5875 from gsiv/fix/caddy-builddev
Update Makefiles for Caddy
2021-02-08 18:01:57 +01:00
Gernot Schulz 0ed4e27725 Update Makefiles for Caddy
The reload-haproxy target has simply been removed due to time
constraints.
2021-02-08 17:42:56 +01:00
Emanuel Schütze 5910d2c914
Merge pull request #5874 from emanuelschuetze/translations-20210208
Updated translations
2021-02-08 17:22:17 +01:00
Emanuel Schütze 612bf78871 Updated translations 2021-02-08 17:07:32 +01:00
Emanuel Schütze c569835ce1
Merge pull request #5872 from gsiv/dev/caddy
Docker: Rename proxy-related variables
2021-02-08 16:42:09 +01:00
Gernot Schulz a1e65e8a47 Docker: Rename proxy-related variables
HAProxy was replaced by Caddy, so obviously variables should not longer
carry HAPROXY in their names to avoid confusion.

To hopefully make future changes less likely to break configurations,
we'll keep variable and image names generic (proxy instead of caddy).
2021-02-08 14:53:45 +01:00
Gernot Schulz 67202a4a4b Remove haproxy
Replaced by Caddy
2021-02-08 13:45:36 +01:00
Emanuel Schütze ac0a27276c
Merge pull request #5862 from ostcar/caddy-dev
Use https in dev mode
2021-02-06 14:28:25 +01:00
Oskar Hahn ab2a8ca419 Use https in dev mode 2021-02-06 11:47:38 +01:00
Emanuel Schütze 180de2d3a9
Merge pull request #5860 from ostcar/fix_development
Fix Development
2021-02-05 22:10:34 +01:00
Oskar Hahn d7d8dcb3c9 Fix Development 2021-02-05 19:26:04 +01:00
Emanuel Schütze d05958ca10
Merge pull request #5851 from ostcar/caddy
Use caddy
2021-02-05 18:17:51 +01:00
Gernot Schulz f7d228a600 Docker: Update build scripts for Caddy
For now, keep using HAProxy's name in most places, e.g.,
DOCKER_OPENSLIDES_HAPROXY_NAME.  To avoid confusion, the specific
mentions of HAProxy should be made more generic, e.g.,
DOCKER_OPENSLIDES_PROXY_NAME.
2021-02-05 18:00:04 +01:00
Gernot Schulz 40dc0e08fa Caddy: Add config to image instead of using a volume 2021-02-05 17:59:57 +01:00
Gernot Schulz 470168c58c Caddy: Do not use TLS for autoupdate connection 2021-02-05 17:59:36 +01:00
Emanuel Schütze b08948f3e5
Merge pull request #5857 from normanjaeckel/HistoryReally
Added information to poll start and stop.
2021-02-05 15:37:40 +01:00
Norman Jäckel c3de6dc870 Added information to poll start and stop. 2021-02-05 15:28:37 +01:00
Emanuel Schütze 09bc7f093a
Merge pull request #5770 from tsiegleauq/youtube-urls
Support youtube as livestream URL
2021-02-05 14:29:51 +01:00
Sean 372f1eaa7e Support youtube as livestream URL
Allows to use youtube as as live stream url config,
will load the youtube embedded player in an iframe instead
of video.js
2021-02-05 14:00:52 +01:00
Finn Stutzenstein f57fe05e26
Merge pull request #5853 from normanjaeckel/HistoryInformationChanges
Updated README
2021-02-05 13:47:31 +01:00
Emanuel Schütze 26744fde9f
Merge pull request #5856 from FinnStutzenstein/updateClient
update client
2021-02-05 13:04:07 +01:00
Finn Stutzenstein 1b482871ac
update client 2021-02-05 12:54:09 +01:00
Finn Stutzenstein 8746496d2d
Merge pull request #5852 from emanuelschuetze/removed-setuppy
Removed pip support
2021-02-05 12:03:58 +01:00
Norman Jäckel cc5bcf1a81 Updated README 2021-02-05 12:01:48 +01:00
Norman Jäckel f74cf10ff3
Merge pull request #5854 from FinnStutzenstein/fixDockercomposeDev
Fix docker compose dev
2021-02-05 12:00:43 +01:00
Finn Stutzenstein 9e38ed955f
Fix docker compose dev 2021-02-05 12:00:11 +01:00
Sean 058a7f71ae
Merge pull request #5823 from tsiegleauq/fix-group-import
Fix csv importing users with groups
2021-02-05 10:38:36 +01:00
Oskar Hahn 799dd08e0d client listens 4200 in prod 2021-02-05 09:47:04 +01:00
Emanuel Schütze 3b1b396e9a Removed setup.py and MANIFEST
Reason: pip supported is dropped for next OpenSlides release (3.4)
because the python server cannot run stand-alone anymore. You will cannot
install OpenSlides 3.4 via 'pip install openslides'.
2021-02-04 22:17:46 +01:00
Emanuel Schütze 72b7162eeb
Merge pull request #5842 from FinnStutzenstein/sortPooSpeakers
Order point of order speakers in the request order (closes #5816)
2021-02-04 22:03:11 +01:00
Oskar Hahn dc58752575 Use caddy 2021-02-04 20:54:57 +01:00
Finn Stutzenstein 5b2f8409e4
Merge pull request #5847 from FinnStutzenstein/loadingIndicator
Wait for a stable app (closes #5813)
2021-02-04 13:08:05 +01:00
Finn Stutzenstein a839294add
Wait for a stable app (closes #5813) 2021-02-04 12:33:20 +01:00
Emanuel Schütze e5f0ebd6e5
Merge pull request #5832 from tsiegleauq/jitsi-helpdesk
Add helpdesk icon and feature
2021-02-03 11:42:59 +01:00
Sean bd65b5d41c
Merge pull request #5841 from tsiegleauq/clean-some-applause
Cleanup some applause code
2021-02-03 10:02:39 +01:00
Finn Stutzenstein 94c943cdb5
Order point of order speakers in the request order (closes #5816) 2021-02-03 09:15:28 +01:00
Sean 04eedc7c37 Cleanup some applause code
Cleans some applause code
2021-02-02 15:52:55 +01:00
Finn Stutzenstein a20641fe44
Merge pull request #5828 from FinnStutzenstein/offlineBarReconnect
Fixes offline bar on successful reconnections (closes #5810).
2021-02-02 13:12:09 +01:00
Sean 024b9c74e6 Add helpdesk icon and feature
Adds a "helpdesk" Jitsi room feature.
Can be enabled using the OpenSlides config page

Shows a 'Call support' button in the conference control bar
clicking the support button will connect the user
to a "support" jitsi room
The name of the support room will be
`JITSI_ROOM_NAME`-SUPPORT
2021-02-02 13:03:30 +01:00
Finn Stutzenstein 1380812924
Fixes offline bar on successful reconnections (closes #5810). 2021-02-02 11:56:04 +01:00
Sean cc65b756c7
Merge pull request #5811 from tsiegleauq/applause-client
Add applause in client
2021-02-01 13:34:30 +01:00
Sean e3d718cad0 Add applause in client
Applause button in Jitsi Bar,
Add Applause Service,
Add Applause Display component,
Add Config varriables,
Integrate applause display component in Jitsi bar,
Integrate custom vertical progress bar as own component,
  - vertical and more customizable than the mat-progress-bar
  - includes an optional end icon
  - animated and themed
Add custom clapping icon
applause particles using tsparticles
custom particle component
dynamic add and remove functions to alter particles in runtime
Set own particle shape
Use smooth emitter for clean particle spawning
2021-01-28 15:42:19 +01:00
Finn Stutzenstein 47a2204921
Merge pull request #5822 from mdickopp/mdickopp/pwGeneration
Improve client-side password generation
2021-01-26 11:21:55 +01:00
Sean 878f3a7ab3 Fix csv importing users with groups
old groups
new groups created on the fly
2021-01-26 11:11:05 +01:00
Finn Stutzenstein 676bda8cc3
Merge pull request #5821 from danilobuerger/django-env
Allow preset env variable for django secret
2021-01-26 09:19:48 +01:00
Martin Dickopp 2130f4970f Improve password generation
Generating an 8-bit random number and reducing it modulo 56
(characters.length) does not choose all numbers 0 to 55 with equal
probability, but chooses 0 to 31 with higher probability than 32 to 55.
This change improves the password generation algorithms by choosing all
characters with equal probability.
2021-01-24 19:14:20 +01:00
Danilo Bürger 11d7f7b888
Allow preset env variable for django secret
The source command fails if /run/secrets/django does not exist. This is however not important if DJANGO_SECRET_KEY is already set. This is checked in the next step
2021-01-22 21:08:34 +01:00
Gernot Schulz 7e67e0db12
Merge pull request #5815 from gsiv/fix/entrypoint-db
server: Fix database config in Docker entrypoint
2021-01-22 16:33:42 +01:00
Emanuel Schütze 97950d5baa
Merge pull request #5817 from FinnStutzenstein/debugging
Performance improvements for OS3+
2021-01-21 13:35:03 +01:00
Finn Stutzenstein 8049bfa91e
Performance improvements for OS3+
- Cleans up log messages in the client
- Refactored the autoupdate bundle code into an own file
- Added bulk creates for History in Postgresql. This is the only database system
  that supports returning all ids whan multiple elements are inserted. We can
  make usage out of it.
- Added a `disable_history`, that is request-wide
- Disabled history on poll vote requests
- Removed unnecessary user ordering
- Reduced the queries for creating motion vote objects by one
- removed final_data: This was not prefetched. Using the normal data collection
  the data is prefetched
- removed unnecessary user query if vore delegation is not used
2021-01-21 12:53:51 +01:00
Gernot Schulz 0f0d750d83 server: Fix database config in Docker entrypoint
Firstly, this patch ensures that the server connects to the database
according to the given configuration variables.  Up until now, there was
a pg_isready check for the hard-coded hostname "db".  While this
generally worked in the default setup – db is an alias for pgbouncer in
the provided YAML configuration files – it is obviously wrong and would
lead to unexpected behavior in customized setups.

Secondly, the .pgpass setup, which was a remnant of another time and not
used anymore, has been removed.
2021-01-18 14:00:58 +01:00
Finn Stutzenstein 4f4bff9bb3
Merge pull request #5814 from FinnStutzenstein/docs
Docs and security email
2021-01-18 07:25:57 +01:00
Finn Stutzenstein b2e6d2f2ac Docs and security email 2021-01-17 21:18:52 +01:00
Finn Stutzenstein b200cfbd07
Merge pull request #5533 from FinnStutzenstein/externalAutoupdateService
External autoupdate service
2021-01-14 13:39:32 +01:00
FinnStutzenstein e225a57f97
OpenSlides3+: External Autoupdate Service
- Removing channels. Going back to a wsgi deployment
- Removed server projector code
- Autoupdate throttling is now in the client
- New communication stack in the client
- Adopted all deployment methods: Docker stack and docker compose (prod and dev)
- Added autoupdate service as submodule
2021-01-14 07:55:41 +01:00
Sean 1145ae1460
Merge pull request #5719 from FinnStutzenstein/minVotesAmount
Minimum amount of votes
2021-01-13 11:23:49 +01:00
Sean bc382df68f Client: Add form validation and request handling 2021-01-13 11:14:12 +01:00
Finn Stutzenstein ea180246c7
fixed server, rebased on current master 2021-01-12 09:50:13 +01:00
Sean 0b01b5576b
Fixing min amounts in assignment meta info 2021-01-12 09:29:28 +01:00
Finn Stutzenstein 7e763e8c07
Minumum amount of votes
renamed votes_amount to max_votes_amount
2021-01-12 09:29:24 +01:00
Emanuel Schütze 010b61cce2
Merge pull request #5800 from FinnStutzenstein/fixUserUpdateVoteDelegations
Fix vote_delegated_from_user_ids on user update
2021-01-08 15:39:05 +01:00
Emanuel Schütze 8542817129
Merge pull request #5791 from FinnStutzenstein/pwGeneration
Change PW generation to a secure method
2021-01-08 15:37:38 +01:00
Finn Stutzenstein b0ba30b454
Fix vote_delegated_from_user_ids on user update 2021-01-08 08:24:34 +01:00
Finn Stutzenstein 183c511046
Change PW generation to a secure method 2021-01-05 07:42:19 +01:00
Emanuel Schütze ea277adf9e
Merge pull request #5784 from emanuelschuetze/release3.3
Release3.3
2020-12-18 01:04:36 +01:00
Emanuel Schütze ef135837f7 Update version to 3.4-master 2020-12-18 00:43:02 +01:00
Emanuel Schütze a37e2196b3 Release 3.3 2020-12-18 00:43:02 +01:00
Emanuel Schütze 273debf99a Readded setup.py 2020-12-18 00:42:59 +01:00
Emanuel Schütze ab2fbaac79
Merge pull request #5778 from tsiegleauq/changelog-3.3
Add Changelog for 3.3 release
2020-12-17 23:56:00 +01:00
Emanuel Schütze aeaedabb87
Merge pull request #5780 from emanuelschuetze/translation20201216
Updated translations
2020-12-17 23:23:28 +01:00
Sean 52c4aa6c58 Updated Changelog for 3.3 release 2020-12-17 23:22:46 +01:00
Emanuel Schütze 227dfd0c26 Updated translations 2020-12-17 10:30:46 +01:00
Emanuel Schütze b179930cc8
Merge pull request #5779 from tsiegleauq/add-more-server-messages-to-translations
Add more server messages to translations
2020-12-16 20:05:33 +01:00
Sean 3b062a52e7 Add more server messages to translations
Adds some ValidationErrors send from the server to the translations in
the client
2020-12-16 16:21:56 +01:00
Emanuel Schütze 266c129e04
Merge pull request #5777 from tsiegleauq/see-own-vote-delegations-in-profile
Show vote delegations to users on ownPage
2020-12-16 13:35:35 +01:00
Sean f07cc4e176 Show vote delegations to users on ownPage
Users can see vote delegations concerning them if visiting their own
user detail page
2020-12-16 12:37:12 +01:00
Emanuel Schütze 222a2ea581
Merge pull request #5775 from tsiegleauq/more-powerful-vote-delegations
Add more dynamic vote delegations to user list
2020-12-15 17:35:01 +01:00
Emanuel Schütze 057e03a82c
Merge pull request #5774 from tsiegleauq/los-go-button-layouting
Responsive add-self buttons in list-of-speakers
2020-12-15 17:20:54 +01:00
Emanuel Schütze 1ebad842de
Merge pull request #5773 from tsiegleauq/mat-chips-in-search-val
Overwork chip list in search value selector
2020-12-15 17:18:37 +01:00
Sean cb73f52345 Add more dynamic vote delegations to user list
The user list now shows vote delegations from and to in every form and
preview
2020-12-15 16:40:27 +01:00
Sean 53be648c23 Responsive add-self buttons in list-of-speakers 2020-12-15 15:36:45 +01:00
Sean 2a224cb3b5 Overwork chip list in search value selector
Removed the placeholder, fixed the height, made it scrollable like the
filter bars, using elipsis for very long names, inidkated long names
using tooltips
2020-12-15 14:04:11 +01:00
Emanuel Schütze acd33b8207
Merge pull request #5765 from tsiegleauq/poll-create-layout-enhance
Enhance poll dialog layout
2020-12-13 21:37:08 +01:00
Sean e2cabbaf62 Enhance poll dialog layout
declutters the the poll dialogs
2020-12-13 21:27:08 +01:00
Emanuel Schütze 19df8184d0
Merge pull request #5762 from tsiegleauq/poll-progress-bar-ap
Show vote progress in autopilot
2020-12-13 20:56:58 +01:00
Emanuel Schütze 0a80a73f2e
Merge pull request #5761 from jwinzer/poll-progress-users
Fix user filter of poll progress
2020-12-13 20:52:50 +01:00
Sean b9f36f1cea Hide poll progress bar by perms 2020-12-11 11:21:11 +01:00
Emanuel Schütze 5d5a5b3e39
Merge pull request #5755 from FinnStutzenstein/fixUserDelegationSelfvote
Fix user delegation selfvote
2020-12-10 10:10:42 +01:00
Finn Stutzenstein ef42a2293d
Adjust restricter, so poll managers can see the delegation structure 2020-12-10 09:20:50 +01:00
Sean ac63a04666 Show vote progress in autopilot
Shows the vote progress in the autopilot
changes the theme of the progress bar to support its normal buffer color
Shows "autopilot" in browser title
2020-12-09 14:17:27 +01:00
Sean 78cfa4875e
Merge pull request #5683 from jwinzer/bug-fix-perms
Fix unresolved permissions
2020-12-09 12:54:22 +01:00
Jochen Winzer 689bfcac61 Fix unresolved permissions. 2020-12-09 12:36:35 +01:00
Jochen Winzer ec36d4d64e User is only allowed to vote if present and has not delegated his vote or has delegated his vote to a user who is present. 2020-12-09 11:09:11 +01:00
Emanuel Schütze 5cc464b250
Merge pull request #5750 from tsiegleauq/avoid-zombie-jitsi
Avoid stream/jitsi cases without information
2020-12-08 17:40:54 +01:00
Sean 92cf811921 Avoid stream/jitsi cases without information
Rewrites the jitsi/stream state management.
After every reload and after a stream becomes available (while not
currently in a jitsi conference) you will switch to live stream, if
available
2020-12-08 17:15:25 +01:00
Sean 1af78df328
Merge pull request #5742 from tsiegleauq/show-poll-infos-more-present
Add poll meta info component
2020-12-08 16:42:59 +01:00
Sean 9c738b5d8e Add poll meta info component
Adds a component to display the assignment polls meta info
2020-12-08 16:25:08 +01:00
Emanuel Schütze 9ddb3a9179
Merge pull request #5705 from tsiegleauq/show-poll-result-in-ap
Show latest meaningful poll results in autopilot
2020-12-08 16:17:49 +01:00
Sean 10614ca57b Show latest meaningfull poll results in autopilot
Shows the latest meaningfull poll result in autopilot
The last published poll result from the corresponding content object
2020-12-08 14:45:08 +01:00
Finn Stutzenstein 182759e794
Merge pull request #5749 from mathiashro/patch-1
Update DEVELOPMENT.rst
2020-12-08 09:01:34 +01:00
Finn Stutzenstein 78f0b29921
Disallows users, who have delegated their votes to others, to selfvote. It is enforced, that the user who gets the vote right must vote for the right-provder. 2020-12-04 06:52:25 +01:00
Emanuel Schütze 2943c969ab
Merge pull request #5751 from tsiegleauq/motion-to-motion-nav
Reset the CR Mode by motion-motion navigation
2020-12-03 15:02:10 +01:00
Emanuel Schütze ea4ec53fb1
Merge pull request #5746 from tsiegleauq/poster-image
Add poster image in 404 cases
2020-12-03 12:24:57 +01:00
Sean 944685696a
Merge pull request #5739 from tsiegleauq/hide-started-assignment-polls
Hide started assignment polls
2020-12-03 12:19:38 +01:00
Emanuel Schütze 5629c73b4b
Merge pull request #5734 from tsiegleauq/deleted-users-in-polls
Show "Deleted user" if a poll user cannot be found
2020-12-03 12:09:56 +01:00
Sean d0ed5448e8 Add poster image in 404 cases
Shows the assigned poster image if the stream is not
reachable
2020-12-03 12:09:30 +01:00
Emanuel Schütze 00b148edbd
Merge pull request #5736 from tsiegleauq/hide-motion-id-in-pdf
Consider show-seq-number config during pdf export
2020-12-03 11:59:46 +01:00
Sean 5e1b5b5658 Show "Deleted user" if a poll user cannot be found
Fixes an error that would freeze OpenSlides
If a user was deleted but registered as a poll option,
The Assignment Detail View would freeze
2020-12-03 11:51:41 +01:00
Emanuel Schütze 96d464bcfa
Merge pull request #5748 from tsiegleauq/vjs-pip
Re-Enable pip option for VJS
2020-12-03 11:36:43 +01:00
Emanuel Schütze 85de17611f
Merge pull request #5735 from tsiegleauq/trim-vjs-strings
Trim livestream URL
2020-12-03 11:33:05 +01:00
Sean a410083349 Reset the CR Mode by motion-motion navigation
Set the CR mode to default if navigating from one motion
to the other motion instead of keeping the previously selected
CD mode
2020-12-02 12:42:03 +01:00
mathiashro bdd44f78eb
Update DEVELOPMENT.rst 2020-12-01 18:16:08 +01:00
Sean ce2f71a9da Re-Enable pip option for VJS
re enables the previously disabled pip mode for videojs
2020-12-01 17:09:02 +01:00
Sean 90ac27ff43 Hide started assignment polls
Hide assignment polls in started state if user does not have
the manage permission
2020-11-30 15:27:12 +01:00
Finn Stutzenstein 652b727386
Merge pull request #5738 from FinnStutzenstein/fixGetWhoAmIError
Fix error handling in WhoAmI data view
2020-11-30 13:44:29 +01:00
Finn Stutzenstein e1183fff60
Fix error handling in WhoAmI data view 2020-11-30 13:00:03 +01:00
Sean 629ad4ec1f Consider show-seq-number config during pdf export
If not stated in the export dialog, the sequential
motion number will not be printed in the PDF if the config
option is unset
2020-11-27 17:09:33 +01:00
Sean 00066806d6 Trim livestream URL
Trims whitespaces from the livestream URL
2020-11-27 16:58:58 +01:00
Emanuel Schütze 96f96f09ee
Merge pull request #5732 from emanuelschuetze/fixAssignmentManagePermission
Fixed permission for assignment submenu.
2020-11-26 12:44:27 +01:00
Emanuel Schütze 48c09ed4c5
Merge pull request #5731 from emanuelschuetze/translation20201126
Updated translations
2020-11-26 12:39:14 +01:00
Emanuel Schütze c4f37999f3 Fixed permission for assignment submenu. 2020-11-26 12:29:00 +01:00
Emanuel Schütze ad907de958 Updated translations 2020-11-26 12:20:21 +01:00
Emanuel Schütze 8b94829a2c
Merge pull request #5695 from tsiegleauq/active-wait-for-perms
Show "please grant mic access" info
2020-11-26 12:06:27 +01:00
Emanuel Schütze fbed661dfb
Merge pull request #5730 from emanuelschuetze/motionPollTitle
Show motion poll title in motion pdf
2020-11-26 12:05:33 +01:00
Emanuel Schütze b318bfda99
Merge pull request #5729 from FinnStutzenstein/fixAssignmentOptionSorting
Fix sorting of assignment options
2020-11-26 11:55:55 +01:00
Emanuel Schütze 5577bac7c9 Show motion poll title in motion pdf 2020-11-26 11:47:18 +01:00
Finn Stutzenstein c7405c36d8
Fix sorting of assignment options 2020-11-26 11:39:43 +01:00
Joshua Sangmeister fca688a1f7 fix config usage 2020-11-26 10:37:45 +01:00
Sean 26ac618ddf Show "please grant mic access" info
If the browser does not have the permission to use the microphone,
OpenSlides will show a spinner to inform the user to grant
mic access
2020-11-25 15:49:18 +01:00
Emanuel Schütze c323eabd6f
Merge pull request #5727 from FinnStutzenstein/enableIsPresentBulkForSAMLUsers
Enable bulk set presence for non-default auth type users
2020-11-25 15:30:13 +01:00
Sean a51103b7b7
Merge pull request #5707 from tsiegleauq/automatically-join-jitsi
Autoconnect the next X speaker to jitsi
2020-11-25 15:29:24 +01:00
Sean d6467d5bbf Autoconnect the next X speaker to jitsi
In config, set "general_system_conference_auto_connect_next_speakers"
to let the next X speakers on the current list of speakers
automatically join the jitsi conference.
Updates automatically
2020-11-25 15:13:02 +01:00
Emanuel Schütze 00bb266098
Merge pull request #5726 from FinnStutzenstein/fixDebugEmailFlag
enable --debug-email for start and runserver
2020-11-25 15:07:54 +01:00
Finn Stutzenstein 0a67c24138
Enable bulk set presence for non-default auth type users
closes #5715
2020-11-25 15:06:17 +01:00
Finn Stutzenstein 667a841051
enable --debug-email for start and runserver
closes #5702
2020-11-25 14:53:41 +01:00
Finn Stutzenstein fd7a4cb64b
Merge pull request #5725 from FinnStutzenstein/hotfixUserRestrictor
Hotfix user restrictor. Wrong usage of set's add method
2020-11-25 12:28:50 +01:00
Finn Stutzenstein 4f24a38da8
Hotfix user restrictor. Wrong usage of set's add method 2020-11-25 12:04:24 +01:00
Emanuel Schütze 03cb8592fe
Merge pull request #5724 from emanuelschuetze/translation20201125
Updated translations
2020-11-25 11:49:22 +01:00
Emanuel Schütze 1f302b466a Updated translations 2020-11-25 11:39:57 +01:00
Sean 65c7d3491c
Merge pull request #5717 from emanuelschuetze/listType
Add ol list type.
2020-11-25 11:23:50 +01:00
Emanuel Schütze b611642ecb
Merge pull request #5721 from FinnStutzenstein/fixGroupDeletionInConfig
Fix group deletion for config variables
2020-11-25 10:39:08 +01:00
Sean 1eee3bc56d
Merge pull request #5718 from tsiegleauq/negative-voting
Allow negative voting
2020-11-25 10:01:12 +01:00
Sean b5cb694fc7 Allow negative voting
Adds "no" as the opposite of "votes" as assignment poll method
Added global_yes, enabled new voting mode `N` in the server
Layout, Tables, Charts, Projector, Vote CSS, Cleanups, Percent bases,
analog votes and more
2020-11-24 23:14:12 +01:00
Finn Stutzenstein f609e6362f
Fix group deletion for config variables 2020-11-24 13:43:05 +01:00
Emanuel Schütze 36506a7383
Merge pull request #5720 from FinnStutzenstein/fixSelectableNonAnalogPollsIfDisabled
Remove the possibility to select default non-analog poll types, if el…
2020-11-24 11:37:16 +01:00
Finn Stutzenstein 07a003717d
Remove the possibility to select default non-analog poll types, if electronic voting is disabled 2020-11-24 10:28:56 +01:00
Emanuel Schütze ab230fe7a9 Allowed ol list types (in html and pdf). 2020-11-23 20:40:22 +01:00
jsangmeister 26e414e3d1
Merge pull request #5714 from jsangmeister/fix-html-validation
Fixed HTML attribute validation
2020-11-20 16:23:16 +01:00
Joshua Sangmeister f3809fc8a9 Fixed HTML attribute validation 2020-11-20 16:10:32 +01:00
Sean bed9b3a958
Merge pull request #5709 from tsiegleauq/replace-travis-with-github-actions
Replace travis with github actions
2020-11-19 09:29:30 +01:00
Sean 5b2fe01965 Replace travis with github actions
Remove travis, add github actions
2020-11-18 18:35:13 +01:00
Finn Stutzenstein 38534d4e01
Include vote_delegated_from_users_id in required users (#5706)
* Include vote_delegated_from_users_id in required users

* Fix restrictors if users do not have base perms

* Fix from username building in the projector
2020-11-18 08:14:33 +01:00
Norman Jäckel 6c35e225a5
Merge pull request #5700 from bernhardreiter/patch-1
README: fix (minor) typo
2020-11-13 09:35:45 +01:00
Bernhard E. Reiter 6abaeb2155
README: fix (minor) typo 2020-11-12 11:20:45 +01:00
Emanuel Schütze d446382f70
Merge pull request #5694 from tsiegleauq/po-order-closed-lists
Allow point-of-order on closed list of speakers
2020-11-11 21:04:38 +01:00
Emanuel Schütze 34070843c2
Merge pull request #5690 from tsiegleauq/search-value-single-val
Fix non-multipl os-search-val-selector
2020-11-11 20:54:36 +01:00
Finn Stutzenstein 01206cb7c6
Merge pull request #5689 from FinnStutzenstein/fixManageSpeakerValidation
Add user_id validation for LOS and assignment candidates
2020-11-11 08:44:45 +01:00
Sean 2736917c7e Allow point-of-order on closed list of speakers
Allows to submit a new point of order even though the list of
speakers is closed
2020-11-10 13:10:35 +01:00
Finn Stutzenstein 72dc55558f
Add user_id validation for LOS and assignment candidates 2020-11-10 12:30:54 +01:00
Sean 9ce8fe8233
Merge pull request #5692 from tsiegleauq/quick-toggle-los-open-ap
Add los-close toggle to autopilot
2020-11-10 12:09:00 +01:00
Sean dd3dbea482
Merge pull request #5691 from tsiegleauq/fix-remove-all-speakers
Fix "remove all speaker" button
2020-11-10 12:08:30 +01:00
Sean a188abed48 Fix non-multipl os-search-val-selector
Fixes a bug there non multiple uses of search value
selector was submitting the value twice due to late
alterations of internal form objects
2020-11-10 11:14:28 +01:00
Sean ade2d4b977 Add los-close toggle to autopilot
Adds a open/close list-of-speakers toggle option to autopilot component
Also: hides the list of speakers if the current content object does not
allow a list-of-speakers (mediafiles, users)
2020-11-09 19:06:41 +01:00
Sean be0deefdce Fix "remove all speaker" button
Remove all speakers was always deactivated
2020-11-09 16:18:26 +01:00
Emanuel Schütze 7b0f8e3c25
Merge pull request #5687 from emanuelschuetze/translation-20201107
Updated translations
2020-11-08 20:15:14 +01:00
Emanuel Schütze 88b25acd0a Updated translations 2020-11-07 18:36:07 +01:00
Emanuel Schütze 22f9108b49
Merge pull request #5684 from tsiegleauq/vscroll-selection-bugs
Fix a bug where vscroll select lists lost content
2020-11-07 18:26:39 +01:00
Emanuel Schütze 019c097c26
Merge pull request #5685 from emanuelschuetze/point-of-order-on-projector
Show point of order icon on projector (los slide)
2020-11-07 18:09:27 +01:00
Emanuel Schütze f9796027ef Show point of order icon on projector (los slide) 2020-11-07 17:19:59 +01:00
Sean 2364ed66ff Fix a bug where vscroll select lists lost content
Large virtual scroll select lists lost the
content after scrolling. Form values
were removed from the component if the
buffer was exceeding the view
2020-11-06 18:33:13 +01:00
Emanuel Schütze 9fcb6cdcba
Merge pull request #5676 from tsiegleauq/show-vote-totals-in-user-list
Show vote weight totals in user list
2020-11-06 11:16:16 +01:00
Emanuel Schütze 5b84bddc2a
Merge pull request #5680 from jwinzer/presence-disabled-fix
Disable presence checkbox
2020-11-05 22:55:32 +01:00
Emanuel Schütze def6e8d59d
Merge pull request #5682 from emanuelschuetze/translation-20201105
Updated translations
2020-11-05 22:54:23 +01:00
Emanuel Schütze a49ed17b45
Merge pull request #5674 from jwinzer/cinema-speaker-list
Cinema needs perm to show list of speakers. Use content-container.
2020-11-05 22:48:00 +01:00
Emanuel Schütze 7d97cede2d
Merge pull request #5672 from jwinzer/assignment-update
Call assignment details from new route after a route change.
2020-11-05 22:39:09 +01:00
Sean 17e5d42d17 Show vote weight totals in user list
Shows the total vote weight in user list
2020-11-05 22:33:15 +01:00
Emanuel Schütze 4c7bf0a203
Merge pull request #5677 from FinnStutzenstein/envMedia
Add env variables for media cache
2020-11-05 22:24:28 +01:00
Emanuel Schütze 85852d158a
Merge pull request #5678 from jsangmeister/deactivate-empty-votes
Deactivate empty votes for assignment polls
2020-11-05 22:23:04 +01:00
Emanuel Schütze beb59cee73 Updated translations 2020-11-05 22:03:00 +01:00
Jochen Winzer d89c7cfdb0 Disable checkbox if user is not permitted to change presence. 2020-11-05 21:33:04 +01:00
Joshua Sangmeister 883463ea87 Deactivate empty votes for assignment polls 2020-11-05 16:44:22 +01:00
Finn Stutzenstein 0c66afc34a
Add env variables for media cache 2020-11-05 16:19:44 +01:00
Jochen Winzer 513f1477af Call assignment details from new route after a route change.
Make private.
2020-11-04 18:05:10 +01:00
Jochen Winzer 837af97d57 Need perm to show list of speakers. Use content-container.
Fix link.
2020-11-04 18:01:38 +01:00
Sean 6bc2c104b1
Merge pull request #5673 from jwinzer/poll-collection-refresh
Bug fix preventing undesired recreation of poll-collection
2020-11-04 16:23:51 +01:00
Sean d4b92a2b4e
Merge pull request #5628 from tsiegleauq/vjs-404
catch and refresh unreachable video streams
2020-11-04 16:15:01 +01:00
Sean 7ad3b78eb2
Merge pull request #5644 from tsiegleauq/point-of-order-in-listofspeakers
Add "point of order" feature to ListOfSpeakers
2020-11-04 14:36:40 +01:00
Jochen Winzer 5ef1869a10 Bug fix preventing undesired recreation of poll-collection and poll-votes on poll change. 2020-11-04 09:38:53 +01:00
Sean ccc48e6b3f
Add "point of order" feature to ListOfSpeakers
Adds the option "point of order" to the list of speakers
- You can make a point of order even though you normally have no
permission to add yourself to the ListOfSpeakers
- You can make a point of order even though you are already on theListOfSpeakers (but you may only be there once)
- new points of order will be on top of the list of speakers
- Point of orders will be highlighted by a red triangle

This feature can be used to request to speak with a higher level of
urgency
2020-11-03 12:40:36 +01:00
Finn Stutzenstein 866acfe7f5
Merge pull request #5658 from FinnStutzenstein/exposeMoreEmailSettings
Expose more email settings
2020-11-02 07:49:21 +01:00
Finn Stutzenstein 6943c3d18f
Expose more email settings 2020-11-02 07:26:09 +01:00
Sean b391ed0dfe catch and refresh unreachable video streams
Detects if a live stream is reachable or not.
Adds a refresh key to recheck if a stream can be reached
Live adding and removing streams made easier
2020-10-29 09:20:18 +01:00
Finn Stutzenstein 06044e81c0
Merge pull request #5651 from simonla82/armSupport
Update Dockerfile to allow build via docker-compose on arm architectures
2020-10-29 07:56:36 +01:00
Finn Stutzenstein eacccd8f5c
Merge pull request #5657 from FinnStutzenstein/smallFixes
Catch some tracebacks
2020-10-29 07:50:13 +01:00
Finn Stutzenstein 61c5f77d29
Merge pull request #5548 from FinnStutzenstein/createinitialuser
Create initial user command
2020-10-29 07:50:00 +01:00
simonla82 5502e5337a
Update Dockerfile to allow build via docker-compose on arm architectures
As stated in issue #5638 the installation via docker-compose fails on arm architectures since two librariers are not correctly installed. This is fixed by installing those manually in the /server/docker/Dockerfile
2020-10-28 13:16:38 +01:00
Finn Stutzenstein 0956153ea4
Create initial user command 2020-10-28 08:22:32 +01:00
Finn Stutzenstein 266f9b73e9
Merge pull request #5639 from FinnStutzenstein/useCacheInManagementCommands
Use cache in management commands
2020-10-28 08:20:01 +01:00
Finn Stutzenstein d4577ed8aa
Merge pull request #5656 from FinnStutzenstein/SmtpException
Improve reporting of SMTP exception
2020-10-28 08:18:43 +01:00
Finn Stutzenstein 582215042d
Catch some tracebacks 2020-10-28 08:18:20 +01:00
Finn Stutzenstein 1dd86a29be
Improve reporting of SMTP exception 2020-10-27 07:59:37 +01:00
Finn Stutzenstein 961a2da888
Merge pull request #5655 from ManniLoe/fix-saml-bulk
no exclustion of SAML Users on bulk-alter-group
2020-10-27 07:25:37 +01:00
Manfred Löbling 2b5abf72a4 no exclustion of SAML Users on bulk-alter-group 2020-10-25 20:25:43 +01:00
Finn Stutzenstein 7277a1bb01
Merge pull request #5647 from bahuma20/patch-1
saml: commands: create-saml-settings: Fix check on wrong variable
2020-10-20 15:46:50 +02:00
Max Bachhuber b214a69136
Fix check on wrong variable 2020-10-20 14:53:21 +02:00
Finn Stutzenstein b864d67cda
Merge pull request #5645 from gsiv/fix/m4-quote-env
Docker config: Quote env vars read by m4
2020-10-20 12:59:25 +02:00
Gernot Schulz 2305ca9d21 Docker config: Quote env vars read by m4 2020-10-20 12:05:11 +02:00
Finn Stutzenstein ca56b4f8b4
Use cache in management commands
This requires to have a correct setup when using these commands
2020-10-19 15:29:59 +02:00
Finn Stutzenstein d2043f508c
Merge pull request #5633 from FinnStutzenstein/fixMigrations
Fix migrations to not use the cache
2020-10-14 08:05:01 +02:00
Finn Stutzenstein d317e032e7
Fix migrations to not use the cache 2020-10-14 07:48:40 +02:00
Finn Stutzenstein 6c60834f37
Merge pull request #5627 from jwinzer/saml
Correct sp attributes
2020-10-13 07:57:18 +02:00
Jochen Winzer 4fef8ed4dc Correct sp attributes. 2020-10-12 16:03:35 +02:00
Finn Stutzenstein b9f78f501d
Merge pull request #5607 from FinnStutzenstein/fixTbs
Attempt to fix some tracebacks
2020-10-08 10:52:06 +02:00
Finn Stutzenstein f809db0430
Attempt to fix some tracebacks 2020-10-08 10:28:27 +02:00
Finn Stutzenstein 1707c1f4fd
Merge pull request #5545 from FinnStutzenstein/atomicSpeakers
Add a transaction to speaker handling
2020-10-08 09:44:34 +02:00
Finn Stutzenstein 12e6090fa7
Merge pull request #5546 from FinnStutzenstein/checkForCookies
Added missing client part for cookie checking on login
2020-10-08 09:17:02 +02:00
Finn Stutzenstein d739d401c4
Add a transaction to speaker handling
to prevent adding a users multiple times to the waiting queue of a list of speakers
2020-10-08 09:14:43 +02:00
Emanuel Schütze b050a87bb2
Merge pull request #5618 from emanuelschuetze/translations-20201007
Updated translations
2020-10-08 08:04:53 +02:00
Emanuel Schütze e77b2518d5 Updated translations 2020-10-07 23:32:28 +02:00
Emanuel Schütze 21990aa568
Merge pull request #5602 from FinnStutzenstein/addDemoModeToDocker
Add demo mode to all docker setups
2020-10-07 23:30:35 +02:00
Emanuel Schütze 58db337a40
Merge pull request #5617 from tsiegleauq/only-non-stable-slides-in-ap
Show first unstable element in autopilot
2020-10-07 23:30:19 +02:00
Sean 70ea4f3658
Merge pull request #5568 from tsiegleauq/show-amendment-cr-in-lead-motion-slide
Add amendment cr to lead motion slide
2020-10-07 18:17:52 +02:00
Emanuel Schütze 25d83b4419
Merge pull request #5609 from tsiegleauq/show-delegations-in-single-vote-table
Add "represented by" to single votes table
2020-10-07 17:35:20 +02:00
Sean 22a318bde2 Show first unstable element in autopilot
Skips all unstable elements in autopilot
2020-10-07 17:24:44 +02:00
Finn Stutzenstein fd2fd8d73a Add demo mode to all docker setups 2020-10-07 16:50:08 +02:00
Sean Engelhardt 823a87c164 Add amendment cr to lead motion slide
Adds amendment cr to the lead motion slide.
Previously the amendmet cr was only present in the amendment slide but was forgotten in the motion slide
2020-10-07 16:47:16 +02:00
Sean 2162f2b049 Add "represented by" to single votes table
Adds a "represented by" field to the single votes table, to indicate a
vote delegation.
Also fixes an height issue that occured in large single vote tables when
the user was navigating from a list view table to a single vote table
2020-10-07 16:45:31 +02:00
Emanuel Schütze 28be46cf5a
Merge pull request #5603 from tsiegleauq/poll-values-on-projector
Use numbers exclusively in PollNumberPipe
2020-10-07 16:37:59 +02:00
Finn Stutzenstein 435bb59472 Add parsing of decimal fields for projector data
Fixes an issue where the projector would not show special poll values,
such as "majority" or "not counted"
2020-10-07 15:01:58 +02:00
Sean de474e9eae
Merge pull request #5613 from tsiegleauq/invisible-agenda-item-issue
Fix hidden motions in agenda list
2020-10-07 10:29:31 +02:00
Emanuel Schütze 706c1d9e36
Merge pull request #5592 from tsiegleauq/assignment-projector-percentage
Fix assignment polldata discovery in percent pipe
2020-10-06 22:33:34 +02:00
Emanuel Schütze e6fc32b9b4
Merge pull request #5598 from tsiegleauq/export-vote-weight
Export users with username and vote weight
2020-10-06 22:31:03 +02:00
Emanuel Schütze 2bb0134cd8
Merge pull request #5610 from tsiegleauq/mediafile-multi-download-perms
Fix mediafile multi download permission
2020-10-06 22:26:04 +02:00
Emanuel Schütze be3fafd907
Merge pull request #5608 from tsiegleauq/vote-delegation-search-filter
Add search and filter for vote delegation
2020-10-06 22:13:20 +02:00
Sean daaf404756 Fix hidden motions in agenda list
Fixes an error where hidden motions in the agenda list would throw an
error
2020-10-06 16:11:17 +02:00
Sean d22e0bf2f6 Fix mediafile multi download permission
Fixes a permission issue in the mediafile list.
Without manage right, the vertical dot menu was not visible.
Also allows multi selection and selective download without manage
rights.

Fixes a ngrid css error in mediafile list
2020-10-05 17:28:22 +02:00
Sean ed1c3eaa7a Add search and filter for vote delegation
Adds a new filter "Vote delegation" to user list:
Transferred vote right
Received vote reight
neither received not transferred vote right

Adjusts the user list search and the global search to include vote
deligations
2020-10-05 12:39:17 +02:00
Sean 9607f05454
Merge pull request #5507 from tsiegleauq/repair-compodoc
Update and fix compodoc generation
2020-10-05 11:05:20 +02:00
Sean 2ca157bb7c
Merge pull request #5604 from tsiegleauq/select-search-throws-errors
Fix mat select search errors
2020-10-05 10:47:05 +02:00
Sean 25878f297f Fix mat select search errors
Mat select search wants a mat-option now
2020-10-02 15:27:30 +02:00
Finn Stutzenstein 8d2a7f1b12
Merge pull request #5541 from jsangmeister/vote-delegation
Add vote delegation
2020-10-02 14:15:06 +02:00
Manfred a94ce67c22
Update saml-setup.sh (#5580)
* Update saml-setup.sh

create symbolic link fails when container is restarted
2020-10-02 07:40:04 +02:00
Finn Stutzenstein deddd68121
Merge pull request #5591 from ManniLoe/admin_secret
admin.env not loaded by m4
2020-10-02 07:31:20 +02:00
Sean 28bac117be Export users with username and vote weight
Importer expects both username and vote weight,
Thus, exported csv could not easily be reimported
2020-10-01 15:44:20 +02:00
Sean 98a8de3c2d Fix assignment polldata discovery in percent pipe
Fixes an issue where projector data was not delivering enough
information to guess if a poll data belongs to an assignment or a
motion.
This error resultet that the percent base pipe used the percent bases
for motions to calculate for assignments
2020-10-01 13:51:25 +02:00
Manfred Löbling 49a3bcd930 admin.env is not loaded. docker-*.m4 defines ADMIN_SECRET_AVAILABLE if adminsecret.env is present 2020-10-01 13:30:25 +02:00
Sean 8c28b03ffc Vote delegations on client
Add "your vote was delegated" error

Observe operator alterations during vote

Add "canVoteFor" getter

Adjust poll progress bars
2020-10-01 12:18:44 +02:00
Joshua Sangmeister 3ac8569712
Add vote delegation on server side
Add user_has_voted_for_delegations. Add tests

Prevent self delegation

Make delegated_user visible
2020-09-30 14:05:22 +02:00
Finn Stutzenstein c0fb65316c
Merge pull request #5584 from FinnStutzenstein/updateDockerignore
Update .dockerignore to reduce the context size
2020-09-29 17:05:25 +02:00
Finn Stutzenstein 90e13a0f8e
Update .dockerignore to reduce the context size 2020-09-29 16:16:58 +02:00
topelrapha 88994efac3
Fix server stop working because of changed python package (#5582)
* Fix server stop working because of changed python package

* Update AUTHORS
2020-09-29 16:06:28 +02:00
Finn Stutzenstein ed2c298928
Merge pull request #5555 from jsangmeister/fix-non-nominal-votes-assignment-poll
Fix non-nominal voting on assignment polls with type votes
2020-09-28 15:26:09 +02:00
Sean 677a93e2ca Update and fix compodoc generation
Fixes and updates compodoc.
2020-09-28 13:02:56 +02:00
Sean 991c08d57d
Merge pull request #5534 from tsiegleauq/protected-values-cause-user-error
Cleanup previous return statements in void functions
2020-09-28 11:53:57 +02:00
Sean adcf98a69b
Merge pull request #5575 from tsiegleauq/packagelock-to-dockerfile
Add package lock to dockerfile
2020-09-23 15:49:56 +02:00
Sean 6606e46f68 Add package lock to dockerfile
Adds the package-lock file to the clients dockerfile, so npm-ci works
2020-09-23 14:44:33 +02:00
Emanuel Schütze e1d4a4152a
Merge pull request #5537 from tsiegleauq/angular-10
Angular 10
2020-09-23 11:18:25 +02:00
Sean 42dd397fae Update to Angular 10
Update @angular/core @angular/cli
Update material, search-select, cdk, ngrid
Update localstorage
Update rxjs
Update ngx-translate
Update ngx-translate-extract
Update @schematics/angular
Update some deps
Update dev deps
Fix css imports, lint
Downgrad CDK and Material for ngrid
Update docker
Provide pdf worker
Move static file output to django folder
Update readme / used files
Update package-lock.json for npm ci
2020-09-23 10:29:57 +02:00
Emanuel Schütze 3b2fbe8915
Merge pull request #5574 from emanuelschuetze/translation20200923
Updated translations
2020-09-23 09:55:19 +02:00
Emanuel Schütze 69299808b6 Updated translations 2020-09-23 09:36:57 +02:00
Emanuel Schütze cf4573cb54
Merge pull request #5515 from tsiegleauq/lazy-user-mode
Implement cinema mode (autopilot)
2020-09-23 08:37:33 +02:00
Emanuel Schütze b392ac83aa
Merge pull request #5570 from tsiegleauq/vjs-poster-url-config
Add vjs live steam poster url as config
2020-09-23 08:22:37 +02:00
Sean Engelhardt 7f53636b7b Add vjs live steam poster url as config
Adds a new config to show a poster url in the live stream view
2020-09-22 14:53:31 +02:00
Emanuel Schütze 40c2a7fae4
Merge pull request #5569 from tsiegleauq/scss-fix-for-change-recommendation-overview
fix white change-recommendation-overview
2020-09-18 16:44:34 +02:00
Sean Engelhardt bc540180dd fix white change-recommendation-overview
Fixes a bug where a white or blank change-recommendation-overview appeared
2020-09-18 16:29:13 +02:00
Sean ec13ab56e8 Implement cinema mode
Implements a viewer mode containing the most important information and
Heavily refactors ListOfSpeaker and CurrentListOfSpeaker
interaction heavy components on a single view:

Current List of Speakers
Currently Open Polls
Current projector

Permission in migration
2020-09-17 18:38:57 +02:00
Emanuel Schütze acbddd3c53
Merge pull request #5567 from tsiegleauq/pdf-list-cr-amendment-fixes
More reliable text/stack defintion in cr pdf
2020-09-17 18:31:28 +02:00
Sean 6007799f1d More reliable text/stack defintion in cr pdf
Motion pdfs with line numbers, CR/Amendment and bullet points with
multiple changes have a better chance to produce expected results
2020-09-17 18:11:45 +02:00
Emanuel Schütze 2687d1abba
Merge pull request #5564 from tsiegleauq/amendment-projection-default
Add amendment projection defaults
2020-09-17 17:26:29 +02:00
Emanuel Schütze c4a2b02f5d
Merge pull request #5565 from CatoTH/bugfix/show-changed-amendment-in-slides
Fix changed amendment in slides
2020-09-17 16:54:10 +02:00
Emanuel Schütze e8e39b1e89
Merge pull request #5550 from tsiegleauq/vscroll-selector-component
Add virtual scrolling for search value selector
2020-09-17 16:48:12 +02:00
Sean a42205e47f Hide diff-box in projector 2020-09-17 15:40:43 +02:00
Emanuel Schütze 855db8241b
Merge pull request #5542 from tsiegleauq/vscroll-user-import
Vscroll for user import
2020-09-17 15:14:01 +02:00
Emanuel Schütze 688b1b276d
Merge pull request #5563 from tsiegleauq/hide-id-in-amendment-list
Show or hide motion id in amendment list by config
2020-09-17 15:03:42 +02:00
Emanuel Schütze 3d7bfe652c
Merge pull request #5562 from tsiegleauq/amendment-filter-new-workflow
Evaluate default amendment workflow in list
2020-09-17 15:00:57 +02:00
Tobias Hößl 1a0e017f80
Fix changed amendment in slides 2020-09-16 19:52:37 +02:00
Sean df2e26c3ed Show or hide motion id in amendment list by config
Hides the motion id in the list of amendments if the given config was
enabled or disabled
2020-09-16 15:51:03 +02:00
Sean 4712707d6b Add amendment projection defaults
Creates projection defaults for (paragraph based) amendments.
Adds a migration to add amendments as projection defaults.

Will only work for paragraph based amendments, other amendments will
still be considered motions
2020-09-16 15:41:11 +02:00
Emanuel Schütze 909a7539c5
Merge pull request #5561 from tsiegleauq/fix-csv
Adjust the example csv gender fields
2020-09-16 14:21:56 +02:00
Sean 51512fd589 Evaluate default amendment workflow in list
Evaluates the default amendment workflow in the amendment list so users
can se the filters accordingly
2020-09-16 11:55:02 +02:00
Sean 34f23b3d0e Adjust the example csv gender fields
The example fields are now as they would be expected by the OpenSlides
translation
2020-09-15 17:44:47 +02:00
Joshua Sangmeister 8d92353047 Fix non-nominal voting on assignment polls with type votes 2020-09-15 14:48:26 +02:00
Emanuel Schütze 04477d9ebd
Merge pull request #5559 from tsiegleauq/no-final-version-in-amendments
Avoid final version to amendments
2020-09-15 09:31:45 +02:00
Sean bc333a6b51 Avoid final version to amendments
If the motion is a paragraph based amendment the user cannot create a
final version anymore
2020-09-14 16:40:08 +02:00
Sean 594777960b Adjust code style in user repo
Cleans up return statements in void functions
2020-09-14 12:29:21 +02:00
Emanuel Schütze 2759f8ce2b
Merge pull request #5551 from FinnStutzenstein/crInProjector
Always include CRs in motion slide
2020-09-11 11:38:57 +02:00
Emanuel Schütze b555de8510
Merge pull request #5556 from tsiegleauq/accepted-amendment-cr-in-main-motion
Show amendments change reco in main motion
2020-09-11 09:59:51 +02:00
Sean b596bf0ca5 Show amendments change reco in main motion
Fixes a bug which was preventing change recos from amendments to display
in main motions
2020-09-11 00:07:33 +02:00
Finn Stutzenstein d893f3dbe5 Always include CRs in motion slide
Show Amendment CR in Projector

Shows the amendment CR in the projector if the projected slide is in
diff version.
Only shows direct Change recos to amended paragraphs
2020-09-10 19:38:25 +02:00
Emanuel Schütze 9148d97f7a
Merge pull request #5554 from gsiv/dev/templ-no-none
Docker templates: Set empty values instead of None
2020-09-10 14:31:16 +02:00
Gernot Schulz ed9e50a1b4 Docker templates: Set empty values instead of None 2020-09-10 10:12:16 +02:00
Sean eb98289b84 Add virtual scrolling for search value selector
Adds virtual scrolling for the search value selector.
2020-09-08 12:57:27 +02:00
Sean f3fe98436e Vscroll for user import
Allows to import giant sets of users as CSV.
Tested 500k. The client is fine. The python server and the SQL data base really do not like that.
2020-09-07 16:00:11 +02:00
Finn Stutzenstein 5b63809b12
Merge pull request #5547 from gsiv/master
Docker build.sh: Run aux build scripts in subshell
2020-09-07 11:21:41 +02:00
Gernot Schulz a408ee62ee Docker build.sh: Run aux build scripts in subshell 2020-09-07 10:29:18 +02:00
Finn Stutzenstein 7446effe0f
Added missing client part for cookie checking on login 2020-09-07 08:36:02 +02:00
Emanuel Schütze fb27f8ce8a
Merge pull request #5519 from tsiegleauq/fix-jitsi-password-doc
Change jitsi_password to jitsi_room_password
2020-09-04 15:10:09 +02:00
Emanuel Schütze 792f0e5d06
Merge pull request #5543 from tsiegleauq/adjust-live-conf-perms
Adjust live stream permission
2020-09-04 15:01:39 +02:00
Emanuel Schütze 392c32fd92
Merge pull request #5544 from tsiegleauq/firefox-80-state-issues
Fix Firefox indexedDB state change issue
2020-09-04 14:43:47 +02:00
Sean 28878a0b12 Fix Firefox indexedDB state change issue
Firefox ignores the state change of the conf state
after reloading
2020-09-04 14:19:50 +02:00
Sean 059ace3a11 Adjust live stream permission
New rules to show the live stream and jitsi bar
2020-09-04 14:14:36 +02:00
Emanuel Schütze 06974b559e
Merge pull request #5536 from tsiegleauq/pdf-line-number-in-long-lists
PDF line number alignment
2020-09-01 15:47:37 +02:00
Emanuel Schütze 7a1e7c298d
Merge pull request #5530 from FinnStutzenstein/hotfix
Fixed errors in client
2020-09-01 10:06:13 +02:00
Sean ef87f05454 PDF line number alignment
- Aligns line number for long lists
- Better support for changes to the default font
2020-08-31 15:15:00 +02:00
Finn Stutzenstein a329031942
Fixed errors in client 2020-08-31 13:45:59 +02:00
Finn Stutzenstein 0367398cb5
Merge pull request #5535 from FinnStutzenstein/fix-black
Fixing black
2020-08-31 13:41:03 +02:00
Finn Stutzenstein 325c5ea1f4
Fixing black 2020-08-31 13:24:16 +02:00
Emanuel Schütze 9a4f8e1781
Merge pull request #5514 from CatoTH/create-line-number-performance-tweak
Performance tweak for creating line numbers
2020-08-27 09:01:25 +02:00
Emanuel Schütze 38af3d3b8a
Merge pull request #5529 from tsiegleauq/export-dialog-supporter
Add "supporter" to motion export dialog
2020-08-27 08:35:23 +02:00
Emanuel Schütze 4960a8f115
Merge pull request #5516 from tsiegleauq/no-final-may-decide
Hide motion preamble in final state
2020-08-27 08:28:59 +02:00
Sean 0d16b487d5 Add "supporter" to motion export dialog
Allows PDF, CSV and XLSX to export the supporter manually
2020-08-26 15:52:08 +02:00
Sean 756fdc9c66
Merge pull request #5522 from tsiegleauq/clean-submitter-component
Fix selector with in submitter component
2020-08-26 11:05:52 +02:00
Tobias Hößl fcdfad1c2e
Improve performance of line range detection + html2fragment / avoid recalculation of full changeset 2020-08-25 19:53:10 +02:00
Sean 5070069910 Fix selector with in submitter component
Fixes the too small search value selector in the submitter component

fixed the ad-hoc creation
2020-08-24 14:08:58 +02:00
Sean a9c1578ebb Hide motion preamble in final state
If a motion is in final state, the preamble does not get shown in detail
view and pdf
2020-08-24 13:57:09 +02:00
Emanuel Schütze aed17360e6
Merge pull request #5517 from tsiegleauq/motion-projection-controls
Allow motion of non existing change recos
2020-08-21 18:23:08 +02:00
Emanuel Schütze d8f62a05ba
Merge pull request #5520 from tsiegleauq/more-permission-checks
Hide group selector without manage rights
2020-08-21 17:49:04 +02:00
Emanuel Schütze 1b6b70c080
Merge pull request #5521 from tsiegleauq/update-supported-browsers
Update supported browsers
2020-08-21 17:29:35 +02:00
Sean 7bf8e880fd Update supported browsers
Push latest Firefox ESR as latest supported browser
Push opera 70 as latest supported browser
2020-08-21 16:15:50 +02:00
Sean 7af65f790e Hide group selector without manage rights
Fixes a bug where the user group selector was shown even though the user
had not permission to manage the users
2020-08-21 16:09:06 +02:00
Sean 0933bb6abd Change jitsi_password to jitsi_room_password
Fixes false naming in both documentation, template
and docker
2020-08-21 15:46:43 +02:00
Finn Stutzenstein 216e4f00a3
Merge pull request #5440 from FinnStutzenstein/restructure
WIP: Repository restructure
2020-08-21 08:36:27 +02:00
Gernot Schulz 8faa2ad38f
Docker: Add --ask-push option to build.sh 2020-08-21 08:11:16 +02:00
Finn Stutzenstein 9ddf9ddb8c
fixed lost changes 2020-08-21 08:11:16 +02:00
Gernot Schulz 251296f42f
Docker: No longer install Vim in server 2020-08-21 08:11:16 +02:00
Gernot Schulz 9a2d3a3760
Docker: Add SAML configuration
To configure SAML, ENABLE_SAML must be set to True in .env.
Additionally, the following files must be provided in ./secrets/saml/:

  - sp.crt
  - sp.key
  - saml_settings.json

The files will be added as Docker secrets.

Even though saml_settings.json does not contain secret information
per se it is nonetheless added as a secret for simplicity.  Technically,
the file is equally suited to be configured as a "Docker config".

Please note:

  - This patch has not been tested yet.
  - python3-saml's version should probably be pinned.
2020-08-21 08:11:16 +02:00
Gernot Schulz 3cb3ef2974
README: Include production setup info
Now that this repository has become the entry point through which
OpenSlides instances are configured, this patch transfers some relevant
information formerly included in
https://github.com/OpenSlides/openslides-docker-compose/ to this
repository's README file.
2020-08-21 08:11:16 +02:00
Gernot Schulz 2b7e4d3d19
Docker: Add backend variables to .env and templates
This setup chooses to avoid the env_file option available for Docker
Compose files.  Docker has a peculiar way of parsing variables which
makes it, for example, include quotes verbatim.

This is both confusing and incompatible with shells parsing the same
file which is a requirement.  For this reason, the configuration does
not import the complete environment using env_file but assigns variables
explicitly on a need-to-know basis in the YAML file, much like Docker
secrets.

Since the configuration is generated automatically, the burden on users
is the same as with env_file: they only need to edit .env for
customizations.
2020-08-21 08:11:15 +02:00
Gernot Schulz d1640bc98d
Docker: Add .env
This file was copied from
https://github.com/OpenSlides/openslides-docker-compose/
and still needs to be adjusted for the present setup.
2020-08-21 08:11:15 +02:00
Gernot Schulz 1c0724341c
build.sh: More convenient way to push images
Since build.sh now builds more than just two images, individual queries
whether to push each image to a registry may become tedious.  This patch
replaces the queries with a single checklist menu.  After making
a selection, the images get pushed all at once.

In the menu, all images are unchecked by default in order to prevent
accidental uploads.  This, too, may become tedious.  In that case, the
default could be flipped or a new option could be introduced.
2020-08-21 08:11:15 +02:00
Gernot Schulz 418480bff5
build.sh: Make build script more useful
build.sh replaces docker-compose as an image build tool.  Instead, all
OpenSlides services can be built using this script which offers various
important options such as tagging and configurable defaults.

The now-redundant build instructions have been removed from the YAML
templates.

The almost identical server and client build scripts have been made
fully identical.
2020-08-21 08:11:15 +02:00
Gernot Schulz 9c9f268fbf
Docker: Shorten server-db-setup service name 2020-08-21 08:11:15 +02:00
Gernot Schulz f694e9b2c4
Update AUTHORS 2020-08-21 08:11:15 +02:00
Gernot Schulz 774fa4c204
README: Typos, language, formatting 2020-08-21 08:11:15 +02:00
Gernot Schulz 13db5687cb
README: Add Django secret info 2020-08-21 08:11:14 +02:00
Gernot Schulz 63c4bc3ff7
build.sh: Build config from template 2020-08-21 08:11:14 +02:00
Gernot Schulz 4f194a8794
Docker: Add a Docker secret for the Django key
We have decided against including an insecure default key with a mere
warning.  Therefore, unlike the admin and user secrets, the availability
of this secret is a hard requirement.  The instance will not be able to
start before a secret has been generated manually or by a management
tool.
2020-08-21 08:11:14 +02:00
Gernot Schulz d48794ae8a
Docker: Add YAML templates
These templates were copied from
https://github.com/OpenSlides/openslides-docker-compose and adapted for
the new server-db-setup service.
2020-08-21 08:11:14 +02:00
Gernot Schulz 683aed56bb
server: Fix exec args in entrypoint 2020-08-21 08:11:14 +02:00
Finn Stutzenstein 030378b48a
Rebase changes for current master 2020-08-21 08:11:14 +02:00
FinnStutzenstein 2bcab5d098
Repository restructure
- moved all server related things into the folder `server`, so this
configuration is parallel to the client.
- All main "services" are now folders in the root directory
- Added Dockerfiles to each service (currently server and client)
- Added a docker compose configuration to start everything together.
Currently there are heavy dependencies into https://github.com/OpenSlides/openslides-docker-compose
- Resturctured the .gitignore. If someone needs something excluded,
please add it to the right section.
- Added initial build setup with Docker and docker-compose.
- removed setup.py. We won't deliver OpenSlides via pip anymore.
2020-08-21 08:11:13 +02:00
Finn Stutzenstein 2c85bb28f1
Merge pull request #5504 from tsiegleauq/demo-mode
Add demo mode
2020-08-21 08:10:45 +02:00
Sean 2b55388870 Allow motion of non existing change recos
Allows to project any version of the motions, event if they do not yet
exist.
Gives the user more control about the content they want to project.
Currently, if a motion has no logical changed version, they would
always project the original version which does not automatically
update if a changed version exists. This behaviour would require the
user to update their current projection
2020-08-20 13:22:47 +02:00
Sean fbf424e570
Add demo mode
Adds a demo mode in the settings.py to prevent certain obvious
kinds of vandalism on public openslides testing instances
2020-08-17 07:25:53 +02:00
Finn Stutzenstein 2e8e32454e
Merge pull request #5508 from FinnStutzenstein/restartPostgresIdSequence
Fixed errors while creating countdown due to postgres id sequences
2020-08-14 11:15:09 +02:00
Finn Stutzenstein 389a244615
Fixed errors while creating countdown due to postgres id sequences 2020-08-14 10:30:33 +02:00
Emanuel Schütze a46d8ec7ad
Merge pull request #5506 from tsiegleauq/update-to-tinymce-5.4.1
Update TinyMCE and TinyMCE angular
2020-08-13 20:03:02 +02:00
Sean b59c69e086 Update TinyMCE and TinyMCE angular
Updates TinyMCE and TinyMCE angular to the latest version
2020-08-13 12:27:14 +02:00
Emanuel Schütze 2b3766b758
Merge pull request #5502 from tsiegleauq/enter-animation-without-css
Replace meeting room keyframe animation
2020-08-12 18:43:50 +02:00
Sean c0f5c7b548 Replace meeting room keyframe animation
Replaces the meeting room keyframe css animation with anuglar
animations.
The css keyframe animation declaration was
"optimized away" on prod build
2020-08-12 18:18:04 +02:00
Emanuel Schütze c6abbb629e
Merge pull request #5505 from tsiegleauq/multiple-file-download
Download mutliple files as zip
2020-08-12 17:13:41 +02:00
Emanuel Schütze a40657e153
Merge pull request #5495 from FinnStutzenstein/redisConnectionError
Ping redis connection when popped from pool
2020-08-12 17:00:13 +02:00
Sean e75573e139 Download mutliple files as zip
Adds the possitibility to download folders
and multiple files as zip.
2020-08-12 15:24:37 +02:00
Emanuel Schütze 5f5f704057
Merge pull request #5498 from tsiegleauq/jitsi-meet-api-update
Update jitsi-meet external api
2020-08-12 15:05:29 +02:00
Emanuel Schütze b726801747
Merge pull request #5500 from tsiegleauq/crate-users-svc
Add "create user" to more search value selectors
2020-08-12 15:00:55 +02:00
Emanuel Schütze a48592af50
Merge pull request #5497 from tsiegleauq/fix-parent-motion-title-change-error
Cleaner pdf getUnifiedChanges solution
2020-08-12 14:49:33 +02:00
Emanuel Schütze 72a53c5cd0
Merge pull request #5501 from FinnStutzenstein/fixes
Fixed poll required users and motion state extension selector
2020-08-12 14:43:25 +02:00
Finn Stutzenstein d682d0d134
Fixed poll required users and motion state extension selector 2020-08-11 13:21:51 +02:00
Sean bfe72497cd Add "create user" to more search value selectors
Adds the "create new user" function to more search value
selectors.
2020-08-11 12:15:47 +02:00
Finn Stutzenstein c6bc5978e2
Ping redis connection when popped from pool 2020-08-10 16:31:38 +02:00
Sean 65ee468c21 Update jitsi-meet external api
Updates jitsi-meet external api the the current version
Hide "security" and "invite" in jitsi-iFrame
2020-08-10 15:37:14 +02:00
Sean 09a10c7e92 Cleaner pdf getUnifiedChanges solution
Fixes a bug where cercain amendments where causing an
"titleChange of undefined" error
2020-08-10 14:17:43 +02:00
Finn Stutzenstein ccc3e38427
Merge pull request #5494 from manuelgrabowski/patch-1
Fix typo
2020-08-10 10:06:36 +02:00
Manu 8d25f6ae15
Fix typo 2020-08-09 00:21:33 +02:00
Emanuel Schütze 23ae32a758
Merge pull request #5480 from tsiegleauq/amendment-without-parent-motion
Detect if the parent motion was deleted
2020-07-17 16:55:47 +02:00
Emanuel Schütze fbbcd6fa94
Merge pull request #5479 from tsiegleauq/config-amendments-workflow
Add "motions_amendments_workflow" config
2020-07-17 16:51:34 +02:00
Sean 2c17d7b7aa Add "motions_amendments_workflow" config
Adds a config for the default workflow for amendments and maked the
necessary changes to motion detail and amendment wizzard
2020-07-17 16:10:45 +02:00
Sean e268903536 Detect if the parent motion was deleted
Fixes an error where the motion detail page would not open if the parent
motion to an amendment was deleted.
Show a warning if the parent motion to an amendment is missing
2020-07-17 16:08:15 +02:00
Emanuel Schütze 6e2e1ebe7a
Merge pull request #5473 from emanuelschuetze/release3.2
Release 3.2
2020-07-15 16:56:33 +02:00
Emanuel Schütze 30e8f7d87f Updated to version 3.3-master 2020-07-15 16:19:05 +02:00
Emanuel Schütze 9aefb122e6 Release 3.2 2020-07-15 16:16:00 +02:00
Emanuel Schütze 5618c04416 Use twisted[tls] in production.txt to install required packages. 2020-07-15 16:15:39 +02:00
Emanuel Schütze ee344032b7
Merge pull request #5472 from emanuelschuetze/changelog
Updated Changelog for 3.2
2020-07-15 14:15:52 +02:00
Emanuel Schütze 6e80ff5f00 Updated Changelog for 3.2 2020-07-15 12:45:16 +02:00
Emanuel Schütze 47113f14fc
Merge pull request #5470 from tsiegleauq/cannot-click-bg
Fix clicking behind conference bar
2020-07-15 12:43:10 +02:00
Sean 7d912d82de Fix clicking behind conference bar
Fixes an issue with the live stream.
Clicks in the background were falsely cought.
2020-07-14 15:10:01 +02:00
Emanuel Schütze ebf8325ded
Merge pull request #5468 from tsiegleauq/safari-list-views
Fix list views for safari
2020-07-14 14:28:08 +02:00
Emanuel Schütze 03acae26ff
Merge pull request #5465 from tsiegleauq/better-jitsi-icons
Add link to CLOS to meeting room indicator
2020-07-14 14:24:54 +02:00
Emanuel Schütze 271ccdd46a
Merge pull request #5467 from tsiegleauq/pdf-error-free-text-amendment
Fix PDF generation for motion with free amendments
2020-07-14 14:15:47 +02:00
Sean 109fea791d Fix list views for safari
Usually useless hight information fixes rendering issues on safari
browsers
2020-07-14 13:13:49 +02:00
Sean c2bd7c16a9 Fix PDF generation for motion with free amendments
Fixes an issue where motions which had free form amendments were not be
able to render as pdf
2020-07-14 12:34:05 +02:00
Sean 7ded2cd8a1 Add link to CLOS to meeting room indicator
Links the "cannot enter meeting room"-indicator to the CLOS.
Changes the minimize icon
2020-07-14 10:53:02 +02:00
Emanuel Schütze 85a22ed99c
Merge pull request #5329 from tsiegleauq/changelog-3.2
Changelog for 3.2
2020-07-13 14:59:57 +02:00
Sean e2597002e2 Changelog for 3.2 2020-07-12 22:13:20 +02:00
Emanuel Schütze 01ce1409d3
Merge pull request #5463 from emanuelschuetze/translations-20200712
Updated translations (DE, CZ, RU)
2020-07-12 18:04:17 +02:00
Emanuel Schütze 74e3ea119e Updated translations (DE, CZ, RU) 2020-07-12 17:41:06 +02:00
Emanuel Schütze 9e55cb1480
Merge pull request #5462 from tsiegleauq/more-jitsi-improvements
Improve Jitsi UI
2020-07-12 17:22:32 +02:00
Emanuel Schütze 20175a1a6b
Merge pull request #5461 from tsiegleauq/list-of-speaker-slide-fix
Add prefix to topic list of speaker slide
2020-07-12 17:16:53 +02:00
Sean 719d1d1cf1 Improve Jitsi UI
Some slight jitsi/stream look and feel improvements

The "closed door" now has a tooltip "add yourseld to los to enter
conference"
Use "keyboard_hide" to indicate minimize/hide
Hide the potentially obsolete quick controls when the full jitsi dialog
is open
Use the "voice_chat" icon to indicate showing the full jitsi dialog
Remove "hangup" "raise hand" and "help" from the jitsi iframe
Add hangup and leave to the jitsi dialog in replacement of hangup in the
iFrame
The "enter live conference" icon is now highlighted by a fade animation
2020-07-10 16:46:29 +02:00
Sean 2835e746e8 Add prefix to topic list of speaker slide
The topics prefix (number) was missing in the list of speakers slide
2020-07-09 12:56:58 +02:00
Emanuel Schütze a7703a5557
Merge pull request #5454 from tsiegleauq/whole-agenda-slide-misses-top
Add top to agenda projection
2020-07-09 09:25:36 +02:00
Emanuel Schütze da4092768e
Merge pull request #5453 from tsiegleauq/use-a-tag-for-external-jitsi
Use a-tag for external link
2020-07-09 08:31:06 +02:00
Sean 32775b0a2a Add top to agenda projection
Show the agenda title prefix in the whole agenda slide projection
2020-07-09 08:16:57 +02:00
Sean 011c23093f Use a-tag for external link
Allows to left-click, right-click and middle-click the "open external"
button. Left clicking will disconnect from the current jitsi connection
in OpenSlides. Useful for tests and power using, such as multiple jitsi
connections or easier copying the link.
2020-07-09 08:11:39 +02:00
Emanuel Schütze 3063a9e9fc
Merge pull request #5452 from tsiegleauq/tiny-jitsi
Improve Jitsi on mobile
2020-07-09 08:10:23 +02:00
Sean 656fcccee1 Improve Jitsi on mobile
Improves Jitsi and various other stuff for small to smallest devices.
The mobile main action button is a little higher and aligns with the
jitsi bar.
Some mat-cards have extra space to be more jitsi-bar-friendly.
Hides the list filter option on phones
2020-07-08 13:43:54 +02:00
Emanuel Schütze e35b658731
Merge pull request #5456 from emanuelschuetze/fixTypo
Remove debug output
2020-07-08 11:58:46 +02:00
Emanuel Schütze d76d74e225 Remove debug output 2020-07-08 11:43:39 +02:00
Finn Stutzenstein 9eeb287425
Merge pull request #5455 from tsiegleauq/another-travis-fix
Pin isort, repair tests
2020-07-08 07:16:27 +02:00
Sean 5666749e62 Pin isort, repair tests
Pins isort for testing and execute all tests
2020-07-07 16:14:08 +02:00
Finn Stutzenstein eeb97c44fd
Merge pull request #5450 from ApolloLV/patch-1
Update nodeJS to 12.x in Dockerfile
2020-07-03 13:57:55 +02:00
Emanuel Schütze fa1347f611
Merge pull request #5451 from emanuelschuetze/default-workflow
Fixed config value for default workflow
2020-07-03 09:14:14 +02:00
Emanuel Schütze 278b33c2d7 Fixed config value for default workflow 2020-07-02 23:47:26 +02:00
ApolloLV 1cb8ef2d14
Update nodeJS to 12.x in Dockerfile
The Angular CLI requires a minimum Node.js version of either v10.13 or v12.0
2020-07-02 22:25:49 +02:00
Emanuel Schütze ba3c5e07f7
Merge pull request #5449 from emanuelschuetze/translations-20200702
Updated translations
2020-07-02 22:05:25 +02:00
Emanuel Schütze 55f1d02fcc
Merge pull request #5429 from tsiegleauq/main-page-cleanup
Cleanup main routing page and ngrid lists
2020-07-02 22:04:53 +02:00
Emanuel Schütze 378d091dbd
Merge pull request #5448 from tsiegleauq/fix-duration-for-ff-headless
Fix duration service for non chrome browsers
2020-07-02 21:46:38 +02:00
Emanuel Schütze cb8f219163 Updated translations 2020-07-02 21:44:22 +02:00
Emanuel Schütze 66757b04ae
Merge pull request #5446 from tsiegleauq/login-ignore-warning
Add "login anyway" button
2020-07-02 21:38:35 +02:00
Sean 346413fbb0 Fix duration service for non chrome browsers
-0, 0 and other uses of negative values in the duration service hat a
change of producing undesired results.
Added tests and fixed the function
2020-07-02 15:52:31 +02:00
Finn Stutzenstein cb190331f3
Merge pull request #5447 from tsiegleauq/allow-negative-duration
Allow negative duration
2020-07-02 11:43:25 +02:00
Sean 23ee6a2951 Allow negative duration
Allow negative values for durationToString.
The use case is fairly constructed, but now logically correct
2020-07-01 17:44:33 +02:00
Sean f59ce9ef3b
Merge pull request #5444 from FinnStutzenstein/fixDependencyFromCoreToSite
Fixed import of the clos into the operator
2020-07-01 13:26:26 +02:00
Sean f5654f3a8c Add "login anyway" button
Skip browser warning easier.
2020-07-01 12:22:37 +02:00
FinnStutzenstein 4a96aa31c1
Fixed import of the clos into the operator 2020-06-29 09:25:50 +02:00
Emanuel Schütze fab51091b1
Merge pull request #5443 from tsiegleauq/pdf-left-footer
Render left pdf footer image and page number
2020-06-27 12:59:55 +02:00
Emanuel Schütze c1d63b320d
Merge pull request #5442 from tsiegleauq/jitsi-iframe-control-adjustment
Always show the toolbar in jitsi iframe
2020-06-27 12:55:37 +02:00
Sean 988ee0fe93 Render left pdf footer image and page number
Using the web worker, the PDF page number in combination with a left
footer image could not be rendered together.
2020-06-26 14:31:22 +02:00
Sean 3d252060c9 Always show the toolbar in jitsi iframe
Try to prevent that the toolbar hides itself
2020-06-26 10:35:26 +02:00
Emanuel Schütze 6898458695
Merge pull request #5437 from tsiegleauq/another-fake-dialog
Replace jitsi mat dialog
2020-06-25 17:50:39 +02:00
Sean c2a1b62c8b Replace jitsi mat dialog
Replace Jitsis Mat Dialog with an div container
Fixes an issue where observables in jitsi would register multiple times
Clear the IndexedDB on logout
2020-06-25 16:34:36 +02:00
Sean bb10c25974
Merge pull request #5438 from tsiegleauq/clean-deps
Updates some npm deps, fixes travis
2020-06-23 12:28:27 +02:00
Sean fde745530e Updates some npm deps, fixes travis
Another thing for travis
2020-06-23 12:12:01 +02:00
Emanuel Schütze 9a47cff7fa
Merge pull request #5435 from emanuelschuetze/translations20200619
Updated translations
2020-06-19 17:29:04 +02:00
Sean 22a374a150 Cleanup main routing page and ngrid lists
Sets the main container to absolute,
makes it easier to detect the pages height
ngrid lists can dynamically detect the page height using flexbox,
remove external class injection from list-views,
add "spacer-bottom-60" class for the jitsi-container,
add it everywhere where the jitsi container could hide the content.
2020-06-19 16:47:40 +02:00
Emanuel Schütze f70953f454
Merge pull request #5434 from tsiegleauq/fix-poll-progress
Fix poll progress
2020-06-19 16:37:56 +02:00
Emanuel Schütze 435f555559 Updated translations 2020-06-19 16:29:52 +02:00
Sean 9cf602f0c1 Fix poll progress
Fixes a regression in the poll progress.
Poll progress war no longer working for assignments
2020-06-19 13:26:23 +02:00
Emanuel Schütze 2fd4e70b0c
Merge pull request #5433 from tsiegleauq/import-doubled-topics
Allow to import the same topic multiple times
2020-06-18 23:05:03 +02:00
Emanuel Schütze 81b021ab47
Merge pull request #5432 from FinnStutzenstein/fixSaml
Fix SAML default group ids setting
2020-06-18 17:56:50 +02:00
FinnStutzenstein fd371b87e4
Fix SAML default group ids setting 2020-06-18 15:58:19 +02:00
Emanuel Schütze e20c93d445
Merge pull request #5430 from tsiegleauq/stream-jitsi-perms
separate stream permission from jitsi
2020-06-18 15:57:15 +02:00
Sean 55f65576f0 Allow to import the same topic multiple times
Remove checking for duplicated topic names.
Allows to import topics more than once, usefull if you have to import an
agenda over and over again.
2020-06-18 15:51:51 +02:00
Emanuel Schütze d558c293b2
Merge pull request #5424 from jsangmeister/delegate-view-fixes
Fix some delegate view bugs
2020-06-18 15:51:08 +02:00
Joshua Sangmeister 44f1d1e819 Fix some delegate view bugs 2020-06-18 15:26:50 +02:00
Sean 677595fe5b
Merge pull request #5431 from tsiegleauq/dark-theme-improvements
Improve dark themes
2020-06-18 13:35:17 +02:00
Sean 912a528f8a Improve dark themes
Add better contrast to dark themes on various instances
Add yellow as accent color for red themes
2020-06-18 13:11:45 +02:00
Sean 9feaa59ebb Seperate stream permission from jitsi
Allows jitsi conferences without stream permission,
allowing detailed speration from users who can jitsi streams and see
stream
2020-06-18 12:06:59 +02:00
Emanuel Schütze b712af2d6d
Merge pull request #5427 from tsiegleauq/amendments-without-id
Use title for amendments if an id does not exist
2020-06-17 15:56:37 +02:00
Emanuel Schütze 81c2df3458
Merge pull request #5428 from FinnStutzenstein/fixAsgirefVersion
Use newer asgiref release than the broken 3.2.8 one
2020-06-17 15:51:02 +02:00
FinnStutzenstein 6a59e678a9
Use newer asgiref release than the broken 3.2.8 one 2020-06-17 15:38:07 +02:00
Sean 00e644292d Use title for amendments if an id does not exist
Also some slight head-bar cleanups
2020-06-17 11:08:22 +02:00
Emanuel Schütze b43151fd59
Merge pull request #5425 from tsiegleauq/motion-csv-impexp
Export motion id latest
2020-06-16 22:17:04 +02:00
Sean fbbc4389fb Export motion id latest
Put the motion id to the end of all export used.
Solves the issue that users cannot re-import CSVs if they export them
with sequential numbers
2020-06-16 21:35:24 +02:00
Emanuel Schütze d53e85b853
Merge pull request #5423 from emanuelschuetze/rename-permission
Rename and check "can see extra data" permission.
2020-06-16 21:15:56 +02:00
Emanuel Schütze 68c77fe52c Rename and check "can see extra data" permission. 2020-06-16 21:00:37 +02:00
Sean bc1373b696
Merge pull request #5416 from tsiegleauq/solarized-dark-theme
Enhance dark theme
2020-06-16 13:25:43 +02:00
Sean b9fbf4209b Cleanup theming related code
Enhance color usage, add more default themes, add solarized theme
2020-06-16 12:56:08 +02:00
Sean ec2ec08333
Merge pull request #5422 from tsiegleauq/some-python-updates
Fix python 3.6 and 3.7 tests
2020-06-16 12:50:27 +02:00
Sean 958f0fb786 Fix python 3.6 and 3.7 tests
Hotfix for python tests
2020-06-16 12:36:25 +02:00
Emanuel Schütze ac4cb39105
Merge pull request #5419 from tsiegleauq/agenda-slide-distance
Add more distance for topics in agenda slide
2020-06-15 15:12:52 +02:00
Emanuel Schütze b5bc855dfe
Merge pull request #5417 from tsiegleauq/agenda-numbered-topics-pdf
Prevent double "TOP" in agenda PDF
2020-06-15 15:10:22 +02:00
Sean 1f876ec6dd Prevent double "TOP" in agenda pdf
Also: changes the definition of "getTitle" and "getAgendaTitle" to be more
consistent and easier to guess. getTitle will simply return the title, while getAgendaTitle
returns the title with the agenda-prefix
2020-06-12 16:18:57 +02:00
Emanuel Schütze c1605929e9
Merge pull request #5410 from tsiegleauq/better-login-page
Add better login page
2020-06-12 15:13:41 +02:00
Emanuel Schütze 2ea95937d7
Merge pull request #5420 from tsiegleauq/user-set-present-link-dup
Remove presence link in user multi select mode
2020-06-12 15:09:50 +02:00
Emanuel Schütze a80915397d
Merge pull request #5418 from FinnStutzenstein/fixSamlUserCreation
Fix Saml II and saml default groups
2020-06-12 14:55:51 +02:00
Sean f06f2dee9f Remove presence link in user multi select mode
Removes the link to "users/presence" while in the user lists multi
selection view. The link was duplicated to be present in both multi
select and single selct view
2020-06-12 14:51:05 +02:00
Sean 33ba8c4628 Add more distance for topics in agenda slide
Makes the projection of the agenda looks cleaner and more relaxed
2020-06-12 14:43:40 +02:00
FinnStutzenstein dc7dfc1936
Fix Saml II and saml default groups 2020-06-12 14:37:43 +02:00
Sean 7d3280707d
Merge pull request #5414 from tsiegleauq/support-chrome-80
Lower chrome/chromium support version to version
2020-06-12 11:08:26 +02:00
Sean 13cbece9d9 Lower chrome/chromium support version to version
Lowers the supported version for chrome and chromium based browsers to
80. Keep in mind that you can always skip the checks!
2020-06-12 10:50:36 +02:00
Sean 5ed9c88ae4 Add better login page
Themes now cover the login page
more responsive on both large, small and smalest screens
the footer behaves like a footer
2020-06-12 10:23:10 +02:00
Finn Stutzenstein 5239e40858
Merge pull request #5412 from FinnStutzenstein/fixMigrationPath
Fix migration path for non-existing fonts
2020-06-12 08:13:11 +02:00
FinnStutzenstein 081f13e2ff
Fix migration path for non-existing fonts 2020-06-12 07:26:29 +02:00
Emanuel Schütze 438b3558bf
Merge pull request #5411 from emanuelschuetze/translations-20200511
Updated translations.
2020-06-11 17:46:13 +02:00
Emanuel Schütze ff4324117e Updated translations. 2020-06-11 17:23:42 +02:00
Emanuel Schütze f590994875
Merge pull request #5403 from tsiegleauq/blacklist-browsers
Catch unsupported browsers
2020-06-11 15:42:45 +02:00
Emanuel Schütze 2cdb3f4ef3
Merge pull request #5407 from tsiegleauq/assignment-pdf-reduce
Fix assignment pdf results
2020-06-11 15:10:36 +02:00
Emanuel Schütze e3c1d5432b
Merge pull request #5408 from tsiegleauq/monospace-font-fix
Add roboto-condensed-bold as default
2020-06-11 15:04:28 +02:00
Sean 9387a3f394 Catch unsupported browsers
unspoorted browsers trying to access the login mask will be forwarded to
an info page.
The info page shows that the browser is not suppoted and hints the smallest
supported version of their current browser.
As it works best and might prevent some support calls, I added an hint
for chrome as the favored browser by OpenSlides (debateable)

To update/downgrade the supported versions, simply edit the enum in the
service.

If we cannot detect the browser, we assume it was supported.
2020-06-11 14:39:14 +02:00
Sean 1853028cf0 Add roboto-condensed-bold as default
Fixes a severe regression. New databases could not generate any PDF or use
the countdown.
2020-06-11 14:30:01 +02:00
Sean 56b47214bc Fix assignment pdf results
Filters out results unfitting to the current election method.
2020-06-11 14:12:10 +02:00
Emanuel Schütze 43b13e314e
Merge pull request #5376 from tsiegleauq/integrate-streams
Integrate streams
2020-06-11 13:53:04 +02:00
Sean 0d9738b72d Integrate streams
Integrate live streaming inside the jitsi/rtc components.
Live streaming works without jitsi, but is using the same components for
a fluid integration.
A streaming URL can be set in the settings page.
Users EITHER consume the live stream OR are presend in a jitsi live
conference.

To consume both the live stream and the jitsi conference, users may use
a dedicated jitsi tab in their session.

The jitsi users can be restricted to only allow thouse with the right
the manage speakers or being present on the "current list of speakers",
automatically simulating a virtual plenum
2020-06-11 11:20:00 +02:00
Emanuel Schütze 47795b57d1
Merge pull request #5405 from FinnStutzenstein/fixSamlUserCreation
Fix the creation of saml users
2020-06-10 15:32:03 +02:00
FinnStutzenstein 7d455b34f5
Fix the creation of saml users
When created, they are put into the cache.
Also allows bulk-delete for saml users.
2020-06-10 15:06:13 +02:00
Emanuel Schütze fbb0be6fb4
Merge pull request #5404 from emanuelschuetze/whitelist
Updated server whitelist of allowed styles.
2020-06-09 23:15:17 +02:00
Emanuel Schütze acf499f6e1
Merge pull request #5378 from jsangmeister/monospace-countdown
Replaces the countdown font with a monospaced one
2020-06-09 23:15:06 +02:00
Emanuel Schütze 79e3780a26 Updated server whitelist of allowed styles.
added 'vertical-align'
2020-06-09 22:59:41 +02:00
Joshua Sangmeister e653021eff Add a configurable monospace font for the countdown
use Roboto Condensed bold by default
2020-06-09 22:54:31 +02:00
Emanuel Schütze aeb893a8d9
Merge pull request #5399 from tsiegleauq/normal-amendments-in-list
Fix error for freely editable amendments
2020-06-09 21:48:13 +02:00
Emanuel Schütze 82efbe76bd
Merge pull request #5356 from flowluap/patch-2
added proxy pass to nginx configuration file
2020-06-09 21:22:08 +02:00
Emanuel Schütze ff9125fb9f
Merge pull request #5401 from tsiegleauq/assignment-poll-chart-abstain
Hide abstain bar in assignment polls chart
2020-06-09 21:20:54 +02:00
Sean d4f211e344 Fix error for freely editable amendments
Recent changes to amendment had various bugs.
This one regarded the amendment list, where amendments without amendmend
paragraphs returned several errors.
2020-06-09 21:16:04 +02:00
Emanuel Schütze 4673c741e9
Merge pull request #5402 from jsangmeister/fix-tiny-mce
Fix change detection for Tiny MCE
2020-06-09 20:56:29 +02:00
Emanuel Schütze e1345cb808
Merge pull request #5398 from CatoTH/bugfix-non-paragraph-based-amendments
Bugfix non paragraph based amendments
2020-06-09 20:53:03 +02:00
Joshua Sangmeister bf35c55956 Fix change detection for Tiny MCE 2020-06-09 13:14:05 +02:00
Sean 6efdc9a3dd Hide abstain bar in assignment polls chart
If the percent base does not include "abstain", the grey abstain bar
will not be shown in the assignment poll chart
2020-06-09 12:47:38 +02:00
Tobias Hößl cadef6d42e
Don't show amendment CR-diff-view if no CRs exist 2020-06-07 15:02:35 +02:00
Tobias Hößl bc3b8be78d
Increase recalculation performance by additional caching 2020-06-07 11:44:07 +02:00
Tobias Hößl 18bc495bd8
Bugfix: errors with non-paragraph-based amendments 2020-06-07 10:54:57 +02:00
Emanuel Schütze 8451cd2d88
Merge pull request #5395 from emanuelschuetze/translation-20200604
Updated translations
2020-06-04 17:06:55 +02:00
Emanuel Schütze 5072e66a7e Updated translations 2020-06-04 17:06:08 +02:00
Emanuel Schütze 3109337004
Merge pull request #5394 from tsiegleauq/jitsi-iframe-dialog
Show Jitsi iFrame in Dialog
2020-06-04 16:37:01 +02:00
Sean 3ca4714812 Show Jitsi iFrame in Dialog
Replace the external link button with a (real) Dialog containing the
jitsi iFrame.
The dialog can be hidden but not entirly closed.
Replace all dialogService.closeAll() functions by closing the specific
dialog rather than all of them.
2020-06-04 16:29:35 +02:00
FinnStutzenstein 429473dcf9
Merge branch 'pollprogressCd' 2020-06-04 15:16:20 +02:00
FinnStutzenstein c186a575f6
Fixed incomplete autoupdates
A conceptional issue in `get_data_since` leads to incomplete
autoupdates. The behaviour was long time in the code, but only with a
lot of autoupdates (high concurrency) and the autoupdate delay I noticed
the bug during testing. I'm sure, that this issue might have caused
incomplete autoupdates (which the user may experience as "lost
autoupdates") in previous productive instances. Instead of quering a
range (from_change_id to to_change_id) one now can only get data from a
change id up to the max change id in the element cache. The max change
id gets now returned by `get_data_since`.

I also added a get_all_data with the capability of returning the
max_change_id at this point of time.

As a usability-"fix" (more like a fix the result of a bug, not the bug
itself) a refresh button for a poll was added, that issues an autoupdate
for the poll and all options.
2020-06-04 15:16:05 +02:00
Emanuel Schütze c4f482b70c
Merge pull request #5391 from jsangmeister/update-poll-slide
Fixes motion poll slide icon size & rounding
2020-06-03 19:16:29 +02:00
Emanuel Schütze 0275df6ab2
Merge pull request #5392 from tsiegleauq/vote-await-server-answer
Wait for server while voting
2020-06-03 17:35:19 +02:00
Sean dced8fbcc7 Wait for server while voting
Blocks voting state changes and prevents the user from sending multiple
vote values while the server is not responding during voting
2020-06-03 14:53:31 +02:00
Joshua Sangmeister f4907e6604 Fixes motion poll slide icon size & rounding 2020-06-02 22:41:23 +02:00
Emanuel Schütze d7408b40f9
Merge pull request #5390 from jsangmeister/hide-amendments-when-deactivated
Hides amendments entry in menu if deactivated
2020-06-02 22:26:14 +02:00
Finn Stutzenstein e215a23b80
Merge pull request #5389 from jsangmeister/reverse-motion-relations
Adds reverse relations for motions and blocks
2020-06-02 16:26:07 +02:00
Joshua Sangmeister a31fa7dda6 Adds reverse relations for motions and blocks 2020-06-02 14:44:56 +02:00
Emanuel Schütze 7665634d42
Merge pull request #5375 from FinnStutzenstein/autoupdatePerformance
Autoupdate performance
2020-05-29 17:31:32 +02:00
Finn Stutzenstein 9c7b9b0920
Merge pull request #5387 from FinnStutzenstein/hugeautoupdatesInRedis
Inserting changed and deleted elements into redis in batches (fixes #5386)
2020-05-29 15:48:21 +02:00
FinnStutzenstein 0eee839736
Small improvements and first attempt to make to poll progress responsive
to massive autoupdates. The "optimization" didn't help, so this has to
be continued in another PR.
2020-05-29 15:46:19 +02:00
Joshua Sangmeister a84bfccd07 Hides amendments entry in menu if deactivated 2020-05-28 15:26:56 +02:00
FinnStutzenstein 600b9c148b
Inserting changed and deleted elements into redis in batches (fixes #5386) 2020-05-28 14:00:57 +02:00
FinnStutzenstein d8b21c5fb5
(WIP) Ordered and delayed autoupdates:
- Extracted autoupdate code from consumers
- collect autoupdates until a AUTOUPDATE_DELAY is reached (since the first autoupdate)
- Added the AUTOUPDATE_DELAY parameter in the settings.py
- moved some autoupdate code to utils/autoupdate
- moved core/websocket to utils/websocket_client_messages
- add the autoupdate in the response (there are some todos left)
- do not send autoupdates on error (4xx, 5xx)
- the client blindly injects the autoupdate in the response
- removed the unused autoupdate on/off feature
- the clients sends now the maxChangeId (instead of maxChangeId+1) on connection
- the server accepts this.
2020-05-27 16:05:27 +02:00
Emanuel Schütze dcf5d5316c
Merge pull request #5384 from FinnStutzenstein/logErrorsOnServer
Log APIExceptions on the server
2020-05-25 14:52:34 +02:00
FinnStutzenstein fba043fedf
Log APIExceptions on the server 2020-05-25 09:16:37 +02:00
Emanuel Schütze 762d1f9912
Merge pull request #5382 from topelrapha/master
Fix tinymce version to 5.2.2
2020-05-23 10:37:34 +02:00
Raphael Topel 60621bf4d0 Fix tinymce version to 5.2.2 2020-05-22 22:19:45 +02:00
FinnStutzenstein bf88cea200
Rewrite projector code to be cache friendly
This speeds up the requests/seconds by a factor of 100
2020-05-22 15:23:54 +02:00
FinnStutzenstein 23842fd496
Synchronize autoupdate code in the client
If autoupdates are too fast, the first one may not be fully executed. Especially when the maxChangeId is not yet updated, the second Autoupdate will trigger a refresh, because for the client it "lay in the future". This can be prevented by synchronizing the autoupdate-handling code with a mutex.
2020-05-22 15:23:53 +02:00
Sean 4ac7b1eb4b
Merge pull request #5380 from FinnStutzenstein/closService
Fix the CLOS service to trigger on CLOS updates
2020-05-20 18:29:11 +02:00
FinnStutzenstein 17049cc0f3 Fix the CLOS service to trigger on CLOS updates
additionally fixed some naming issues
2020-05-20 18:09:50 +02:00
Emanuel Schütze fd026e165f
Merge pull request #5377 from jsangmeister/migrate-deprecated-slides
Adds migration to remove deprecated slides
2020-05-19 14:35:07 +02:00
Joshua Sangmeister e52697ad7e Adds migration to remove deprecated slides 2020-05-19 12:55:47 +02:00
Emanuel Schütze 0c93c44f0d
Merge pull request #5374 from emanuelschuetze/translations-20200514
Updated translations
2020-05-14 20:09:36 +02:00
Emanuel Schütze 4b95398ac1
Merge pull request #5371 from jsangmeister/jitsi-settings
Added jitsi settings to template and readme
2020-05-14 19:59:28 +02:00
Emanuel Schütze 37c3ac5aff Updated translations 2020-05-14 19:52:18 +02:00
Joshua Sangmeister 3f03f27cdb added jitsi settings to template and readme 2020-05-14 19:41:35 +02:00
Emanuel Schütze f694e2355d
Merge pull request #5373 from tsiegleauq/assignment-poll-card-with-table
Show assignment poll result table instad of chart
2020-05-14 19:36:43 +02:00
Sean 3820e09b89 Show assignment poll result table instad of chart
Replaces the assignment result chart with the result table from the
detail view and projector
2020-05-14 16:40:19 +02:00
Emanuel Schütze 1ca3196a75
Merge pull request #5370 from tsiegleauq/tags-for-agenda
Add tags for agenda items
2020-05-14 15:23:55 +02:00
Emanuel Schütze ee6076f168
Merge pull request #5372 from tsiegleauq/unset-gender
Allow unset gender in user form
2020-05-14 15:18:00 +02:00
Sean b6bb1fe767 Add tags for agenda items
Adds tags for agnda items, adds tag filter in agenda list view, server
changes, client relations, adjust agenda csv exporter
2020-05-14 15:07:59 +02:00
Sean 7609a0c3db Allow unset gender in user form
Fixes a bug that prevented the form from unsetting the gender value
2020-05-14 14:53:44 +02:00
Emanuel Schütze b090e46b66
Merge pull request #5359 from tsiegleauq/clean-projector-permissions
Add permission as lookup object
2020-05-14 14:48:37 +02:00
Emanuel Schütze ca039860f7
Merge pull request #5365 from jsangmeister/default-voting-type
Added config for default poll type
2020-05-14 13:02:51 +02:00
Sean fca4154bb5 Add permission as lookup object
Adds a lookup object for a more solid approach to handling permissions.
Permissions are now an actual type rather than just a string.
2020-05-14 10:12:22 +02:00
Emanuel Schütze 621d0f4e1a
Merge pull request #5366 from tsiegleauq/motion-not-switching-state
Change motion cr mode view behavior
2020-05-14 08:25:01 +02:00
Emanuel Schütze d1b6ed8d29
Merge pull request #5367 from tsiegleauq/hide-submitter-in-projector
Hide the submitter-box in motion slide if empty
2020-05-14 08:22:28 +02:00
Emanuel Schütze 8058a4d695
Merge pull request #5369 from FinnStutzenstein/fixProjectorSubtitle
Fix projector subtitles for items without agenda items
2020-05-14 08:20:55 +02:00
Sean 853bc31e21 Change motion cr mode view behavior
if the default cr mode is 'original' nothing really happens
if the default cr mode is 'changed' you will stay in original view after
creating cr's. That's due to the autoupdate limitation. Changing this
would mean that you cannot change the view anymore
if the default cd mode is 'diff' you will switch to diff view after
creating a cr. It seems that the diff view has an automatic fallback to
the original view if no cr exists, perhaps Tobias knows more about that.
If the default cr mode if 'final' you will try to change to
mod-final-version if it exists. If there is no change-reco, you will
fall back to original version.
2020-05-14 07:42:25 +02:00
Joshua Sangmeister fa63ef0307 added config for default poll type 2020-05-14 07:38:14 +02:00
Sean fef3cf41bb Hide the submitter-box in motion slide if empty
Hides the "submitter" text and corresponding boxes if they are empty
2020-05-14 07:37:18 +02:00
FinnStutzenstein 34d85c996c Fix projector subtitles for items without agenda items 2020-05-14 07:32:44 +02:00
Emanuel Schütze b7b27d2e88
Merge pull request #5368 from jsangmeister/fix-flake
Fixed formatting after flake update
2020-05-14 07:31:15 +02:00
Joshua Sangmeister b0bf4990f8 fixed formatting after flake update 2020-05-13 16:16:03 +02:00
Emanuel Schütze 0ee70b7434
Merge pull request #5363 from emanuelschuetze/translation-20200511
Updated translations
2020-05-11 15:45:29 +02:00
Emanuel Schütze 9938a68865 Updated translations 2020-05-11 15:33:58 +02:00
Sean 3e19840b08
Merge pull request #5352 from tsiegleauq/speaker-list-change-detection
Manual cd for list of speakers
2020-05-11 11:19:55 +02:00
Sean 7a31cff612
Merge pull request #5347 from GabrielInTheWorld/statistics
Create statistics of closed list of speakers
2020-05-11 11:07:53 +02:00
GabrielMeyer e7de593b54 Create statistics of closed list of speakers 2020-05-11 07:56:50 +02:00
Sean 602d1c8e7b Manual cd for lost of speakers
Adds better cd for list of speakers
2020-05-08 12:20:15 +02:00
Emanuel Schütze c5dd2ea261
Merge pull request #5348 from CatoTH/toggle-show-all-amendments
Toggle to show all amendments in diff view
2020-05-08 08:50:47 +02:00
Paul Wolf 8796eeeb62
added proxy pass to nginx configuration file
prevent request hostname being localhost:8000 | passthrough server hostname from nginx
2020-05-07 07:42:48 +02:00
Tobias Hößl 25839ea709
Toggle to show all amendments in diff view 2020-05-06 20:28:19 +02:00
Emanuel Schütze ea830f53b0
Merge pull request #5355 from FinnStutzenstein/SamlInfitySpinnerFix
fixed endless spinner on SAML login
2020-05-06 19:20:28 +02:00
FinnStutzenstein c643a233ae
fixed endless spinner on SAML login 2020-05-06 18:23:38 +02:00
Emanuel Schütze 5aa895bda2
Merge pull request #5354 from FinnStutzenstein/fixUnnenessaryBrokenLock
Fix unnecessary and unfunctional lock for production usage with gunicorn
2020-05-06 17:29:28 +02:00
FinnStutzenstein 2910701422
Fix unnecessary and unfunctional lock for production usage with gunicorn 2020-05-06 16:57:50 +02:00
Sean 1e2395c1e6
Merge pull request #5351 from tsiegleauq/ios-touch-callout
Fix poll results under iOS
2020-05-06 15:07:34 +02:00
Sean fede11b59f Fix poll results under iOS
Fixes an issue on iOS devices regarding click-bindungs in structural
loops. iOS devices did not respond to touch inputs in the given view
2020-05-06 14:32:46 +02:00
Emanuel Schütze 77cf3e2785
Merge pull request #5321 from tsiegleauq/projector-indicator-on-agenda
Projector indicator list view tables
2020-05-05 17:50:27 +02:00
Sean 4e624384e7 Projector indicator list view tables
Shows a projector indicator in the list view tables.
Width is 24px, just as small as mat-icon needs.
Works in every list view that allows projector buttons (which should
cover everything where an indicator could be demanded)
2020-05-05 16:08:32 +02:00
Emanuel Schütze f9cd3ebd89
Merge pull request #5304 from CatoTH/change-recommendations-for-amendments
Change recommendations for amendments
2020-05-05 15:46:00 +02:00
Emanuel Schütze 6a6e90067a
Merge pull request #5349 from tsiegleauq/tinymce-link-settings
Set tinymce urls to absolute
2020-05-05 15:41:52 +02:00
Sean 1a653c3fa7 Set tinymce urls to absolute
Potentially fixes a deeper TinyMCE issue that causes TinyMCE to remove
important parts of pasted URLs
2020-05-04 17:19:05 +02:00
Tobias Hößl b51787129b
Change recommendations for amendments 2020-05-02 18:40:59 +02:00
Emanuel Schütze e0069f734a
Merge pull request #5346 from emanuelschuetze/translations-20200430
Updated translations
2020-04-30 19:29:59 +02:00
Emanuel Schütze f415fd0554 Updated translations 2020-04-30 17:43:48 +02:00
Emanuel Schütze c6836ff6c5
Merge pull request #5294 from GabrielInTheWorld/sort-speakers
Changes workflow for sorting speakers
2020-04-30 16:42:52 +02:00
GabrielMeyer 4a24da12da Changes workflow for sorting speakers 2020-04-30 15:58:49 +02:00
Emanuel Schütze 3842f66877
Merge pull request #5330 from tsiegleauq/mark-first-time-speaker
Show first contribution hint in list of speaker
2020-04-30 15:03:55 +02:00
Sean 38ee6bb2f1 Show first contribution hint in list of speaker
Shows a hint in the list of speakers if a speaker contributes for the
first time.
2020-04-30 14:06:37 +02:00
Emanuel Schütze a47285c0ff
Merge pull request #5309 from tsiegleauq/integrate-jitsi-meet-client
Integrate jitsi-meet in OpenSlides
2020-04-30 13:05:12 +02:00
Sean 1439444b2e Integrate jitsi-meet in OpenSlides
- minimal jitsi client in the bottom right of the screen
- can be shown and hidden like a messenger
- allows to mute, unmute, call, stop call
- automatically connects to a conference
- shows a list of users connected to the room
- jitsi iframe is currently hidden
- "open in jitsi meet" link
- only one connection will be opened if using multiple tabs
- JITSI_DOMAIN and JITSI_ROOM_NAME must be present in the settings.py
- config variables in settings
2020-04-30 11:07:32 +02:00
Emanuel Schütze cce76118c3
Merge pull request #5344 from FinnStutzenstein/voteWeight
Removed vote weight from votes_cast
2020-04-30 10:54:28 +02:00
FinnStutzenstein aa1a2cec89
Removed vote weight from votes_cast 2020-04-30 08:10:39 +02:00
Emanuel Schütze 46d0bbd8f5
Merge pull request #5341 from FinnStutzenstein/loadConfigsBeforeModels
Load configs before models
2020-04-27 10:00:53 +02:00
FinnStutzenstein b78372f8a3
Load configs before models 2020-04-27 09:41:23 +02:00
Emanuel Schütze fd9b8b1c5c
Merge pull request #5340 from emanuelschuetze/translations-20200424
Updated German translations.
2020-04-24 14:40:40 +02:00
Emanuel Schütze 7a25a2496d Updated German translations. 2020-04-24 14:28:26 +02:00
Emanuel Schütze ddfe7d0c5a
Merge pull request #5295 from GabrielInTheWorld/duplicate-topics
Duplicates single and multiple items in the agenda
2020-04-24 14:03:44 +02:00
FinnStutzenstein 152401a9a3
Duplicates single and multiple topics in the agenda 2020-04-24 12:34:22 +02:00
Emanuel Schütze 2057150076
Merge pull request #5322 from FinnStutzenstein/delete-all-speakers
delete all speakers of all lists of speakers
2020-04-24 12:25:49 +02:00
Emanuel Schütze cb52347354
Merge pull request #5328 from FinnStutzenstein/projectorSubtitles
Subtitles for projected elements in the projector detail view
2020-04-24 12:12:22 +02:00
Emanuel Schütze 3169e4f30b
Merge pull request #5336 from FinnStutzenstein/fixVoting
Fixed validation of options in asignment polls
2020-04-24 12:10:04 +02:00
FinnStutzenstein 4221351223 Fixed validation of options in asignment polls
Also fixed #5334
2020-04-24 09:31:39 +02:00
Emanuel Schütze 0c6da9799c
Merge pull request #5335 from GabrielInTheWorld/fixTopicPreview
Fixes preview of topics in global search
2020-04-24 09:13:36 +02:00
Emanuel Schütze a71e36c861
Merge pull request #5331 from emanuelschuetze/translations-20200423
Updated German translations
2020-04-24 08:55:39 +02:00
GabrielMeyer 41b9065807 Fixes preview of topics in global search 2020-04-23 23:36:10 +02:00
Emanuel Schütze 527f947143 Updated German translations 2020-04-23 20:35:59 +02:00
FinnStutzenstein c8faa982ac Subtitles for projected elements in the projector detail view 2020-04-23 16:01:18 +02:00
FinnStutzenstein 38486463bc delete all speakers of all lists of speakers 2020-04-23 09:43:04 +02:00
Finn Stutzenstein 6a488eb78e
Merge pull request #5323 from tsiegleauq/linter-prettier-updates
Update linting rules
2020-04-22 17:32:42 +02:00
Emanuel Schütze 0aef3f79ce
Merge pull request #5305 from tsiegleauq/weight-votes
Implement vote weight in client
2020-04-22 17:28:02 +02:00
Sean 97c2299aec Implement vote weight in client
Implements vote weight in client
The user detail page has a new property
change deserialize to parse floats
change "yes"-voting to send "Y" and "0" instead of "1" and "0"
add vote weight to user list, filter, sort
add vote weight to single voting result
votesvalid and votescast respect the individual vote weight
fix parse-poll pipe and null in pdf
2020-04-22 16:54:50 +02:00
Sean e702843f07 Update linting rules
Includes strickt(er) tslint line-length of 120, with an exception
for import statements (prettier does not like these)
2020-04-22 16:24:08 +02:00
Emanuel Schütze 0f3d07f151
Merge pull request #5324 from tsiegleauq/another-round-of-python-travis-fixes
Set version of pytest-django and pytest-asyncio
2020-04-22 16:09:23 +02:00
Sean aa097ee689 Set version of pytest-django and pytest-asyncio
Apparently, updates in these two libraries are not compatible
with out current testing setup
2020-04-22 14:56:25 +02:00
Sean f7a97cf886
Merge pull request #5299 from CatoTH/delete-obsolete-tests
Delete old test files that are reimplemented in Typescript by now
2020-04-21 13:21:16 +02:00
Emanuel Schütze 25f8f42c92
Merge pull request #5320 from sarah-github/sarah-github-typo-settings-rst
typo with brackets in settings.rst for logging config
2020-04-21 12:49:04 +02:00
Sarah 523eb96f9d
typo with brackets 2020-04-20 22:37:01 +02:00
Emanuel Schütze 2c548d2dfb
Merge pull request #5319 from tsiegleauq/fix-admin-self-set-present
Fixes an issue regarding self-set-present
2020-04-20 19:38:11 +02:00
Sean 91d4b3c7af Fixes an issue regarding self-set-present 2020-04-20 19:17:03 +02:00
Emanuel Schütze d210496146
Merge pull request #5293 from GabrielInTheWorld/subline-comment
Adds a third line for comments
2020-04-20 18:09:52 +02:00
Emanuel Schütze 35ce596706
Merge pull request #5317 from tsiegleauq/set_presence_on_userlist
Allow set present on user list if config was set
2020-04-20 17:13:48 +02:00
Tobias Hößl f007e07544
Delete old test files that are reimplemented in Typescript by now 2020-04-20 15:15:00 +02:00
Sean 70aadcdd28
Merge pull request #5307 from GabrielInTheWorld/no-found-button
Adds a 'not found'-button to search-value-selector
2020-04-20 14:52:46 +02:00
Sean 9ffbb39e95 Allow set present on user list if config was set
Depending on "selt self presence" config, allow users to set
themselves as present on the user list
2020-04-20 13:46:14 +02:00
GabrielMeyer 170aa1c8f0 Adds a third line for comments 2020-04-20 07:46:37 +02:00
jsangmeister ad4ed3443a
Merge pull request #5316 from jsangmeister/voting-autoupdate-fix
Fixes the voting autoupdate bug
2020-04-17 16:58:50 +02:00
Joshua Sangmeister 42fbe93314 fixes the voting autoupdate bug 2020-04-17 16:38:38 +02:00
Emanuel Schütze 6cdf9a5582
Merge pull request #5315 from emanuelschuetze/prepare-3.2
Prepare new 3.2 release
2020-04-16 13:55:24 +02:00
Emanuel Schütze 75ebf5bc77 Prepare new 3.2 release 2020-04-16 13:41:56 +02:00
Emanuel Schütze c26ef8c0bb
Merge pull request #5314 from jsangmeister/permissiveHtmlValidation
Adds more permissive html validation for topics and welcome page
2020-04-16 10:57:43 +02:00
Joshua Sangmeister 6eae497abe adds more permissive html validation 2020-04-16 10:26:48 +02:00
Finn Stutzenstein 1570b5b806
Merge pull request #5313 from jsangmeister/fix-user-import
Fixed user import
2020-04-15 13:44:30 +02:00
Emanuel Schütze 537eeadce4
Merge pull request #5312 from jsangmeister/adjustSettingsReadme
Add ENABLE_ELECTRONIC_VOTING to settings readme
2020-04-15 11:09:48 +02:00
Joshua Sangmeister 96ee1c0af3 fixed user import 2020-04-15 10:27:07 +02:00
Joshua Sangmeister 99416e3043 added ENABLE_ELECTRONIC_VOTING to settings readme 2020-04-15 10:15:19 +02:00
Emanuel Schütze 0f8167e39c
Merge pull request #5311 from jsangmeister/fixPseudoanonymize
Fix pseudoanonymize on client
2020-04-15 09:54:21 +02:00
Joshua Sangmeister 9864ff3847 fixed pseudoanonymize on client 2020-04-15 09:42:22 +02:00
Emanuel Schütze a7518ed5b6
Merge pull request #5310 from tsiegleauq/better-category-sorting
Sort motions in categories by inner weight (again)
2020-04-15 09:25:19 +02:00
Sean 5b7bbfd0bb Sort motions in categories by inner weight (again)
Fixes sorting categories by inner weight
2020-04-14 15:01:42 +02:00
Emanuel Schütze b7566fcc69
Merge pull request #5308 from tsiegleauq/sort-categories-by-weight
Sort motion list by category weight
2020-04-08 22:35:31 +02:00
Sean 82c6929a8d Sort motion list by category weight
Sorts the motion list by the weight of the category
rather than by name
2020-04-08 22:22:02 +02:00
GabrielMeyer 35a67017a3 Adds a 'not found'-button to search-value-selector 2020-04-08 11:48:18 +02:00
Finn Stutzenstein 4841343c02
Merge pull request #5303 from FinnStutzenstein/fixVoting
Cleanup for #5300
2020-04-07 10:07:07 +02:00
FinnStutzenstein 7a97aa1b79 Cleanup for #5300 2020-04-07 09:53:16 +02:00
Emanuel Schütze 12bc926b44
Merge pull request #5302 from emanuelschuetze/translations-20200407
Updated translations.
2020-04-07 09:40:02 +02:00
Emanuel Schütze 53b4b1c1f9 Updated translations. 2020-04-07 07:56:51 +02:00
Emanuel Schütze cc372cfba5
Merge pull request #5300 from FinnStutzenstein/fixVoting
Added vote weight and fixed named voting
2020-04-07 07:47:12 +02:00
Joshua Sangmeister b7b8620153 removed race condition & cleanup 2020-04-07 07:27:54 +02:00
FinnStutzenstein 7882ea1a25 Added vote weight and fixed named voting 2020-04-07 07:27:54 +02:00
Sean 04a7ce22fd
Merge pull request #5297 from tsiegleauq/esr-cd
Fix malfunctions in Firefox ESR
2020-04-06 23:34:32 +02:00
Sean 820a47123a Fix malfunctions in Firefox ESR
Various cryptic issues that had no usefull debug output.

ngx-translates "translate" directive was causing Firefox ESR
to jump in the "drainMicroTaskQueue" infinite recursion when
using the directive in the same component as nGrid.

Therefore, I changed all uses of the translate directive
to the pipe (arround 700 or so, regex is my best friend now)

open todo: adjust the linter that we may never use the translate
directive again.

There was another cryptic issue with the current version
of exceljs which was also causing firefox to go crash
when loading a workbook object. It was sufficient to
have any Workbook() function declared in a module to cause
firefox to crash.
2020-04-06 20:36:24 +02:00
Emanuel Schütze 42af962248
Merge pull request #5301 from tsiegleauq/new-extract-function
Update translation extractor
2020-04-06 17:52:16 +02:00
Sean 7b5f2648af Update translation extractor 2020-04-06 17:18:04 +02:00
Sean a1e2c49815
Merge pull request #5234 from tsiegleauq/angular9
Migrate to angular 9
2020-04-01 18:12:34 +02:00
Sean Engelhardt e1acf6e9d6 Update to Anulgar 9
Updates Angular to version 9.1
Updates most-to-all npm components
Removes deprecated components and npm commands
Updates travis node version
Adjust the whole code base to angular 9 standard
Increase TypeScipt version to 3.8
2020-04-01 14:50:15 +02:00
Finn Stutzenstein 83d57e9da7
Merge pull request #5290 from FinnStutzenstein/redisWaitForReplication
Redis: Wait for replication on writes
2020-04-01 13:32:11 +02:00
FinnStutzenstein bb2f958eb5 Redis: Wait for replication on writes
Since channels_redis does not support dedicated read-redis instances, the
autoupdate message may be received before the data was replicated. All workers
read the autoupdate message from the write host, so there is a race between
getting this message and a finished replication. For large payloads, the
replication is slower in the most cases (even more in a distributed setup, where
the master and replica are on different nodes). The easy way is to wait for
replication. But there is one difficulty: The number of replicas has to be
known. There is a new settings-variable "AMOUNT_REPLICAS" which defaults to 1.
It needs to be set correctly! If it is too high, every autoupdate will be
delayed by 1 seconds because of a timeout witing for non-existent replicas. If
it is too low, some autoupdates may be wrong (and not detectable by the client!)
becuase of reading from non-synchronised relicas.

The other possibility is to fork channel_redis and add the feature of a
read-only redis. This ould help, because on a single redis instance all commands
are ordered: First, the data is synced, then the autoupdate message. Attention:
This means, if redis-replicas are scaled up, one must make sure to read from the
same instance. I think this is not possible in the way how dockers overlay
networks work. The only way would be to open one connection and reuse the
connection from channels_redis in OpenSlides. This would mean a heavy
integration of channels_redis (meaning including the source code in our repo).

For the first fix, this one is easy and should work.
2020-04-01 13:09:48 +02:00
Emanuel Schütze 7b0a2d8ec2
Merge pull request #5291 from FinnStutzenstein/updateTwisted
Update Twisted
2020-04-01 10:46:17 +02:00
FinnStutzenstein b2d05f81fe Update Twisted 2020-04-01 10:33:18 +02:00
Emanuel Schütze 4419e76223
Merge pull request #5289 from FinnStutzenstein/fixMissingMigrations
Added missing migrations
2020-03-30 11:16:38 +02:00
Emanuel Schütze 1e3c83babc
Merge pull request #5288 from emanuelschuetze/translations-20200330
Updated Translations
2020-03-30 11:08:32 +02:00
FinnStutzenstein 3be28ec50a Added missing migrations 2020-03-30 10:58:42 +02:00
Emanuel Schütze baa1787189 Updated Translations 2020-03-30 10:57:10 +02:00
Emanuel Schütze 8119507b8a
Merge pull request #5283 from tsiegleauq/user-active-toggle
Add set present toggle in user menu
2020-03-30 10:14:27 +02:00
Sean 39ccfe3147 Add set present toggle in user menu
adds a "is present" toggle to the user menu
Refactor user menu into own component
Add a config variable to determine if the user is allowed
to set themselve as present
2020-03-30 09:57:57 +02:00
Emanuel Schütze 106816a733
Merge pull request #5286 from emanuelschuetze/translations-20200326
Updated translations.
2020-03-26 18:42:44 +01:00
Emanuel Schütze c257baa14b Updated translations. 2020-03-26 18:21:36 +01:00
Emanuel Schütze 04c625b3d5
Merge pull request #5282 from tsiegleauq/dynamic-bar-chart-aspect-ratio
Define chart height
2020-03-26 17:58:48 +01:00
Sean d646691961 Define chart height
Bar chart height will be calculated by the number of labels to show
Circle chart height should scale with their container
2020-03-26 17:39:47 +01:00
Emanuel Schütze aaea4ec2e9
Merge pull request #5284 from tsiegleauq/clickable-motion-charts
Remove tooltips for charts
2020-03-26 17:19:19 +01:00
Emanuel Schütze 5b878f4814
Merge pull request #5285 from tsiegleauq/untranslated-comment-field
Fix untranslateable motion comment section dialog
2020-03-26 17:17:01 +01:00
Sean 5bdbe4778a Remove tooltips for charts
Removes all hover-tooltips for charts
Add detail link to motion poll chart
2020-03-26 17:04:17 +01:00
Sean fbff4de431 Fix untranslateable motion comment section dialog
Missing translation tags in comment section dialog
2020-03-26 16:37:47 +01:00
Sean af6c5faac8
Merge pull request #5281 from tsiegleauq/poll-list-filter
Filter only evoting polls, change filter options
2020-03-26 12:27:33 +01:00
Sean 14de67a09d Filter only evoting polls, change filter options
Pre filters all analog polls from poll list.
Creates a filter to find thouse with open polls for the user
2020-03-26 11:54:05 +01:00
Emanuel Schütze 6f7c6036c2
Merge pull request #5280 from tsiegleauq/workflow-manager-scrolling
Enhance workflow matrix for small screens
2020-03-25 15:26:18 +01:00
Emanuel Schütze 19af02a315
Merge pull request #5278 from tsiegleauq/tinymce-in-settings-page
Manual change detection for config list
2020-03-25 15:21:41 +01:00
Emanuel Schütze d50899c407
Merge pull request #5279 from tsiegleauq/less-verbose-user-count
Remove counting of IndexedDB and LocalStore
2020-03-25 13:10:00 +01:00
Sean 73fc936306 Enhance workflow matrix for small screens
makes scrolling horizontally in the workflow manager easier.
The offline banner and the voting banner will lead to unwanted results.
2020-03-25 13:01:25 +01:00
Sean c2406fcc03 Remove counting of IndexedDB and LocalStore
Mainly simplify the user counting component.

It seems that counting IndexedDB has no value anymore,
since even Firefox ESR uses IndexedDB just fine and the LS fallback
proves to be reliable enough
2020-03-25 12:18:57 +01:00
Sean 557824f5f1 Manual change detection for config list
Adds manual change detection to the config page.
Behaves better regarding "changed after checked" errors
2020-03-25 11:22:54 +01:00
Emanuel Schütze 91be76a263
Merge pull request #5276 from tsiegleauq/changeable-created-polls
Remove pseudo-disabled values in poll form
2020-03-24 16:01:17 +01:00
Emanuel Schütze eadc09dc56
Merge pull request #5271 from tsiegleauq/reset-workflow-reco
Allow empty input values in recommendation
2020-03-24 15:58:43 +01:00
Emanuel Schütze c43e180494
Merge pull request #5277 from FinnStutzenstein/removeCheckUpdate
Remove Check update for others feature
2020-03-24 15:56:50 +01:00
Sean 6fddddd9f4 Allow empty input values in recommendation
Allows the client to send empty values as workflow
recommendation label in the workflow detail view.
This is required to remove a recommendation from a
workflow
2020-03-24 15:44:46 +01:00
FinnStutzenstein cf50295ca4 Remove Check update for others feature 2020-03-24 15:25:47 +01:00
Sean 7af2f70494 Remove pseudo-disabled values in poll form
Removed the previously disabled-looking poll options from the poll form
component.
I figguered that it would be more intuitive and closer to my
expectations if I could change the poll values even if the poll was
already created.
2020-03-24 14:22:59 +01:00
Emanuel Schütze cd3435064c
Merge pull request #5275 from FinnStutzenstein/default169projector
Changed the default projector aspect ratio to 16:9 on initial databas…
2020-03-24 12:44:05 +01:00
FinnStutzenstein 123df7660f Changed the default projector aspect ratio to 16:9 on initial database creation 2020-03-24 12:09:11 +01:00
Sean 2fb372ead9
Merge pull request #5274 from tsiegleauq/untranslated-default-projector
Show translated values in projector preview
2020-03-23 17:57:09 +01:00
Sean 7d86f62e2d
Merge pull request #5273 from tsiegleauq/new-projector-reference
Create new projectors with clos reference
2020-03-23 17:31:13 +01:00
Sean d92622410f Show translated values in projector preview
Shows the translated value of "default projector"
in projector preview component.
I reomved a observable that I suppose was outdated
2020-03-23 17:28:09 +01:00
Sean 99c3afb417 Create new projectors with clos reference
New projectors will be created on a reference to the current list of
of speakers
2020-03-23 16:57:29 +01:00
Emanuel Schütze 23a105bdb8
Merge pull request #5272 from tsiegleauq/los-select-speaker-css
Full width for LOS search user
2020-03-23 16:57:01 +01:00
Sean bf0eadebb7 Full width for LOS search user
Puts the List-Of-Speakers search user section to full width
2020-03-23 16:32:48 +01:00
Sean fe71322199
Merge pull request #5270 from tsiegleauq/hotkey-for-poll-dialogs
Fix Shift-Enter to save poll
2020-03-23 14:45:54 +01:00
Sean 5bf3dfadff Fix Shift-Enter to save poll
Fixes a bug which caused the event listener not to register
in BasePollDialog

Also hot fixes a bug in babel
2020-03-23 14:26:38 +01:00
Emanuel Schütze 5617b02804
Merge pull request #5266 from emanuelschuetze/translations-20200320
Updated translations
2020-03-20 21:00:10 +01:00
Emanuel Schütze 5a6d2d2e42 Updated translations 2020-03-20 17:19:12 +01:00
Sean 661fd55c67
Merge pull request #5265 from tsiegleauq/assignment-poll-slide-rework
Rework assignment poll slide
2020-03-20 17:14:44 +01:00
Sean 072ec937a1 Rework assignment poll slide
Reworked assignment poll slide
and refactored the assignment poll detail table
into an own component
2020-03-20 16:53:31 +01:00
Emanuel Schütze b873dc156b
Merge pull request #5264 from tsiegleauq/bar-chart-rework
Rework Chart component
2020-03-20 13:31:20 +01:00
Emanuel Schütze 4acadd33ca
Merge pull request #5262 from FinnStutzenstein/catchErrorOnInvalidCache
Catch error on invalid cache data
2020-03-20 13:24:53 +01:00
Sean f0e396b3a4 Rework Chart component
Cleans up the chart component
Speed up the rendering using async pipe instead of passing obserbables
Thiner bar-charts.
Fixes some bugs, some bugs are still present.
2020-03-20 10:28:59 +01:00
FinnStutzenstein 73eff81edd Catch error on invalid cache data 2020-03-19 14:00:56 +01:00
Emanuel Schütze 54dd97399e
Merge pull request #5260 from tsiegleauq/various-voting-issues
Various small fixes
2020-03-17 21:53:40 +01:00
Sean ee07e8f0ce Various small fixes
- Fix an issue in motion PDF which affected motion result percent
  values
- Fix an issue where the voting result bar chart hat a chance to show
  "null"
- Change the available votes display to count down instead of up
- Add the correct button class to the global abstain button
- Add some translatable strings
2020-03-17 21:33:46 +01:00
Sean d12e052030
Merge pull request #5259 from tsiegleauq/clean-usage-of-pollservice
Refactor usage of PollService
2020-03-17 18:39:18 +01:00
Sean 0ab4532ac8 Refactor usage of PollService
prevents the direct use of the abstract
"PollService".
2020-03-17 18:21:13 +01:00
Finn Stutzenstein 58483d7024
Merge pull request #5255 from OpenSlides/development
Voting 🎉
2020-03-17 07:48:00 +01:00
Sean 3c9f6ed278 Some overall improvements
Common:
	delete unused motion poll list

Poll Create form:
	Fix ugly multi line mat hints
	(workaround, see https://github.com/angular/components/issues/5227 )

Poll List:
	Fix too tiny column size
	user_has_voted_valid (ceck icon) was not shown

Motion Poll Card:
	Enhance subtitle layout (type + state)

Assignment Poll Card:
	Open warning after clicking the hint icon

Assignment Poll Chart:
	Show Absolute values and percents in chart label

Assignment Detail:
	Add new ballot button with plus icon instead of chart icon
2020-03-17 07:24:50 +01:00
FinnStutzenstein 64f2720b1a Last changes and cleanup some todos 2020-03-17 07:24:50 +01:00
Emanuel Schütze d15c9892ed Updated translation strings and German translation. 2020-03-17 07:24:49 +01:00
Sean Engelhardt ee4c6aa0bf Even more voting refinement
Various additional refinements for a more well rounded
voting experience
2020-03-17 07:24:49 +01:00
FinnStutzenstein a05662a0f8 Show global votes in the single votes table 2020-03-17 07:24:49 +01:00
Sean Engelhardt 29a9a09bc6 Motion poll detail als slide
Refactor the code to use the motion poll detail als slide component
2020-03-17 07:24:49 +01:00
Sean Engelhardt 3c36441967 Add global no and abstain to form
Minur UI changes
Minor Chart enhancements
Server Changes
2020-03-17 07:24:48 +01:00
Sean Engelhardt 8fe5a0c9f4 Rework assignment voting
- Remove "assignments.can_manage_polls" permission
- Let the client handle some user errors
- Add a send button to manually submit polls
- Show a hint that the user already submitted a vote
  - will not (and should not) work for non-nominal voting
- submitting a vote cannot be changed anymore
  - user will have to confirm sending
- enable deselecting YNA-votings
- nomainal voting will behace the same as non nominal voting
- submitting empty votes should be possible

Perhaps server side adjustments might still be required
2020-03-17 07:24:48 +01:00
Sean Engelhardt 61b7731073 Enhance charts and tables for assignments
Also some various improvements
2020-03-17 07:24:48 +01:00
FinnStutzenstein e2feeb4b65 Fixed pseudoanonymous voting for motions 2020-03-17 07:24:47 +01:00
Sean Engelhardt 53b9ce73f2 Enhance table layouts
Enhance the result table layout for assignments
2020-03-17 07:24:47 +01:00
Joshua Sangmeister 9d7028ea5f now filters percent bases correctly 2020-03-17 07:24:47 +01:00
Joshua Sangmeister 72678770bb table update on pseudoanonymize, view base classes for votes and
options, renaming for assignment percent bases
2020-03-17 07:24:46 +01:00
Joshua Sangmeister 82c8ade0ba adds all user that voted to required users for single votes table 2020-03-17 07:24:46 +01:00
Emanuel Schütze 2d13519c35 Updated voting strings for translation 2020-03-17 07:24:46 +01:00
Joshua Sangmeister e72bcc1eaf removed default group from 'entitled to vote' selection 2020-03-17 07:24:46 +01:00
Sean Engelhardt 97a5bb4aa6 Cleanup Voting, enhance UI and UX
removed certain unnecesary fields
cleaned up a lot of code
redone some of the UI
some database and server adjustments
2020-03-17 07:24:45 +01:00
Joshua Sangmeister 7598fc5367 Fixed the numbering of assignment candidates for projector and PDF 2020-03-17 07:24:45 +01:00
Joshua Sangmeister b48ca8c434 added chart projection for polls 2020-03-17 07:24:45 +01:00
Sean Engelhardt 6ba0d0c5e6 Client side changes 2020-03-17 07:24:44 +01:00
FinnStutzenstein 0b37c5a857 WIP: Partial requests 2020-03-17 07:24:44 +01:00
Joshua Sangmeister d4599a435b added virtual scrolling for single votes tables 2020-03-17 07:24:44 +01:00
Sean Engelhardt 93dc78c7d6 Result PDF for Voting
- Add result PDF for Motion and Assignments
- Add "getPercentBase" for Assignment
2020-03-17 07:24:44 +01:00
Sean Engelhardt 6044c63c28 Enhance Assignment Voting
- repaired the PDF Service for ballots
- fixed some permission errors
- analog voting has no "started" option anymore
- more-link as button
- named voting has a progress bar
- Shows the poll type for eVoting
- Moves and declutters meta info
- Enhance the grid and the layout in detail view
- declutter and enhance the dot-menus
- some other layout changes
- remove breadcrumbs in assignment detail
- other cleanups refinements
- Voting in Assignment over instead of forms
(requires more server changes)
2020-03-17 07:24:43 +01:00
Sean 524a97cdcc Enhance voting
- cleaned up a lot of code
- removed required majotiry from forms
- renamed verbose "Majority method" to "Required majority"
- poll-progress-bar only counts present user
- enhanced motion poll tile chart layout
- removed PercentBase.Votes
- added pollPercentBase pipe
- Show the voting percent next to chart in motion detail
- change the head bar to "Voting is open"
  and "Ballot is open"
- merged the voting configs to their corresponding config-categories
- re-add ballot paper configs
- Add "more" button to motion polls
- Adjusted the motion results table
  - Hide entries without information
  - Show icons for Y N A
  - Show percentage next to Y N A
2020-03-17 07:24:43 +01:00
GabrielMeyer 6c1317e25f Fixes labelling for charts 2020-03-17 07:24:43 +01:00
GabrielMeyer 294b75c320 Replaces the mat-table with a classic table 2020-03-17 07:24:42 +01:00
GabrielMeyer 09b0d19de0 Fixes permissions for assignments
- There were some fields that user could see/click/handle, although the user has not the correct permission for the action
2020-03-17 07:24:42 +01:00
Joshua Sangmeister df1047fc76 various improvements for polls 2020-03-17 07:24:42 +01:00
Joshua Sangmeister bc54a6eb46 improved 'votes' pollmethod 2020-03-17 07:24:42 +01:00
Joshua Sangmeister 1de73d5701 improved shared poll list 2020-03-17 07:24:41 +01:00
GabrielMeyer a0c3a28456 Adds a chart for assignment-poll-detail 2020-03-17 07:24:41 +01:00
GabrielMeyer c46369c6a7 Reworks the banner showing if there are polls
- Makes it higher on mobilephones
- Changes title, if there is only one poll
2020-03-17 07:24:41 +01:00
Joshua Sangmeister b16afaa285 number poll candidates depending on setting 2020-03-17 07:24:40 +01:00
FinnStutzenstein e2585fb757 Projector for polls: Server, client structure and data modeling 2020-03-17 07:24:40 +01:00
Sean Engelhardt 84a39ccb62 More voting UI improvements
For Motion poll:
- Overworked how motion poll chart displays the legend
- Added the vote counter to the motion detail
- Added a progress bar to the vote counter
- Fixed some perm errors with the chart
- Show a "Singe Votes" link as button for published named polls
- Replace the edit-button with a dot-menu
  - Having project, Edit, PDF and Delete

For Motion Poll detail:
- enhance search panel
- Remove the breadcrumbs
- Remove the vote counter
- Enhanced the single-vote grid, table and filter bar
- Enhance how the poll state enum was checkend

For the Motion Poll Create/Update Form:
- Remove the selection of poll-methode (whenever possible)
- only show "publish imediately" during creation
2020-03-17 07:24:40 +01:00
Joshua Sangmeister 682db96b7c added vote per user table and progress for polls
added update for options after stopping a poll
2020-03-17 07:24:39 +01:00
Sean 604df9d48b Enhance voting ux 2020-03-17 07:24:39 +01:00
FinnStutzenstein 7ab5346198 disable caching for reverse relations 2020-03-17 07:24:39 +01:00
Joshua Sangmeister e67ca77ad1 default motion poll method set, changed permission from 'can_manage_metadata' to 'can_manage' 2020-03-17 07:24:39 +01:00
GabrielMeyer fff1f15b6c Polls for motions and assignments
- Adds charts to assignments
- Creates base-classes for polls
2020-03-17 07:24:38 +01:00
GabrielMeyer 96aa3b0084 Adds the chart and dialog for analog voting 2020-03-17 07:24:38 +01:00
jsangmeister 72ff1b1f09 api changes to allow some edits on finished polls 2020-03-17 07:24:38 +01:00
FinnStutzenstein fafb81daca Fix assignment access permissions
Also improves unnecessary history-savings of users in the list of speakers
2020-03-17 07:24:37 +01:00
FinnStutzenstein b50cf42543 Prevent stopping an analog poll
Fixed too much logging with the new autoupdate bundling
2020-03-17 07:24:37 +01:00
jsangmeister 90b04366b5 added option to number poll cadidates 2020-03-17 07:24:37 +01:00
GabrielMeyer 8d77c0495b Initial polling 2020-03-17 07:24:36 +01:00
jsangmeister 1b761d31c0 added tests for user creation and try-catch for a probably race-based IntegrityError 2020-03-17 07:24:36 +01:00
jsangmeister 09ef3c5071 add settings variable ENABLE_ELECTRONIC_VOTING 2020-03-17 07:24:36 +01:00
FinnStutzenstein 046a152ec5 generate less queries in the autoupdate system 2020-03-17 07:24:36 +01:00
jsangmeister 6605934a33 added count query decorator 2020-03-17 07:24:35 +01:00
FinnStutzenstein 1246dd54ad majorities in polls 2020-03-17 07:24:35 +01:00
jsangmeister 5fa8341614 added testing for named and pseudoanonymous assignment voting
added queries count tests for assignment and motion polls and votes
2020-03-17 07:24:35 +01:00
FinnStutzenstein ce171980e8 Relations in the client 2020-03-17 07:24:34 +01:00
FinnStutzenstein ced40cab74 Initial work for supporting voting 2020-03-17 07:24:34 +01:00
Emanuel Schütze 4d4697eee0
Merge pull request #5246 from jsangmeister/motion-block-check
Added an indicator if all motions of a block are finished
2020-03-12 21:24:01 +01:00
Joshua Sangmeister aa46922c8b added an indicator if all motions of a block are finished 2020-03-12 16:21:41 +01:00
Emanuel Schütze ec17376e8e
Merge pull request #5245 from jsangmeister/amendment-navigation
Added navigation between amendments when not shown in main list
2020-03-12 15:39:26 +01:00
Emanuel Schütze 35d9fd9d8e
Merge pull request #5244 from jsangmeister/fix-final-print-template
Fix the error when creating a final print template after a title change recommendation
2020-03-12 15:14:58 +01:00
Joshua Sangmeister 7acf2157fa added navigation between amendments when not shown in main list 2020-03-12 11:53:29 +01:00
Joshua Sangmeister 70fc5a69ab fix the error when creating a final print template after a title change recommendation 2020-03-12 09:39:56 +01:00
Emanuel Schütze 3ad8944b9c
Merge pull request #5230 from FinnStutzenstein/exportMediafiles
Export mediafiles to the media service
2020-03-06 10:56:53 +01:00
Emanuel Schütze 847482bb5f
Merge pull request #5228 from GabrielInTheWorld/updatingDarkTheme
Updates dark-theme
2020-02-27 18:40:17 +01:00
FinnStutzenstein 219103129d Export mediafiles to the media service 2020-02-26 15:15:45 +01:00
GabrielMeyer 13de88c136 Updates dark-theme 2020-02-26 13:39:07 +01:00
Sean 98146a29c7
Merge pull request #5227 from FinnStutzenstein/projectorLoadChildrenCallback
Use LoadChildrenCallback instead of deprecated NgModuleFactoryLoader
2020-02-25 10:50:19 +01:00
Finn Stutzenstein 758e059f9b
Merge pull request #5153 from FinnStutzenstein/mediafilesInPostgresql
External postgres as mediafile store
2020-02-20 08:01:39 +01:00
FinnStutzenstein 7204d59d66 [WIP] External postgres as mediafile store 2020-02-17 14:38:32 +01:00
FinnStutzenstein 76bd184ff4 Use LoadChildrenCallback instead of deprecated NgModuleFactoryLoader 2020-02-17 14:38:02 +01:00
Emanuel Schütze fbe5ea2056
Merge pull request #5209 from tsiegleauq/hide-create-final-version-button
Hide print-template button
2020-01-30 14:18:31 +01:00
Sean Engelhardt 2236f63fe9 Hide print-template button
Hides the "create final print template" button if
a print template exists.
Should be more user friendly and less dangerous to use.
2020-01-30 13:13:52 +01:00
Emanuel Schütze ec79f70648
Merge pull request #5206 from emanuelschuetze/translation202001
Updated German translation
2020-01-30 09:48:46 +01:00
Emanuel Schütze 0267b0cb42 Updated German translation 2020-01-30 09:35:57 +01:00
Emanuel Schütze 2ac01a5ea3
Merge pull request #5193 from tsiegleauq/final-version-without-reco
Allow final versions without change reco
2020-01-30 09:31:36 +01:00
Sean a51720e18b
Merge pull request #5191 from tsiegleauq/pdf-catalog-optional-page-breaks
Add option to export motions with pagebreaks
2020-01-29 16:33:37 +01:00
Sean Engelhardt 27e8301131 Allow final versions without change reco
Alters the creation process of final versions
in a way one can create them independantly
of change recomendations.
2020-01-29 16:28:36 +01:00
Sean Engelhardt 407a430419 Add option to export motions with pagebreaks
Adds an option to the PDF motion list exporter
that allows users to explicitly enforce or prevent
page breaks before a new motion was printed.

The option is enabled by default
2020-01-29 15:18:56 +01:00
Emanuel Schütze a6bdaedff1
Merge pull request #5192 from tsiegleauq/reco-and-state-in-pdf-toc
Show final state in Motion PDF toc
2020-01-29 15:09:50 +01:00
Sean 59795f32e3
Merge pull request #5195 from tsiegleauq/fix-flat-map-function
Fix flat map
2020-01-29 15:03:49 +01:00
Sean Engelhardt a161bca028 Show final state in Motion PDF toc
Renamed the option
"Show submitters and recommendation in table of contents"
to
"Show submitters and recommendation/state in table of contents"
If the option is selected, the PDF-TOC for motions will print
the name of the current state (if final) rather than the
current recomendation
2020-01-29 14:28:56 +01:00
Sean Engelhardt 6f114d0072 Fix flat map
Fixes a bug which was breaking flatMap
2020-01-29 12:47:42 +01:00
Sean 8012bfbfc0
Merge pull request #5187 from tsiegleauq/ngrid-updates
Update ngrid, redefine prototypes of datatypes
2020-01-23 15:10:18 +01:00
Sean d311042806
Merge pull request #5173 from tsiegleauq/amendment-wizzard-for-amendments
Use amendmend wizzard for amendments
2020-01-23 13:06:54 +01:00
Sean Engelhardt faf8004280 Use amendmend wizzard for amendments
Allows paragraph based amendments for other paragraph based
amendments.
Amendments to amendments will be amended to the main motion,
but will contain all the changes to the amendments they were
refering to

solves #5171
2020-01-23 12:46:19 +01:00
Sean Engelhardt c2ad39a2c5 Update ngrid, redefine prototypes of datatypes
Updates ngrid to the latest version
Use Object.defineProperty instead of
Number.prototype to safer extend native datatypes
2020-01-23 12:38:46 +01:00
Emanuel Schütze 7a23139f5e
Merge pull request #5168 from FinnStutzenstein/validateHtml
Validate Config HTML
2020-01-08 13:36:17 +01:00
Emanuel Schütze b9e40717de
Merge pull request #5172 from tsiegleauq/fix-group-creation
Fix group creation
2020-01-08 13:34:04 +01:00
Sean Engelhardt 5f8e64140a Fix group creation
Fixes an error that was preventing groups from being created.
Groups did require a list of permissions which is now optional
2020-01-08 10:46:11 +01:00
Emanuel Schütze a2d561f667
Merge pull request #5167 from emanuelschuetze/createopenslidesusercommand
Added optional argument '--email' for createopenslidesuser command.
2020-01-07 08:55:32 +01:00
Emanuel Schütze b3c98dd207 Added optional argument '--email' for createopenslidesuser command. 2020-01-05 15:17:09 +01:00
FinnStutzenstein a35fa105ed Validate Config HTML 2020-01-04 16:58:11 +01:00
1590 changed files with 118396 additions and 42691 deletions

View File

@ -10,11 +10,12 @@ assignees: ""
A clear and concise description of what the bug is.
**How to Reproduce**
Steps to reproduce the behavior: preferably on [nighty](https://nightly.demo.openslides.org)
Steps to reproduce the behavior: preferably on [nightly](https://nightly.demo.openslides.org) (for
3.4) or the [OS4 demo](https://demo.os4.openslides.com/).
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
2. Click on '...'
3. Scroll down to '...'
4. See error
**Expected behavior**
@ -22,16 +23,16 @@ A clear and concise description of what you expected to happen.
**System information**
- OpenSlides-Version: [e.g. 2.1, 2.2, 2.3, 3,0]
- Additional version information (if any): [e.g. commit hash or installation-month]
- Python Version: [e.g. 3,5, 3.6, 3.7]
- Device: [e.g. iPhone6, Notebook]
- OS: [e.g. Andoird 7, iOS8.1, Windows 10]
- OpenSlides-Version: [e.g. 3.4, 4.0-beta]
- Additional version information (if any): [e.g. commit hash or installation month]
- Python Version: [e.g. 3.6, 3.7, 3.8]
- Device: [e.g. iPhone 6, Notebook]
- OS: [e.g. Android 7, iOS 8.1, Windows 10]
- Browser: [e.g. chrome, firefox, opera, edge, safari]
- Browser-Version: [e.g. 22]
**Additional context**
Add any other information to comprehend your problem
Add any other information to comprehend your problem.
**Screenshots**
If applicable, add screenshots to help explain your problem.

133
.github/workflows/run-tests.yml vendored Normal file
View File

@ -0,0 +1,133 @@
name: CI
on:
pull_request:
jobs:
test-server:
runs-on: ubuntu-latest
env:
USING_COVERAGE: "3.6,3.8"
strategy:
matrix:
python-version: ["3.6", "3.7", "3.8"]
defaults:
run:
working-directory: ./server
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "${{ matrix.python-version }}"
- name: install python dependencies
run: |
pip install --upgrade pip
pip install --upgrade --requirement requirements.txt
pip freeze
- name: lint with flake8
run: flake8 openslides tests
- name: lint with isort
run: isort --check-only --diff --recursive openslides tests
- name: lint with black
run: black --check --diff openslides tests
- name: test using mypy
run: mypy openslides/ tests/
- name: test using pytest
run: pytest --cov --cov-fail-under=73
install-client-dependencies:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./client
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
id: node-module-cache
with:
path: |
**/node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
node_modules-
- uses: actions/setup-node@v2-beta
with:
node-version: "14"
- name: install client dependencies
if: steps.node-module-cache.outputs.cache-hit != 'true'
run: npm ci
check-client-code:
needs: install-client-dependencies
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./client
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: |
**/node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- name: check code using linter
run: npm run lint-check
- name: check code using prettify
run: npm run prettify-check
test-client:
needs: install-client-dependencies
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./client
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: |
**/node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- name: test client
run: npm run test-silently
build-client-debug:
needs: install-client-dependencies
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./client
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: |
**/node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- name: build client debug
run: npm run build-debug
build-client-prod:
needs: install-client-dependencies
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./client
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: |
**/node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- name: build client prod
run: npm run build

110
.gitignore vendored
View File

@ -1,55 +1,9 @@
# General
## 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
# Unit test and coverage reports
.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
@ -58,13 +12,58 @@ client/node_modules
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
*.code-workspace
# System Files
.DS_Store
Thumbs.db
# Virtual Environment
.virtualenv*
.venv
# misc
## Compatibility
# OS4-Submodules and aux-directories
/openslides-*/
/docker/keys/
/docker/secrets/auth_*_key
/docs/
# OS3+-Submodules
/autoupdate/
# Plugin development
openslides_*
# Old OS3 stuff
/tests/
## Server
# Local user data (settings, database, media, search index, static files)
personal_data/*
server/personal_data/*
server/openslides/static/*
# Unit test and coverage reports
.coverage
server/tests/file/*
server/tests/db.sqlite3.test
.pytest_cache
# Package building
*.egg-info
# Mypy cache for typechecking
.mypy_cache
## OpenSlides 3 Client
# Javascript tools and libraries
**/node_modules/*
**/bower_components/*
# compiled output
client/dist
client/static
client/tmp
client/out-tsc
# docs
client/documentation
Compodoc
Compodocmodules
CHANGELOG.md
# build artifacts
client/.sass-cache
client/connect.lock
client/coverage
@ -78,6 +77,11 @@ package-lock.json
client/package-lock.json
cypress.json
# System Files
client/.DS_Store
client/Thumbs.db
## Deployment
# Docker build artifacts
docker/docker-compose.yml
docker/docker-stack.yml
*-version.txt
*.pem
# secrets
docker/secrets/*.env

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "autoupdate"]
path = autoupdate
url = https://github.com/OpenSlides/openslides3-autoupdate-service.git

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

64
ADVANCED.rst Normal file
View File

@ -0,0 +1,64 @@
Advanced configuration
======================
Docker Swarm Mode
-----------------
OpenSlides may also be deployed in Swarm mode. Distributing instances over
multiple nodes may increase performance and offer failure resistance.
An example configuration file, ``docker-stack.yml.m4``, is provided. Unlike
the Docker Compose setup, this configuration will most likely need to be
customized, especially its placement constraints and database-related
preferences.
Before deploying an instance on Swarm, please see `Database Configuration`_ and
`Backups`_, and review your ``docker-stack.yml``
Database Configuration
----------------------
It is fairly easy to get an OpenSlides instance up an running; however, for
production setups it is strongly advised to review the database configuration.
By default, the primary database cluster will archive all WAL files in its
volume. Regularly pruning old data is left up to the host system, i.e., you.
Alternatively, you may disable WAL archiving by setting
``PGNODE_WAL_ARCHIVING=off`` in ``.env`` before starting the instance.
The provided ``docker-stack.yml.m4`` file includes additional database
services which can act as hot standby clusters with automatic failover
functionality. To take advantage of this setup, the database services need to
be configured with proper placement constraints. Before relying on this setup,
please familiarize yourself with `repmgr <https://repmgr.org/>`_.
Backups
-------
All important data is stored in the database. Additionally, the project
directory should be included in backups to ensure a smooth recovery.
The primary database usually runs in the ``pgnode1`` service (but see `Database
Configuration`_ above).
In some cases, it may be sufficient to generate SQL dumps with ``pg_dump``
through ``docker exec`` to create backups. However, for proper incremental
backups, the host system can backup the cluster's data directory and WAL
archives.
The cluster's data directory is available as a volume on the host system.
Additionally, the database archives its WAL files in the same volume by
default. This way, the host system can include the database volume in its
regular filesystem-based backup routine and create efficient database backups
suitable for point-in-time recovery.
The `former management repository
<https://github.com/OpenSlides/openslides-docker-compose/>`_ provides the
script `openslides-pg-mgr.sh` which can enable Postgres' backup mode in all
OpenSlides database containers.
In Swarm mode, the primary database cluster may get placed on a number of
nodes. It is, therefore, crucial to restrict the placement of database
services to nodes on which appropriate backups have been configured.

View File

@ -31,3 +31,6 @@ Authors of OpenSlides in chronological order of first contribution:
Fadi Abbud <fmfn13@hotmail.com>
Gabriel Meyer <meyergabriel@live.de>
Joshua Sangmeister <joshua.sangmeister@gmail.com>
Gernot Schulz <gernot@intevtion.de>
Raphael Topel <info@rtopel.de>
Martin Dickopp <martin@zero-based.org>

View File

@ -4,9 +4,244 @@
https://openslides.com
Version 3.4 (2021-10-07)
========================
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/3.4>`_
General:
- New feature: group chat module [#5876 #5902 #5929 #6072 #6100]
- New external autoupdate service for better performance [#5533, #5817]
- Improved loading indicator (spinner) [#5847, #5897, #5981, #6129]
- Fixed persistent offline bar on successful reconnection [#5828]
- Added own session redis [#5850]
- Use caddy as new proxy [#5851]
- Removed pip support [#5852]
- Updated to Angular12 [#6146]
- Handled more timeout errors [#5936]
- Fixed umlauts using full text search [#6040]
- Added point-of-order amount in speaker statistics [#6078]
- Refined autopilot interaction [#6084]
- Write changes of logo and font definitions to configs [#6118]
- Fixes the usage of groups in the settings [#6222]
- Added export for OpenSlides4 [#6120]
eVoting:
- Added history information for start/stop voting [#5857]
- Speed up stopping a poll [#6054]
- Added prompt dialog for stop voting [#6070]
- Allow list of speakers manager to see voting progress [#6037]
- Lock poll to prevent race conditions [#6006]
- Show "unpublished" for finished polls in autopilot [#6074]
- Clear all votes after poll reset [#6113]
- New 100% base: All entitled users
- Prevent multiple entries in entitled_users_at_stop [#5997]
- Removed voted_ids [#5918]
- Fixed vote pending state [#6127]
- Fixed vote delegation update error [#6024]
- Fixed entitled user calculation and display of voting banner in case of vote delegations [#6031]
Jitsi/Livestream:
- New feature: virtual applause [#5811]
- New feature: helpdesk jitsi room [#5832]
- Added support for YouTube and Nanocosmos livestream player [#5770, #6228]
- Completely restructure Jitsi/livestream components [#5961]
- Updated Jitsi iFrame and API [#6103]
- Hide chat in Jitsi iFrame [#6177]
Agenda:
- New feature: Mark speakers for pro/contra. New note for point of order. [#6023]
- New config option: List of speakers is initially closed [#5910]
- New multiselect action: open/close list of speakers in agenda [#6087]
- Added speaker information (pro/contra/point-of-order) also for active/finished speakers [#6073]
- Improved point-of-order dialog [#6075]
- Fixed point of order sorting/weighting [#5882, #5913]
- Fixed showSubtitle config. Reordered agenda config. [#5924]
- Fixed point of order creation permission (for agenda.can_be_speaker only) [#5927]
Motions:
- Added warning when editing motion with existing amendments [#5957]
- Added additional special characters for better diff handling [#5987, #6173]
- Change recommendation extension can now be set with can_manage_metadata [#6001]
- Allow formatting-only-changes without breaking the inline diff [#5992]
- Fixed paragraph based amendments in iOS [#5888]
- Fixed handling of inconsistent states in amendments [#5920]
- Fixed wrong diff view for amendments in list items [#6047]
- Fixed top navigation between motions [#6086]
- Fixed an error where the workflow would break agenda [#6185]
- Clean HTML before pasting in tinymce [#6212]
- Added supporters to CSV import [#6186]
- Enhance amendments change recommendation list in motion detail view [#6009]
- Better hyphenation for motion detail [#6076]
Elections:
- New feature: minimum amount of votes [#5719]
- Fixed user cannot see candidate names [#6049]
- Fixed order of assignment options by weight [#5928]
- Disabled general approval/rejection depending on poll method [#5979]
- Added general approval/rejection/abstain to analog polls [#6050]
- Number candidates in voting result table [#6051, #6081]
- Remove chart for analog polls with a majority [#6130]
- Fixed projection of analog polls & prevent percent base 'entitled' for analog polls [#6134]
Users:
- Improve client-side password generation [#5791, #5822]
- Fix vote_delegated_from_user_ids on user update [#5800]
- Fix csv importing users with groups [#5823]
- Update users on can_see_extra_data permission change [#5935]
- Add login errors for inactive users [#5967]
- Adding attribute matchers for group assignments to SAML [#6017]
- Validate the from email for invalid characters [#6025]
- Hide pw generate button while editing a user [#6171]
Mediafiles:
- New settings for the mediafile database tablename [#6026]
- Fixed updating logo and font configs when deleting a mediafile [#6123]
- Fixed missing zip extension when filename has a dot [#6172]
Projector:
- Don't show diff view on motion slides if no change recommendations exist [#5940]
- Hide cursor in full screen projector [#6066]
Version 3.3 (2020-12-18)
========================
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/3.3>`_
General:
- Fixed HTML attribute validation (XSS in all user-editable HTML fields) [#5714]
- Improved system libraries (u.g. updated to Angular 10 and TinyMCE editor 5.4)
- Improved repository structure (moved server code into subdirectory)
- Improved docker setup (config and build scripts)
- Improved search-value-selectors (e.g. virtual scrolling, multiselect)
- Improved management commands to use cache
- Improved reporting of SMTP exception
- Replaced travis with github actions
- Allowd demo mode in settings.py
- Fixed Firefox indexedDB state change issue [#5544]
- Fixed saml issues and improved saml config commands
- Various cleanups and improvements to usability, performance and translation.
eVoting:
- New feature for delegation of vote
- New election method 'No per candidate'
- Show progress bar also in autopilot (for managers only)
- Show last voting result always in autopilot (keep current projection and list of speakers)
- Show information for candidates and election method for each created ballot
- Improved layout of create voting dialog
- Improved user export with username and vote weight
- Prevent empty ballot paper
Jitsi/Livestream:
- New config option for livestream poster url (if livestream gets 404 error)
- New config option for auto conntecting next x speakers to Jitsi
- New config options to open microphone/camera by entering Jitsi
- Improved browser permission check for microphone/camera before entering Jitsi
- Improved switching between livestream and jitsi
- Added Picture-in-Picture (PIP) option to livestream player
- Updated jitsi-meet lib
Agenda:
- New autopilot mode to show always current agenda item/motion/list-of-speakers/voting/projector
- New "point of order" button in list of speakers
Motions:
- New config option for default workflow for amendments
- Improved performance for amendments (esp. for diff view in list and main motion view)
- Improved motion detail view and PDF to hide motion preamble in final state
- Improved motion export dialog to select supporters
- Improved navigation between amendments and main motions
- Show change recommendations of amendments also in main motion
- Fixed PDF issues
Elections:
- Improved handling for new candidates (quick create of new candidates)
Users:
- Improved user import preview by virtual srolling
- Show vote weight totals in user list
- Show vote delegations to users on ownPage
Mediafiles:
- New possibility to download full directory as zip
Projector:
- Always include change recommendations in motion slide
- Added amendments to projection defaults
Version 3.2 (2020-07-15)
========================
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/3.2>`_
General:
- New electronic voting integrated for motions and elections [#5255].
- New WebRTC based voice and video conferences using Jitsi-Meet (requires external Jitsi-Meet Server) [#5309, #5371, #5394, #5430, #5437, #5442, #5452, #5453].
- Improved system libraries (upgraded to Angular 9 which uses the Ivy rendering engine) [#5234].
- Improved the load of autoupdate system [#5109, #5375].
- Improved relations (i.e discovery of Motion - User - Motions). [#5091, #5180, #5389].
- Improved server validation of HTML in the OpenSlides config [#5168].
- Improved UI, UX, stability and theming [#5228, #5238, #5262, #5270, #5272, #5274, #5278, #5410, #5429].
- Improved themes (new: default dark, red light, green dark and solarized) and better support for dark themes [#5416, #5431, #5451].
- Improved HTML validation for welcome page and agenda topics to allow more tags (e.g. div, video) and attributes/styles [#5314].
- Improved permission checking system in client [#5359].
- Improved browser support by catching unsuported browsers on login page [#5403, #5446].
- Improved SAML support [#5405, #5418, #5432].
- Fixed wrong relative urls in TinyMCE [#5349].
- Fixed PDF generation if a left footer image was set [#5443].
- Removed the "check update for other clients" button [#5277].
- Various cleanups and improvements to usability, performance and translation.
Agenda:
- New tags for agenda items [#5370].
- New possibility to duplicate selected topic items [#5433].
- New 'create user' button in list of speakers if user was not found in the search box [#5307].
- New list of speakers statistic section on legal notice page [#5347].
- New "first contribution" hint for speakers [#5330].
- Improved showing comments in agenda list (as separate line) and projector queue [#5293].
- Improved line height of agenda slide [#5419].
- Fixed agenda PDF where the agenda item number was printed twice [#5417, #5454].
- Fixed negative speakers duration [#5447, #5448].
Motions:
- New electronic voting feature for motions [#5255].
- New possibility to create paragraph based amendments of paragraph based amendments [#5173].
- New option for page breaks in motion PDF export [#5191].
- New option to show all changes of amendments in main motion (clientside) [#5348].
- New 'done' indicator for motion block if all motions reached their final state [#5246].
- Improved PDF table of content (hide the recommendation if state is final) [#5192].
- Improved creating "final print version" (modified final version) also for motions without change recommendations [#5193, #5209].
- Improved voting results with nice charts.
- Improved navigation between amendments (reflects sorting of amendment list if the option "show amendments together with motions" is disabled) [#5245].
- Improved workflow manager for small devices [#5280].
- Improved sorting motions by category (sorts the list by category weight instead of the identifier) [#5308, #5310].
- Improved preselection and fallback behavior for motions with various change recommendation settings [#5366]
- Improved CSV/XLSX export (moved motion id as last column) for easier import via CSV [#5425].
- Fixed error by removing recommendation string in workflow manager [#5271].
- Fixed bug where TinyMCE changes would not update a motions save button [#5402].
Elections:
- New electronic voting feature for elections [#5255].
- Improved voting results with nice charts.
- Fixed some permission errors [#5194].
Users:
- New option to activate vote weight [#5305].
- New option to allow users to set themselves as present [#5283, #5317, #5319].
- Improved the permission "can see extra data" (only the fields email, comment, is_active, last_email_send are allowed) [#5423].
Mediafiles:
- External servers can be used to store media files [#5153, #5230].
Projector:
- New projector indicator for the currently projected element in all list view tables (visible for users without projector manage permission) [#5321].
- Improved "current list of speakers" reference for new projectors [#5273].
- Improved motion slide to hide submitter box if empty [#5367].
- New (configurable) monospace font for the countdown [#5378, #5408].
Version 3.1 (2019-12-13)
========================
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/3.0>`_
`Milestone <https://github.com/OpenSlides/OpenSlides/milestones/3.1>`_
General:
- Improved loading time of OpenSlides [#5061, 5087, #5110, #5146 - Breaks IE11].

View File

@ -2,280 +2,92 @@
OpenSlides Development
========================
This instruction helps you to setup a development environment for OpenSlides.
Check requirements
''''''''''''''''''
- ``docker``
- ``docker-compose``
- ``git``
- ``make``
**Note about migrating from development setups before version 3.4**: You must set the
``OPENSLIDES_USER_DATA_DIR`` variable in your ``server/personal_data/var/settings.py``
to ``'/app/personal_data/var'``. Another way is to just delete this file. It is
recreated with the right paths afterwards.
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
'''''''''''''''''''''''''''''
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
git clone https://github.com/OpenSlides/OpenSlides.git --recurse-submodules
cd OpenSlides
When updating the repository, submodules must be updated explicitly, too::
git submodule update
Start the development setup
'''''''''''''''''''''''''''
Use `make` to start the setup::
make run-dev
All your data (database, config, mediafiles) is stored in ``server/personal_data/var``.
To stop the setup press Ctrl+C. To clean up the docker containers run::
make stop-dev
Running the test cases
''''''''''''''''''''''
For all services in submodules check out the documentation there.
c. Setup a virtual Python environment (optional)
''''''''''''''''''''''''''''''''''''''''''''''''
Server tests and scripts
------------------------
See step 1. b. in the installation section in the `README.rst
<https://github.com/OpenSlides/OpenSlides/blob/master/README.rst>`_.
You need to have python (>=3.8) and python-venv installed. Change your workdirectory to the server::
cd server
d. Finish the server
''''''''''''''''''''
Setup an python virtual environment. If you have already done it, you can skip this step::
Install all required Python packages::
python3 -m venv .venv
source .venv/bin/activate
pip install -U -r requirements.txt
$ pip install --requirement requirements.txt
Make sure you are using the correct python version (e.g. try with explicit minor version: ``python3.8``). Activate it::
Create a settings file, run migrations and start the server::
source .venv/bin/activate
$ python manage.py createsettings
$ python manage.py migrate
$ python manage.py runserver
To deactivate it type ``deactivate``. Running all tests and linters::
To get help on the command line options run::
black openslides/ tests/
flake8 openslides/ tests/
mypy openslides/ tests/
isort -rc openslides/ tests/
pytest tests/
$ python manage.py --help
Client tests
------------
Later you might want to restart the server with one of the following commands.
You need `node` and `npm` installed. Change to the client's directory. For the first time, install all dependencies::
To start OpenSlides with this command and to avoid opening new browser windows
run::
cd client/
npm install
$ python manage.py start --no-browser
Run client tests::
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
npm test
Fix the code format and lint it with::
$ npm run prettify-write
$ npm run lint
npm run cleanup
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";
}
}
}
npm run extract

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)
Copyright (c) 2011-2019 Authors of OpenSlides, see AUTHORS
Copyright (c) 2011-2021 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__/*

23
Makefile Normal file
View File

@ -0,0 +1,23 @@
build-dev:
make -C caddy build-dev
git submodule foreach 'make build-dev'
docker-compose -f docker/docker-compose.dev.yml build
run-dev: | build-dev
USER_ID=$$(id -u $${USER}) GROUP_ID=$$(id -g $${USER}) docker-compose -f docker/docker-compose.dev.yml up
stop-dev:
docker-compose -f docker/docker-compose.dev.yml down
server-shell:
docker-compose -f docker/docker-compose.dev.yml run --entrypoint="" server docker/wait-for-dev-dependencies.sh
USER_ID=$$(id -u $${USER}) GROUP_ID=$$(id -g $${USER}) docker-compose -f docker/docker-compose.dev.yml run --entrypoint="" server bash
docker-compose -f docker/docker-compose.dev.yml down
reload-proxy:
docker-compose -f docker/docker-compose.dev.yml exec -w /etc/caddy proxy caddy reload
clear-cache:
docker-compose -f docker/docker-compose.dev.yml exec redis redis-cli flushall
docker-compose -f docker/docker-compose.dev.yml restart autoupdate
docker-compose -f docker/docker-compose.dev.yml restart server

View File

@ -5,159 +5,140 @@
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
OpenSlides is a free, web-based presentation and assembly system for
managing and projecting agenda, motions, and elections of assemblies. 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.
The main deployment method is using Git, Docker and Docker Compose. You only need
to have these tools installed and no further dependencies (m4 may not come preinstalled on your system). If you want a simpler
setup or are interested in developing, please refer to `development
instructions <DEVELOPMENT.rst>`_.
Get OpenSlides
--------------
First, you have to clone this repository to the latest stable branch::
git clone -b stable/3.4.x --single-branch https://github.com/OpenSlides/OpenSlides.git --recurse-submodules
**Note about migrating from version 3.3 or earlier**: With OpenSlides 3.4 submodules
and a Docker setup were introduced. If you ran into problems try to delete your
``settings.py``. If you have an old checkout you need to check out the current master
first and initialize all submodules::
git submodule update --init
Setup Docker Compose
--------------------
You need to build the Docker images and have to setup some configuration. First,
configure HTTPS by checking the `Using HTTPS`_ section. In this section are
reasons why HTTPS is required for large deployments.
Go to ``docker`` subdirectory::
cd docker
Then build all images with this script::
./build.sh all
You must define a Django secret key in ``secrets/django.env``, for example::
printf "DJANGO_SECRET_KEY='%s'\n" \
"$(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 64)" > secrets/django.env
We also strongly recommend that you set a secure admin password but it is not
strictly required. If you do not set an admin password, the default login
credentials will be displayed on the login page. Setting the admin password::
cp secrets/adminsecret.env.example secrets/adminsecret.env
vi secrets/adminsecret.env
Afterwards, generate the configuration file::
m4 docker-compose.yml.m4 > docker-compose.yml
You can configure OpenSlides using the `.env` file. See `More settings`_. Another
hint: If you choose to deploy the default configuration, a https certificate is
needed, so make sure you have set it up beforehand.
Finally, you can start the instance using ``docker-compose``::
docker-compose up
OpenSlides is accessible on https://localhost/ (or https, if configured).
You can also use daemonized instance::
docker-compose up -d
docker-compose logs
docker-compose down
Using HTTPS
-----------
The main reason (next to obviously security ones) HTTPS is required originates
from the need of HTTP/2. OpenSlides uses streaming responses to asynchronously
send data to the client. With HTTP/1.1 one TCP-Connection per request is opened.
Browsers limit the amount of concurrent connections
(`reference <https://docs.pushtechnology.com/cloud/latest/manual/html/designguide/solution/support/connection_limitations.html>`_),
so you are limited in opening tabs. HTTPS/2 just uses one connection per browser
and eliminates these restrictions. The main point to use HTTPS is that browsers
only use HTTP/2 if HTTPS is enabled.
Setting up HTTPS
""""""""""""""""
Use common providers for retrieving a certificate and private key for your
deployment. Place the certificate and private key in ``caddy/certs/cert.pem``
and ``caddy/certs/key.pem``. To use a self-signed localhost certificate, you can
execute ``caddy/make-localhost-cert.sh``.
The certificate and key are put into the docker image into ``/certs/``, so
setting up these files needs to be done before calling ``./build.sh``. When you
update the files, you must run ``./build.sh proxy`` again. If you want to have a
more flexible setup without the files in the image, you can also mount the
folder or the certificate and key into the running containers if you wish to do
so.
If both files are not present, OpenSlides will be configured to run with HTTP
only. When mounting the files make sure, that they are present during the
container startup.
Caddy, the proxy used, wants the user to persist the ``/data`` directory. If you
are going to use HTTPS add a volume in your ``docker-compose.yml`` /
``docker-stack.yml`` persisting the ``/data`` directory.
More settings
-------------
When generating the ``docker-compose.yml``, more settings can be adjusted in the
``docker/.env`` file. All changes for the backend are passed into djangos ``settings.py``.
You can find more information about most settings in the `settings documentation
<server/SETTINGS.rst>`_. To generate the ``docker-compose.yml`` use this command::
cd docker
( set -a; source .env; m4 docker-compose.yml.m4 ) > docker-compose.yml
For an advanced database setup refer to the `advanced configuration
<ADVANCED.rst>`_.
1. Installation on GNU/Linux or Mac OS X
----------------------------------------
Bugs, features and development
================================
a. Check requirements
'''''''''''''''''''''
Feel free to open issues here on GitHub! Please use the right templates for
bugs and features, and use them correctly. Pull requests are also welcome. For
a general overview of the development setup refer the `development instructions
<DEVELOPMENT.rst>`_.
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>`_.
For security relevant issues **do not** create public issues and refer to
our `security policy <SECURITY.md>`_.
Used software
@ -165,14 +146,14 @@ Used software
OpenSlides uses the following projects or parts of them:
* Several Python packages (see ``requirements/production.txt`` and ``requirements/big_mode.txt``).
* several Python packages (see ``server/requirements/production.txt``)
* Several JavaScript packages (see ``client/package.json``)
* 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.
under the MIT License, see `LICENSE file <LICENSE>`_. The authors of OpenSlides are
mentioned in the `AUTHORS file <AUTHORS>`_.

14
SECURITY.md Normal file
View File

@ -0,0 +1,14 @@
# Security Policy
## Supported Versions
Currently the recent versions (a year old at max) of the current major
release are supported to get security fixes. For older versions we urge
you to update to the most recent version of OpenSlides.
## Reporting a Vulnerability
Please let us know about the vulnerability by an email to
[security@openslides.com](mailto:security@openslides.com). Do not create
public issues for such issues. We will get in touch with you after reporting
the issue.

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.

1
autoupdate Submodule

@ -0,0 +1 @@
Subproject commit fa74dfe888dfeb4d071279eeaff5bc221ecf85f8

20
caddy/Caddyfile Normal file
View File

@ -0,0 +1,20 @@
import endpoint
import invalid_host*
reverse_proxy /system/* autoupdate:8002 {
flush_interval -1
}
@server {
path /apps/*
path /rest/*
path /server-version.txt
}
reverse_proxy @server server:8000
reverse_proxy /media/* media:8000
reverse_proxy client:4200
}
import redirect*

15
caddy/Caddyfile.dev Normal file
View File

@ -0,0 +1,15 @@
localhost:8000 {
reverse_proxy /system/* autoupdate:8002 {
flush_interval -1
}
@server {
path /apps/*
path /rest/*
path /server-version.txt
path /media/*
}
reverse_proxy @server server:8000
reverse_proxy client:4200
}

8
caddy/Dockerfile Normal file
View File

@ -0,0 +1,8 @@
FROM caddy:2.3.0-alpine
COPY Caddyfile /etc/caddy/Caddyfile
COPY entrypoint /entrypoint
COPY certs /certs
ENTRYPOINT ["/entrypoint"]
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]

3
caddy/Dockerfile.dev Normal file
View File

@ -0,0 +1,3 @@
FROM caddy:2.3.0-alpine
COPY Caddyfile.dev /etc/caddy/Caddyfile

2
caddy/Makefile Normal file
View File

@ -0,0 +1,2 @@
build-dev:
docker build -t os3-proxy-dev -f Dockerfile.dev .

78
caddy/entrypoint Executable file
View File

@ -0,0 +1,78 @@
#!/bin/sh
set -e
if [[ -z "$EXTERNAL_HTTP_PORT" ]] && [[ -z "$EXTERNAL_HTTPS_PORT" ]]; then
echo "EXTERNAL_HTTP_PORT and EXTERNAL_HTTPS_PORT are not set. Aborting."
exit 1
fi
if [[ -f "/certs/key.pem" ]] && [[ -f "/certs/cert.pem" ]]; then
certs_exists=1
fi
if [[ -n "$EXTERNAL_HTTPS_PORT" ]] && [[ -z "$certs_exists" ]]; then
echo "Configured https, but no certificates found. Aborting"
exit 1
fi
# config: https
if [[ -n "$EXTERNAL_HTTPS_PORT" ]] ; then
cat <<EOF > /etc/caddy/endpoint
https://:8001 {
tls /certs/cert.pem /certs/key.pem
EOF
echo "Configured https"
fi
# config: http and no https
if [[ -n "$EXTERNAL_HTTP_PORT" ]] && [[ -z "$EXTERNAL_HTTPS_PORT" ]] ; then
echo "http://:8000 {" > /etc/caddy/endpoint
echo "Configured http only"
fi
# config: https and additionally http -> create redirect-file
if [[ -n "$EXTERNAL_HTTP_PORT" ]] && [[ -n "$EXTERNAL_HTTPS_PORT" ]] ; then
cat <<EOF > /etc/caddy/redirect
http://:8000 {
redir https://$INSTANCE_DOMAIN{uri}
}
EOF
echo "Configured http to https redirect"
fi
# Add allowed hosts from $ALLOWED_HOSTS
# If the variable is empty, all hosts are allowed.
# The hosts are ORed, so the request is valid, if one host matches.
# Example: ALLOWED_HOSTS="localhost:8000 127.0.0.1:8000"
#
# @invalid-host {
# not {
# header Host localhost:8000
# header Host 127.0.0.1:8000
# }
# }
# respond @invalid-host "Misdirected Request" 421 {
# close
# }
if [[ ! -z "$ALLOWED_HOSTS" ]]; then
cat <<EOF > /etc/caddy/invalid_host
@invalid-host {
not {
EOF
for host in $ALLOWED_HOSTS; do
echo " host $host" >> /etc/caddy/invalid_host
done
cat <<EOF >> /etc/caddy/invalid_host
}
}
respond @invalid-host "Misdirected Request" 421 {
close
}
EOF
echo "Configured allowed hosts: $ALLOWED_HOSTS"
else
echo "All hosts allowed"
fi
exec "$@"

22
caddy/make-localhost-cert.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"
if [[ -f "certs/key.pem" ]] || [[ -f "certs/cert.pem" ]]; then
echo >&2 "Error: Certificate already exists."
exit 1
fi
if ! type 2>&1 >/dev/null openssl ; then
echo >&2 "Error: openssl not found!"
exit 1
fi
echo "Creating certificates..."
echo "You will need to accept an security exception for the"
echo "generated certificate in your browser manually."
openssl req -x509 -newkey rsa:4096 -nodes -days 3650 \
-subj "/C=DE/O=Selfsigned Test/CN=localhost" \
-keyout certs/key.pem -out certs/cert.pem
echo "done"

22
changelog-config.toml Normal file
View File

@ -0,0 +1,22 @@
Owner = "OpenSlides"
RepositoryName = "OpenSlides"
OutputType = "file"
FileName = "CHANGELOG.md"
CurrentRef = "master"
BaseBranch = "master"
PreviousRef = "3.3"
FutureCurrentRefName = "3.4"
ThresholdPreviousRef = 10
ThresholdCurrentRef = 10
Debug = false
DisplayLabel = false
# ignore everything labeld as "meta"
LabelExcludes = ["meta"]
LabelEnhancement = "feature"
LabelDocumentation = "documentation"
LabelBug = "bug"

7
client/.browserslistrc Normal file
View File

@ -0,0 +1,7 @@
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11

10
client/.compodocrc.json Normal file
View File

@ -0,0 +1,10 @@
{
"name": "OpenSlides Client Documentation",
"output": "../Compodoc/",
"tsconfig": "./tsconfig.json",
"disableCoverage": true,
"hideGenerator": true,
"theme": "material",
"customLogo": "./src/assets/img/openslides-logo.svg",
"customFavicon": "./src/assets/img/favicon.png"
}

4
client/.dockerignore Normal file
View File

@ -0,0 +1,4 @@
.git
**/node_modules
**/.vscode
build.sh

View File

@ -1,31 +1,5 @@
# 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`.
@ -38,16 +12,6 @@ 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.
@ -59,76 +23,79 @@ Language files can be found in `/src/assets/i18n`.
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
- [@angular/animations@12.1.1](https://github.com/angular/angular), License: MIT
- [@angular/cdk-experimental@12.1.1](https://github.com/angular/components), License: MIT
- [@angular/cdk@12.1.1](https://github.com/angular/components), License: MIT
- [@angular/common@12.1.1](https://github.com/angular/angular), License: MIT
- [@angular/compiler@12.1.1](https://github.com/angular/angular), License: MIT
- [@angular/core@12.1.1](https://github.com/angular/angular), License: MIT
- [@angular/forms@12.1.1](https://github.com/angular/angular), License: MIT
- [@angular/material-moment-adapter@12.1.1](https://github.com/angular/components), License: MIT
- [@angular/material@12.1.1](https://github.com/angular/components), License: MIT
- [@angular/platform-browser-dynamic@12.1.1](https://github.com/angular/angular), License: MIT
- [@angular/platform-browser@12.1.1](https://github.com/angular/angular), License: MIT
- [@angular/router@12.1.1](https://github.com/angular/angular), License: MIT
- [@angular/service-worker@12.1.1](https://github.com/angular/angular), License: MIT
- [@ngx-pwa/local-storage@12.0.0](https://github.com/cyrilletuzi/angular-async-local-storage), License: MIT
- [@ngx-translate/core@13.0.0](https://github.com/ngx-translate/core), License: MIT
- [@ngx-translate/http-loader@6.0.0](https://github.com/ngx-translate/http-loader), License: MIT
- [@pebula/ngrid-material@4.0.0-alpha.3](undefined), License: UNKNOWN
- [@pebula/ngrid@4.0.0-alpha.3](https://github.com/shlomiassaf/ngrid), License: MIT
- [@pebula/utils@1.0.2](undefined), License: UNKNOWN
- [@tinymce/tinymce-angular@4.2.4](https://github.com/tinymce/tinymce-angular), License: Apache-2.0
- [@videojs/http-streaming@2.9.1](https://github.com/videojs/http-streaming), License: Apache-2.0
- [acorn@8.4.1](https://github.com/acornjs/acorn), License: MIT
- [chart.js@2.9.4](https://github.com/chartjs/Chart.js), License: MIT
- [core-js@3.15.1](https://github.com/zloirock/core-js), License: MIT
- [css-element-queries@1.2.3](https://github.com/marcj/css-element-queries), License: MIT
- [exceljs@4.1.1](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
- [jszip@3.5.0](https://github.com/Stuk/jszip), License: (MIT OR GPL-3.0)
- [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
- [material-design-icons-iconfont@6.1.0](https://github.com/jossef/material-design-icons-iconfont), License: Apache-2.0
- [moment@2.27.0](https://github.com/moment/moment), License: MIT
- [ng-particles@2.6.0](https://github.com/matteobruni/tsparticles), License: MIT
- [ng2-charts@2.4.0](https://github.com/valor-software/ng2-charts), License: ISC
- [ng2-pdf-viewer@6.4.1](git+https://vadimdez@github.com/VadimDez/ng2-pdf-viewer), License: MIT
- [ngx-device-detector@2.0.0](undefined), License: MIT*
- [ngx-file-drop@11.1.0](https://github.com/georgipeltekov/ngx-file-drop), License: MIT
- [ngx-mat-select-search@3.3.0](https://github.com/bithost-gmbh/ngx-mat-select-search), License: MIT
- [ngx-material-timepicker@5.5.3](https://github.com/Agranom/ngx-material-timepicker), License: MIT
- [ngx-papaparse@4.0.4](https://github.com/alberthaff/ngx-papaparse), License: MIT
- [npm-license-crawler@0.2.1](http://github.com/mwittig/npm-license-crawler), License: BSD-3-Clause
- [pdfjs-dist@2.5.207](https://github.com/mozilla/pdfjs-dist), License: Apache-2.0
- [pdfmake@0.1.68](https://github.com/bpampuch/pdfmake), License: MIT
- [po2json@1.0.0-beta-2](https://github.com/mikeedwards/po2json), License: LGPL-2.0-or-later
- [rxjs@6.6.7](https://github.com/reactivex/rxjs), License: Apache-2.0
- [tinymce@5.7.1](https://github.com/tinymce/tinymce-dist), License: LGPL-2.1
- [tslib@2.3.0](https://github.com/Microsoft/tslib), License: 0BSD
- [tsparticles@1.23.0](https://github.com/matteobruni/tsparticles), License: MIT
- [video.js@7.13.3](https://github.com/videojs/video.js), License: Apache-2.0
- [zone.js@0.11.4](https://github.com/angular/angular), License: MIT
- [@angular-devkit/build-angular@12.1.1](https://github.com/angular/angular-cli), License: MIT
- [@angular-devkit/schematics@12.1.1](https://github.com/angular/angular-cli), License: MIT
- [@angular/cli@12.1.1](https://github.com/angular/angular-cli), License: MIT
- [@angular/compiler-cli@12.1.1](https://github.com/angular/angular), License: MIT
- [@angular/language-service@12.1.1](https://github.com/angular/angular), License: MIT
- [@biesbjerg/ngx-translate-extract-marker@1.0.0](https://github.com/biesbjerg/ngx-translate-extract-marker), License: MIT
- [@biesbjerg/ngx-translate-extract@7.0.4](https://github.com/biesbjerg/ngx-translate-extract), License: MIT
- [@compodoc/compodoc@1.0.9](https://github.com/compodoc/compodoc), License: MIT
- [@schematics/angular@10.0.8](https://github.com/angular/angular-cli), License: MIT
- [@types/jasmine@3.6.11](https://github.com/DefinitelyTyped/DefinitelyTyped), License: MIT
- [@types/node@14.6.2](https://github.com/DefinitelyTyped/DefinitelyTyped), License: MIT
- [@types/yargs@15.0.5](https://github.com/DefinitelyTyped/DefinitelyTyped), License: MIT
- [codelyzer@6.0.0](https://github.com/mgechev/codelyzer), License: MIT
- [husky@4.2.5](https://github.com/typicode/husky), License: MIT
- [jasmine-core@3.8.0](https://github.com/jasmine/jasmine), License: MIT
- [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
- [karma-coverage@2.0.3](https://github.com/karma-runner/karma-coverage), License: MIT
- [karma-jasmine-html-reporter@1.5.4](https://github.com/dfederm/karma-jasmine-html-reporter), License: MIT
- [karma-jasmine@4.0.1](https://github.com/karma-runner/karma-jasmine), License: MIT
- [karma@6.3.4](https://github.com/karma-runner/karma), License: MIT
- [prettier@2.3.2](https://github.com/prettier/prettier), License: MIT
- [protractor@7.0.0](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
- [ts-node@9.0.0](https://github.com/TypeStrong/ts-node), License: MIT
- [tslint@6.1.3](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
- [typescript@4.3.5](https://github.com/Microsoft/TypeScript), License: Apache-2.0

View File

@ -7,7 +7,7 @@
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"styleext": "scss"
"style": "scss"
}
},
"root": "",
@ -17,66 +17,83 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "../openslides/static",
"outputPath": "../server/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/"
"input": "node_modules/tinymce",
"output": "/tinymce/"
},
{
"glob": "**/*",
"input": "node_modules/tinymce/themes",
"output": "/tinymce/themes/"
},
{
"glob": "**/*",
"input": "node_modules/tinymce/plugins",
"output": "/tinymce/plugins/"
"glob": "pdf.worker.min.js",
"input": "node_modules/pdfjs-dist/build/",
"output": "/assets/js/"
}
],
"styles": ["src/styles.scss"],
"scripts": ["node_modules/tinymce/tinymce.min.js"],
"webWorkerTsConfig": "tsconfig.worker.json"
"scripts": [
"node_modules/tinymce/tinymce.min.js",
"node_modules/video.js/dist/video.min.js",
"src/assets/jitsi/external_api.js"
],
"webWorkerTsConfig": "tsconfig.worker.json",
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"fileReplacements": [{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}],
"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"
}]
"budgets": [
{
"type": "initial",
"maximumWarning": "5mb",
"maximumError": "10mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
]
},
"es5": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
],
"tsConfig": "./tsconfig-es5.app.json"
}
}
},
"development": {}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "client:build"
},
"configurations": {
"production": {
@ -84,8 +101,12 @@
},
"es5": {
"browserTarget": "client:build:es5"
},
"development": {
"browserTarget": "client:build:development"
}
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
@ -134,14 +155,17 @@
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "client:serve"
"protractorConfig": "e2e/protractor.conf.js"
},
"configurations": {
"production": {
"devServerTarget": "client:serve:production"
},
"development": {
"devServerTarget": "client:serve:development"
}
}
},
"defaultConfiguration": "development"
}
}
}

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

1
client/build.sh Symbolic link
View File

@ -0,0 +1 @@
../server/build.sh

24
client/docker/Dockerfile Normal file
View File

@ -0,0 +1,24 @@
FROM node:16 AS nodejs
RUN mkdir -p /build/app
WORKDIR /build/app
RUN useradd -m openslides
RUN chown -R openslides /build/app
USER root
RUN npm install -g @angular/cli@^12
RUN ng config -g cli.warnings.versionMismatch false
USER openslides
COPY package.json package-lock.json ./
RUN npm -v
RUN npm ci
COPY .browserslistrc *.json ./
COPY src ./src
RUN npm run build-to-dir /build/app/static
COPY docker/client-version.txt static/
FROM nginx
COPY --from=nodejs /build/app/static /usr/share/nginx/html
COPY docker/nginx.conf /etc/nginx/nginx.conf

View File

@ -0,0 +1,12 @@
FROM node:16
WORKDIR /app
COPY package.json .
COPY package-lock.json .
RUN npm ci
RUN npm run postinstall
COPY . .
CMD npm start

33
client/docker/nginx.conf Normal file
View File

@ -0,0 +1,33 @@
worker_processes auto;
events {
worker_connections 32000;
}
http {
server {
listen 4200;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
include /etc/nginx/mime.types;
# Optimizations for OpenSlides
client_max_body_size 100M;
proxy_connect_timeout 300s;
proxy_read_timeout 300s;
proxy_set_header Host $http_host;
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;
}
}
}

View File

@ -9,19 +9,25 @@ module.exports = function (config) {
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser
jasmine: {
timeoutInterval: 10000
}
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/Test'),
subdir: '.',
reporters: [{ type: 'html' }, { type: 'text-summary' }]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
@ -36,11 +42,6 @@ module.exports = function (config) {
}
},
singleRun: false,
proxies: {
'/apps/': 'http://localhost:8000/apps/',
'/media/': 'http://localhost:8000/media/',
'/rest/': 'http://localhost:8000/rest/',
'/ws/': 'ws://localhost:8000/'
}
restartOnFileChange: true
});
};

View File

@ -29,7 +29,7 @@
"dataGroups": [
{
"name": "api",
"urls": ["/rest/*", "/apps/*"],
"urls": ["/rest/*", "/apps/*", "/system/*", "/stats"],
"cacheConfig": {
"maxSize": 0,
"maxAge": "0u",

37662
client/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "OpenSlides3-Client",
"version": "3.1.1",
"version": "3.4.0",
"repository": {
"type": "git",
"url": "git://github.com/OpenSlides/OpenSlides.git"
@ -11,99 +11,107 @@
"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",
"start": "npm run ng-high-memory -- serve --proxy-config proxy.conf.json --host=0.0.0.0",
"start-https": "npm run ng-high-memory -- serve --ssl --ssl-cert localhost.pem --ssl-key localhost-key.pem --proxy-config proxy.conf.json --host=0.0.0.0",
"start-es5": "npm run ng-high-memory -- serve --proxy-config proxy.conf.json --host=0.0.0.0 --configuration es5",
"build": "npm run ng-high-memory -- build --configuration production",
"build-to-dir": "npm run build -- --output-path",
"postinstall": "ngcc",
"build-debug": "npm run ng-high-memory -- build",
"test": "ng test",
"test": "npm run ng-high-memory -- test",
"test-silently": "npm run test -- --watch=false --no-progress --browsers=ChromeHeadlessNoSandbox",
"test-live": "npm run test -- --watch=true --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 _",
"doc-serve": "./node_modules/.bin/compodoc -c .compodocrc.json -s -o -r",
"doc-build": "./node_modules/.bin/compodoc -c .compodocrc.json",
"extract": "ngx-translate-extract -i ./src -o ./src/assets/i18n/template-en.pot --clean --sort --format pot",
"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",
"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 && ./node_modules/.bin/po2json -f mf src/assets/i18n/it.po /dev/stdout | sed -f sed_replacements > src/assets/i18n/it.json && ./node_modules/.bin/po2json -f mf src/assets/i18n/es.po /dev/stdout | sed -f sed_replacements > src/assets/i18n/es.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",
"@angular/animations": "~12.1.1",
"@angular/cdk": "~12.1.1",
"@angular/cdk-experimental": "~12.1.1",
"@angular/common": "~12.1.1",
"@angular/compiler": "~12.1.1",
"@angular/core": "~12.1.1",
"@angular/forms": "~12.1.1",
"@angular/material": "~12.1.1",
"@angular/material-moment-adapter": "~12.1.1",
"@angular/platform-browser": "~12.1.1",
"@angular/platform-browser-dynamic": "~12.1.1",
"@angular/router": "~12.1.1",
"@angular/service-worker": "~12.1.1",
"@ngx-pwa/local-storage": "~12.0.0",
"@ngx-translate/core": "~13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"@pebula/ngrid": "4.0.0-alpha.3",
"@pebula/ngrid-material": "4.0.0-alpha.3",
"@pebula/utils": "1.0.2",
"@tinymce/tinymce-angular": "^4.2.4",
"@videojs/http-streaming": "^2.9.1",
"acorn": "^8.0.1",
"chart.js": "^2.9.3",
"core-js": "^3.6.5",
"css-element-queries": "^1.2.3",
"exceljs": "4.1.1",
"file-saver": "^2.0.2",
"hammerjs": "^2.0.8",
"jszip": "^3.7.1",
"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"
"material-design-icons-iconfont": "6.1.0",
"moment": "^2.27.0",
"ng-particles": "^2.6.0",
"ng2-charts": "^2.4.0",
"ng2-pdf-viewer": "^6.4.1",
"ngx-device-detector": "^2.0.0",
"ngx-file-drop": "^11.1.0",
"ngx-mat-select-search": "^3.3.0",
"ngx-material-timepicker": "^5.5.3",
"ngx-papaparse": "^4.0.4",
"pdfjs-dist": "2.5.207",
"pdfmake": "^0.1.68",
"po2json": "^1.0.0-beta-2",
"rxjs": "^6.6.2",
"tinymce": "5.7.1",
"tslib": "^2.0.0",
"tsparticles": "^1.23.0",
"video.js": "^7.8.4",
"zone.js": "~0.11.4"
},
"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",
"@angular-devkit/build-angular": "~12.1.1",
"@angular-devkit/schematics": "^12.1.1",
"@angular/cli": "~12.1.1",
"@angular/compiler-cli": "~12.1.1",
"@angular/language-service": "~12.1.1",
"@biesbjerg/ngx-translate-extract": "^7.0.4",
"@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
"@compodoc/compodoc": "^1.0.9",
"@schematics/angular": "^10.0.8",
"@types/jasmine": "~3.6.0",
"@types/node": "^14.6.2",
"@types/yargs": "^15.0.4",
"codelyzer": "^6.0.0",
"husky": "^4.2.3",
"jasmine-core": "~3.8.0",
"karma": "^6.3.4",
"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",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"prettier": "2.3.2",
"protractor": "^7.0.0",
"resize-observer-polyfill": "^1.5.1",
"source-map-explorer": "^2.0.1",
"ts-node": "~8.3.0",
"tslint": "~5.19.0",
"ts-node": "~9.0.0",
"tslint": "~6.1.3",
"tsutils": "3.17.1",
"typescript": "~3.5.3",
"webpack-bundle-analyzer": "^3.3.2"
"typescript": "~4.3.5"
}
}

View File

@ -15,5 +15,9 @@
"target": "ws://localhost:8000",
"secure": false,
"ws": true
},
"/system/": {
"target": "https://localhost:8002",
"secure": false
}
}

View File

@ -1,5 +1,5 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { Route, RouterModule } 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';
@ -7,11 +7,12 @@ import { LoginPrivacyPolicyComponent } from './site/login/components/login-priva
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';
import { UnsupportedBrowserComponent } from './site/login/components/unsupported-browser/unsupported-browser.component';
/**
* Global app routing
*/
const routes: Routes = [
const routes: Route[] = [
{
path: 'login',
component: LoginWrapperComponent,
@ -20,7 +21,8 @@ const routes: Routes = [
{ path: 'reset-password', component: ResetPasswordComponent },
{ path: 'reset-password-confirm', component: ResetPasswordConfirmComponent },
{ path: 'legalnotice', component: LoginLegalNoticeComponent },
{ path: 'privacypolicy', component: LoginPrivacyPolicyComponent }
{ path: 'privacypolicy', component: LoginPrivacyPolicyComponent },
{ path: 'unsupported-browser', component: UnsupportedBrowserComponent }
]
},
{
@ -34,7 +36,7 @@ const routes: Routes = [
];
@NgModule({
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })],
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload', relativeLinkResolution: 'legacy' })],
exports: [RouterModule]
})
export class AppRoutingModule {}

View File

@ -1,3 +1,4 @@
.content {
flex: 1;
height: 100vh;
}

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,22 +1,27 @@
import { ApplicationRef, Component } from '@angular/core';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { filter, take } from 'rxjs/operators';
import { first, tap } from 'rxjs/operators';
import { ChatNotificationService } from './site/chat/services/chat-notification.service';
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 { OfflineService } from './core/core-services/offline.service';
import { OpenSlidesStatusService } from './core/core-services/openslides-status.service';
import { OpenSlidesService } from './core/core-services/openslides.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';
import { VotingBannerService } from './core/ui-services/voting-banner.service';
declare global {
/**
@ -25,6 +30,12 @@ declare global {
*/
interface Array<T> {
flatMap(o: any): any[];
intersect(a: T[]): T[];
mapToObject(f: (item: T) => { [key: string]: any }): { [key: string]: any };
}
interface Set<T> {
equals(other: Set<T>): boolean;
}
/**
@ -34,8 +45,17 @@ declare global {
interface Number {
modulo(n: number): number;
}
interface String {
decode(): string;
}
}
/**
* May only be created once since this thing is fat
*/
const domParser = new DOMParser();
/**
* Angular's global App Component
*/
@ -52,37 +72,34 @@ export class AppComponent {
*
* 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
* Most of the injected service are not used - this is ok. It is needed to definitly
* run their constructors at app loading time
*/
public constructor(
private matIconRegistry: MatIconRegistry,
private domSanitizer: DomSanitizer,
translate: TranslateService,
appRef: ApplicationRef,
servertimeService: ServertimeService,
openslidesService: OpenSlidesService,
openslidesStatus: OpenSlidesStatusService,
router: Router,
offlineService: OfflineService,
operator: OperatorService,
loginDataService: LoginDataService,
constantsService: ConstantsService, // Needs to be started, so it can register itself to the WebsocketService
constantsService: ConstantsService,
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
routingState: RoutingStateService,
votingBannerService: VotingBannerService, // needed for initialisation,
chatNotificationService: ChatNotificationService
) {
// manually add the supported languages
translate.addLangs(['en', 'de', 'cs', 'ru']);
translate.addLangs(['en', 'de', 'it', 'es', '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
@ -91,60 +108,121 @@ export class AppComponent {
translate.use(translate.getLangs().includes(browserLang) ? browserLang : 'en');
// change default JS functions
this.overloadArrayToString();
this.overloadFlatMap();
this.overloadArrayFunctions();
this.overloadSetFunctions();
this.overloadModulo();
this.loadCustomIcons();
this.overloadDecodeString();
// 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)
first(stable => stable),
tap(() => console.debug('App is now stable!'))
)
.subscribe(() => servertimeService.startScheduler());
.subscribe(() => {
openslidesStatus.setStable();
servertimeService.startScheduler();
});
}
private overloadArrayFunctions(): void {
Object.defineProperty(Array.prototype, 'toString', {
value: 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;
},
enumerable: false
});
Object.defineProperty(Array.prototype, 'flatMap', {
value: function (o: any): any[] {
const concatFunction = (x: any, y: any[]) => x.concat(y);
const flatMapLogic = (f: any, xs: any) => xs.map(f).reduce(concatFunction, []);
return flatMapLogic(o, this);
},
enumerable: false
});
Object.defineProperty(Array.prototype, 'intersect', {
value: function <T>(other: T[]): T[] {
let a = this;
let b = other;
// indexOf to loop over shorter
if (b.length > a.length) {
[a, b] = [b, a];
}
return a.filter(e => b.indexOf(e) > -1);
},
enumerable: false
});
Object.defineProperty(Array.prototype, 'mapToObject', {
value: function <T>(f: (item: T) => { [key: string]: any }): { [key: string]: any } {
return this.reduce((aggr, item) => {
const res = f(item);
for (const key in res) {
if (res.hasOwnProperty(key)) {
aggr[key] = res[key];
}
}
return aggr;
}, {});
},
enumerable: false
});
}
/**
* 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.
* Adds some functions to Set.
*/
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];
private overloadSetFunctions(): void {
Object.defineProperty(Set.prototype, 'equals', {
value: function <T>(other: Set<T>): boolean {
const difference = new Set(this);
for (const elem of other) {
if (difference.has(elem)) {
difference.delete(elem);
} else {
return false;
}
}
return !difference.size;
},
enumerable: false
});
}
if (i < iterations - 1) {
string += ', ';
} else if (i === iterations && this.length > iterations) {
string += ', ...';
}
/**
* This is not the fastest solution but the most reliable one.
* Certain languages and TinyMCE do not follow the any predictable
* behaviour when it comes to encoding UTF8.
* decodeURI and decodeURIComponent were not able to successfully
* replace any ;&*uml with something meaningfull.
*/
private overloadDecodeString(): void {
Object.defineProperty(String.prototype, 'decode', {
enumerable: false,
value(): string {
const doc = domParser.parseFromString(this, 'text/html');
return doc.body.textContent || '';
}
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);
};
});
}
/**
@ -152,8 +230,18 @@ export class AppComponent {
* 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;
};
Object.defineProperty(Number.prototype, 'modulo', {
value: function (n: number): number {
return ((this % n) + n) % n;
},
enumerable: false
});
}
private loadCustomIcons(): void {
this.matIconRegistry.addSvgIcon(
`clapping_hands`,
this.domSanitizer.bypassSecurityTrustResourceUrl('../assets/svg/clapping_hands.svg')
);
}
}

View File

@ -1,14 +1,18 @@
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ServiceWorkerModule } from '@angular/service-worker';
import { StorageModule } from '@ngx-pwa/local-storage';
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 { ErrorService } from './core/core-services/error.service';
import { httpInterceptorProviders } from './core/core-services/http-interceptors';
import { LoginModule } from './site/login/login.module';
import { OpenSlidesTranslateModule } from './core/translate/openslides-translate-module';
import { SlidesModule } from './slides/slides.module';
@ -39,9 +43,14 @@ export function AppLoaderFactory(appLoadService: AppLoadService): () => Promise<
CoreModule,
LoginModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
SlidesModule.forRoot()
SlidesModule.forRoot(),
StorageModule.forRoot({ IDBNoWrap: false })
],
providers: [
{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true },
httpInterceptorProviders,
{ provide: ErrorHandler, useClass: ErrorService }
],
providers: [{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true }],
bootstrap: [AppComponent]
})
export class AppModule {}

View File

@ -2,6 +2,8 @@ import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { Permission } from './core/core-services/operator.service';
/**
* Provides functionalities that will be used by most components
* currently able to set the title with the suffix ' - OpenSlides'
@ -10,6 +12,11 @@ import { TranslateService } from '@ngx-translate/core';
* Components in the 'Side'- or 'projector' Folder are BaseComponents
*/
export abstract class BaseComponent {
/**
* To check permissions in templates using permission.[...]
*/
public permission = Permission;
/**
* To manipulate the browser title bar, adds the Suffix "OpenSlides"
*
@ -58,7 +65,10 @@ export abstract class BaseComponent {
mobile: {
theme: 'mobile',
plugins: ['autosave', 'lists', 'autolink']
}
},
relative_urls: false,
remove_script_host: true,
paste_preprocess: this.pastePreprocess
};
public constructor(protected titleService: Title, protected translate: TranslateService) {
@ -66,6 +76,29 @@ export abstract class BaseComponent {
this.tinyMceSettings.language = this.translate.currentLang;
}
/**
* Clean pasted HTML.
* If the user decides to copy-paste HTML (like from another OpenSlides motion detail)
* - remove all classes
* - remove data-line-number="X"
* - remove contenteditable="false"
*
* Not doing so would save control sequences from diff/linenumbering into the
* model which will open pandoras pox during PDF generation (and potentially web view)
* @param _
* @param args
*/
private pastePreprocess(_: any, args: any): void {
const getClassesRe: RegExp = new RegExp(/\s*class\=\"[\w\W]*?\"/, 'gi');
const getDataLineNumberRe: RegExp = new RegExp(/\s*data-line-number\=\"\d+\"/, 'gi');
const getContentEditableRe: RegExp = new RegExp(/\s*contenteditable\=\"\w+\"/, 'gi');
const cleanedContent = (args.content as string)
.replace(getClassesRe, '')
.replace(getDataLineNumberRe, '')
.replace(getContentEditableRe, '');
args.content = cleanedContent;
}
/**
* Set the title in web browser using angulars TitleService
* @param prefix The title prefix. Should be translated here.
@ -103,6 +136,5 @@ export abstract class BaseComponent {
protected onLeaveTinyMce(event: any): void {
console.log('tinyevent:', event.event.type);
this.saveHint = false;
// console.log("event: ", event.event.type);
}
}

View File

@ -3,6 +3,8 @@ 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 { ChatAppConfig } from 'app/site/chat/chat.config';
import { CinemaAppConfig } from 'app/site/cinema/cinema.config';
import { HistoryAppConfig } from 'app/site/history/history.config';
import { ProjectorAppConfig } from 'app/site/projector/projector.config';
import { TopicsAppConfig } from 'app/site/topics/topics.config';
@ -35,7 +37,9 @@ const appConfigs: AppConfig[] = [
UsersAppConfig,
HistoryAppConfig,
ProjectorAppConfig,
TopicsAppConfig
TopicsAppConfig,
CinemaAppConfig,
ChatAppConfig
];
/**
@ -68,15 +72,10 @@ export class AppLoadService {
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
);
this.modelMapper.registerCollectionElement(entry.model, entry.viewModel, repository);
if (this.isSearchableModelEntry(entry)) {
this.searchService.registerModel(
entry.collectionString,
entry.model.COLLECTIONSTRING,
repository,
entry.searchOrder,
entry.openInNewTab
@ -104,11 +103,11 @@ export class AppLoadService {
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.
// 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.`
`Wrong configuration for ${entry.model.COLLECTIONSTRING}: you gave a searchOrder, but the model is not searchable.`
);
}
return true;

View File

@ -3,7 +3,7 @@ import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router } from '@
import { FallbackRoutesService } from './fallback-routes.service';
import { OpenSlidesService } from './openslides.service';
import { OperatorService } from './operator.service';
import { OperatorService, Permission } from './operator.service';
/**
* Classical Auth-Guard. Checks if the user has to correct permissions to enter a page, and forwards to login if not.
@ -36,7 +36,7 @@ export class AuthGuard implements CanActivate, CanActivateChild {
* @param route the route the user wants to navigate to
*/
public canActivate(route: ActivatedRouteSnapshot): boolean {
const basePerm: string | string[] = route.data.basePerm;
const basePerm: Permission | Permission[] = route.data.basePerm;
if (!basePerm) {
return true;

View File

@ -8,6 +8,13 @@ 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';
import { StorageService } from './storage.service';
interface LoginData {
username: string;
password: string;
cookies?: boolean;
}
/**
* Authenticates an OpenSlides user with username and password
@ -29,7 +36,8 @@ export class AuthService {
private operator: OperatorService,
private OpenSlides: OpenSlidesService,
private router: Router,
private DS: DataStoreService
private DS: DataStoreService,
private storageService: StorageService
) {}
/**
@ -45,17 +53,23 @@ export class AuthService {
earlySuccessCallback: () => void
): Promise<void> {
if (authType === 'default') {
const user = {
const data: LoginData = {
username: username,
password: password
};
const response = await this.http.post<WhoAmI>(environment.urlPrefix + '/users/login/', user);
if (!navigator.cookieEnabled) {
data.cookies = false;
}
const response = await this.http.post<WhoAmI>(environment.urlPrefix + '/users/login/', data);
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') {
await this.operator.clearWhoAmIFromStorage(); // This is important:
// Then returning to the page, we do not want to have anything cached so a
// fresh whoami is executed.
window.location.href = environment.urlPrefix + '/saml/?sso'; // Bye
} else {
throw new Error(`Unsupported auth type "${authType}"`);
@ -67,7 +81,7 @@ export class AuthService {
* if it wasn't done before.
*/
public async redirectUser(userId: number): Promise<void> {
if (!this.OpenSlides.booted) {
if (!this.OpenSlides.isBooted) {
await this.OpenSlides.afterLoginBootup(userId);
}
@ -103,10 +117,12 @@ export class AuthService {
// We do nothing on failures. Reboot OpenSlides anyway.
}
this.router.navigate(['/']);
await this.storageService.clear();
await this.DS.clear();
await this.operator.setWhoAmI(response);
await this.OpenSlides.reboot();
} else if (authType === 'saml') {
await this.storageService.clear();
await this.DS.clear();
await this.operator.setWhoAmI(null);
window.location.href = environment.urlPrefix + '/saml/?slo'; // Bye

View File

@ -0,0 +1,177 @@
import { EventEmitter, Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { AutoupdateFormat } from 'app/core/definitions/autoupdate-format';
import { trailingThrottleTime } from 'app/core/rxjs/trailing-throttle-time';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { ConstantsService } from './constants.service';
import { HttpService } from './http.service';
interface ThrottleSettings {
AUTOUPDATE_DELAY?: number;
}
@Injectable({
providedIn: 'root'
})
export class AutoupdateThrottleService {
private readonly _autoupdatesToInject = new Subject<AutoupdateFormat>();
public get autoupdatesToInject(): Observable<AutoupdateFormat> {
return this._autoupdatesToInject.asObservable();
}
private readonly receivedAutoupdate = new EventEmitter<void>();
private pendingAutoupdates = [];
private disabledUntil: number | null = null;
private delay = 0;
private maxSeenChangeId = 0;
private get isActive(): boolean {
return this.delay !== 0;
}
public constructor(private constantsService: ConstantsService, private httpService: HttpService) {
this.constantsService.get<ThrottleSettings>('Settings').subscribe(settings => {
// This is a one-shot. If the delay was set one time >0, it cannot be changed afterwards.
// A change is more complicated since you have to unsubscribe, clean pending autoupdates,
// subscribe again and make sure, that no autoupdate is missed.
if (this.delay === 0 && settings.AUTOUPDATE_DELAY) {
this.delay = 1000 * settings.AUTOUPDATE_DELAY;
console.log(`Configured autoupdate delay: ${this.delay}ms`);
this.receivedAutoupdate
.pipe(trailingThrottleTime(this.delay))
.subscribe(() => this.processPendingAutoupdates());
} else if (this.delay === 0) {
console.log('No autoupdate delay');
}
});
this.httpService.responseChangeIds.subscribe(changeId => this.disableUntil(changeId));
}
public newAutoupdate(autoupdate: AutoupdateFormat): void {
if (autoupdate.to_change_id > this.maxSeenChangeId) {
this.maxSeenChangeId = autoupdate.to_change_id;
}
if (!this.isActive) {
this._autoupdatesToInject.next(autoupdate);
} else if (this.disabledUntil !== null) {
this._autoupdatesToInject.next(autoupdate);
if (autoupdate.to_change_id >= this.disabledUntil) {
this.disabledUntil = null;
console.log('Throttling autoupdates again');
}
} else if (autoupdate.all_data) {
// all_data=true (aka initial data) should be processed immediatly
// but since there can be pending autoupdates, add it there and
// process them now!
this.pendingAutoupdates.push(autoupdate);
this.processPendingAutoupdates();
} else {
this.pendingAutoupdates.push(autoupdate);
this.receivedAutoupdate.emit();
}
}
public disableUntil(changeId: number): void {
// Wait for an autoupdate with to_id >= changeId.
if (!this.isActive) {
return;
}
this.processPendingAutoupdates();
// Checking with maxSeenChangeId is for the following race condition:
// If the autoupdate comes before the response, it must not be throttled.
// But flushing pending autoupdates is important since *if* the autoupdate
// was early, it is in the pending queue.
if (changeId <= this.maxSeenChangeId) {
return;
}
console.log('Disable autoupdate until change id', changeId);
this.disabledUntil = changeId;
}
/**
* discard all pending autoupdates and resets the timer
*/
public discard(): void {
this.pendingAutoupdates = [];
}
private processPendingAutoupdates(): void {
if (this.pendingAutoupdates.length === 0) {
return;
}
const autoupdates = this.pendingAutoupdates;
this.discard();
console.log(`Processing ${autoupdates.length} pending autoupdates`);
const autoupdate = this.mergeAutoupdates(autoupdates);
this._autoupdatesToInject.next(autoupdate);
}
private mergeAutoupdates(autoupdates: AutoupdateFormat[]): AutoupdateFormat {
const mergedAutoupdate: AutoupdateFormat = {
changed: {},
deleted: {},
from_change_id: autoupdates[0].from_change_id,
to_change_id: autoupdates[autoupdates.length - 1].to_change_id,
all_data: false
};
let lastToChangeId = null;
for (const au of autoupdates) {
if (lastToChangeId === null) {
lastToChangeId = au.to_change_id;
} else {
if (au.from_change_id !== lastToChangeId) {
console.warn('!!!', autoupdates, au);
}
lastToChangeId = au.to_change_id;
}
this.applyAutoupdate(au, mergedAutoupdate);
}
return mergedAutoupdate;
}
private applyAutoupdate(from: AutoupdateFormat, into: AutoupdateFormat): void {
if (from.all_data) {
into.all_data = true;
into.changed = from.changed;
into.deleted = from.deleted;
return;
}
for (const collection of Object.keys(from.deleted)) {
for (const id of from.deleted[collection]) {
if (into.changed[collection]) {
into.changed[collection] = into.changed[collection].filter(obj => (obj as Identifiable).id !== id);
}
if (!into.deleted[collection]) {
into.deleted[collection] = [];
}
into.deleted[collection].push(id);
}
}
for (const collection of Object.keys(from.changed)) {
for (const obj of from.changed[collection]) {
if (into.deleted[collection]) {
into.deleted[collection] = into.deleted[collection].filter(id => id !== (obj as Identifiable).id);
}
if (!into.changed[collection]) {
into.changed[collection] = [];
}
into.changed[collection].push(obj);
}
}
}
}

View File

@ -1,43 +1,16 @@
import { Injectable } from '@angular/core';
import { AutoupdateFormat } from '../definitions/autoupdate-format';
import { AutoupdateThrottleService } from './autoupdate-throttle.service';
import { BaseModel } from '../../shared/models/base/base-model';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { CommunicationManagerService, OfflineError } from './communication-manager.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;
}
import { HttpService } from './http.service';
import { Mutex } from '../promises/mutex';
/**
* Handles the initial update and automatic updates using the {@link WebsocketService}
* Handles the initial update and automatic updates
* Incoming objects, usually BaseModels, will be saved in the dataStore (`this.DS`)
* This service usually creates all models
*/
@ -45,32 +18,52 @@ interface AutoupdateFormat {
providedIn: 'root'
})
export class AutoupdateService {
/**
* Constructor to create the AutoupdateService. Calls the constructor of the parent class.
* @param websocketService
* @param DS
* @param modelMapper
*/
private mutex = new Mutex();
private streamCloseFn: () => void | null = null;
private lastMessageContainedAllData = false;
public constructor(
private websocketService: WebsocketService,
private DS: DataStoreService,
private modelMapper: CollectionStringMapperService,
private DSUpdateManager: DataStoreUpdateManagerService
private DSUpdateManager: DataStoreUpdateManagerService,
private communicationManager: CommunicationManagerService,
private autoupdateThrottle: AutoupdateThrottleService
) {
this.websocketService.getOberservable<AutoupdateFormat>('autoupdate').subscribe(response => {
this.storeResponse(response);
});
this.communicationManager.startCommunicationEvent.subscribe(() => this.startAutoupdate());
// 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();
this.autoupdateThrottle.autoupdatesToInject.subscribe(autoupdate => this.storeAutoupdate(autoupdate));
}
public async startAutoupdate(changeId?: number): Promise<void> {
this.stopAutoupdate();
try {
this.streamCloseFn = await this.communicationManager.subscribe<AutoupdateFormat>(
'/system/autoupdate',
autoupdate => {
this.autoupdateThrottle.newAutoupdate(autoupdate);
},
() => ({ change_id: (changeId ? changeId : this.DS.maxChangeId).toString() })
);
} catch (e) {
if (!(e instanceof OfflineError)) {
console.error(e);
}
});
}
}
public stopAutoupdate(): void {
if (this.streamCloseFn) {
this.streamCloseFn();
this.streamCloseFn = null;
}
this.autoupdateThrottle.discard();
}
/**
* Handle the answer of incoming data via {@link WebsocketService}.
* Handle the answer of incoming data, after it was throttled.
*
* 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
@ -78,16 +71,19 @@ export class AutoupdateService {
*
* Handles the change ids of all autoupdates.
*/
private async storeResponse(autoupdate: AutoupdateFormat): Promise<void> {
private async storeAutoupdate(autoupdate: AutoupdateFormat): Promise<void> {
const unlock = await this.mutex.lock();
this.lastMessageContainedAllData = autoupdate.all_data;
if (autoupdate.all_data) {
await this.storeAllData(autoupdate);
} else {
await this.storePartialAutoupdate(autoupdate);
}
unlock();
}
/**
* Stores all data from the autoupdate. This means, that the DS is resettet and filled with just the
* Stores all data from the autoupdate. This means, that the DS is resetted and filled with just the
* given data from the autoupdate.
* @param autoupdate The autoupdate
*/
@ -116,27 +112,34 @@ export class AutoupdateService {
// 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);
await this.injectAutupdateIntoDS(autoupdate, true);
} else {
// autoupdate fully in the future. we are missing something!
this.requestChanges();
console.log('Autoupdate in the future', maxChangeId, autoupdate.from_change_id, autoupdate.to_change_id);
this.startAutoupdate(); // restarts it.
}
}
private async injectAutupdateIntoDS(autoupdate: AutoupdateFormat, flush: boolean): Promise<void> {
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]));
}
if (flush) {
await this.DS.flushToStorage(autoupdate.to_change_id);
}
this.DSUpdateManager.commit(updateSlot, autoupdate.to_change_id);
}
/**
* Creates baseModels for each plain object. If the collection is not registered,
* A console error will be issued and an empty list returned.
@ -155,36 +158,21 @@ export class AutoupdateService {
}
}
/**
* 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.`);
}
if (this.lastMessageContainedAllData) {
console.log('full update requested. Skipping, last message already contained all data');
} else {
console.log('requesting full update.');
// The mutex is needed, so the DS is not cleared, if there is
// another autoupdate running.
const unlock = await this.mutex.lock();
this.stopAutoupdate();
await this.DS.clear();
this.startAutoupdate();
unlock();
}
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

@ -47,12 +47,11 @@ export class CollectionStringMapperService {
* @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];
this.collectionStringMapping[model.COLLECTIONSTRING] = [model, viewModel, repository];
}
/**

View File

@ -0,0 +1,191 @@
import { HttpParams } from '@angular/common/http';
import { EventEmitter, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpService } from './http.service';
import { OfflineBroadcastService, OfflineReason } from './offline-broadcast.service';
import { OperatorService } from './operator.service';
import { SleepPromise } from '../promises/sleep';
import {
CommunicationError,
ErrorType,
Stream,
StreamContainer,
StreamingCommunicationService,
verboseErrorType
} from './streaming-communication.service';
type HttpParamsGetter = () => HttpParams | { [param: string]: string | string[] };
const MAX_STREAM_FAILURE_RETRIES = 3;
export class OfflineError extends Error {
public constructor() {
super('');
this.name = 'OfflineError';
}
}
interface StreamConnectionWrapper {
id: number;
url: string;
messageHandler: (message: any) => void;
params: HttpParamsGetter;
stream?: Stream<any>;
hasErroredAmount: number;
}
@Injectable({
providedIn: 'root'
})
export class CommunicationManagerService {
private communicationAllowed = false;
private readonly _startCommunicationEvent = new EventEmitter<void>();
public get startCommunicationEvent(): Observable<void> {
return this._startCommunicationEvent.asObservable();
}
private readonly _stopCommunicationEvent = new EventEmitter<void>();
public get stopCommunicationEvent(): Observable<void> {
return this._stopCommunicationEvent.asObservable();
}
private streamContainers: { [id: number]: StreamContainer<any> } = {};
public constructor(
private streamingCommunicationService: StreamingCommunicationService,
private offlineBroadcastService: OfflineBroadcastService,
private http: HttpService,
private operatorService: OperatorService
) {
this.offlineBroadcastService.goOfflineObservable.subscribe(() => this.closeConnections());
}
public async subscribe<T>(
url: string,
messageHandler: (message: T) => void,
params?: HttpParamsGetter
): Promise<() => void> {
if (!params) {
params = () => null;
}
const streamContainer = new StreamContainer(url, messageHandler, params);
return await this.connectWithWrapper(streamContainer);
}
public startCommunication(): void {
if (this.communicationAllowed) {
console.error('Illegal state! Do not emit this event multiple times');
} else {
this.communicationAllowed = true;
this._startCommunicationEvent.emit();
}
}
private async connectWithWrapper<T>(streamContainer: StreamContainer<T>): Promise<() => void> {
console.log('connect', streamContainer, streamContainer.stream);
const errorHandler = (type: ErrorType, error: CommunicationError, message: string) =>
this.handleError(streamContainer, type, error, message);
this.streamingCommunicationService.subscribe(streamContainer, errorHandler);
this.streamContainers[streamContainer.id] = streamContainer;
return () => this.close(streamContainer);
}
private async handleError<T>(
streamContainer: StreamContainer<T>,
type: ErrorType,
error: CommunicationError,
message: string
): Promise<void> {
console.log('handle Error', streamContainer, streamContainer.stream, verboseErrorType(type), error, message);
streamContainer.stream.close();
streamContainer.stream = null;
streamContainer.hasErroredAmount++;
if (streamContainer.hasErroredAmount > MAX_STREAM_FAILURE_RETRIES) {
this.goOffline(streamContainer, OfflineReason.ConnectionLost);
} else if (type === ErrorType.Client && error.type === 'auth_required') {
this.goOffline(streamContainer, OfflineReason.WhoAmIFailed);
} else {
// retry it after some time:
console.log(
`Retry no. ${streamContainer.hasErroredAmount} of ${MAX_STREAM_FAILURE_RETRIES} for ${streamContainer.url}`
);
try {
await this.delayAndCheckReconnection(streamContainer);
await this.connectWithWrapper(streamContainer);
} catch (e) {
// delayAndCheckReconnection can throw an OfflineError,
// which are just an 'abord mission' signal. Here, those errors can be ignored.
}
}
}
private async delayAndCheckReconnection<T>(streamContainer: StreamContainer<T>): Promise<void> {
let delay;
if (streamContainer.hasErroredAmount === 1) {
delay = 500; // the first error has a small delay since these error can happen normally.
} else {
delay = Math.floor(Math.random() * 3000 + 2000);
}
console.log(`retry again in ${delay} ms`);
await SleepPromise(delay);
// do not continue, if we are offline!
if (this.offlineBroadcastService.isOffline()) {
console.log('we are offline?');
throw new OfflineError();
}
// do not continue, if we are offline!
if (!this.shouldRetryConnecting()) {
console.log('operator changed, do not rety');
throw new OfflineError(); // TODO: This error is not really good....
}
}
public closeConnections(): void {
for (const streamWrapper of Object.values(this.streamContainers)) {
if (streamWrapper.stream) {
streamWrapper.stream.close();
}
}
this.streamContainers = {};
this.communicationAllowed = false;
this._stopCommunicationEvent.emit();
}
private goOffline<T>(streamContainer: StreamContainer<T>, reason: OfflineReason): void {
delete this.streamContainers[streamContainer.id];
this.closeConnections(); // here we close the connections early.
this.offlineBroadcastService.goOffline(reason);
}
private close(streamConnectionWrapper: StreamConnectionWrapper): void {
if (this.streamContainers[streamConnectionWrapper.id]) {
this.streamContainers[streamConnectionWrapper.id].stream.close();
delete this.streamContainers[streamConnectionWrapper.id];
}
}
// Checks the operator: If we do not have a valid user,
// do not even try to connect again..
private shouldRetryConnecting(): boolean {
return this.operatorService.guestsEnabled || !!this.operatorService.user;
}
public async isCommunicationServiceOnline(): Promise<boolean> {
try {
const response = await this.http.get<{ healthy: boolean }>('/system/health');
return !!response.healthy;
} catch (e) {
return false;
}
}
}

View File

@ -1,9 +1,11 @@
import { Injectable } from '@angular/core';
import { environment } from 'environments/environment';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
import { WebsocketService } from './websocket.service';
import { CommunicationManagerService } from './communication-manager.service';
import { HttpService } from './http.service';
/**
* constants have a key associated with the data.
@ -36,24 +38,13 @@ export class ConstantsService {
*/
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;
public constructor(communicationManager: CommunicationManagerService, private http: HttpService) {
communicationManager.startCommunicationEvent.subscribe(async () => {
this.constants = await this.http.get<Constants>(environment.urlPrefix + '/core/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();
});
}
/**
@ -66,14 +57,4 @@ export class ConstantsService {
}
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

@ -5,6 +5,7 @@ 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 { OpenSlidesStatusService } from './openslides-status.service';
import { RelationCacheService } from './relation-cache.service';
import { StorageService } from './storage.service';
@ -160,7 +161,8 @@ interface JsonStorage {
}
/**
* TODO: Avoid circular dependencies between `DataStoreUpdateManagerService` and `DataStoreService` and split them into two files
* TODO: Avoid circular dependencies between `DataStoreUpdateManagerService` and
* `DataStoreService` and split them into two files
*/
@Injectable({
providedIn: 'root'
@ -247,8 +249,17 @@ export class DataStoreUpdateManagerService {
slot.DS.triggerModifiedObservable();
// serve next slot request
this.serveNextSlot();
}
public dropUpdateSlot(): void {
this.currentUpdateSlot = null;
this.serveNextSlot();
}
private serveNextSlot(): void {
if (this.updateSlotRequests.length > 0) {
console.log('Concurrent update slots');
const request = this.updateSlotRequests.pop();
request.resolve();
}
@ -328,7 +339,8 @@ export class DataStoreService {
public constructor(
private storageService: StorageService,
private modelMapper: CollectionStringMapperService,
private DSUpdateManager: DataStoreUpdateManagerService
private DSUpdateManager: DataStoreUpdateManagerService,
private statusService: OpenSlidesStatusService
) {}
/**
@ -347,14 +359,21 @@ export class DataStoreService {
/**
* Gets the DataStore from cache and instantiate all models out of the serialized version.
* If something fails, the DS is cleared, so fresh data can be requrested from the server.
*
* @returns The max change id.
*/
public async initFromStorage(): Promise<number> {
public async initFromStorage(): Promise<void> {
// 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);
if (!store) {
await this.clear();
return;
}
const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this);
try {
// There is a store. Deserialize it
this.jsonStore = store;
this.modelStore = this.deserializeJsonStore(this.jsonStore);
@ -374,10 +393,10 @@ export class DataStoreService {
});
this.DSUpdateManager.commit(updateSlot, maxChangeId, true);
} else {
} catch (e) {
this.DSUpdateManager.dropUpdateSlot();
await this.clear();
}
return this.maxChangeId;
}
/**
@ -645,7 +664,21 @@ export class DataStoreService {
*/
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);
try {
await this.storageService.set(DataStoreService.cachePrefix + 'DS', this.jsonStore);
await this.storageService.set(DataStoreService.cachePrefix + 'maxChangeId', changeId);
} catch (e) {
if (e?.name === 'QuotaExceededError') {
this.statusService.setTooLessLocalStorage();
} else {
throw e;
}
}
}
public print(): void {
console.log('Max change id', this.maxChangeId);
console.log(JSON.stringify(this.jsonStore));
console.log(JSON.parse(JSON.stringify(this.modelStore)));
}
}

View File

@ -0,0 +1,36 @@
import { ErrorHandler, Injectable } from '@angular/core';
import { OpenSlidesStatusService } from './openslides-status.service';
@Injectable({
providedIn: 'root'
})
export class ErrorService extends ErrorHandler {
// TODO: This service cannot be injected into other services since it is constructed twice.
public constructor(private statusService: OpenSlidesStatusService) {
super();
}
public handleError(error: any): void {
const errorInformation = {
error,
name: this.guessName(error)
};
this.statusService.currentError.next(errorInformation);
super.handleError(error);
}
private guessName(error: any): string | null {
if (!error) {
return;
}
if (error.rejection?.name) {
return error.rejection.name;
}
if (error.name) {
return error.name;
}
}
}

View File

@ -1,11 +1,11 @@
import { Injectable } from '@angular/core';
import { OperatorService } from './operator.service';
import { OperatorService, Permission } from './operator.service';
export interface AuthGuardFallbackEntry {
route: string;
weight: number;
permission: string;
permission: Permission;
}
/**

View File

@ -0,0 +1,7 @@
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { StableInterceptorService } from './stable-interceptor.service';
export const httpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: StableInterceptorService, multi: true }
];

View File

@ -0,0 +1,36 @@
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { first, mergeMap } from 'rxjs/operators';
import { OpenSlidesStatusService } from '../openslides-status.service';
/**
* An http interceptor to make sure, that every request is made
* only when this application is stable.
*/
@Injectable({
providedIn: 'root'
})
export class StableInterceptorService implements HttpInterceptor {
private readonly stableSubject = new BehaviorSubject<boolean>(false);
public constructor(private openslidesStatus: OpenSlidesStatusService) {
this.update();
}
public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.stableSubject.pipe(
first(stable => stable),
mergeMap(() => {
return next.handle(req);
})
);
}
private async update(): Promise<void> {
await this.openslidesStatus.stable;
this.stableSubject.next(true);
}
}

View File

@ -10,7 +10,7 @@ describe('HttpService', () => {
});
// TODO: Write a working Test
// it('should be created', () => {
// const service: HttpService = TestBed.get(HttpService);
// const service: HttpService = TestBed.inject(HttpService);
// expect(service).toBeTruthy();
// });
});

View File

@ -2,27 +2,20 @@ import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Subject } from 'rxjs';
import { AutoupdateFormat } from '../definitions/autoupdate-format';
import { AutoupdateThrottleService } from './autoupdate-throttle.service';
import { HTTPMethod } from '../definitions/http-methods';
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 {
export interface ErrorDetailResponse {
detail: string | string[];
args?: string[];
}
function isDetailResponse(obj: any): obj is DetailResponse {
function isErrorDetailResponse(obj: any): obj is ErrorDetailResponse {
return (
obj &&
typeof obj === 'object' &&
@ -31,6 +24,15 @@ function isDetailResponse(obj: any): obj is DetailResponse {
);
}
interface AutoupdateResponse {
change_id: number;
data?: any;
}
function isAutoupdateReponse(obj: any): obj is AutoupdateResponse {
return obj && typeof obj === 'object' && typeof (obj as AutoupdateResponse).change_id === 'number';
}
/**
* Service for managing HTTP requests. Allows to send data for every method. Also (TODO) will do generic error handling.
*/
@ -43,6 +45,8 @@ export class HttpService {
*/
private defaultHeaders: HttpHeaders;
public readonly responseChangeIds = new Subject<number>();
/**
* Construct a HttpService
*
@ -82,7 +86,7 @@ export class HttpService {
): 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');
throw this.processError('You cannot make changes while in history mode');
}
// there is a current bug with the responseType.
@ -108,9 +112,10 @@ export class HttpService {
};
try {
return await this.http.request<T>(method, url, options).toPromise();
} catch (e) {
throw this.handleError(e);
const responseData: T = await this.http.request<T>(method, url, options).toPromise();
return this.processResponse(responseData);
} catch (error) {
throw this.processError(error);
}
}
@ -120,7 +125,7 @@ export class HttpService {
* @param e The error thrown.
* @returns The prepared and translated message for the user
*/
private handleError(e: any): string {
private processError(e: any): string {
let error = this.translate.instant('Error') + ': ';
// If the error is a string already, return it.
if (typeof e === 'string') {
@ -142,15 +147,20 @@ export class HttpService {
} 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);
if (isErrorDetailResponse(e.error)) {
error += this.processErrorDetailResponse(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(', ');
const errorList = Object.keys(e.error).map(key => {
const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
let detail = e.error[key];
if (detail instanceof Array) {
detail = detail.join(' ');
} else {
detail = this.processErrorDetailResponse(detail);
}
return `${this.translate.instant(capitalizedKey)}: ${detail}`;
});
error = errorList.join(', ');
}
} else if (e.status === 500) {
error += this.translate.instant('A server error occured. Please contact your system administrator.');
@ -169,11 +179,9 @@ export class HttpService {
* @param str a string or a string array to join together.
* @returns Error text(s) as single string
*/
private processDetailResponse(response: DetailResponse): string {
private processErrorDetailResponse(response: ErrorDetailResponse): string {
let message: string;
if (response instanceof Array) {
message = response.join(' ');
} else if (response.detail instanceof Array) {
if (response.detail instanceof Array) {
message = response.detail.join(' ');
} else {
message = response.detail;
@ -188,6 +196,14 @@ export class HttpService {
return message;
}
private processResponse<T>(responseData: T): T {
if (isAutoupdateReponse(responseData)) {
this.responseChangeIds.next(responseData.change_id);
return responseData.data;
}
return responseData;
}
/**
* Executes a get on a path with a certain object
* @param path The path to send the request to.
@ -254,4 +270,26 @@ export class HttpService {
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);
}
/**
* Retrieves a binary file from the url and returns a base64 value
*
* @param url file url
* @returns a promise with a base64 string
*/
public async downloadAsBase64(url: string): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
const headers = new HttpHeaders();
const file = await this.get<Blob>(url, {}, {}, headers, 'blob');
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
const resultStr: string = reader.result as string;
resolve(resultStr.split(',')[1]);
};
reader.onerror = error => {
reject(error);
};
});
}
}

View File

@ -2,6 +2,8 @@ import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { Permission } from './operator.service';
/**
* This represents one entry in the main menu
*/
@ -28,7 +30,7 @@ export interface MainMenuEntry {
/**
* The permission to see the entry.
*/
permission: string;
permission: Permission;
}
/**

View File

@ -2,8 +2,9 @@ import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { CommunicationManagerService, OfflineError } from './communication-manager.service';
import { HttpService } from './http.service';
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.
@ -17,7 +18,12 @@ interface NotifyBase<T> {
/**
* The content to send.
*/
content: T;
message: T;
}
function isNotifyBase(obj: object): obj is NotifyResponse<any> {
const base = obj as NotifyBase<any>;
return !!obj && base.message !== undefined && base.name !== undefined;
}
/**
@ -26,15 +32,18 @@ interface NotifyBase<T> {
* channel names.
*/
export interface NotifyRequest<T> extends NotifyBase<T> {
channel_id: string;
to_all?: boolean;
/**
* User ids (or `true` for all users) to send this message to.
*/
users?: number[] | boolean;
to_users?: number[];
/**
* An array of channels to send this message to.
*/
replyChannels?: string[];
to_channels?: string[];
}
/**
@ -45,12 +54,12 @@ 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;
sender_channel_id: string;
/**
* The user id of the user who sends this message. It is 0 for Anonymous.
*/
senderUserId: number;
sender_user_id: number;
/**
* This is validated here and is true, if the senderUserId matches the current operator's id.
@ -59,6 +68,20 @@ export interface NotifyResponse<T> extends NotifyBase<T> {
sendByThisUser: boolean;
}
function isNotifyResponse(obj: object): obj is NotifyResponse<any> {
const response = obj as NotifyResponse<any>;
// Note: we do not test for sendByThisUser, since it is set later in our code.
return isNotifyBase(obj) && response.sender_channel_id !== undefined && response.sender_user_id !== undefined;
}
interface ChannelIdResponse {
channel_id: string;
}
function isChannelIdResponse(obj: object): obj is ChannelIdResponse {
return !!obj && (obj as ChannelIdResponse).channel_id !== undefined;
}
/**
* Handles all incoming and outgoing notify messages via {@link WebsocketService}.
*/
@ -78,18 +101,41 @@ export class NotifyService {
[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);
private channelId: string;
public constructor(
private communicationManager: CommunicationManagerService,
private http: HttpService,
private operator: OperatorService
) {
this.communicationManager.startCommunicationEvent.subscribe(() => this.startListening());
this.communicationManager.stopCommunicationEvent.subscribe(() => (this.channelId = null));
}
private async startListening(): Promise<void> {
try {
await this.communicationManager.subscribe<NotifyResponse<any> | ChannelIdResponse>(
'/system/notify',
notify => {
if (isChannelIdResponse(notify)) {
this.channelId = notify.channel_id;
} else if (isNotifyResponse(notify)) {
notify.sendByThisUser =
notify.sender_user_id === (this.operator.user ? this.operator.user.id : 0);
this.notifySubject.next(notify);
if (this.messageSubjects[notify.name]) {
this.messageSubjects[notify.name].next(notify);
}
} else {
console.error('Unknwon notify message', notify);
}
}
);
} catch (e) {
if (!(e instanceof OfflineError)) {
console.error(e);
}
});
}
}
/**
@ -97,8 +143,8 @@ export class NotifyService {
* @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);
public async sendToAllUsers<T>(name: string, content: T): Promise<void> {
await this.send(name, content, true);
}
/**
@ -107,8 +153,11 @@ export class NotifyService {
* @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);
public async sendToUsers<T>(name: string, content: T, ...users: number[]): Promise<void> {
if (users.length < 1) {
throw new Error('You have to provide at least one user');
}
await this.send(name, content, false, users);
}
/**
@ -117,35 +166,48 @@ export class NotifyService {
* @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 {
public async sendToChannels<T>(name: string, content: T, ...channels: string[]): Promise<void> {
if (channels.length < 1) {
throw new Error('You have to provide at least one channel');
}
this.send(name, content, null, channels);
await this.send(name, content, false, null, channels);
}
/**
* General send function for notify messages.
* @param name The name of the notify message
* @param content The payload to send.
* @param message 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 {
private async send<T>(
name: string,
message: T,
toAll?: boolean,
users?: number[],
channels?: string[]
): Promise<void> {
if (!this.channelId) {
throw new Error('No channel id!');
}
const notify: NotifyRequest<T> = {
name: name,
content: content
message: message,
channel_id: this.channelId
};
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 (toAll === true) {
notify.to_all = true;
}
if (users !== null) {
notify.users = users;
if (users) {
notify.to_users = users;
}
if (channels !== null) {
notify.replyChannels = channels;
if (channels) {
notify.to_channels = channels;
}
this.websocketService.send('notify', notify);
console.debug('send notify', notify);
await this.http.post<unknown>('/system/notify/send', notify);
}
/**

View File

@ -0,0 +1,37 @@
import { EventEmitter, Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
export enum OfflineReason {
WhoAmIFailed,
ConnectionLost
}
@Injectable({
providedIn: 'root'
})
export class OfflineBroadcastService {
public readonly isOfflineSubject = new BehaviorSubject<boolean>(false);
public get isOfflineObservable(): Observable<boolean> {
return this.isOfflineSubject.asObservable();
}
private readonly _goOffline = new EventEmitter<OfflineReason>();
public get goOfflineObservable(): Observable<OfflineReason> {
return this._goOffline.asObservable();
}
public constructor() {}
public goOffline(reason: OfflineReason): void {
this._goOffline.emit(reason);
}
public isOffline(): boolean {
return this.isOfflineSubject.getValue();
}
public isOnline(): boolean {
return !this.isOffline();
}
}

View File

@ -1,6 +1,9 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { CommunicationManagerService } from './communication-manager.service';
import { OfflineBroadcastService, OfflineReason } from './offline-broadcast.service';
import { OpenSlidesService } from './openslides.service';
import { OperatorService, WhoAmI } from './operator.service';
/**
* This service handles everything connected with being offline.
@ -12,44 +15,111 @@ import { BehaviorSubject, Observable } from 'rxjs';
providedIn: 'root'
})
export class OfflineService {
/**
* BehaviorSubject to receive further status values.
*/
private offline = new BehaviorSubject<boolean>(false);
private reason: OfflineReason | null;
/**
* 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;
public constructor(
private OpenSlides: OpenSlidesService,
private offlineBroadcastService: OfflineBroadcastService,
private operatorService: OperatorService,
private communicationManager: CommunicationManagerService
) {
this.offlineBroadcastService.goOfflineObservable.subscribe((reason: OfflineReason) => this.goOffline(reason));
}
/**
* Sets the offline flag. Restores the DataStoreService to the last known configuration.
* Helper function to set offline status
*/
public goOfflineBecauseFailedWhoAmI(): void {
if (!this.offline.getValue()) {
console.log('offline because whoami failed.');
public goOffline(reason: OfflineReason): void {
if (this.offlineBroadcastService.isOffline()) {
return;
}
this.offline.next(true);
}
this.reason = reason;
/**
* Sets the offline flag, because there is no connection to the server.
*/
public goOfflineBecauseConnectionLost(): void {
if (!this.offline.getValue()) {
if (reason === OfflineReason.ConnectionLost) {
console.log('offline because connection lost.');
} else if (reason === OfflineReason.WhoAmIFailed) {
console.log('offline because whoami failed.');
} else {
console.error('No such offline reason', reason);
}
this.offline.next(true);
this.offlineBroadcastService.isOfflineSubject.next(true);
this.checkStillOffline();
}
private checkStillOffline(): void {
const timeout = Math.floor(Math.random() * 3000 + 2000);
console.log(`Try to go online in ${timeout} ms`);
setTimeout(async () => {
let online: boolean;
let whoami: WhoAmI | null = null;
if (this.reason === OfflineReason.ConnectionLost) {
online = await this.communicationManager.isCommunicationServiceOnline();
console.log('is communication online? ', online);
} else if (this.reason === OfflineReason.WhoAmIFailed) {
const result = await this.operatorService.whoAmI();
online = result.online;
whoami = result.whoami;
console.log('is whoami reachable?', online);
}
if (online) {
await this.goOnline(whoami);
// TODO: check all other reasons -> e.g. if the
// connection was lost, the operator must be checked and the other way
// around the comminucation must be started!!
// stop trying.
} else {
// continue trying.
this.checkStillOffline();
}
}, timeout);
}
/**
* Function to return to online-status.
*
* First, we have to check, if all other sources (except this.reason) are online, too.
* This results in definetly having a whoami response at this point.
* If this is the case, we need to setup everything again:
* 1) check the operator. If this allowes for an logged in state (or anonymous is OK), do
* step 2, otherwise done.
* 2) enable communications.
*/
public goOnline(): void {
this.offline.next(false);
private async goOnline(whoami?: WhoAmI): Promise<void> {
console.log('go online!', this.reason, whoami);
if (this.reason === OfflineReason.ConnectionLost) {
// now we have to check whoami
const result = await this.operatorService.whoAmI();
if (!result.online) {
console.log('whoami down.');
this.reason = OfflineReason.WhoAmIFailed;
this.checkStillOffline();
return;
}
whoami = result.whoami;
} else if (this.reason === OfflineReason.WhoAmIFailed) {
const online = await this.communicationManager.isCommunicationServiceOnline();
if (!online) {
console.log('communication down.');
this.reason = OfflineReason.ConnectionLost;
this.checkStillOffline();
return;
}
}
console.log('we are online!');
// Ok, we are online now!
const isLoggedIn = await this.OpenSlides.checkWhoAmI(whoami);
console.log('logged in:', isLoggedIn);
if (isLoggedIn) {
this.communicationManager.startCommunication();
}
console.log('done');
this.offlineBroadcastService.isOfflineSubject.next(false);
}
}

View File

@ -1,6 +1,15 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { History } from 'app/shared/models/core/history';
import { BannerDefinition, BannerService } from '../ui-services/banner.service';
import { Deferred } from '../promises/deferred';
export interface ErrorInformation {
error: any;
name?: string;
}
/**
* Holds information about OpenSlides. This is not included into other services to
@ -14,6 +23,11 @@ export class OpenSlidesStatusService {
* in History mode, saves the history point.
*/
private history: History = null;
private historyBanner: BannerDefinition = {
type: 'history'
};
private tooLessLocalStorage = false;
/**
* Returns, if OpenSlides is in the history mode.
@ -22,12 +36,26 @@ export class OpenSlidesStatusService {
return !!this.history;
}
public get stable(): Promise<void> {
return this._stable;
}
public isPrioritizedClient = false;
public readonly currentError = new BehaviorSubject<ErrorInformation | null>(null);
private _stable = new Deferred();
private _bootedSubject = new BehaviorSubject<boolean>(false);
/**
* Ctor, does nothing.
*/
public constructor() {}
public constructor(private banner: BannerService) {}
public setStable(): void {
this._stable.resolve();
this._bootedSubject.next(true);
}
/**
* Calls the getLocaleString function of the history object, if present.
@ -44,6 +72,7 @@ export class OpenSlidesStatusService {
*/
public enterHistoryMode(history: History): void {
this.history = history;
this.banner.addBanner(this.historyBanner);
}
/**
@ -51,5 +80,13 @@ export class OpenSlidesStatusService {
*/
public leaveHistoryMode(): void {
this.history = null;
this.banner.removeBanner(this.historyBanner);
}
public setTooLessLocalStorage(): void {
if (!this.tooLessLocalStorage) {
this.tooLessLocalStorage = true;
this.banner.addBanner({ type: 'tooLessLocalStorage' });
}
}
}

View File

@ -3,12 +3,12 @@ import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { AutoupdateService } from './autoupdate.service';
import { ConstantsService } from './constants.service';
import { CommunicationManagerService } from './communication-manager.service';
import { DataStoreService } from './data-store.service';
import { OperatorService } from './operator.service';
import { OfflineBroadcastService, OfflineReason } from './offline-broadcast.service';
import { OpenSlidesStatusService } from './openslides-status.service';
import { OperatorService, WhoAmI } from './operator.service';
import { StorageService } from './storage.service';
import { WebsocketService } from './websocket.service';
/**
* Handles the bootup/showdown of this application.
@ -35,30 +35,15 @@ export class OpenSlidesService {
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 openslidesStatus: OpenSlidesStatusService,
private router: Router,
private autoupdateService: AutoupdateService,
private DS: DataStoreService,
private constantsService: ConstantsService
private communicationManager: CommunicationManagerService,
private offlineBroadcastService: OfflineBroadcastService
) {
// 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();
}
@ -68,20 +53,24 @@ export class OpenSlidesService {
*/
public async bootup(): Promise<void> {
// start autoupdate if the user is logged in:
let response = await this.operator.whoAmIFromStorage();
const needToCheckOperator = !!response;
let whoami = await this.operator.whoAmIFromStorage();
const needToCheckOperator = !!whoami;
if (!response) {
response = await this.operator.whoAmI();
if (!whoami) {
const response = await this.operator.whoAmI();
if (!response.online) {
this.offlineBroadcastService.goOffline(OfflineReason.WhoAmIFailed);
}
whoami = response.whoami;
}
if (!response.user && !response.guest_enabled) {
if (!whoami.user && !whoami.guest_enabled) {
if (!location.pathname.includes('error')) {
this.redirectUrl = location.pathname;
}
this.redirectToLoginIfNotSubpage();
} else {
await this.afterLoginBootup(response.user_id);
await this.afterLoginBootup(whoami.user_id);
}
if (needToCheckOperator) {
@ -121,7 +110,7 @@ export class OpenSlidesService {
await this.DS.clear();
await this.storageService.set('lastUserLoggedIn', userId);
}
await this.setupDataStoreAndWebSocket();
await this.setupDataStoreAndStartCommunication();
// Now finally booted.
this.booted.next(true);
}
@ -129,26 +118,17 @@ export class OpenSlidesService {
/**
* 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.
private async setupDataStoreAndStartCommunication(): Promise<void> {
await this.DS.initFromStorage();
await this.openslidesStatus.stable;
this.communicationManager.startCommunication();
}
/**
* Shuts down OpenSlides. The websocket connection is closed and the operator is not set.
* Shuts down OpenSlides.
*/
public async shutdown(): Promise<void> {
await this.websocketService.close();
this.communicationManager.closeConnections();
this.booted.next(false);
}
@ -170,29 +150,37 @@ export class OpenSlidesService {
await this.bootup();
}
public async checkOperator(requestChanges: boolean = true): Promise<void> {
const response = await this.operator.whoAmI();
if (!response.online) {
this.offlineBroadcastService.goOffline(OfflineReason.WhoAmIFailed);
}
await this.checkWhoAmI(response.whoami, requestChanges);
}
/**
* Verify that the operator is the same as it was before. Should be alled on a reconnect.
*
* @returns true, if the user is still logged in
*/
private async checkOperator(requestChanges: boolean = true): Promise<void> {
const response = await this.operator.whoAmI();
public async checkWhoAmI(whoami: WhoAmI, requestChanges: boolean = true): Promise<boolean> {
let isLoggedIn = false;
// User logged off.
if (!response.user && !response.guest_enabled) {
this.websocketService.cancelReconnectenRetry();
if (!whoami.user && !whoami.guest_enabled) {
await this.shutdown();
this.redirectToLoginIfNotSubpage();
} else {
isLoggedIn = true;
if (
(this.operator.user && this.operator.user.id !== response.user_id) ||
(!this.operator.user && response.user_id)
(this.operator.user && this.operator.user.id !== whoami.user_id) ||
(!this.operator.user && whoami.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();
}
}
return isLoggedIn;
}
}

View File

@ -10,7 +10,6 @@ import { CollectionStringMapperService } from './collection-string-mapper.servic
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';
@ -21,7 +20,42 @@ import { UserRepositoryService } from '../repositories/users/user-repository.ser
* Permissions on the client are just strings. This makes clear, that
* permissions instead of arbitrary strings should be given.
*/
export type Permission = string;
export enum Permission {
agendaCanManage = 'agenda.can_manage',
agendaCanSee = 'agenda.can_see',
agendaCanSeeInternalItems = 'agenda.can_see_internal_items',
agendaCanManageListOfSpeakers = 'agenda.can_manage_list_of_speakers',
agendaCanSeeListOfSpeakers = 'agenda.can_see_list_of_speakers',
agendaCanBeSpeaker = 'agenda.can_be_speaker',
assignmentsCanManage = 'assignments.can_manage',
assignmentsCanNominateOther = 'assignments.can_nominate_other',
assignmentsCanNominateSelf = 'assignments.can_nominate_self',
assignmentsCanSee = 'assignments.can_see',
coreCanManageConfig = 'core.can_manage_config',
coreCanManageLogosAndFonts = 'core.can_manage_logos_and_fonts',
coreCanSeeHistory = 'core.can_see_history',
coreCanManageProjector = 'core.can_manage_projector',
coreCanSeeFrontpage = 'core.can_see_frontpage',
coreCanSeeProjector = 'core.can_see_projector',
coreCanManageTags = 'core.can_manage_tags',
coreCanSeeLiveStream = 'core.can_see_livestream',
coreCanSeeAutopilot = 'core.can_see_autopilot',
mediafilesCanManage = 'mediafiles.can_manage',
mediafilesCanSee = 'mediafiles.can_see',
motionsCanCreate = 'motions.can_create',
motionsCanCreateAmendments = 'motions.can_create_amendments',
motionsCanManage = 'motions.can_manage',
motionsCanManageMetadata = 'motions.can_manage_metadata',
motionsCanManagePolls = 'motions.can_manage_polls',
motionsCanSee = 'motions.can_see',
motionsCanSeeInternal = 'motions.can_see_internal',
motionsCanSupport = 'motions.can_support',
usersCanChangePassword = 'users.can_change_password',
usersCanManage = 'users.can_manage',
usersCanSeeExtraData = 'users.can_see_extra_data',
usersCanSeeName = 'users.can_see_name',
chatCanManage = 'chat.can_manage'
}
/**
* Response format of the WhoAmI request.
@ -83,6 +117,13 @@ export class OperatorService implements OnAfterAppsLoaded {
return this._viewUser;
}
/**
* Returns 0 for the anonymous
*/
public get userId(): number {
return this.user?.id || 0;
}
public get isAnonymous(): boolean {
return !this.user || this.user.id === 0;
}
@ -173,7 +214,6 @@ export class OperatorService implements OnAfterAppsLoaded {
public constructor(
private http: HttpService,
private DS: DataStoreService,
private offlineService: OfflineService,
private collectionStringMapper: CollectionStringMapperService,
private storageService: StorageService,
private OSStatus: OpenSlidesStatusService
@ -252,6 +292,10 @@ export class OperatorService implements OnAfterAppsLoaded {
return response;
}
public async clearWhoAmIFromStorage(): Promise<void> {
await this.storageService.remove(WHOAMI_STORAGE_KEY);
}
/**
* Sets the operator user. Will be saved to storage
* @param user The new operator.
@ -268,18 +312,19 @@ export class OperatorService implements OnAfterAppsLoaded {
*
* @returns The response of the WhoAmI request.
*/
public async whoAmI(): Promise<WhoAmI> {
public async whoAmI(): Promise<{ whoami: WhoAmI; online: boolean }> {
let online = true;
try {
const response = await this.http.get(environment.urlPrefix + '/users/whoami/');
if (isWhoAmI(response)) {
await this.updateCurrentWhoAmI(response);
} else {
this.offlineService.goOfflineBecauseFailedWhoAmI();
online = false;
}
} catch (e) {
this.offlineService.goOfflineBecauseFailedWhoAmI();
online = false;
}
return this.currentWhoAmI;
return { whoami: this.currentWhoAmI, online };
}
/**
@ -390,12 +435,12 @@ export class OperatorService implements OnAfterAppsLoaded {
} 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);
const defaultGroup: Group = this.DS.get<Group>('users/group', 1);
if (defaultGroup && defaultGroup.permissions instanceof Array) {
this.permissions = defaultGroup.permissions;
}
} else {
const permissionSet = new Set<string>();
const permissionSet = new Set<Permission>();
this.DS.getMany(Group, this.user.groups_id).forEach(group => {
group.permissions.forEach(permission => {
permissionSet.add(permission);
@ -416,6 +461,13 @@ export class OperatorService implements OnAfterAppsLoaded {
this.operatorSubject.next(this.user);
}
/**
* Set the operators presence to isPresent
*/
public async setPresence(isPresent: boolean): Promise<void> {
await this.http.post(environment.urlPrefix + '/users/setpresence/', isPresent);
}
/**
* Returns a default WhoAmI response
*/

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

@ -3,8 +3,8 @@ 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';
import { CommunicationManagerService, OfflineError } from './communication-manager.service';
export interface SlideData<T = { error?: string }, P extends ProjectorElement = ProjectorElement> {
data: T;
@ -15,13 +15,13 @@ export interface SlideData<T = { error?: string }, P extends ProjectorElement =
export type ProjectorData = SlideData[];
interface AllProjectorData {
[id: number]: ProjectorData | { error: string };
[id: number]: ProjectorData;
}
/**
* Received data from server.
*/
interface ProjectorWebsocketMessage {
interface ProjectorDataMessage {
/**
* The `change_id` of the current update.
*/
@ -63,38 +63,63 @@ export class ProjectorDataService {
*/
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;
});
private streamCloseFn: () => void | null = null;
// The service need to re-register, if the websocket connection was lost.
this.websocketService.generalConnectEvent.subscribe(() => this.updateProjectorDataSubscription());
public constructor(private communicationManager: CommunicationManagerService) {
this.communicationManager.startCommunicationEvent.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 });
this.requestProjectors(allActiveProjectorIds);
});
}
public async requestProjectors(allActiveProjectorIds: number[]): Promise<void> {
this.cancelCurrentServerSubscription();
if (allActiveProjectorIds.length === 0) {
return;
}
try {
this.streamCloseFn = await this.communicationManager.subscribe<ProjectorDataMessage>(
'/system/projector',
message => {
this.handleMesage(message);
},
() => ({ projector_ids: allActiveProjectorIds.join(',') })
);
} catch (e) {
if (!(e instanceof OfflineError)) {
console.error(e);
}
}
}
public cancelCurrentServerSubscription(): void {
if (this.streamCloseFn) {
this.streamCloseFn();
this.streamCloseFn = null;
}
}
private handleMesage(message: ProjectorDataMessage): void {
if (this.currentChangeId > message.change_id) {
console.log('Projector: Change id too low:', this.currentChangeId, message.change_id);
return;
}
Object.keys(message.data).forEach(_id => {
const id = parseInt(_id, 10);
if (this.currentProjectorData[id]) {
this.currentProjectorData[id].next(message.data[id] as ProjectorData);
}
});
this.currentChangeId = message.change_id;
}
/**
* Gets an observable for the projector data.
*

View File

@ -25,6 +25,11 @@ import { HttpService } from './http.service';
import { ProjectorDataService } from './projector-data.service';
import { ViewModelStoreService } from './view-model-store.service';
export interface ProjectorTitle {
title: string;
subtitle?: string;
}
/**
* This service cares about Projectables being projected and manage all projection-related
* actions.
@ -35,12 +40,6 @@ import { ViewModelStoreService } from './view-model-store.service';
providedIn: 'root'
})
export class ProjectorService {
/**
* Constructor.
*
* @param DS
* @param dataSend
*/
public constructor(
private DS: DataStoreService,
private http: HttpService,
@ -77,9 +76,12 @@ export class ProjectorService {
*/
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);
});
if (element?.getIdentifiers) {
return this.DS.getAll<Projector>('core/projector').some(projector => {
return projector.isElementShown(element);
});
}
return false;
}
/**
@ -250,7 +252,7 @@ export class ProjectorService {
projectorData.forEach(entry => {
if (entry.data.error && entry.element.stable) {
// Remove this element
const idElementToRemove = this.slideManager.getIdentifialbeProjectorElement(entry.element);
const idElementToRemove = this.slideManager.getIdentifiableProjectorElement(entry.element);
elements = elements.filter(element => {
return !elementIdentifies(idElementToRemove, element);
});
@ -312,7 +314,7 @@ export class ProjectorService {
* @param element The projector element
* @returns the view model from the projector element
*/
public getViewModelFromProjectorElement<T extends BaseProjectableViewModel>(
public getViewModelFromIdentifiableProjectorElement<T extends BaseProjectableViewModel>(
element: IdentifiableProjectorElement
): T {
this.assertElementIsMappable(element);
@ -323,12 +325,16 @@ export class ProjectorService {
return viewModel;
}
public getViewModelFromProjectorElement<T extends BaseProjectableViewModel>(element: ProjectorElement): T {
const idElement = this.slideManager.getIdentifiableProjectorElement(element);
return this.getViewModelFromIdentifiableProjectorElement(idElement);
}
/**
*/
public getSlideTitle(element: ProjectorElement): string {
public getSlideTitle(element: ProjectorElement): ProjectorTitle {
if (this.slideManager.canSlideBeMappedToModel(element.name)) {
const idElement = this.slideManager.getIdentifialbeProjectorElement(element);
const viewModel = this.getViewModelFromProjectorElement(idElement);
const viewModel = this.getViewModelFromProjectorElement(element);
if (viewModel) {
return viewModel.getProjectorTitle();
}
@ -338,7 +344,7 @@ export class ProjectorService {
return configuration.getSlideTitle(element, this.translate, this.viewModelStore);
}
return this.translate.instant(this.slideManager.getSlideVerboseName(element.name));
return { title: this.translate.instant(this.slideManager.getSlideVerboseName(element.name)) };
}
/**

View File

@ -98,6 +98,17 @@ export class RelationManagerService {
viewModel: BaseViewModel,
relation: RelationDefinition
): any {
// No cache for reverse relations.
// The issue: we cannot invalidate the cache, if a new object is created (The
// following example is for a O2M foreign relation):
// There is no possibility to detect the create case: The target does not update,
// all related models does not update. The autoupdate does not provide the created-
// information. So we may check, if the relaten has changed in length every time. But
// this is the same as just resolving the relation every time it is requested. So no cache here.
if (isReverseRelationDefinition(relation)) {
return this.handleRelation(model, viewModel, relation) as BaseViewModel | BaseViewModel[];
}
let result: any;
const cacheProperty = '__' + property;
@ -187,9 +198,20 @@ export class RelationManagerService {
const _model: M = target.getModel();
const relation = typeof property === 'string' ? relationsByKey[property] : null;
// try to find a getter for property
if (property in target) {
const descriptor = Object.getOwnPropertyDescriptor(viewModelCtor.prototype, property);
// iterate over prototype chain
let prototypeFunc = viewModelCtor,
descriptor = null;
do {
descriptor = Object.getOwnPropertyDescriptor(prototypeFunc.prototype, property);
if (!descriptor || !descriptor.get) {
prototypeFunc = Object.getPrototypeOf(prototypeFunc);
}
} while (!(descriptor && descriptor.get) && prototypeFunc && prototypeFunc.prototype);
if (descriptor && descriptor.get) {
// if getter was found in prototype chain, bind it with this proxy for right `this` access
result = descriptor.get.bind(viewModel)();
} else {
result = target[property];

View File

@ -0,0 +1,286 @@
import {
HttpClient,
HttpDownloadProgressEvent,
HttpEvent,
HttpHeaderResponse,
HttpHeaders,
HttpParams
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { HTTPMethod } from '../definitions/http-methods';
const HEADER_EVENT_TYPE = 2;
const PROGRESS_EVENT_TYPE = 3;
const FINISH_EVENT_TYPE = 4;
export type Params = HttpParams | { [param: string]: string | string[] };
export enum ErrorType {
Client,
Server, // or network errors, they are the same.
Unknown
}
export function verboseErrorType(type: ErrorType): string {
switch (type) {
case ErrorType.Client:
return 'Client';
case ErrorType.Server:
return 'Server';
case ErrorType.Unknown:
return 'Unknown';
}
}
export interface CommunicationError {
type: string;
msg: string;
}
export function isCommunicationError(obj: any): obj is CommunicationError {
const _obj = obj as CommunicationError;
return typeof obj === 'object' && typeof _obj.msg === 'string' && typeof _obj.type === 'string';
}
interface CommunicationErrorWrapper {
error: CommunicationError;
}
export function isCommunicationErrorWrapper(obj: any): obj is CommunicationErrorWrapper {
return typeof obj === 'object' && isCommunicationError(obj.error);
}
export type ErrorHandler = (type: ErrorType, error: CommunicationError, message: string) => void;
export class StreamConnectionError extends Error {
public constructor(public code: number, message: string) {
super(message);
this.name = 'StreamConnectionError';
}
}
export class StreamContainer<T> {
public readonly id = Math.floor(Math.random() * (900000 - 1) + 100000); // [100000, 999999]
public messageHandler: (message: T) => void;
public hasErroredAmount = 0;
public stream?: Stream<T>;
public constructor(public url: string, messageHandler: (message: T) => void, public params: () => Params) {
this.messageHandler = (message: T) => {
// {connected: true} is a special message just to trigger the code below
if ((<any>message).connected) {
console.log(`resetting error amount for ${this.url} since there was a connect message`);
this.hasErroredAmount = 0;
} else {
messageHandler(message);
}
};
}
}
export class Stream<T> {
private subscription: Subscription = null;
private hasError = false;
private reportedError = false;
private _statuscode: number;
public get statuscode(): number {
return this._statuscode;
}
private _errorContent: CommunicationError;
public get errorContent(): CommunicationError {
return this._errorContent;
}
/**
* This is the index where we checked, if there is a \n in the read buffer (event.partialText)
* This position is always >= contentStartIndex and is > contentStartIndex, if the message
* was too big to fit into one buffer. So we have just a partial message.
*
* The difference between this index and contentStartIndex is that this index remembers the position
* we checked for a \n which lay in the middle of the next JOSN-packet.
*/
private checkedUntilIndex = 0;
/**
* This index holds always the position of the current JOSN-packet, that we are receiving.
*/
private contentStartIndex = 0;
private closed = false;
public constructor(
observable: Observable<HttpEvent<string>>,
private messageHandler: (message: T) => void,
private errorHandler: ErrorHandler
) {
this.subscription = observable.subscribe(
(event: HttpEvent<string>) => {
if (this.closed) {
return;
}
if (event.type === HEADER_EVENT_TYPE) {
const headerResponse = event as HttpHeaderResponse;
this._statuscode = headerResponse.status;
if (headerResponse.status >= 400) {
this.hasError = true;
}
} else if ((<HttpEvent<string>>event).type === PROGRESS_EVENT_TYPE) {
this.handleMessage(event as HttpDownloadProgressEvent);
} else if ((<HttpEvent<string>>event).type === FINISH_EVENT_TYPE) {
this.errorHandler(ErrorType.Server, null, 'The stream was closed');
}
},
error => {
this.errorHandler(ErrorType.Server, error, 'Network error');
},
() => {
console.log('The stream was completed');
}
);
}
private handleMessage(event: HttpDownloadProgressEvent): void {
if (this.hasError) {
if (!this.reportedError) {
this.reportedError = true;
// try to get the `error` key from object
this._errorContent = this.tryParseError(event.partialText);
this.errorHandler(this.getErrorTypeFromStatusCode(), this._errorContent, 'Reported error by server');
}
return;
}
// Maybe we get multiple messages, so continue, until the complete buffer is checked.
while (this.checkedUntilIndex < event.loaded) {
// check if there is a \n somewhere in [checkedUntilIndex, ...]
const LF_index = event.partialText.indexOf('\n', this.checkedUntilIndex);
if (LF_index >= 0) {
// take string in [contentStartIndex, LF_index-1]. This must be valid JSON.
// In substring, the last character is exlusive.
const content = event.partialText.substring(this.contentStartIndex, LF_index);
// move pointer: next JSON starts at LF_index + 1
this.checkedUntilIndex = LF_index + 1;
this.contentStartIndex = LF_index + 1;
const parsedContent = this.tryParseJson(content);
if (isCommunicationError(parsedContent)) {
if (this.hasError && this.reportedError) {
return;
}
this.hasError = true;
this._errorContent = parsedContent;
// Do not trigger the error handler, if the connection-retry-routine is still handling this issue
if (!this.reportedError) {
this.reportedError = true;
console.error(this._errorContent);
this.errorHandler(
this.getErrorTypeFromStatusCode(),
this._errorContent,
'Reported error by server'
);
}
return;
} else {
console.debug('received', parsedContent);
this.messageHandler(parsedContent);
}
} else {
this.checkedUntilIndex = event.loaded;
}
}
}
private getErrorTypeFromStatusCode(): ErrorType {
if (!this.statuscode) {
return ErrorType.Unknown;
}
if (this.statuscode >= 400 && this.statuscode < 500) {
return ErrorType.Client;
}
if (this.statuscode >= 500) {
return ErrorType.Server;
}
return ErrorType.Unknown;
}
private tryParseJson(json: string): T | CommunicationError {
try {
return JSON.parse(json) as T;
} catch (e) {
return this.tryParseError(json);
}
}
/**
* This one is a bit tricky. Error can be:
* - string with HTML, e.g. provided by proxies if the service is unreachable
* - string with json of form {"error": {"type": ..., "msg": ...}}
*/
private tryParseError(error: any): CommunicationError {
if (typeof error === 'string') {
try {
error = JSON.parse(error);
} catch (e) {
return { type: 'Unknown Error', msg: error };
}
}
if (isCommunicationErrorWrapper(error)) {
return error.error;
} else if (isCommunicationError(error)) {
return error;
}
// we have something else.... ??
console.error('Unknown error', error);
throw new Error('Unknown error: ' + error.toString());
}
public close(): void {
this.subscription.unsubscribe();
this.subscription = null;
this.closed = true;
}
}
@Injectable({
providedIn: 'root'
})
export class StreamingCommunicationService {
public constructor(private http: HttpClient) {}
public subscribe<T>(streamContainer: StreamContainer<T>, errorHandler: ErrorHandler): void {
const options: {
body?: any;
headers?: HttpHeaders | { [header: string]: string | string[] };
observe: 'events';
params?: HttpParams | { [param: string]: string | string[] };
reportProgress?: boolean;
responseType: 'text';
withCredentials?: boolean;
} = {
headers: {
'Content-Type': 'application/json',
'ngsw-bypass': 'yes'
},
responseType: 'text',
observe: 'events',
reportProgress: true
};
const params = streamContainer.params();
if (params) {
options.params = params;
}
const observable = this.http.request(HTTPMethod.GET, streamContainer.url, options);
if (streamContainer.stream) {
console.error('Illegal state!');
}
const stream = new Stream<T>(observable, streamContainer.messageHandler, errorHandler);
streamContainer.stream = stream;
}
}

View File

@ -13,7 +13,7 @@ describe('TimeTravelService', () => {
);
it('should be created', () => {
const service: TimeTravelService = TestBed.get(TimeTravelService);
const service: TimeTravelService = TestBed.inject(TimeTravelService);
expect(service).toBeTruthy();
});
});

View File

@ -9,7 +9,6 @@ import { DataStoreService, DataStoreUpdateManagerService } from './data-store.se
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[];
@ -31,7 +30,6 @@ 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
@ -39,7 +37,6 @@ export class TimeTravelService {
*/
public constructor(
private httpService: HttpService,
private webSocketService: WebsocketService,
private modelMapperService: CollectionStringMapperService,
private DS: DataStoreService,
private OSStatus: OpenSlidesStatusService,
@ -100,7 +97,8 @@ export class TimeTravelService {
* Clears the DataStore and stops the WebSocket connection
*/
private async stopTime(history: History): Promise<void> {
await this.webSocketService.close();
// await this.webSocketService.close();
// TODO
await this.DS.set(); // Same as clear, but not persistent.
this.OSStatus.enterHistoryMode(history);
}

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

@ -2,11 +2,8 @@ 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];
@ -15,8 +12,7 @@ export const ServicesToLoadOnAppsLoaded: Type<OnAfterAppsLoaded>[] = [OperatorSe
*/
@NgModule({
imports: [CommonModule],
providers: [Title],
entryComponents: [PromptDialogComponent, ChoiceDialogComponent, ProjectionDialogComponent]
providers: [Title]
})
export class CoreModule {
/** make sure CoreModule is imported only by one NgModule, the AppModule */

View File

@ -7,7 +7,6 @@ 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>;
}

View File

@ -0,0 +1,43 @@
export 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;
}
export function isAutoupdateFormat(obj: any): obj is AutoupdateFormat {
const format = obj as AutoupdateFormat;
return (
obj &&
typeof obj === 'object' &&
format.changed !== undefined &&
format.deleted !== undefined &&
format.from_change_id !== undefined &&
format.to_change_id !== undefined &&
format.all_data !== undefined
);
}

View File

@ -0,0 +1,13 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
/**
* Define custom error classes here
*/
export class PreventedInDemo extends Error {
public constructor(message: string = _('Cannot do that in demo mode!'), name: string = 'Error') {
super(message);
this.name = name;
Object.setPrototypeOf(this, PreventedInDemo.prototype);
}
}

View File

@ -0,0 +1,5 @@
import { Observable } from 'rxjs';
export interface HasViewModelListObservable<V> {
getViewModelListObservable(): Observable<V[]>;
}

View File

@ -0,0 +1,10 @@
/**
* Enum for different HTTPMethods
*/
export enum HTTPMethod {
GET = 'get',
POST = 'post',
PUT = 'put',
PATCH = 'patch',
DELETE = 'delete'
}

View File

@ -32,11 +32,6 @@ export class HtmlToPdfService {
*/
private lineNumberingMode: LineNumberingMode;
/**
* Space between list elements
*/
private LI_MARGIN_BOTTOM = 8;
/**
* Normal line height for paragraphs
*/
@ -176,6 +171,9 @@ export class HtmlToPdfService {
docDef.push(parsedElement);
}
// DEBUG: printing the following. Do not remove, just comment out
// console.log('MakePDF doc :\n---\n', JSON.stringify(docDef), '\n---\n');
return docDef;
}
@ -183,12 +181,13 @@ export class HtmlToPdfService {
* Converts a single HTML element to pdfmake, calls itself recursively for child html elements
*
* @param element can be an HTML element (<p>) or plain text ("Hello World")
* @param currentParagraph usually holds the parent element, to allow nested structures
* @param styles holds the style attributes of HTML elements (`<div style="color: green">...`)
* @returns the doc def to the given element in consideration to the given paragraph and styles
*/
public parseElement(element: Element, styles?: string[]): any {
const nodeName = element.nodeName.toLowerCase();
const childNodes = Array.from(element.childNodes) as Element[];
const directChildIsCrNode = childNodes.some(child => this.isCrElement(child));
let classes = [];
let newParagraph: any;
@ -234,7 +233,11 @@ export class HtmlToPdfService {
case 'div': {
const children = this.parseChildren(element, styles);
if (this.lineNumberingMode === LineNumberingMode.Outside && !classes.includes('insert')) {
if (
this.lineNumberingMode === LineNumberingMode.Outside &&
!classes.includes('insert') &&
nodeName !== `li`
) {
newParagraph = this.create('stack');
newParagraph.stack = children;
} else {
@ -273,6 +276,26 @@ export class HtmlToPdfService {
...this.computeStyle(styles),
...this.computeStyle(this.elementStyles[nodeName])
};
// if the ol list has specific list type
if (nodeName === 'li' && element.parentNode.nodeName === 'OL') {
const type = element.parentElement.getAttribute('type');
switch (type) {
case 'a':
newParagraph.listType = 'lower-alpha';
break;
case 'A':
newParagraph.listType = 'upper-alpha';
break;
case 'i':
newParagraph.listType = 'lower-roman';
break;
case 'I':
newParagraph.listType = 'upper-roman';
break;
default:
break;
}
}
break;
}
case 'a':
@ -319,18 +342,15 @@ export class HtmlToPdfService {
break;
}
case 'br': {
if (
(this.lineNumberingMode === LineNumberingMode.None && classes.includes('os-line-break')) ||
(this.lineNumberingMode === LineNumberingMode.Outside && this.isInsideAList(element))
) {
if (this.lineNumberingMode === LineNumberingMode.None && classes.includes('os-line-break')) {
break;
} else {
newParagraph = this.create('text');
// yep thats all
newParagraph.text = '\n';
newParagraph.lineHeight = this.LINE_HEIGHT;
break;
}
break;
}
case 'ul':
case 'ol': {
@ -364,7 +384,7 @@ export class HtmlToPdfService {
// if this is a "fake list" lower put it close to the element above
if (this.isFakeList(element)) {
listCol.margin[3] = -this.LI_MARGIN_BOTTOM;
listCol.margin[3] = -this.P_MARGIN_BOTTOM;
}
for (const line of lines) {
@ -458,7 +478,6 @@ export class HtmlToPdfService {
// Add a blank with the normal font size here, so in rare cases the text
// is rendered on the next page and the line number on the previous page.
text: ' ',
fontSize: 10,
decoration: ''
},
{
@ -499,7 +518,7 @@ export class HtmlToPdfService {
children[i].remove();
}
if (children[i].childNodes.length > 0) {
if (children[i]?.childNodes.length > 0) {
const cleanChildren = this.cleanLineNumbers(children[i] as Element);
elementCopy.replaceChild(cleanChildren, children[i]);
}
@ -540,10 +559,8 @@ export class HtmlToPdfService {
}
}
// If this is an list item, add some space to the lineNumbers:
if (childrenLineNumbers.length && element.nodeName === 'LI') {
childrenLineNumbers[childrenLineNumbers.length - 1].marginBottom = this.LI_MARGIN_BOTTOM;
} else if (childrenLineNumbers.length && element.parentNode.nodeName === 'LI') {
// if the found element is a list item, add some spacing
if (childrenLineNumbers.length && (element.nodeName === 'LI' || element.parentNode.nodeName === 'LI')) {
childrenLineNumbers[childrenLineNumbers.length - 1].marginBottom = this.P_MARGIN_BOTTOM;
}
@ -618,10 +635,7 @@ export class HtmlToPdfService {
const styleObject: any = {};
if (styles && styles.length > 0) {
for (const style of styles) {
const styleDefinition = style
.trim()
.toLowerCase()
.split(':');
const styleDefinition = style.trim().toLowerCase().split(':');
const key = styleDefinition[0];
const value = styleDefinition[1];
@ -692,6 +706,15 @@ export class HtmlToPdfService {
return styleObject;
}
/**
* Detect if the given element is a change recommendation exclusive node
*/
private isCrElement(element: Element): boolean {
const nodeName = element.nodeName.toLowerCase();
const crNodeNames = ['ins', 'del'];
return crNodeNames.includes(nodeName);
}
/**
* Returns the color in a hex format (e.g. #12ff00).
* Also tries to convert RGB colors into hex values

View File

@ -1,6 +1,5 @@
import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { MatSnackBar } from '@angular/material/snack-bar';
import { TranslateService } from '@ngx-translate/core';
import { saveAs } from 'file-saver';
@ -47,8 +46,8 @@ export class PdfError extends Error {
* Provides the general document structure for PDF documents, such as page margins, header, footer and styles.
* Also provides general purpose open and download functions.
*
* Use a local pdf service (i.e. MotionPdfService) to get the document definition for the content and use this service to
* open or download the pdf document
* Use a local pdf service (i.e. MotionPdfService) to get the document definition for the content and
* use this service to open or download the pdf document
*
* @example
* ```ts
@ -100,7 +99,7 @@ export class PdfDocumentService {
);
const promises = fontPathList.map(fontPath => {
return this.convertUrlToBase64(fontPath).then(base64 => {
return this.httpService.downloadAsBase64(fontPath).then(base64 => {
return {
[fontPath.split('/').pop()]: base64
};
@ -117,29 +116,6 @@ export class PdfDocumentService {
return vfs;
}
/**
* Retrieves a binary file from the url and returns a base64 value
*
* @param url file url
* @returns a promise with a base64 string
*/
private async convertUrlToBase64(url: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
const headers = new HttpHeaders();
this.httpService.get<Blob>(url, {}, {}, headers, 'blob').then(file => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
const resultStr: string = reader.result as string;
resolve(resultStr.split(',')[1]);
};
reader.onerror = error => {
reject(error);
};
});
});
}
/**
* Returns the name of a font from the value of the given
* config variable.
@ -256,14 +232,11 @@ export class PdfDocumentService {
if (logoHeaderLeftUrl && logoHeaderRightUrl) {
text = '';
} else {
const general_event_name = this.configService.instant<string>('general_event_name');
const general_event_description = this.configService.instant<string>('general_event_description');
const line1 = [
this.translate.instant(general_event_name),
this.translate.instant(general_event_description)
]
.filter(Boolean)
.join(' ');
const general_event_name = this.translate.instant(this.configService.instant<string>('general_event_name'));
const general_event_description = this.translate.instant(
this.configService.instant<string>('general_event_description')
);
const line1 = [general_event_name, general_event_description].filter(Boolean).join(' - ');
const line2 = [
this.configService.instant('general_event_location'),
this.configService.instant('general_event_date')
@ -469,7 +442,7 @@ export class PdfDocumentService {
const isIE = /msie\s|trident\//i.test(window.navigator.userAgent);
if (typeof Worker !== 'undefined' && !isIE) {
this.pdfWorker = new Worker('./pdf-worker.worker', {
this.pdfWorker = new Worker(new URL('./pdf-worker.worker', import.meta.url), {
type: 'module'
});
@ -668,7 +641,7 @@ export class PdfDocumentService {
}
if (!vfs[url]) {
const base64 = await this.convertUrlToBase64(url);
const base64 = await this.httpService.downloadAsBase64(url);
vfs[url] = base64;
}
}
@ -712,6 +685,13 @@ export class PdfDocumentService {
};
}
public getSpacer(): Object {
return {
text: '',
margin: [0, 10]
};
}
/**
* Generates the table definition for the TOC
*

View File

@ -77,10 +77,14 @@ function addPageNumbers(data: any): void {
data.doc.footer = (currentPage, pageCount) => {
const footer = data.doc.tmpfooter;
// if the tmpfooter starts with an image, the pagenumber will be found in column 1
const pageNumberColIndex = !!footer.columns[0].image ? 1 : 0;
// "%PAGENR% needs to be found once. After that, the same position should always update page numbers"
if (footer.columns[0].stack[0] === '%PAGENR%' || countPageNumbers) {
if (footer.columns[pageNumberColIndex]?.stack[0] === '%PAGENR%' || countPageNumbers) {
countPageNumbers = true;
footer.columns[0].stack[0] = `${currentPage} / ${pageCount}`;
footer.columns[pageNumberColIndex].stack[0] = `${currentPage} / ${pageCount}`;
}
return footer;
};

View File

@ -14,16 +14,16 @@
* ```
*/
export class Deferred<T = void> extends Promise<T> {
/**
* The promise to wait for
*/
public readonly promise: Promise<T>;
/**
* custom resolve function
*/
private _resolve: (val?: T) => void;
private _wasResolved;
public get wasResolved(): boolean {
return this._wasResolved;
}
/**
* Creates the promise and overloads the resolve function
*/
@ -32,6 +32,7 @@ export class Deferred<T = void> extends Promise<T> {
super(resolve => {
preResolve = resolve;
});
this._wasResolved = false;
this._resolve = preResolve;
}
@ -40,5 +41,6 @@ export class Deferred<T = void> extends Promise<T> {
*/
public resolve(val?: T): void {
this._resolve(val);
this._wasResolved = true;
}
}

View File

@ -0,0 +1,30 @@
/**
* A mutex as described in every textbook
*
* Usage:
* ```
* mutex = new Mutex(); // create e.g. as class member
*
* // Somewhere in the code to lock (must be async code!)
* const unlock = await this.mutex.lock()
* // ...the code to synchronize
* unlock()
* ```
*/
export class Mutex {
private mutex = Promise.resolve();
public lock(): PromiseLike<() => void> {
// this will capture the code-to-synchronize
let begin: (unlock: () => void) => void = () => {};
// All "requests" to execute code are chained in a promise-chain
this.mutex = this.mutex.then(() => {
return new Promise(begin);
});
return new Promise(res => {
begin = res;
});
}
}

View File

@ -0,0 +1,10 @@
/**
* Wraps a promise and let it reject after the given timeout (in ms), if it was
* not resolved before this timeout.
*
* @param delay The time to sleep in miliseconds
* @returns a new Promise
*/
export function SleepPromise(delay: number): Promise<void> {
return new Promise((resolve, _) => setTimeout(resolve, delay));
}

View File

@ -14,11 +14,13 @@ import { Identifiable } from 'app/shared/models/base/identifiable';
import { ItemTitleInformation, ViewItem } from 'app/site/agenda/models/view-item';
import { ViewAssignment } from 'app/site/assignments/models/view-assignment';
import {
AgendaListTitle,
BaseViewModelWithAgendaItem,
isBaseViewModelWithAgendaItem
} from 'app/site/base/base-view-model-with-agenda-item';
import { ViewMotion } from 'app/site/motions/models/view-motion';
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
import { ViewTag } from 'app/site/tags/models/view-tag';
import { ViewTopic } from 'app/site/topics/models/view-topic';
import { BaseHasContentObjectRepository } from '../base-has-content-object-repository';
import { BaseIsAgendaItemContentObjectRepository } from '../base-is-agenda-item-content-object-repository';
@ -33,6 +35,12 @@ const ItemRelations: RelationDefinition[] = [
VForeignVerbose: 'BaseViewModelWithAgendaItem',
ownContentObjectDataKey: 'contentObjectData',
ownKey: 'contentObject'
},
{
type: 'M2M',
ownIdKey: 'tags_id',
ownKey: 'tags',
foreignViewModel: ViewTag
}
];
@ -79,7 +87,7 @@ export class ItemRepositoryService extends BaseHasContentObjectRepository<
return this.translate.instant(plural ? 'Items' : 'Item');
};
public getTitle = (titleInformation: ItemTitleInformation) => {
private getAgendaTitle(titleInformation: ItemTitleInformation): AgendaListTitle {
if (titleInformation.contentObject) {
return titleInformation.contentObject.getAgendaListTitle();
} else {
@ -88,36 +96,14 @@ export class ItemRepositoryService extends BaseHasContentObjectRepository<
) as BaseIsAgendaItemContentObjectRepository<any, any, any>;
return repo.getAgendaListTitle(titleInformation.title_information);
}
}
public getTitle = (titleInformation: ItemTitleInformation) => {
return this.getAgendaTitle(titleInformation).title;
};
/**
* Overrides the base function, if implemented.
*
* @returns An optional subtitle as `string`. Defaults to `null`.
*/
public getSubtitle = (viewItem: ViewItem) => {
if (viewItem.contentObject) {
return viewItem.contentObject.getAgendaSubtitle();
} else {
// The subtitle is not present in the title_information yet.
return null;
}
};
/**
* Overrides the base function.
*
* @returns The title without any prefix like item number.
*/
public getTitleWithoutItemNumber = (titleInformation: ItemTitleInformation) => {
if (titleInformation.contentObject) {
return titleInformation.contentObject.getAgendaListTitleWithoutItemNumber();
} else {
const repo = this.collectionStringMapperService.getRepository(
titleInformation.contentObjectData.collection
) as BaseIsAgendaItemContentObjectRepository<any, any, any>;
return repo.getAgendaListTitleWithoutItemNumber(titleInformation.title_information);
}
public getSubtitle = (titleInformation: ItemTitleInformation) => {
return this.getAgendaTitle(titleInformation).subtitle;
};
/**

View File

@ -62,6 +62,17 @@ const ListOfSpeakersNestedModelDescriptors: NestedModelDescriptors = {
]
};
/**
* An object, that contains information about structure-level,
* speaking-time and finished-speakers.
* Helpful to get a relation between speakers and their structure-level.
*/
export interface SpeakingTimeStructureLevelObject {
structureLevel: string;
finishedSpeakers: ViewSpeaker[];
speakingTime: number;
}
/**
* Repository service for lists of speakers
*
@ -132,6 +143,14 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
}
};
public async delete(viewModel: ViewListOfSpeakers): Promise<void> {
throw new Error('Not supported');
}
public async create(model: ListOfSpeakers): Promise<Identifiable> {
throw new Error('Not supported');
}
/**
* Add a new speaker to a list of speakers.
* Sends the users id to the server
@ -139,9 +158,14 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
* @param userId {@link User} id of the new speaker
* @param listOfSpeakers the target agenda item
*/
public async createSpeaker(listOfSpeakers: ViewListOfSpeakers, userId: number): Promise<Identifiable> {
public async createSpeaker(
listOfSpeakers: ViewListOfSpeakers,
userId: number,
pointOfOrder?: boolean,
note?: string
): Promise<Identifiable> {
const restUrl = this.getRestUrl(listOfSpeakers.id, 'manage_speaker');
return await this.httpService.post<Identifiable>(restUrl, { user: userId });
return await this.httpService.post<Identifiable>(restUrl, { user: userId, point_of_order: pointOfOrder, note });
}
/**
@ -151,9 +175,20 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
* @param speakerId (otional) the speakers id. If no id is given, the speaker with the
* current operator is removed.
*/
public async delete(listOfSpeakers: ViewListOfSpeakers, speakerId?: number): Promise<void> {
public async deleteSpeaker(
listOfSpeakers: ViewListOfSpeakers,
speakerId?: number,
pointOfOrder?: boolean
): Promise<void> {
const restUrl = this.getRestUrl(listOfSpeakers.id, 'manage_speaker');
await this.httpService.delete(restUrl, speakerId ? { speaker: speakerId } : null);
const payload: { speaker?: number; point_of_order?: boolean } = {};
if (speakerId) {
payload.speaker = speakerId;
}
if (pointOfOrder) {
payload.point_of_order = pointOfOrder;
}
await this.httpService.delete(restUrl, payload);
}
/**
@ -169,8 +204,8 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
/**
* Posts an (manually) sorted speaker list to the server
*
* @param listOfSpeakers the target list of speakers, which speaker-list is changed.
* @param speakerIds array of speaker id numbers
* @param Item the target agenda item
*/
public async sortSpeakers(listOfSpeakers: ViewListOfSpeakers, speakerIds: number[]): Promise<void> {
const restUrl = this.getRestUrl(listOfSpeakers.id, 'sort_speakers');
@ -188,15 +223,14 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
}
/**
* Marks all speakers for a given user
*
* @param userId {@link User} id of the user
* @param marked determine if the user should be marked or not
* @param listOfSpeakers the target list of speakers
* Toggles the mark for a given speaker.
*/
public async markSpeaker(listOfSpeakers: ViewListOfSpeakers, speaker: ViewSpeaker, marked: boolean): Promise<void> {
const restUrl = this.getRestUrl(listOfSpeakers.id, 'manage_speaker');
await this.httpService.patch(restUrl, { user: speaker.user.id, marked: marked });
public async toggleMarked(speaker: ViewSpeaker): Promise<void> {
await this.httpService.put(`/rest/agenda/speaker/${speaker.id}/`, { marked: !speaker.marked });
}
public async setProContraSpeech(speaker: ViewSpeaker, proSpeech: boolean | null): Promise<void> {
await this.httpService.put(`/rest/agenda/speaker/${speaker.id}/`, { pro_speech: proSpeech });
}
/**
@ -220,6 +254,115 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
await this.httpService.put(restUrl, { speaker: speaker.id });
}
public async deleteAllSpeakersOfAllListsOfSpeakers(): Promise<void> {
await this.httpService.post('/rest/agenda/list-of-speakers/delete_all_speakers/');
}
public isFirstContribution(speaker: ViewSpeaker): boolean {
return !this.getViewModelList().some(list => list.hasSpeakerSpoken(speaker));
}
public async setListOpenness(listOfSpeakers: ViewListOfSpeakers, open: boolean): Promise<void> {
await this.update({ closed: !open }, listOfSpeakers);
}
/**
* List every speaker only once, who has spoken
*
* @returns A list with all different speakers.
*/
public getAllFirstContributions(): ViewSpeaker[] {
const speakers: ViewSpeaker[] = this.getViewModelList().flatMap(
(los: ViewListOfSpeakers) => los.finishedSpeakers
);
const firstContributions: ViewSpeaker[] = [];
for (const speaker of speakers) {
if (!firstContributions.find(s => s.user_id === speaker.user_id)) {
firstContributions.push(speaker);
}
}
return firstContributions;
}
/**
* @returns the amount of point of orders (sync)
*/
public getPooAmount(): number {
const speakers: ViewSpeaker[] = this.getViewModelList()
.flatMap((los: ViewListOfSpeakers) => los.finishedSpeakers)
.filter(speaker => speaker.point_of_order);
return speakers.length || 0;
}
/**
* Maps structure-level to speaker.
*
* @returns A list, which entries are `SpeakingTimeStructureLevelObject`.
*/
public getSpeakingTimeStructureLevelRelation(): SpeakingTimeStructureLevelObject[] {
let listSpeakingTimeStructureLevel: SpeakingTimeStructureLevelObject[] = [];
for (const los of this.getViewModelList()) {
for (const speaker of los.finishedSpeakers) {
const nextEntry = this.getSpeakingTimeStructureLevelObject(speaker);
listSpeakingTimeStructureLevel = this.getSpeakingTimeStructureLevelList(
nextEntry,
listSpeakingTimeStructureLevel
);
}
}
return listSpeakingTimeStructureLevel;
}
/**
* Helper-function to create a `SpeakingTimeStructureLevelObject` by a given speaker.
*
* @param speaker, with whom structure-level and speaking-time is calculated.
*
* @returns The created `SpeakingTimeStructureLevelObject`.
*/
private getSpeakingTimeStructureLevelObject(speaker: ViewSpeaker): SpeakingTimeStructureLevelObject {
return {
structureLevel:
!speaker.user || (speaker.user && !speaker.user.structure_level) ? '' : speaker.user.structure_level,
finishedSpeakers: [speaker],
speakingTime: this.getSpeakingTimeAsNumber(speaker)
};
}
/**
* Helper-function to update entries in a given list, if already existing, or create entries otherwise.
*
* @param object A `SpeakingTimeStructureLevelObject`, that contains information about speaking-time
* and structure-level.
* @param list A list, at which speaking-time, structure-level and finished_speakers are set.
*
* @returns The updated map.
*/
private getSpeakingTimeStructureLevelList(
object: SpeakingTimeStructureLevelObject,
list: SpeakingTimeStructureLevelObject[]
): SpeakingTimeStructureLevelObject[] {
const index = list.findIndex(entry => entry.structureLevel === object.structureLevel);
if (index >= 0) {
list[index].speakingTime += object.speakingTime;
list[index].finishedSpeakers.push(...object.finishedSpeakers);
} else {
list.push(object);
}
return list;
}
/**
* This function calculates speaking-time as number for a given speaker.
*
* @param speaker The speaker, whose speaking-time should be calculated.
*
* @returns A number, that represents the speaking-time.
*/
private getSpeakingTimeAsNumber(speaker: ViewSpeaker): number {
return Math.floor((new Date(speaker.end_time).valueOf() - new Date(speaker.begin_time).valueOf()) / 1000);
}
/**
* Helper function get the url to the speaker rest address
*

View File

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

View File

@ -0,0 +1,75 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { AssignmentOption } from 'app/shared/models/assignments/assignment-option';
import { ViewAssignmentOption } from 'app/site/assignments/models/view-assignment-option';
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
import { ViewAssignmentVote } from 'app/site/assignments/models/view-assignment-vote';
import { ViewUser } from 'app/site/users/models/view-user';
import { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.service';
const AssignmentOptionRelations: RelationDefinition[] = [
{
type: 'O2M',
foreignIdKey: 'option_id',
ownKey: 'votes',
foreignViewModel: ViewAssignmentVote
},
{
type: 'M2O',
ownIdKey: 'poll_id',
ownKey: 'poll',
foreignViewModel: ViewAssignmentPoll
},
{
type: 'M2O',
ownIdKey: 'user_id',
ownKey: 'user',
foreignViewModel: ViewUser
}
];
/**
* Repository Service for Options.
*
* Documentation partially provided in {@link BaseRepository}
*/
@Injectable({
providedIn: 'root'
})
export class AssignmentOptionRepositoryService extends BaseRepository<ViewAssignmentOption, AssignmentOption, object> {
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService
) {
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
AssignmentOption,
AssignmentOptionRelations
);
}
public getTitle = (titleInformation: object) => {
return 'Option';
};
public getVerboseName = (plural: boolean = false) => {
return this.translate.instant(plural ? 'Options' : 'Option');
};
}

View File

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

View File

@ -0,0 +1,133 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { AssignmentPoll } from 'app/shared/models/assignments/assignment-poll';
import { UserVote } from 'app/shared/models/poll/base-vote';
import { ViewAssignment } from 'app/site/assignments/models/view-assignment';
import { ViewAssignmentOption } from 'app/site/assignments/models/view-assignment-option';
import { AssignmentPollTitleInformation, ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
import { BasePollRepositoryService } from 'app/site/polls/services/base-poll-repository.service';
import { ViewGroup } from 'app/site/users/models/view-group';
import { ViewUser } from 'app/site/users/models/view-user';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.service';
const AssignmentPollRelations: RelationDefinition[] = [
{
type: 'M2M',
ownIdKey: 'groups_id',
ownKey: 'groups',
foreignViewModel: ViewGroup
},
{
type: 'O2M',
ownIdKey: 'options_id',
ownKey: 'options',
foreignViewModel: ViewAssignmentOption,
order: 'weight'
},
{
type: 'M2O',
ownIdKey: 'assignment_id',
ownKey: 'assignment',
foreignViewModel: ViewAssignment
},
{
type: 'M2M',
ownIdKey: 'voted_id',
ownKey: 'voted',
foreignViewModel: ViewUser
}
];
export interface AssignmentAnalogVoteData {
options: {
[key: number]: {
Y: number;
N?: number;
A?: number;
};
};
votesvalid?: number;
votesinvalid?: number;
votescast?: number;
global_yes?: number;
global_no?: number;
global_abstain?: number;
}
export interface VotingData {
votes: Object;
global?: GlobalVote;
}
export type GlobalVote = 'A' | 'N';
/**
* Repository Service for Assignments.
*
* Documentation partially provided in {@link BaseRepository}
*/
@Injectable({
providedIn: 'root'
})
export class AssignmentPollRepositoryService extends BasePollRepositoryService<
ViewAssignmentPoll,
AssignmentPoll,
AssignmentPollTitleInformation
> {
/**
* Constructor for the Assignment Repository.
*
* @param DS DataStore access
* @param dataSend Sending data
* @param mapperService Map models to object
* @param viewModelStoreService Access view models
* @param translate Translate string
* @param httpService make HTTP Requests
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
http: HttpService
) {
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
AssignmentPoll,
AssignmentPollRelations,
{},
http
);
}
public getTitle = (titleInformation: AssignmentPollTitleInformation) => {
return titleInformation.title;
};
public getVerboseName = (plural: boolean = false) => {
return this.translate.instant(plural ? 'Polls' : 'Poll');
};
public vote(data: VotingData, poll_id: number, userId?: number): Promise<void> {
const requestData: UserVote = {
data: data.global ?? data.votes,
user_id: userId ?? undefined
};
return this.http.post(`/rest/assignments/assignment-poll/${poll_id}/vote/`, requestData);
}
}

View File

@ -8,7 +8,7 @@ describe('AssignmentRepositoryService', () => {
beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] }));
it('should be created', () => {
const service: AssignmentRepositoryService = TestBed.get(AssignmentRepositoryService);
const service: AssignmentRepositoryService = TestBed.inject(AssignmentRepositoryService);
expect(service).toBeTruthy();
});
});

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